welovecoding / swaxios Goto Github PK
View Code? Open in Web Editor NEWA Swagger API client generator based on axios and written in TypeScript. ๐
Home Page: https://www.npmjs.com/package/swaxios
A Swagger API client generator based on axios and written in TypeScript. ๐
Home Page: https://www.npmjs.com/package/swaxios
Swagger supports an operationId field which could be used as the generated methods name. It would be possible to control the name of the generated method.
Currently, the return values of the methods are inline interfaces. This way they cannot be used to type any attributes. I would like to use the types for typing a vuex store, but would have to recreate them for this use case. It would be great if one could extract the types and reuse them in other parts of the application.
Input
"/api/v1/account/{id}": {
"delete": {
"consumes": [
"application/json"
],
"parameters": [
{
"in": "path",
"name": "id",
"required": true,
"type": "number"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "",
"schema": {
"type": "number"
}
}
},
"tags": [
"account"
]
},
Output (buggy)
async deleteById(id: number): Promise<void> {
const resource = `/api/v1/account/${id}`;
await this.apiClient.delete(resource);
}
Currently, the names of the services are generated by the path of the request. This could split up services which might belong together. For example, on the server side i have a service for notifications, there are /api/notifications
get and put routes, so i will get a notificationService. But i also have a url like this /api/notifications/stream
which is used for SSE. This route will create a new folder notifications
next to the NotificationService.ts
file which contains a StreamService.ts
for this subroute. For me, they both belong together on the backend.
I think it would be great, if i could use tags for the service naming. I would then add the notifications
tag for all these routes and it will create a notificationsService containing all routes which use the notifications
tag. This way i would have more control over the generated api.
I know this is tricky and that the tags field in swagger is an array, this is why i would make it an option where the default is the path version of the service names.
This:
async getAll(params?: {
base: string;
counter: string;
id: number;
description: string;
})
Should become:
async getAll(params?: {
base?: string;
counter?: string;
id?: number;
description?: string;
})
Swagger Sample:
{
"/api/v1/candle-import": {
"get": {
"consumes": [
"application/json"
],
"parameters": [
{
"in": "query",
"name": "base",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "counter",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "id",
"required": false,
"type": "number"
},
{
"in": "query",
"name": "description",
"required": false,
"type": "string"
}
],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "",
"schema": {
"items": {
"$ref": "#/definitions/CreateCandleImportResponse"
},
"type": "array"
}
}
},
"tags": [
"candle-import"
]
}
}
}
The repository information is currently missing in the package.json
. Therefore npmjs.com is not linking to github. One needs to search swaxios on github to find this repository
Integrate class properties by default.
Change:
async getAll(): Promise<Array<string>> {
const resource = '/api/v1/strategy';
const response = await this.apiClient.get<Array<string>>(resource);
return response.data;
}
Into:
getAll = async (): Promise<Array<string>> => {
const resource = '/api/v1/strategy';
const response = await this.apiClient.get<Array<string>>(resource);
return response.data;
}
Benefit:
It will allow us to pass functions like getAll
around as props
in the context of React.
Example:
<StringArraySelector
getAll={new APIClient('').rest.api.v1.strategyService.getAll}
label={'Strategy'}
onSuccess={() => {}}
/>
Here are some large scale OAS specs to battletest Swaxios:
Official docs:
https://swagger.io/docs/specification/describing-parameters/#common
My valid configuration:
paths:
/posts/{id}:
# This is considered as a request method then trows error
parameters:
- name: id
in: path
description: id
required: true
schema:
type: integer
format: int32
get:
# ...
Error on console:
TypeError: Cannot convert undefined or null to object
at Function.entries (<anonymous>)
at MethodGenerator.includesSuccessResponse (node_modules/swaxios/dist/generators/MethodGenerator.js:62:54)
at new MethodGenerator (node_modules/swaxios/dist/generators/MethodGenerator.js:49:18)
at new ResourceGenerator (node_modules/swaxios/dist/generators/ResourceGenerator.js:40:42)
at node_modules/swaxios/dist/Swaxios.js:45:34
at Generator.next (<anonymous>)
at node_modules/swaxios/dist/Swaxios.js:8:71
at new Promise (<anonymous>)
at __awaiter (node_modules/swaxios/dist/Swaxios.js:4:12)
at exportServices (node_modules/swaxios/dist/Swaxios.js:34:12)
// new MethodGenerator (node_modules/swaxios/dist/generators/MethodGenerator.js:49:18)
const postFix = parameterMatch ? `By${StringUtil.camelCase(parameterMatch.splice(1), true)}` : 'All';
this.parameterMethod = this.operation.operationId || `${this.method}${postFix}`;
if (this.includesSuccessResponse(this.responses)) {
// where
// - postFix: ById
// - parameterMethod: propertiesById
// - this.responses: undefined
// MethodGenerator.includesSuccessResponse (node_modules/swaxios/dist/generators/MethodGenerator.js:62:54
includesSuccessResponse(responses) {
for (const [successCode, response] of Object.entries(responses)) { // <- response is undefined here
So, common parameters should be added to parameters as well, and if common parameters are defined it should be skipped when trying to process methods (operations).
Bearer Authentication can be enabled in Swagger when applying a security
property.
swagger.json
{
"/identity-providers/{id}": {
"delete": {
"consumes": ...,
"parameters": ...,
"produces": ...,
"responses": ...,
"security": [
{
"bearer": []
}
]
}
}
}
Until we figure out how to internally store the access token (this.accessToken
), we can add a parameter to functions which map authenticated endpoints, so that users can supply a callback which is responsible for returning the access token.
Suggestion
async deleteById(id: string, accessTokenCallback: () => Promise<string>): Promise<void> {
const accessToken = await accessTokenCallback();
const config: AxiosRequestConfig = {
headers: {
Authorization: `Bearer ${decodeURIComponent(accessToken)}`
},
method: 'delete',
url: `/identity-providers/${id}`,
withCredentials: true,
};
await this.apiClient.request(config);
}
It would be cool if we could use a URL for --input
.
Example
swaxios -i http://myservice.com/api/swagger.json -o ./src/api-client
ts-morph
can create TypeScript with TypeScript. We should use it instead of using a template engine.
Bearer Authentication for the whole API can be enabled in Swagger when applying a security
property.
swagger.json
{
"paths": {
// ...
},
"security": [
{
"Bearer": []
}
],
"swagger": "2.0",
}
Let's implement a general authorization setting (as suggested in #62 (comment)).
Suggestion
async deleteById(id: string): Promise<void> {
const config: AxiosRequestConfig = {
headers: {
Authorization: this.config.accessToken,
},
method: 'delete',
url: `/identity-providers/${id}`,
withCredentials: true,
};
await this.apiClient.request(config);
}
From the specification there can be a consumes
property. We should read it and add it to the "headers" section within the AxiosRequestConfig
object.
Example:
"/api/v1/exchange/{id}": {
"delete": {
"consumes": [
"application/json"
],
// ...
}
->
const config: AxiosRequestConfig = {
headers: { 'content-type': 'application/json' },
// ...
};
Since it's possible to add the same operation ID in two paths we should check that we don't create the same function twice in a class.
I found this StackOverflow question which wants to create an interface for Color
based on the following JSON input:
{
// ...
"definitions": {
"Color": {
"description": "",
"enum": [
0,
1,
2
],
"type": "integer",
"x-enumNames": [
"RED",
"GREEN",
"BLUE"
]
}
}
}
The expected result is:
export enum Color {
RED = 0,
GREEN = 1,
BLUE = 2,
}
But swaxios 0.1.2 generates:
/* tslint:disable */
/**
* This file was automatically generated by "Swaxios".
* It should not be modified by hand.
*/
export type Color = number;
Complete input file:
{
"consumes": [
"application/json"
],
"definitions": {
"Bar": {
"additionalProperties": false,
"properties": {
"A": {
"type": "string"
},
"B": {
"format": "int32",
"type": "integer"
},
"Baz": {
"$ref": "#/definitions/Baz"
},
"C": {
"format": "date-time",
"type": "string"
}
},
"required": [
"B",
"C"
],
"type": "object"
},
"Baz": {
"additionalProperties": false,
"properties": {
"Color": {
"$ref": "#/definitions/Color"
},
"D": {
"format": "decimal",
"type": "number"
}
},
"required": [
"D",
"Color"
],
"type": "object"
},
"Color": {
"description": "",
"enum": [
0,
1,
2
],
"type": "integer",
"x-enumNames": [
"RED",
"GREEN",
"BLUE"
]
}
},
"info": {
"title": "",
"version": ""
},
"parameters": {},
"paths": {
"/api/Foo/GetBar": {
"get": {
"operationId": "Foo_GetBar",
"parameters": [
{
"format": "int32",
"in": "query",
"name": "id",
"required": true,
"type": "integer",
"x-nullable": false
}
],
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/Bar"
},
"x-nullable": true
}
},
"tags": [
"Foo"
]
}
},
"/api/Foo/GetBarDescriptions": {
"get": {
"operationId": "Foo_GetBarDescriptions",
"parameters": [],
"responses": {
"200": {
"description": "",
"schema": {
"items": {
"type": "string"
},
"type": "array"
},
"x-nullable": true
}
},
"tags": [
"Foo"
]
}
},
"/api/Foo/SetBar": {
"post": {
"operationId": "Foo_SetBar",
"parameters": [
{
"in": "body",
"name": "value",
"required": true,
"schema": {
"$ref": "#/definitions/Bar"
},
"x-nullable": true
}
],
"responses": {
"204": {
"description": ""
}
},
"tags": [
"Foo"
]
}
}
},
"produces": [
"application/json"
],
"responses": {},
"schemes": [],
"securityDefinitions": {},
"swagger": "2.0",
"x-generator": "NSwag v11.14.0.0 (NJsonSchema v9.10.24.0 (Newtonsoft.Json v9.0.0.0))"
}
The x-enumNames
property is a custom property (indicated by the leading x-
) but it seems to be very popular because it has been asked for in many other projects before:
Doc on enum flag:
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.