ferdikoomen / openapi-typescript-codegen Goto Github PK
View Code? Open in Web Editor NEWNodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification
License: MIT License
NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification
License: MIT License
When I generate using these options:
exportCore: false, exportModels: true, exportSchemas: true, exportServices: false,
Then the barrel file has the following problems:
Right now we don't support anyOf in the response (i think):
"responses": {
"200": {
"description": "The request was succesfull",
"schema": {
"anyOf": [
{ "$ref": "#/definitions/Dog" },
{ "$ref": "#/definitions/Cat" },
{ "$ref": "#/definitions/Animal" }
]
}
}
Here's the problem I've met.Because JS/TS hasn't named parameter.The normal solution is
interface Names {
name1: boolean
name2: boolean
name3: boolean
name4: boolean
}
function myFunction({name1, name2, name3, name4}: Names) {
// name1, etc. are boolean
}
And currrently the generated code is like
function myFunction(name1:boolean, name2:boolean, name3:boolean, name4:boolean) {
// name1, etc. are boolean
}
It's frustrating that if I want to pass just one parameter which is in the rear,I have to write a bunch of undefined before it.
So if we use the former way,the problom will be solved.And we have named parameter passing(by the object literal).And the codegen don't need to sort them by required,maybe could in alphabetical order?Or we just put optional parameters into a object.
Anyway the former way seems more idiomatic,so I hope you can change to it.Honestly your work is very great,the official ts-codegen sucks ๐.Thanks for your wonderful work.
This line making prettier to ignore only next statement, not the whole file (reference)
/* prettier-ignore */
And there is no actual single-line solution to ignore the whole file
There's basically tow options.The first is use another field likeenumNames:string[]
in OpenApiSchema
to generate enums with custom names.
Schema like this:
"state": {
"title": "State",
"enum": [
0,
1,
2
],
"type": "integer",
"default": 0,
"enumNames": ["initial", "working", "finished"]
}
Generated code:
enum state {
INITIAL = 0,
WORKING = 1,
FINISHED = 2
}
But enumNames is not a valid openapi schema field.
Another option is expanding the getEnumFromDescription
.
We want openapi.generate() to be called with JSON instead:
const OpenAPI = require('openapi-typescript-codegen');
const json = {
...
};
OpenAPI.generate(json, ./dist');
I'm getting multiple linting errors from the script created by running
npx openapi-typescript-codegen -i http://localhost:8080/openapi.json -o ./src/api
The first error I get is
TypeScript error in ./src/api/core/ApiError.ts(4,13):
'=' expected. TS1005
2 | /* tslint:disable */
3 | /* eslint-disable */
> 4 | import type { ApiResult } from './ApiResult';
| ^
5 |
6 | export class ApiError extends Error {
7 | public readonly url: string;
I'm also getting Parsing error: Declaration or statement expected
in VSCode when looking in ./src/api/index.ts
on the export type
statements. I'm clueless whats actually the issue here?
{
"description": "This is the parameter that goes into the request header",
"name": "parameterHeader",
"in": "header",
"required": true,
"nullable": true,
"schema": {
"type": "string"
}
}
The required
and nullable
are true.But the isRequired
and isNullable
will be false after executing the code above.Which is wrong in my opinion.
I think the code should be like:
operationParameter.isRequired = operationParameter.isRequired || model.default || model.isRequired;
Thank you for a great bit of work!
In the case where the property of a model is defined directly:
"properties": {
"active": {
"description": "Active",
"type": "boolean"
},
}
The emitted code for the model includes the description as a comment:
/**
* Active
*/
active?: boolean;
However, when the property is defined via $ref, no comment is emitted:
"properties": {
"updated": {
"$ref": "#/components/schemas/dateTime"
},
where dateTime is defined:
"components": {
"schemas": {
"dateTime": {
"description": "Accept RFC3339, RFC3339_EXTENDED or timestamp - Defaults to RFC3339_EXTENDED",
"type": "string",
"format": "date-time"
},
}
The emitted code is:
updated?: dateTime;
I think I would expect the description from the references schema to appear in the output.
Reference to code
Problem is: some API returns HTTP 401 Unauthorized with empty response body and response.json()
throws an error JSON.parse('')
(instead of relying on status code)
One breaking change of OpenAPI 3.0 is that form and body parameters are both described using requestBody instead of using the old parameter with formdata as in value. See https://swagger.io/docs/specification/describing-request-body/ and https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#requestBodyObject
Unfortunately this doesn't work with this library since it still uses the old way 2.0 of looking for form data among the parameter list. See:
Instead, it should take form parameters from request body if it uses the content type multipart/form-data
or application/x-www-form-urlencoded
.
Here are two examples where the OpenAPI spec describes form data that does not work:
Binary form content under the "file" name:
"requestBody": {
"content": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"file": {
"type": "string",
"format": "binary",
"nullable": true
}
}
},
"encoding": {
"file": {
"style": "form"
}
}
}
}
}
(This is from my own swagger.json and the reason I write this ๐ )
A pet form with two string string values under the names "name" and "status":
"requestBody": {
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"type": "object",
"properties": {
"name": {
"description": "Updated name of the pet",
"type": "string"
},
"status": {
"description": "Updated status of the pet",
"type": "string"
}
},
"required": ["status"]
}
}
}
(Taken directly from the OpenAPI 3.0 spec file: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#requestBodyObject)
I think my conclusions above are correct but you have way better understanding of the OpenAPI spec and this library than I do so don't take it for a fact ๐
Nice gen. My requests are working great except those that have the body to be sent as text/plain.
request.body = JSON.stringify(options.body);
This line in request.ts adds extra quotes to the body string.
Suggestion, something like this (maybe, works for my case):
if (options.body instanceof Blob) {
request.body = options.body;
if (options.body.type) {
headers.append('Content-Type', options.body.type);
}
} else if (typeof options.body === 'string') {
request.body = options.body;
headers.append('Content-Type', 'text/plain');
} else {
request.body = JSON.stringify(options.body);
headers.append('Content-Type', 'application/json');
}
Expect:
"/api": {
"get": {
...
"parameters": [
{
"name": "ProcessFlag",
"in": "query",
"description": "Format - int32. Identifies added processing: 0 = no additional, 1 = run verification first",
"type": "integer",
"default": "0"
}
],
To be converted to:
public static async getApi(
processFlag: number = 0,
)
But in result:
public static async getApi(
processFlag: number = '0',
)
I think, JSON.parse(default)
can resolve this
Is it possible to add a way to add custom headers?
Basically a dictionary that custom headers can be added to (in the config file), and just traverse and add them in the request? It is not uncommon for applications to provide additional headers.
Im happy to provide a pull-request if that makes it easier for you:)
Given the yaml OpenAPI 3.0:
SomeAbstractDto:
properties:
name:
type: string
age:
type: number
SomeDto:
required:
- name
allOf:
- $ref: '#/components/schemas/SomeAbstractDto'
This generates:
interface SomeAbstractDto {
name?: string;
age?: number;
}
interface SomeDto extends SomeAbstractDto {}
Expected is:
interface SomeAbstractDto {
name?: string;
age?: number;
}
interface SomeDto extends SomeAbstractDto {
name: string;
}
I hope this can be resolved!
To allow us to use cookie based authentication and make the browser pass cookies to the server on each request we need to tell fetch to allow it. This is done by setting credentials to "same-origin" or "include" when doing requests. Right now this is hardcoded to "same-origin" which only works if the client and API has the same origin.
I would like to set it to "include" by passing some option so I can have the client and API on different origins and still use cookie authentication.
Is it possible to add custom headers?
Currently looking to add an Authorization
header: Basic XXXXXXXXXX
- can only see a way documented for adding Bearer
tokens ~ is there a way to add custom headers?
Improve maintainability score: https://codeclimate.com/github/ferdikoomen/openapi-typescript-codegen
I just tried the 0.5.0 beta and came across a generation bug where the File
model name is turned into any
. Typescript will complain about Interface name cannot be 'any'
Here's a minimum spec to reproduce the behavior:
swagger: "2.0"
info:
version: v1
definitions:
File:
type: object
How about to add settings param (e.g. console param --indent ) wich will setting up indents in generated code?
Not all users uses 4 spaces indents so will be very cool if we can to set indents.
P.S. sorry for my english ๐
openapi-typescript-codegen/src/openApi/v3/parser/getOperation.ts
Lines 55 to 60 in 836b5da
The requestBody
is always the last of parameters.If there's any optional parameter in front of it,there will be an TS error 1016: A required parameter cannot follow an optional parameter.
My simple solution is
operation.parameters.push(requestBody);
operation.parameters = operation.parameters.sort(sortByRequired);
First off, thanks so much for this library. This is really helpful.
I am currently using the OpenAPI.TOKEN
syntax to set my auth token. This works well, but it isn't perfect for my use case. The reason is that my token is accessed asynchronously.
Ideally, I could wrap the fetch
function used by this lib, such that it gets my token asynchronously before every call.
I'm using Firebase auth, which 1) refreshes the token often, and 2) has an asynchronous function to get the current token.
Currently, I do this:
onAuthStateChanged(async (user) => {
if (user) {
// the user is set as signed in here, so I update the app's auth state
setIsSignedIn(true) // <- this is pseudo-code. This is what Firebase does for me, for illustration.
// thus, they're now seeing screens that require an auth token
// it's possible to be stuck here, where the app state has updated to show authenticated screens
// but the token promise hasn't resolved yet
// next, I set the user's token for request
OpenAPI.TOKEN = await user.getIdToken()
} else {
OpenAPI.TOKEN = ''
}
})
The problem with this is that await user.getIdToken()
is async. Since I render authenticated screens right when user
exists, then it's possible that the user will see certain screens before I have set the OpenAPI.TOKEN
. This means requests that should have a token will be sent without one.
At the root of my app, it would nice to be able to do something like this:
import { OpenAPI } from 'openapi-typescript-codegen'
OpenAPI.onBeforeRequest = async () => {
OpenAPI.TOKEN = await firebase.auth().currentUser?.getIdToken()
}
This way, before any request is sent, I know with certainty that the latest auth token is injected in my requests.
Currently, I have to manually call this before every request:
import { SomeService } from '../generated'
export const callServer = async () => {
OpenAPI.TOKEN = await firebase.auth().currentUser?.getIdToken()
return SomeService.someCall()
}
Doing this manually isn't ideal, since I might forget some.
I'm definitely open to a better solution, and am happy to explain better if this is unclear. Thank you!
It would be very useful if you could specify an url as input when generating the client instead of just a local file. I always use an automatically generated OpenAPI spec together with Swagger UI and pointing it to the http served specification file is very natural.
Sure you can easily download it and generate a new client but I would prefer to just point it to the url of our test enviroment and fire away everytime we need to update the client.
Thanks for an awesome tool btw!
Describe the solution you'd like
Since nullable
isn't supported in OpenApi v2, some frameworks (e.g., drf-yasg) are using the x-nullable
vendor extension to indicate if e.g. property is nullable. I believe we should use this to mark a property as nullable:
The specification
{
"ModelWithNullableString": {
"required": ["requiredProp"],
"description": "This is a model with one string property",
"type": "object",
"properties": {
"prop": {
"description": "This is a simple string property",
"type": "string",
"x-nullable": true
},
"requiredProp": {
"description": "This is a simple string property",
"type": "string",
"x-nullable": true,
}
}
}
}
should yield
enum ModelWithNullableString {
prop?: string | null,
requiredProp: string | null
}
I want to use this from a node.js client, but there's no type declaration shipped with the package and no @types package either.
Describe the solution you'd like
A clear and concise description of what you want to happen. Ideally with a small example of the proposed changes.
ๅธๆๅขๅ ่พๅบ RequestOptions, ๆฏๅฆ๏ผ
export const AppRequestOptions = {
url: '/api/v1/getApps',
}
่ฟๆ ทๅฏไปฅไฝฟ็จ่ฟไธชๆฐๆฎ๏ผ่ชๅทฑ้ๆฉ ajax client ๅฐ่ฃ requestๅฎ็ฐ
Describe the bug
Such an enum creates broken typescript
"ResponseEntity": {
"type": "object",
"properties": {
"body": { "type": "object" },
"statusCode": {
"type": "string",
"enum": [
"100 CONTINUE",
"101 SWITCHING_PROTOCOLS",
"102 PROCESSING",
"103 CHECKPOINT",
"200 OK",
"201 CREATED",
"202 ACCEPTED",
"203 NON_AUTHORITATIVE_INFORMATION",
"204 NO_CONTENT",
"205 RESET_CONTENT",
"206 PARTIAL_CONTENT",
"207 MULTI_STATUS",
"208 ALREADY_REPORTED",
"226 IM_USED",
"300 MULTIPLE_CHOICES",
"301 MOVED_PERMANENTLY",
"302 FOUND",
"302 MOVED_TEMPORARILY",
"303 SEE_OTHER",
"304 NOT_MODIFIED",
"305 USE_PROXY",
"307 TEMPORARY_REDIRECT",
"308 PERMANENT_REDIRECT",
"400 BAD_REQUEST",
"401 UNAUTHORIZED",
"402 PAYMENT_REQUIRED",
"403 FORBIDDEN",
"404 NOT_FOUND",
"405 METHOD_NOT_ALLOWED",
"406 NOT_ACCEPTABLE",
"407 PROXY_AUTHENTICATION_REQUIRED",
"408 REQUEST_TIMEOUT",
"409 CONFLICT",
"410 GONE",
"411 LENGTH_REQUIRED",
"412 PRECONDITION_FAILED",
"413 PAYLOAD_TOO_LARGE",
"413 REQUEST_ENTITY_TOO_LARGE",
"414 URI_TOO_LONG",
"414 REQUEST_URI_TOO_LONG",
"415 UNSUPPORTED_MEDIA_TYPE",
"416 REQUESTED_RANGE_NOT_SATISFIABLE",
"417 EXPECTATION_FAILED",
"418 I_AM_A_TEAPOT",
"419 INSUFFICIENT_SPACE_ON_RESOURCE",
"420 METHOD_FAILURE",
"421 DESTINATION_LOCKED",
"422 UNPROCESSABLE_ENTITY",
"423 LOCKED",
"424 FAILED_DEPENDENCY",
"426 UPGRADE_REQUIRED",
"428 PRECONDITION_REQUIRED",
"429 TOO_MANY_REQUESTS",
"431 REQUEST_HEADER_FIELDS_TOO_LARGE",
"451 UNAVAILABLE_FOR_LEGAL_REASONS",
"500 INTERNAL_SERVER_ERROR",
"501 NOT_IMPLEMENTED",
"502 BAD_GATEWAY",
"503 SERVICE_UNAVAILABLE",
"504 GATEWAY_TIMEOUT",
"505 HTTP_VERSION_NOT_SUPPORTED",
"506 VARIANT_ALSO_NEGOTIATES",
"507 INSUFFICIENT_STORAGE",
"508 LOOP_DETECTED",
"509 BANDWIDTH_LIMIT_EXCEEDED",
"510 NOT_EXTENDED",
"511 NETWORK_AUTHENTICATION_REQUIRED"
]
},
"statusCodeValue": { "type": "integer", "format": "int32" }
},
"title": "ResponseEntity"
},
Results in this TypeScript
export enum statusCode {
100 CONTINUE = '100 CONTINUE',
101 SWITCHING_PROTOCOLS = '101 SWITCHING_PROTOCOLS',
102 PROCESSING = '102 PROCESSING',
103 CHECKPOINT = '103 CHECKPOINT',
200 OK = '200 OK',
201 CREATED = '201 CREATED',
202 ACCEPTED = '202 ACCEPTED',
203 NON_AUTHORITATIVE_INFORMATION = '203 NON_AUTHORITATIVE_INFORMATION',
204 NO_CONTENT = '204 NO_CONTENT',
205 RESET_CONTENT = '205 RESET_CONTENT',
206 PARTIAL_CONTENT = '206 PARTIAL_CONTENT',
207 MULTI_STATUS = '207 MULTI_STATUS',
208 ALREADY_REPORTED = '208 ALREADY_REPORTED',
226 IM_USED = '226 IM_USED',
300 MULTIPLE_CHOICES = '300 MULTIPLE_CHOICES',
301 MOVED_PERMANENTLY = '301 MOVED_PERMANENTLY',
302 FOUND = '302 FOUND',
302 MOVED_TEMPORARILY = '302 MOVED_TEMPORARILY',
303 SEE_OTHER = '303 SEE_OTHER',
304 NOT_MODIFIED = '304 NOT_MODIFIED',
305 USE_PROXY = '305 USE_PROXY',
307 TEMPORARY_REDIRECT = '307 TEMPORARY_REDIRECT',
308 PERMANENT_REDIRECT = '308 PERMANENT_REDIRECT',
400 BAD_REQUEST = '400 BAD_REQUEST',
401 UNAUTHORIZED = '401 UNAUTHORIZED',
402 PAYMENT_REQUIRED = '402 PAYMENT_REQUIRED',
403 FORBIDDEN = '403 FORBIDDEN',
404 NOT_FOUND = '404 NOT_FOUND',
405 METHOD_NOT_ALLOWED = '405 METHOD_NOT_ALLOWED',
406 NOT_ACCEPTABLE = '406 NOT_ACCEPTABLE',
407 PROXY_AUTHENTICATION_REQUIRED = '407 PROXY_AUTHENTICATION_REQUIRED',
408 REQUEST_TIMEOUT = '408 REQUEST_TIMEOUT',
409 CONFLICT = '409 CONFLICT',
410 GONE = '410 GONE',
411 LENGTH_REQUIRED = '411 LENGTH_REQUIRED',
412 PRECONDITION_FAILED = '412 PRECONDITION_FAILED',
413 PAYLOAD_TOO_LARGE = '413 PAYLOAD_TOO_LARGE',
413 REQUEST_ENTITY_TOO_LARGE = '413 REQUEST_ENTITY_TOO_LARGE',
414 URI_TOO_LONG = '414 URI_TOO_LONG',
414 REQUEST_URI_TOO_LONG = '414 REQUEST_URI_TOO_LONG',
415 UNSUPPORTED_MEDIA_TYPE = '415 UNSUPPORTED_MEDIA_TYPE',
416 REQUESTED_RANGE_NOT_SATISFIABLE = '416 REQUESTED_RANGE_NOT_SATISFIABLE',
417 EXPECTATION_FAILED = '417 EXPECTATION_FAILED',
418 I_AM_A_TEAPOT = '418 I_AM_A_TEAPOT',
419 INSUFFICIENT_SPACE_ON_RESOURCE = '419 INSUFFICIENT_SPACE_ON_RESOURCE',
420 METHOD_FAILURE = '420 METHOD_FAILURE',
421 DESTINATION_LOCKED = '421 DESTINATION_LOCKED',
422 UNPROCESSABLE_ENTITY = '422 UNPROCESSABLE_ENTITY',
423 LOCKED = '423 LOCKED',
424 FAILED_DEPENDENCY = '424 FAILED_DEPENDENCY',
426 UPGRADE_REQUIRED = '426 UPGRADE_REQUIRED',
428 PRECONDITION_REQUIRED = '428 PRECONDITION_REQUIRED',
429 TOO_MANY_REQUESTS = '429 TOO_MANY_REQUESTS',
431 REQUEST_HEADER_FIELDS_TOO_LARGE = '431 REQUEST_HEADER_FIELDS_TOO_LARGE',
451 UNAVAILABLE_FOR_LEGAL_REASONS = '451 UNAVAILABLE_FOR_LEGAL_REASONS',
500 INTERNAL_SERVER_ERROR = '500 INTERNAL_SERVER_ERROR',
501 NOT_IMPLEMENTED = '501 NOT_IMPLEMENTED',
502 BAD_GATEWAY = '502 BAD_GATEWAY',
503 SERVICE_UNAVAILABLE = '503 SERVICE_UNAVAILABLE',
504 GATEWAY_TIMEOUT = '504 GATEWAY_TIMEOUT',
505 HTTP_VERSION_NOT_SUPPORTED = '505 HTTP_VERSION_NOT_SUPPORTED',
506 VARIANT_ALSO_NEGOTIATES = '506 VARIANT_ALSO_NEGOTIATES',
507 INSUFFICIENT_STORAGE = '507 INSUFFICIENT_STORAGE',
508 LOOP_DETECTED = '508 LOOP_DETECTED',
509 BANDWIDTH_LIMIT_EXCEEDED = '509 BANDWIDTH_LIMIT_EXCEEDED',
510 NOT_EXTENDED = '510 NOT_EXTENDED',
511 NETWORK_AUTHENTICATION_REQUIRED = '511 NETWORK_AUTHENTICATION_REQUIRED',
}
Which is not valid
Describe the bug
For some reason the following interface is generated when the command is run against the spec
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
export interface string {
}
Openapi 3.0
First of all, nice generator. Models and ApiServices seem to work fine. But for the schemas, I do have some issues, and generating Schemas is something that is really beneficial, above others.
The array is defined like:
issues:
type: array
items:
$ref: '#/components/schemas/Issue'
Let's assume we use these schemas for frontend validations for input fields:
name
is fine, I see it's a type. Any props like isRequired, pattern, I can use.entity
is less fine, but I could just import * as Schemas from
index-of-schemaand do
Schemas[${type}
]`, and check that object.issues
is an issue :) . I see it's an array type, but this is where it stops.Suggestion:
import {$EntityDto} from './$EntityDto';
import {$Issue} from './$IssueDto';
export const $SomeDto = {
properties: {
name: {
type: 'string',
},
entity: {
type: $EntityDto,
},
issues: {
type: $Issue,
array: true,
},
},
};
This way code should be able to dynamically determine what to do for settings properties on input fields, how to validate them..
Describe the bug
Following method is generated as part of the Service.ts
for an endpoint defined like this: /manage/applications/{applicationId}/addons/{addonSystemKey}
. There are two path parameters. The snippet references variables but such variables are not present in the method signature.
public static async installAddonCommand(
requestBody: InstallAddonCommand,
): Promise<AddonInstanceValue> {
const result = await __request({
method: 'post',
path: `/manage/applications/${applicationId}/addons/${addonKey}`,
body: requestBody,
});
OpenAPI 3.0
Hence:
pattern
and required
Also, the schemas are not typed.
Adding turtle: true
to the openapi spec, is not valid, so the typing could be made, because it shouldn't contain random keys
We're having an issue with PATCH
requests in our application using the generated API functions. The request just fails with a net::ERR_HTTP2_PROTOCOL_ERROR
error message.
After digging around a bit I found out that methods are defined as all-lowercase (probably as they're coming from openapi spec?), which works for all other methods thanks to normalization, but patch
is exempt as per the fetch spec. See here: https://fetch.spec.whatwg.org/#concept-method-normalize
Update method definitions to be all-uppercase
I have a model named File
and another named FilePagingResponse
,here's the generated code:
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/* prettier-ignore */
/**
* Abstract base class for generic types.
* A generic type is typically declared by inheriting from
* this class parameterized with one or more type variables.
* For example, a generic mapping type might be defined as::
* class Mapping(Generic[KT, VT]):
* def __getitem__(self, key: KT) -> VT:
* ...
* # Etc.
* This class can then be used as follows::
* def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT:
* try:
* return mapping[key]
* except KeyError:
* return default
*/
export interface FilePagingResponse {
items: Array<File>
total: number
has_more?: boolean
}
So it's just using the builtin File
,which is not my File
model. And other code is OK.So, there must be something wrong in here.
Thank you team for the great work!
OpenAPI 3.0 supports reusable request bodies as shown in the below example. Right now the generator does not support this.
paths:
/pets:
post:
summary: Add a new pet
requestBody:
$ref: '#/components/requestBodies/PetBody'
/pets/{petId}
put:
summary: Update a pet
parameters: [ ... ]
requestBody:
$ref: '#/components/requestBodies/PetBody'
components:
requestBodies:
PetBody:
description: A JSON object containing pet information
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
https://swagger.io/docs/specification/describing-request-body/
(node:73849) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'replace' of null
at /Users/Alex/Development/projects/webdock-sdk/node_modules/openapi-typescript-codegen/dist/index.js:1:19871
at Array.map (<anonymous>)
Link to example yaml: http://apidocs.beta.webdock.io/api-docs/webdock-v10.yaml
Here is a hypothetical generated typescript generated code from your tool.
export interface Response {
name: string;
type: Response.type;
}
export namespace Response {
export enum type {
ERROR = 'ERROR',
SUCCESS = 'SUCCESS',
}
}
We realize this a known issue with Babel 7. However if the enum was generated this way then it works great with Babel 7.
export interface Response {
name: string;
type: ResponseType;
}
export enum ResponseType {
ERROR = 'ERROR',
SUCCESS = 'SUCCESS',
}
Is there a reason why you chose to generate the code using the former method. Is it possible to add a flag to generate enums in the proposed style. The difference is the enum would append Type to the name of the interface.
Thanks, Louis
Hi,
I'm consuming an API that has this path: /api/v1/management/organizations
This generates a OrganizationsService
, which is fine, but there is no mention of management, or even v1 anywhere.
If we assume that the service name should be whatever comes after /api
We could get V1ManagementOrganizationsService
It's horrible, but it works.
Another option is to generate those services in folders following the path, so:
/core
/models
/services
/v1
/management
OrganizationsService.ts
v1 and management don't have anything so we could create just a placeholder object to have the reference to the children:
export class V1{
public static Management: Management = Management
}
export class Management{
public static Organizations: OrganizationsService = OrganizationsService
}
//we have this already
export class OrganizationsService {
}
Following the previous snippet, I could now just V1.Management.Organizations.getAll()
I'm doing this currently by hand, and would be nice to automate it :)
Cheers
Instead of generating enum types I would like to have an option to generate union types instead.
If you like the idea I am very happy to implement this and create a PR.
YAML model:
[...]
schemas:
Rating:
type: object
properties:
id:
type: string
value:
type: string
enum:
- negative
- neutral
- positive
export interface Rating {
id?: string;
value?: Rating.value;
}
export namespace Rating {
export enum value {
NEGATIVE = 'negative',
NEUTRAL = 'neutral',
POSITIVE = 'positive',
}
}
If I want to use this in my code I have to do this:
const exampleRating: Rating = {
id: '364363313',
value: Rating.value.NEUTRAL,
}
export interface Rating {
id?: string;
value?: 'positive' | 'neutral' | 'negative'; // no enum anymore, instead we use union types
}
If I want to use this in my code I can do it much simpler now:
const exampleRating: Rating = {
id: '364363313',
value: 'neutral', // much simpler in my opinion, TS would still complain if I would use something that is not allowed
}
Of course this would be behind a feature flag. At the beginning I thought useUnionTypes
is doing that, but it is not, so I have to find another name for my feature. I thought about useUnionTypesForEnums
, but I'm open for better suggestions.
When running the code generation script against a C# oData API, the properties preceded with an "@" are not properly quoted, and cause compilation errors.
Please see this gist for an example of the output, and how I've manually fixed the issue, by quoting the properties beginning with an "@"
Screenshot of the error thrown when running tsc against the generated code:
A path takes 2 parameters a
and b
, b
is not required and has a default value.
Generated code by now:
function add({ a, b = 1 }: { a: number; b: number }): number {
return a + b
}
Which is making b
's default value useless because we must give it a value to pass the type check.
Correct code should be:
function add({ a, b = 1 }: { a: number; b?: number }): number {
return a + b
}
It has been tested, if call add
like add({a:1})
, b
will set as it's default value.
Describe the bug
The templates reference types that are not imported which results in typescript complaining when using the generated code.
{
"scripts": "ts-node src/client/http/index.ts"
}
You can see this in the code today: https://github.com/ferdikoomen/openapi-typescript-codegen/blob/master/src/templates/core/request.ts#L26 (this uses Headers
but does not import it).
What is expected
I figure that this error would happen to anyone using bare tsc
to compile, so I assume there must be something I'm missing. Perhaps a "User Guide" section in the README.md would suffice to explain the expectations of the compile-time environment. (I'm guessing that this would work just fine if I were using Babel 7 -- and potentially webpack or rollup).
If it is that the library doesn't support tsc
, then perhaps we can move this to a feature request to support a simple tsc
backed use-case.
Notes
I was able to reproduce a compiled version following your test/index.js
approach -- which successfully compiled the generated code in my repo: https://github.com/ferdikoomen/openapi-typescript-codegen/blob/master/test/index.js
I'm not clean on why using this particular configuration of tsc
would work while my version would not.
This still only gets one step further -- at runtime this still fails.
This time with an error when constructing the new Header
in request.js
-- which gets called when you do MyGeneratedService.staticMethodName(body)
:
ReferenceError: Headers is not defined
I have included my tsconfig.json
here
{
"compileOnSave": true,
"compilerOptions": {
"rootDir": ".",
"types": ["node"],
"strict": true,
"sourceMap": true,
"declaration": false,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "es6",
"module": "commonjs",
"typeRoots": ["node_modules/@types"],
"lib": ["es2020", "es6"],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"outDir": "dist",
"baseUrl": ".",
"noImplicitAny": false,
},
"exclude": ["node_modules", "tmp", "dist"],
"include": ["src/**/*.ts"],
}
Describe the solution you'd like
Currently, only XHR and Fetch are supported as clients. What would be required to add Axios as the request manager?
import { MessageDetailsDto } from '../models/MessageDetailsDto';
import { ApiError, catchGenericError } from '../core/ApiError';
import { request as __request } from '../core/request';
import { OpenAPI } from '../core/OpenAPI';
export class MessageService {
ApiError and OpenAPI is not being used in the generated service, and causes a TS6133 error when building it.
Describe the solution you'd like
I'd like to have an option to write enums to their own files (off by default to maintain backward compatibility).
With this option on, this currently generated code:
export interface SomeModel {
status: SomeModel.status;
}
export namespace SomeModel {
export enum status {
started = 'started',
finished = 'finished'
}
}
would instead generate this
// SomeModel.ts
export interface SomeModel {
status: Status;
}
// Status.ts
export enum Status {
started = 'started',
finished = 'finished'
}
If you would accept this, any guidance on how to go about it would be appreciated. I looked through the code and think it can be done with a new post-processor and template, but won't know for sure until I start.
If there's a url like /users/{user_id}/
,the parameter name in generated code will be userId
,but in path it still is user_id
.Which is a bug.
Here's my fix:
import camelCase from 'camelcase';
/**
* Get the final service path, this replaces the "{api-version}" placeholder
* with a new template string placeholder so we can dynamically inject the
* OpenAPI version without the need to hardcode this in the URL.
* @param path
*/
export function getOperationPath(path: string): string {
return path.replace(/{api-version}/g, '{OpenAPI.VERSION}').replace(/\{(.*?)\}/g, (_, w) => `\$\{${camelCase(w)}\}`);
}
In most cases the changes will be in models, schemas, services folders only.
But core functions may differ from project to project.
So, nice to have some flag which disables core folder generation.
Supports using custom requestUsingXX
function besides requestUsingXHR
and requestUsingFetch
.So users can using their custom network stack, for example there is a sort of plaform called 'mini program' in China, which is basically browers-based, but has it's own APIs eg. request
.
It's easy to implement:
import { Result } from './Result'
// ...
export let CLIENT:
| string
| ((url: string, request: Readonly<RequestInit>) => Promise<Result>)
= '{{{httpClient}}}'
And in here add more conditions
openapi-typescript-codegen/src/templates/core/request.ts
Lines 66 to 72 in 83483e6
The generated getResponseBody()
function does not account for OData metadata values set in the Content-Type header.
This is defined in the OData standard which is an ISO/IEC International Standard.
The example where I'm encountering this has the content-type followed by some additional information which should be ignored when determining the content-type.
The code currently does this:
const contentType = response.headers.get('Content-Type'); // 'application/json; odata.metadata=minimal; odata.streaming=true'
if (contentType) {
switch (contentType.toLowerCase()) {
case 'application/json':
case 'application/json; charset=utf-8':
return await response.json();
default:
return await response.text();
}
}
}
Whereas it might be better off doing something like this:
const contentType = response.headers.get('Content-Type'); // 'application/json; odata.metadata=minimal; odata.streaming=true'
const mediaType = contentType?.split(';')[0].toLowerCase() // 'application/json'
if (mediaType === 'application/json') {
return await response.json();
}
return await response.text();
}
Example in editor:
Experienced behavior
I generated types definitions with CLI openapi --input ./api_spec.yaml --output ./openapi
, but it fails to compile in unejected react app created with create-react-app CLI.
There are 2 problems:
Compilation error due to :
C:/xxx/openapi/index.ts
TypeScript error in C:/xxx/openapi/index.ts(10,10):
Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'. TS1205
Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'
8 | export { OpenAPI } from './core/OpenAPI';
9 |
> 10 | export { MyModel } from './models/MyModel';
After commenting out reexported modules I encountered second problem:
Failed to compile.
./openapi/core/ApiError.ts
SyntaxError: C:\xxx\openapi\core\ApiError.ts: Namespace not marked type-only declare. Non-declarative namespaces are only supported experimentally in Babel. To enable and review caveats see: https://babeljs.io/docs/en/babel-plugin-transform-typescript
24 | }
25 |
> 26 | export namespace ApiError {
| ^^^^^^^^
27 | export enum Message {
28 | BAD_REQUEST = 'Bad Request',
29 | UNAUTHORIZED = 'Unauthorized',
Expected behavior
Generated code compiles properly without warnings.
I'm tried to use openapi-typescript-codegen
from command line to generate only the models as I want to use a custom implementation for the requests themselves. So, I don't need core
or services
, just models
.
However, passing --exportCore false
or --exportCore 0
or similar doesn't seem to work. It will still generate all files.
Expected Behavior
The following command should only generate models
but no core
or services
code:
npx openapi-typescript-codegen \
--input https://example.com/swagger.json \
--exportCore false --exportServices false --exportModules true
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.