Skip to content

Commit

Permalink
[typescript] fix: nullable enums should not serialize a null valu…
Browse files Browse the repository at this point in the history
…e to a string (#19540)

* fix: `nullable` enums should not serialize a `null` value to a string

* better solution?

* Update modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java

Co-authored-by: Joscha Feth <[email protected]>

* chore: Update circle_parallel.sh

---------

Co-authored-by: William Cheng <[email protected]>
  • Loading branch information
joscha and wing328 authored Sep 24, 2024
1 parent e370b81 commit 17e0b7c
Show file tree
Hide file tree
Showing 31 changed files with 1,531 additions and 0 deletions.
1 change: 1 addition & 0 deletions CI/circle_parallel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ elif [ "$NODE_INDEX" = "3" ]; then
#(cd samples/openapi3/client/petstore/typescript/tests/deno && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/builds/browser && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/tests/browser && mvn integration-test)
(cd samples/openapi3/client/petstore/typescript/builds/nullable-enum && mvn integration-test)
(cd samples/client/petstore/typescript-fetch/builds/default && mvn integration-test)
(cd samples/client/petstore/typescript-fetch/builds/es6-target && mvn integration-test)
(cd samples/client/petstore/typescript-fetch/builds/with-npm-version && mvn integration-test)
Expand Down
4 changes: 4 additions & 0 deletions bin/configs/typescript-consolidated-nullable-enum.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
generatorName: typescript
outputDir: samples/openapi3/client/petstore/typescript/builds/nullable-enum
inputSpec: modules/openapi-generator/src/test/resources/3_0/typescript/19027-regression.yaml
templateDir: modules/openapi-generator/src/main/resources/typescript
Original file line number Diff line number Diff line change
Expand Up @@ -3949,6 +3949,12 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo
List<Object> _enum = p.getEnum();
property._enum = new ArrayList<>();
for (Object i : _enum) {
// raw null values in enums are unions for nullable
// atttributes, not actual enum values, so we remove them here
if (i == null) {
property.isNullable = true;
continue;
}
property._enum.add(String.valueOf(i));
}
property.isEnum = true;
Expand Down Expand Up @@ -6640,6 +6646,11 @@ protected List<Map<String, Object>> buildEnumVars(List<Object> values, String da
: 0;

for (Object value : values) {
if (value == null) {
// raw null values in enums are unions for nullable
// atttributes, not actual enum values, so we remove them here
continue;
}
Map<String, Object> enumVar = new HashMap<>();
String enumName = truncateIdx == 0
? String.valueOf(value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
openapi: 3.0.0
info:
version: 1.0.0
title: Sample for uniqueItems
servers:
- url: http://localhost:3000
paths:
/unique-items:
get:
operationId: unique_items
responses:
200:
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Response'
components:
schemas:
Response:
type: object
properties:
enrichmentSource:
description: The source of the data in this Field (if it is enriched)
enum:
- "affinity-data"
- "dealroom"
- null
example: "affinity-data"
type: "string"
nullable: true
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator

# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.

# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs

# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux

# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux

# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.gitignore
DefaultApi.md
README.md
apis/DefaultApi.ts
apis/baseapi.ts
apis/exception.ts
auth/auth.ts
configuration.ts
git_push.sh
http/http.ts
http/isomorphic-fetch.ts
index.ts
middleware.ts
models/ObjectSerializer.ts
models/Response.ts
models/all.ts
package.json
rxjsStub.ts
servers.ts
tsconfig.json
types/ObjectParamAPI.ts
types/ObservableAPI.ts
types/PromiseAPI.ts
util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.9.0-SNAPSHOT
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# .DefaultApi

All URIs are relative to *http://localhost:3000*

Method | HTTP request | Description
------------- | ------------- | -------------
[**uniqueItems**](DefaultApi.md#uniqueItems) | **GET** /unique-items |


# **uniqueItems**
> Response uniqueItems()

### Example


```typescript
import { } from '';
import * as fs from 'fs';

const configuration = .createConfiguration();
const apiInstance = new .DefaultApi(configuration);

let body:any = {};

apiInstance.uniqueItems(body).then((data:any) => {
console.log('API called successfully. Returned data: ' + data);
}).catch((error:any) => console.error(error));
```


### Parameters
This endpoint does not need any parameter.


### Return type

**Response**

### Authorization

No authorization required

### HTTP request headers

- **Content-Type**: Not defined
- **Accept**: application/json


### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | OK | - |

[[Back to top]](#) [[Back to API list]](README.md#documentation-for-api-endpoints) [[Back to Model list]](README.md#documentation-for-models) [[Back to README]](README.md)


Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
## @

This generator creates TypeScript/JavaScript client that utilizes fetch-api.

### Building

To build and compile the typescript sources to javascript use:
```
npm install
npm run build
```

### Publishing

First build the package then run ```npm publish```

### Consuming

Navigate to the folder of your consuming project and run one of the following commands.

_published:_

```
npm install @ --save
```

_unPublished (not recommended):_

```
npm install PATH_TO_GENERATED_PACKAGE --save
```

### Usage

Below code snippet shows exemplary usage of the configuration and the API based
on the typical `PetStore` example used for OpenAPI.

```
import * as your_api from 'your_api_package'
// Covers all auth methods included in your OpenAPI yaml definition
const authConfig: your_api.AuthMethodsConfiguration = {
"api_key": "YOUR_API_KEY"
}
// Implements a simple middleware to modify requests before (`pre`) they are sent
// and after (`post`) they have been received
class Test implements your_api.Middleware {
pre(context: your_api.RequestContext): Promise<your_api.RequestContext> {
// Modify context here and return
return Promise.resolve(context);
}
post(context: your_api.ResponseContext): Promise<your_api.ResponseContext> {
return Promise.resolve(context);
}
}
// Create configuration parameter object
const configurationParameters = {
httpApi: new your_api.JQueryHttpLibrary(), // Can also be ignored - default is usually fine
baseServer: your_api.servers[0], // First server is default
authMethods: authConfig, // No auth is default
promiseMiddleware: [new Test()],
}
// Convert to actual configuration
const config = your_api.createConfiguration(configurationParameters);
// Use configuration with your_api
const api = new your_api.PetApi(config);
your_api.Pet p = new your_api.Pet();
p.name = "My new pet";
p.photoUrls = [];
p.tags = [];
p.status = "available";
Promise<your_api.Pet> createdPet = api.addPet(p);
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// TODO: better import syntax?
import {BaseAPIRequestFactory, RequiredError, COLLECTION_FORMATS} from './baseapi';
import {Configuration} from '../configuration';
import {RequestContext, HttpMethod, ResponseContext, HttpFile, HttpInfo} from '../http/http';
import {ObjectSerializer} from '../models/ObjectSerializer';
import {ApiException} from './exception';
import {canConsumeForm, isCodeInRange} from '../util';
import {SecurityAuthentication} from '../auth/auth';


import { Response } from '../models/Response';

/**
* no description
*/
export class DefaultApiRequestFactory extends BaseAPIRequestFactory {

/**
*/
public async uniqueItems(_options?: Configuration): Promise<RequestContext> {
let _config = _options || this.configuration;

// Path Params
const localVarPath = '/unique-items';

// Make Request Context
const requestContext = _config.baseServer.makeRequestContext(localVarPath, HttpMethod.GET);
requestContext.setHeaderParam("Accept", "application/json, */*;q=0.8")



const defaultAuth: SecurityAuthentication | undefined = _options?.authMethods?.default || this.configuration?.authMethods?.default
if (defaultAuth?.applySecurityAuthentication) {
await defaultAuth?.applySecurityAuthentication(requestContext);
}

return requestContext;
}

}

export class DefaultApiResponseProcessor {

/**
* Unwraps the actual response sent by the server from the response context and deserializes the response content
* to the expected objects
*
* @params response Response returned by the server for a request to uniqueItems
* @throws ApiException if the response code was not in [200, 299]
*/
public async uniqueItemsWithHttpInfo(response: ResponseContext): Promise<HttpInfo<Response >> {
const contentType = ObjectSerializer.normalizeMediaType(response.headers["content-type"]);
if (isCodeInRange("200", response.httpStatusCode)) {
const body: Response = ObjectSerializer.deserialize(
ObjectSerializer.parse(await response.body.text(), contentType),
"Response", ""
) as Response;
return new HttpInfo(response.httpStatusCode, response.headers, response.body, body);
}

// Work around for missing responses in specification, e.g. for petstore.yaml
if (response.httpStatusCode >= 200 && response.httpStatusCode <= 299) {
const body: Response = ObjectSerializer.deserialize(
ObjectSerializer.parse(await response.body.text(), contentType),
"Response", ""
) as Response;
return new HttpInfo(response.httpStatusCode, response.headers, response.body, body);
}

throw new ApiException<string | Blob | undefined>(response.httpStatusCode, "Unknown API Status Code!", await response.getBodyAsAny(), response.headers);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Configuration } from '../configuration'

/**
*
* @export
*/
export const COLLECTION_FORMATS = {
csv: ",",
ssv: " ",
tsv: "\t",
pipes: "|",
};


/**
*
* @export
* @class BaseAPI
*/
export class BaseAPIRequestFactory {

constructor(protected configuration: Configuration) {
}
};

/**
*
* @export
* @class RequiredError
* @extends {Error}
*/
export class RequiredError extends Error {
name: "RequiredError" = "RequiredError";
constructor(public api: string, public method: string, public field: string) {
super("Required parameter " + field + " was null or undefined when calling " + api + "." + method + ".");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Represents an error caused by an api call i.e. it has attributes for a HTTP status code
* and the returned body object.
*
* Example
* API returns a ErrorMessageObject whenever HTTP status code is not in [200, 299]
* => ApiException(404, someErrorMessageObject)
*
*/
export class ApiException<T> extends Error {
public constructor(public code: number, message: string, public body: T, public headers: { [key: string]: string; }) {
super("HTTP-Code: " + code + "\nMessage: " + message + "\nBody: " + JSON.stringify(body) + "\nHeaders: " +
JSON.stringify(headers))
}
}
Loading

0 comments on commit 17e0b7c

Please sign in to comment.