mirror of
https://github.com/ferdikoomen/openapi-typescript-codegen.git
synced 2025-12-08 20:16:21 +00:00
- Updated PR with master branch
This commit is contained in:
commit
9424a7b240
@ -7,3 +7,6 @@ trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.hbs]
|
||||
indent_style = tab
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
dist
|
||||
samples
|
||||
test/generated
|
||||
test/e2e/generated
|
||||
node_modules
|
||||
|
||||
21
CHANGELOG.md
Normal file
21
CHANGELOG.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.16.0] - 2021-01-25
|
||||
### Added
|
||||
- Added option to set the indentation (spaces and tabs)
|
||||
- Added option to export separate client file that allows usage for multiple backends
|
||||
### Fixed
|
||||
- Decoupled OpenAPI object from requests
|
||||
- Updated dependencies
|
||||
|
||||
## [0.15.0] - 2021-01-24
|
||||
### Added
|
||||
- Added change log and releases on GitHub
|
||||
|
||||
## [0.14.0] - 2021-01-24
|
||||
### Fixed
|
||||
- Added missing `postfix` options to typedef
|
||||
- Updated escaping of comments and descriptions
|
||||
- Better handling of services without tags
|
||||
- Updated dependencies
|
||||
31
README.md
31
README.md
@ -41,13 +41,15 @@ $ openapi --help
|
||||
-i, --input <value> OpenAPI specification, can be a path, url or string content (required)
|
||||
-o, --output <value> Output directory (required)
|
||||
-c, --client <value> HTTP client to generate [fetch, xhr, axios, node] (default: "fetch")
|
||||
--name <value> Custom client class name
|
||||
--useOptions Use options instead of arguments
|
||||
--useUnionTypes Use union types instead of enums
|
||||
--exportCore <value> Write core files to disk (default: true)
|
||||
--exportServices <value> Write services to disk (default: true)
|
||||
--exportModels <value> Write models to disk (default: true)
|
||||
--exportSchemas <value> Write schemas to disk (default: false)
|
||||
--postfix <value> Service name postfix (default: "Service")
|
||||
--indent <value> Service name postfix (default: "Service")
|
||||
--postfix <value> Indentation options [4, 2, tab] (default: "5")
|
||||
--request <value> Path to custom request file
|
||||
-h, --help display help for command
|
||||
|
||||
@ -95,6 +97,33 @@ OpenAPI.generate({
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
### Generate client instance with `--name` option
|
||||
The OpenAPI generator allows creation of client instances to support the multiple backend services use case.
|
||||
The generated client uses an instance of the server configuration and not the global `OpenAPI` constant.
|
||||
To generate a client instance, set a custom name to the client class, use `--name` option.
|
||||
|
||||
```
|
||||
openapi --input ./spec.json --output ./dist ---name AppClient
|
||||
```
|
||||
|
||||
The generated client will be exported from the `index` file and can be used as shown below:
|
||||
|
||||
```typescript
|
||||
// Create the client instance with server and authentication details
|
||||
const appClient = new AppClient({
|
||||
BASE: 'http://server-host.com',
|
||||
TOKEN: '1234'
|
||||
});
|
||||
|
||||
// Use the client instance to make the API call
|
||||
const res = await appClient.organizations.createOrganization({
|
||||
name: 'OrgName',
|
||||
description: 'OrgDescription',
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### Argument style vs. Object style `--useOptions`
|
||||
There's no [named parameter](https://en.wikipedia.org/wiki/Named_parameter) in JavaScript or TypeScript, because of
|
||||
that, we offer the flag `--useOptions` to generate code in two different styles.
|
||||
|
||||
@ -13,12 +13,14 @@ const params = program
|
||||
.requiredOption('-i, --input <value>', 'OpenAPI specification, can be a path, url or string content (required)')
|
||||
.requiredOption('-o, --output <value>', 'Output directory (required)')
|
||||
.option('-c, --client <value>', 'HTTP client to generate [fetch, xhr, node, axios, angular]', 'fetch')
|
||||
.option('--name <value>', 'Custom client class name')
|
||||
.option('--useOptions', 'Use options instead of arguments')
|
||||
.option('--useUnionTypes', 'Use union types instead of enums')
|
||||
.option('--exportCore <value>', 'Write core files to disk', true)
|
||||
.option('--exportServices <value>', 'Write services to disk', true)
|
||||
.option('--exportModels <value>', 'Write models to disk', true)
|
||||
.option('--exportSchemas <value>', 'Write schemas to disk', false)
|
||||
.option('--indent <value>', 'Indentation options [4, 2, tabs]', '4')
|
||||
.option('--postfix <value>', 'Service name postfix', 'Service')
|
||||
.option('--request <value>', 'Path to custom request file')
|
||||
.parse(process.argv)
|
||||
@ -31,12 +33,14 @@ if (OpenAPI) {
|
||||
input: params.input,
|
||||
output: params.output,
|
||||
httpClient: params.client,
|
||||
clientName: params.name,
|
||||
useOptions: params.useOptions,
|
||||
useUnionTypes: params.useUnionTypes,
|
||||
exportCore: JSON.parse(params.exportCore) === true,
|
||||
exportServices: JSON.parse(params.exportServices) === true,
|
||||
exportModels: JSON.parse(params.exportModels) === true,
|
||||
exportSchemas: JSON.parse(params.exportSchemas) === true,
|
||||
indent: params.indent,
|
||||
postfix: params.postfix,
|
||||
request: params.request,
|
||||
})
|
||||
|
||||
@ -25,6 +25,11 @@ const config: Config.InitialOptions = {
|
||||
'<rootDir>/test/e2e/v3.node.spec.ts',
|
||||
'<rootDir>/test/e2e/v3.axios.spec.ts',
|
||||
'<rootDir>/test/e2e/v3.babel.spec.ts',
|
||||
'<rootDir>/test/e2e/client.fetch.spec.ts',
|
||||
'<rootDir>/test/e2e/client.xhr.spec.ts',
|
||||
'<rootDir>/test/e2e/client.node.spec.ts',
|
||||
'<rootDir>/test/e2e/client.axios.spec.ts',
|
||||
'<rootDir>/test/e2e/client.babel.spec.ts',
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openapi-typescript-codegen",
|
||||
"version": "0.13.0",
|
||||
"version": "0.16.0",
|
||||
"description": "Library that generates Typescript clients based on the OpenAPI specification.",
|
||||
"author": "Ferdi Koomen",
|
||||
"homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen",
|
||||
@ -77,7 +77,7 @@
|
||||
"@types/express": "4.17.13",
|
||||
"@types/glob": "7.2.0",
|
||||
"@types/jest": "27.4.0",
|
||||
"@types/node": "17.0.10",
|
||||
"@types/node": "17.0.12",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/qs": "6.9.7",
|
||||
"@typescript-eslint/eslint-plugin": "5.10.1",
|
||||
@ -96,10 +96,10 @@
|
||||
"jest-cli": "27.4.7",
|
||||
"node-fetch": "^2.6.6",
|
||||
"prettier": "2.5.1",
|
||||
"puppeteer": "13.1.1",
|
||||
"puppeteer": "13.1.2",
|
||||
"qs": "6.10.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "2.66.0",
|
||||
"rollup": "2.66.1",
|
||||
"rollup-plugin-node-externals": "3.1.2",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"rxjs": "7.5.2",
|
||||
|
||||
@ -36,6 +36,7 @@ const handlebarsPlugin = () => ({
|
||||
enumerator: true,
|
||||
escapeComment: true,
|
||||
escapeDescription: true,
|
||||
camelCase: true,
|
||||
},
|
||||
});
|
||||
return `export default ${templateSpec};`;
|
||||
|
||||
5
src/Indent.ts
Normal file
5
src/Indent.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export enum Indent {
|
||||
SPACE_4 = '4',
|
||||
SPACE_2 = '2',
|
||||
TAB = 'tab',
|
||||
}
|
||||
30
src/index.ts
30
src/index.ts
@ -1,4 +1,5 @@
|
||||
import { HttpClient } from './HttpClient';
|
||||
import { Indent } from './Indent';
|
||||
import { parse as parseV2 } from './openApi/v2';
|
||||
import { parse as parseV3 } from './openApi/v3';
|
||||
import { getOpenApiSpec } from './utils/getOpenApiSpec';
|
||||
@ -9,17 +10,20 @@ import { registerHandlebarTemplates } from './utils/registerHandlebarTemplates';
|
||||
import { writeClient } from './utils/writeClient';
|
||||
|
||||
export { HttpClient } from './HttpClient';
|
||||
export { Indent } from './Indent';
|
||||
|
||||
export type Options = {
|
||||
input: string | Record<string, any>;
|
||||
output: string;
|
||||
httpClient?: HttpClient;
|
||||
clientName?: string;
|
||||
useOptions?: boolean;
|
||||
useUnionTypes?: boolean;
|
||||
exportCore?: boolean;
|
||||
exportServices?: boolean;
|
||||
exportModels?: boolean;
|
||||
exportSchemas?: boolean;
|
||||
indent?: Indent;
|
||||
postfix?: string;
|
||||
request?: string;
|
||||
write?: boolean;
|
||||
@ -32,30 +36,34 @@ export type Options = {
|
||||
* @param input The relative location of the OpenAPI spec
|
||||
* @param output The relative location of the output directory
|
||||
* @param httpClient The selected httpClient (fetch, xhr, node or axios)
|
||||
* @param clientName Custom client class name
|
||||
* @param useOptions Use options or arguments functions
|
||||
* @param useUnionTypes Use union types instead of enums
|
||||
* @param exportCore: Generate core client classes
|
||||
* @param exportServices: Generate services
|
||||
* @param exportModels: Generate models
|
||||
* @param exportSchemas: Generate schemas
|
||||
* @param postfix: Service name postfix
|
||||
* @param request: Path to custom request file
|
||||
* @param exportCore Generate core client classes
|
||||
* @param exportServices Generate services
|
||||
* @param exportModels Generate models
|
||||
* @param exportSchemas Generate schemas
|
||||
* @param indent Indentation options (4, 2 or tab)
|
||||
* @param postfix Service name postfix
|
||||
* @param request Path to custom request file
|
||||
* @param write Write the files to disk (true or false)
|
||||
*/
|
||||
export async function generate({
|
||||
export const generate = async ({
|
||||
input,
|
||||
output,
|
||||
httpClient = HttpClient.FETCH,
|
||||
clientName,
|
||||
useOptions = false,
|
||||
useUnionTypes = false,
|
||||
exportCore = true,
|
||||
exportServices = true,
|
||||
exportModels = true,
|
||||
exportSchemas = false,
|
||||
indent = Indent.SPACE_4,
|
||||
postfix = 'Service',
|
||||
request,
|
||||
write = true,
|
||||
}: Options): Promise<void> {
|
||||
}: Options): Promise<void> => {
|
||||
const openApi = isString(input) ? await getOpenApiSpec(input) : input;
|
||||
const openApiVersion = getOpenApiVersion(openApi);
|
||||
const templates = registerHandlebarTemplates({
|
||||
@ -80,7 +88,9 @@ export async function generate({
|
||||
exportServices,
|
||||
exportModels,
|
||||
exportSchemas,
|
||||
indent,
|
||||
postfix,
|
||||
clientName,
|
||||
request
|
||||
);
|
||||
break;
|
||||
@ -101,13 +111,15 @@ export async function generate({
|
||||
exportServices,
|
||||
exportModels,
|
||||
exportSchemas,
|
||||
indent,
|
||||
postfix,
|
||||
clientName,
|
||||
request
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
HttpClient,
|
||||
|
||||
@ -10,11 +10,11 @@ import { getServiceVersion } from './parser/getServiceVersion';
|
||||
* all the models, services and schema's we should output.
|
||||
* @param openApi The OpenAPI spec that we have loaded from disk.
|
||||
*/
|
||||
export function parse(openApi: OpenApi): Client {
|
||||
export const parse = (openApi: OpenApi): Client => {
|
||||
const version = getServiceVersion(openApi.info.version);
|
||||
const server = getServer(openApi);
|
||||
const models = getModels(openApi);
|
||||
const services = getServices(openApi);
|
||||
|
||||
return { version, server, models, services };
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export function escapeName(value: string): string {
|
||||
export const escapeName = (value: string): string => {
|
||||
if (value) {
|
||||
const validName = /^[a-zA-Z_$][\w$]+$/g.test(value);
|
||||
if (!validName) {
|
||||
@ -6,4 +6,4 @@ export function escapeName(value: string): string {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,7 +7,7 @@ import type { WithEnumExtension } from '../interfaces/Extensions/WithEnumExtensi
|
||||
* @param enumerators
|
||||
* @param definition
|
||||
*/
|
||||
export function extendEnum(enumerators: Enum[], definition: WithEnumExtension): Enum[] {
|
||||
export const extendEnum = (enumerators: Enum[], definition: WithEnumExtension): Enum[] => {
|
||||
const names = definition['x-enum-varnames'];
|
||||
const descriptions = definition['x-enum-descriptions'];
|
||||
|
||||
@ -17,4 +17,4 @@ export function extendEnum(enumerators: Enum[], definition: WithEnumExtension):
|
||||
value: enumerator.value,
|
||||
type: enumerator.type,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
import { isDefined } from '../../../utils/isDefined';
|
||||
|
||||
export function getEnum(values?: (string | number)[]): Enum[] {
|
||||
export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
if (Array.isArray(values)) {
|
||||
return values
|
||||
.filter((value, index, arr) => {
|
||||
@ -30,4 +30,4 @@ export function getEnum(values?: (string | number)[]): Enum[] {
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import type { Enum } from '../../../client/interfaces/Enum';
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export function getEnumFromDescription(description: string): Enum[] {
|
||||
export const getEnumFromDescription = (description: string): Enum[] => {
|
||||
// Check if we can find this special format string:
|
||||
// None=0,Something=1,AnotherThing=2
|
||||
if (/^(\w+=[0-9]+)/g.test(description)) {
|
||||
@ -36,4 +36,4 @@ export function getEnumFromDescription(description: string): Enum[] {
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
@ -24,9 +24,9 @@ const TYPE_MAPPINGS = new Map<string, string>([
|
||||
/**
|
||||
* Get mapped type for given type to any basic Typescript/Javascript type.
|
||||
*/
|
||||
export function getMappedType(type: string, format?: string): string | undefined {
|
||||
export const getMappedType = (type: string, format?: string): string | undefined => {
|
||||
if (format === 'binary') {
|
||||
return 'binary';
|
||||
}
|
||||
return TYPE_MAPPINGS.get(type);
|
||||
}
|
||||
};
|
||||
|
||||
@ -9,12 +9,12 @@ import { getModelComposition } from './getModelComposition';
|
||||
import { getModelProperties } from './getModelProperties';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getModel(
|
||||
export const getModel = (
|
||||
openApi: OpenApi,
|
||||
definition: OpenApiSchema,
|
||||
isDefinition: boolean = false,
|
||||
name: string = ''
|
||||
): Model {
|
||||
): Model => {
|
||||
const model: Model = {
|
||||
name,
|
||||
export: 'interface',
|
||||
@ -162,4 +162,4 @@ export function getModel(
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
};
|
||||
|
||||
@ -9,13 +9,13 @@ import { getRequiredPropertiesFromComposition } from './getRequiredPropertiesFro
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel;
|
||||
|
||||
export function getModelComposition(
|
||||
export const getModelComposition = (
|
||||
openApi: OpenApi,
|
||||
definition: OpenApiSchema,
|
||||
definitions: OpenApiSchema[],
|
||||
type: 'one-of' | 'any-of' | 'all-of',
|
||||
getModel: GetModelFn
|
||||
): ModelComposition {
|
||||
): ModelComposition => {
|
||||
const composition: ModelComposition = {
|
||||
type,
|
||||
imports: [],
|
||||
@ -87,4 +87,4 @@ export function getModelComposition(
|
||||
}
|
||||
|
||||
return composition;
|
||||
}
|
||||
};
|
||||
|
||||
@ -9,7 +9,7 @@ import { getType } from './getType';
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel;
|
||||
|
||||
export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema, getModel: GetModelFn): Model[] {
|
||||
export const getModelProperties = (openApi: OpenApi, definition: OpenApiSchema, getModel: GetModelFn): Model[] => {
|
||||
const models: Model[] = [];
|
||||
for (const propertyName in definition.properties) {
|
||||
if (definition.properties.hasOwnProperty(propertyName)) {
|
||||
@ -85,4 +85,4 @@ export function getModelProperties(openApi: OpenApi, definition: OpenApiSchema,
|
||||
}
|
||||
}
|
||||
return models;
|
||||
}
|
||||
};
|
||||
|
||||
@ -6,6 +6,6 @@ import type { Type } from '../../../client/interfaces/Type';
|
||||
* @param modelClass The parsed model class type.
|
||||
* @returns The model template type (<T> or empty).
|
||||
*/
|
||||
export function getModelTemplate(modelClass: Type): string {
|
||||
export const getModelTemplate = (modelClass: Type): string => {
|
||||
return modelClass.template ? '<T>' : '';
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import type { OpenApi } from '../interfaces/OpenApi';
|
||||
import { getModel } from './getModel';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getModels(openApi: OpenApi): Model[] {
|
||||
export const getModels = (openApi: OpenApi): Model[] => {
|
||||
const models: Model[] = [];
|
||||
for (const definitionName in openApi.definitions) {
|
||||
if (openApi.definitions.hasOwnProperty(definitionName)) {
|
||||
@ -14,4 +14,4 @@ export function getModels(openApi: OpenApi): Model[] {
|
||||
}
|
||||
}
|
||||
return models;
|
||||
}
|
||||
};
|
||||
|
||||
@ -5,25 +5,22 @@ import type { OpenApiOperation } from '../interfaces/OpenApiOperation';
|
||||
import { getOperationErrors } from './getOperationErrors';
|
||||
import { getOperationName } from './getOperationName';
|
||||
import { getOperationParameters } from './getOperationParameters';
|
||||
import { getOperationPath } from './getOperationPath';
|
||||
import { getOperationResponseHeader } from './getOperationResponseHeader';
|
||||
import { getOperationResponses } from './getOperationResponses';
|
||||
import { getOperationResults } from './getOperationResults';
|
||||
import { getServiceName } from './getServiceName';
|
||||
import { sortByRequired } from './sortByRequired';
|
||||
|
||||
export function getOperation(
|
||||
export const getOperation = (
|
||||
openApi: OpenApi,
|
||||
url: string,
|
||||
method: string,
|
||||
tag: string,
|
||||
op: OpenApiOperation,
|
||||
pathParams: OperationParameters
|
||||
): Operation {
|
||||
): Operation => {
|
||||
const serviceName = getServiceName(tag);
|
||||
const operationNameFallback = `${method}${serviceName}`;
|
||||
const operationName = getOperationName(op.operationId || operationNameFallback);
|
||||
const operationPath = getOperationPath(url);
|
||||
const operationName = getOperationName(op.operationId || `${method}`);
|
||||
|
||||
// Create a new operation object for this method.
|
||||
const operation: Operation = {
|
||||
@ -33,7 +30,7 @@ export function getOperation(
|
||||
description: op.description || null,
|
||||
deprecated: op.deprecated === true,
|
||||
method: method.toUpperCase(),
|
||||
path: operationPath,
|
||||
path: url,
|
||||
parameters: [...pathParams.parameters],
|
||||
parametersPath: [...pathParams.parametersPath],
|
||||
parametersQuery: [...pathParams.parametersQuery],
|
||||
@ -76,4 +73,4 @@ export function getOperation(
|
||||
operation.parameters = operation.parameters.sort(sortByRequired);
|
||||
|
||||
return operation;
|
||||
}
|
||||
};
|
||||
|
||||
@ -5,7 +5,7 @@ import type { OperationResponse } from '../../../client/interfaces/OperationResp
|
||||
*
|
||||
* @param operationResponses
|
||||
*/
|
||||
export function getOperationErrors(operationResponses: OperationResponse[]): OperationError[] {
|
||||
export const getOperationErrors = (operationResponses: OperationResponse[]): OperationError[] => {
|
||||
return operationResponses
|
||||
.filter(operationResponse => {
|
||||
return operationResponse.code >= 300 && operationResponse.description;
|
||||
@ -14,4 +14,4 @@ export function getOperationErrors(operationResponses: OperationResponse[]): Ope
|
||||
code: response.code,
|
||||
description: response.description!,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
@ -5,10 +5,10 @@ import camelCase from 'camelcase';
|
||||
* This converts the input string to camelCase, so the method name follows
|
||||
* the most popular Javascript and Typescript writing style.
|
||||
*/
|
||||
export function getOperationName(value: string): string {
|
||||
export const getOperationName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim();
|
||||
return camelCase(clean);
|
||||
}
|
||||
};
|
||||
|
||||
@ -12,7 +12,7 @@ import { getOperationParameterName } from './getOperationParameterName';
|
||||
import { getRef } from './getRef';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParameter): OperationParameter {
|
||||
export const getOperationParameter = (openApi: OpenApi, parameter: OpenApiParameter): OperationParameter => {
|
||||
const operationParameter: OperationParameter = {
|
||||
in: parameter.in,
|
||||
prop: parameter.name,
|
||||
@ -147,4 +147,4 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame
|
||||
}
|
||||
|
||||
return operationParameter;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import type { OperationParameter } from '../../../client/interfaces/OperationParameter';
|
||||
import type { OpenApiParameter } from '../interfaces/OpenApiParameter';
|
||||
|
||||
export function getOperationParameterDefault(
|
||||
export const getOperationParameterDefault = (
|
||||
parameter: OpenApiParameter,
|
||||
operationParameter: OperationParameter
|
||||
): string | undefined {
|
||||
): string | undefined => {
|
||||
if (parameter.default === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -39,4 +39,4 @@ export function getOperationParameterDefault(
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,10 +7,10 @@ const reservedWords =
|
||||
* Replaces any invalid characters from a parameter name.
|
||||
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
|
||||
*/
|
||||
export function getOperationParameterName(value: string): string {
|
||||
export const getOperationParameterName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim();
|
||||
return camelCase(clean).replace(reservedWords, '_$1');
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import type { OpenApiParameter } from '../interfaces/OpenApiParameter';
|
||||
import { getOperationParameter } from './getOperationParameter';
|
||||
import { getRef } from './getRef';
|
||||
|
||||
export function getOperationParameters(openApi: OpenApi, parameters: OpenApiParameter[]): OperationParameters {
|
||||
export const getOperationParameters = (openApi: OpenApi, parameters: OpenApiParameter[]): OperationParameters => {
|
||||
const operationParameters: OperationParameters = {
|
||||
imports: [],
|
||||
parameters: [],
|
||||
@ -58,4 +58,4 @@ export function getOperationParameters(openApi: OpenApi, parameters: OpenApiPara
|
||||
}
|
||||
});
|
||||
return operationParameters;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import { getOperationPath } from './getOperationPath';
|
||||
|
||||
describe('getOperationPath', () => {
|
||||
it('should produce correct result', () => {
|
||||
expect(getOperationPath('/api/v{api-version}/list/{id}/{type}')).toEqual(
|
||||
'/api/v${OpenAPI.VERSION}/list/${id}/${type}'
|
||||
);
|
||||
expect(getOperationPath('/api/v{api-version}/list/{id}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}');
|
||||
expect(getOperationPath('/api/v1/list/{id}')).toEqual('/api/v1/list/${id}');
|
||||
expect(getOperationPath('/api/{foobar}')).toEqual('/api/${foobar}');
|
||||
expect(getOperationPath('/api/{fooBar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{foo-bar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{foo_bar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{foo.bar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{Foo-Bar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{FOO-BAR}')).toEqual('/api/${fooBar}');
|
||||
});
|
||||
});
|
||||
@ -1,16 +0,0 @@
|
||||
import { getOperationParameterName } from './getOperationParameterName';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Plus we return the correct parameter names to replace in the URL.
|
||||
* @param path
|
||||
*/
|
||||
export function getOperationPath(path: string): string {
|
||||
return path
|
||||
.replace(/\{(.*?)\}/g, (_, w: string) => {
|
||||
return `\${${getOperationParameterName(w)}}`;
|
||||
})
|
||||
.replace('${apiVersion}', '${OpenAPI.VERSION}');
|
||||
}
|
||||
@ -7,11 +7,11 @@ import { getModel } from './getModel';
|
||||
import { getRef } from './getRef';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getOperationResponse(
|
||||
export const getOperationResponse = (
|
||||
openApi: OpenApi,
|
||||
response: OpenApiResponse,
|
||||
responseCode: number
|
||||
): OperationResponse {
|
||||
): OperationResponse => {
|
||||
const operationResponse: OperationResponse = {
|
||||
in: 'response',
|
||||
name: '',
|
||||
@ -96,4 +96,4 @@ export function getOperationResponse(
|
||||
}
|
||||
|
||||
return operationResponse;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export function getOperationResponseCode(value: string | 'default'): number | null {
|
||||
export const getOperationResponseCode = (value: string | 'default'): number | null => {
|
||||
// You can specify a "default" response, this is treated as HTTP code 200
|
||||
if (value === 'default') {
|
||||
return 200;
|
||||
@ -13,4 +13,4 @@ export function getOperationResponseCode(value: string | 'default'): number | nu
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { OperationResponse } from '../../../client/interfaces/OperationResponse';
|
||||
|
||||
export function getOperationResponseHeader(operationResponses: OperationResponse[]): string | null {
|
||||
export const getOperationResponseHeader = (operationResponses: OperationResponse[]): string | null => {
|
||||
const header = operationResponses.find(operationResponses => {
|
||||
return operationResponses.in === 'header';
|
||||
});
|
||||
@ -8,4 +8,4 @@ export function getOperationResponseHeader(operationResponses: OperationResponse
|
||||
return header.name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -6,7 +6,7 @@ import { getOperationResponse } from './getOperationResponse';
|
||||
import { getOperationResponseCode } from './getOperationResponseCode';
|
||||
import { getRef } from './getRef';
|
||||
|
||||
export function getOperationResponses(openApi: OpenApi, responses: OpenApiResponses): OperationResponse[] {
|
||||
export const getOperationResponses = (openApi: OpenApi, responses: OpenApiResponses): OperationResponse[] => {
|
||||
const operationResponses: OperationResponse[] = [];
|
||||
|
||||
// Iterate over each response code and get the
|
||||
@ -28,4 +28,4 @@ export function getOperationResponses(openApi: OpenApi, responses: OpenApiRespon
|
||||
return operationResponses.sort((a, b): number => {
|
||||
return a.code < b.code ? -1 : a.code > b.code ? 1 : 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import type { Model } from '../../../client/interfaces/Model';
|
||||
import type { OperationResponse } from '../../../client/interfaces/OperationResponse';
|
||||
|
||||
function areEqual(a: Model, b: Model): boolean {
|
||||
const areEqual = (a: Model, b: Model): boolean => {
|
||||
const equal = a.type === b.type && a.base === b.base && a.template === b.template;
|
||||
if (equal && a.link && b.link) {
|
||||
return areEqual(a.link, b.link);
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
};
|
||||
|
||||
export function getOperationResults(operationResponses: OperationResponse[]): OperationResponse[] {
|
||||
export const getOperationResults = (operationResponses: OperationResponse[]): OperationResponse[] => {
|
||||
const operationResults: OperationResponse[] = [];
|
||||
|
||||
// Filter out success response codes, but skip "204 No Content"
|
||||
@ -49,4 +49,4 @@ export function getOperationResults(operationResponses: OperationResponse[]): Op
|
||||
}) === index
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import type { OpenApiReference } from '../interfaces/OpenApiReference';
|
||||
const ESCAPED_REF_SLASH = /~1/g;
|
||||
const ESCAPED_REF_TILDE = /~0/g;
|
||||
|
||||
export function getRef<T>(openApi: OpenApi, item: T & OpenApiReference): T {
|
||||
export const getRef = <T>(openApi: OpenApi, item: T & OpenApiReference): T => {
|
||||
if (item.$ref) {
|
||||
// Fetch the paths to the definitions, this converts:
|
||||
// "#/definitions/Form" to ["definitions", "Form"]
|
||||
@ -29,4 +29,4 @@ export function getRef<T>(openApi: OpenApi, item: T & OpenApiReference): T {
|
||||
return result as T;
|
||||
}
|
||||
return item as T;
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,12 +7,12 @@ import { getRef } from './getRef';
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel;
|
||||
|
||||
export function getRequiredPropertiesFromComposition(
|
||||
export const getRequiredPropertiesFromComposition = (
|
||||
openApi: OpenApi,
|
||||
required: string[],
|
||||
definitions: OpenApiSchema[],
|
||||
getModel: GetModelFn
|
||||
): Model[] {
|
||||
): Model[] => {
|
||||
return definitions
|
||||
.reduce((properties, definition) => {
|
||||
if (definition.$ref) {
|
||||
@ -30,4 +30,4 @@ export function getRequiredPropertiesFromComposition(
|
||||
isRequired: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,10 +4,10 @@ import type { OpenApi } from '../interfaces/OpenApi';
|
||||
* Get the base server url.
|
||||
* @param openApi
|
||||
*/
|
||||
export function getServer(openApi: OpenApi): string {
|
||||
export const getServer = (openApi: OpenApi): string => {
|
||||
const scheme = openApi.schemes?.[0] || 'http';
|
||||
const host = openApi.host;
|
||||
const basePath = openApi.basePath || '';
|
||||
const url = host ? `${scheme}://${host}${basePath}` : basePath;
|
||||
return url.replace(/\/$/g, '');
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,10 +4,10 @@ import camelCase from 'camelcase';
|
||||
* Convert the input value to a correct service name. This converts
|
||||
* the input string to PascalCase.
|
||||
*/
|
||||
export function getServiceName(value: string): string {
|
||||
export const getServiceName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim();
|
||||
return camelCase(clean, { pascalCase: true });
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
* This basically removes any "v" prefix from the version string.
|
||||
* @param version
|
||||
*/
|
||||
export function getServiceVersion(version = '1.0'): string {
|
||||
export const getServiceVersion = (version = '1.0'): string => {
|
||||
return String(version).replace(/^v/gi, '');
|
||||
}
|
||||
};
|
||||
|
||||
@ -26,6 +26,6 @@ describe('getServices', () => {
|
||||
});
|
||||
|
||||
expect(services).toHaveLength(1);
|
||||
expect(services[0].name).toEqual('');
|
||||
expect(services[0].name).toEqual('Default');
|
||||
});
|
||||
});
|
||||
|
||||
@ -7,7 +7,7 @@ import { getOperationParameters } from './getOperationParameters';
|
||||
/**
|
||||
* Get the OpenAPI services
|
||||
*/
|
||||
export function getServices(openApi: OpenApi): Service[] {
|
||||
export const getServices = (openApi: OpenApi): Service[] => {
|
||||
const services = new Map<string, Service>();
|
||||
for (const url in openApi.paths) {
|
||||
if (openApi.paths.hasOwnProperty(url)) {
|
||||
@ -28,7 +28,7 @@ export function getServices(openApi: OpenApi): Service[] {
|
||||
case 'patch':
|
||||
// Each method contains an OpenAPI operation, we parse the operation
|
||||
const op = path[method]!;
|
||||
const tags = op.tags?.length ? op.tags.filter(unique) : [''];
|
||||
const tags = op.tags?.length ? op.tags.filter(unique) : ['Default'];
|
||||
tags.forEach(tag => {
|
||||
const operation = getOperation(openApi, url, method, tag, op, pathParams);
|
||||
|
||||
@ -52,4 +52,4 @@ export function getServices(openApi: OpenApi): Service[] {
|
||||
}
|
||||
}
|
||||
return Array.from(services.values());
|
||||
}
|
||||
};
|
||||
|
||||
@ -2,16 +2,16 @@ import type { Type } from '../../../client/interfaces/Type';
|
||||
import { getMappedType } from './getMappedType';
|
||||
import { stripNamespace } from './stripNamespace';
|
||||
|
||||
function encode(value: string): string {
|
||||
const encode = (value: string): string => {
|
||||
return value.replace(/^[^a-zA-Z_$]+/g, '').replace(/[^\w$]+/g, '_');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse any string value into a type object.
|
||||
* @param type String value like "integer" or "Link[Model]".
|
||||
* @param format String value like "binary" or "date".
|
||||
*/
|
||||
export function getType(type: string = 'any', format?: string): Type {
|
||||
export const getType = (type: string = 'any', format?: string): Type => {
|
||||
const result: Type = {
|
||||
type: 'any',
|
||||
base: 'any',
|
||||
@ -64,4 +64,4 @@ export function getType(type: string = 'any', format?: string): Type {
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { OperationParameter } from '../../../client/interfaces/OperationParameter';
|
||||
|
||||
export function sortByRequired(a: OperationParameter, b: OperationParameter): number {
|
||||
export const sortByRequired = (a: OperationParameter, b: OperationParameter): number => {
|
||||
const aNeedsValue = a.isRequired && a.default === undefined;
|
||||
const bNeedsValue = b.isRequired && b.default === undefined;
|
||||
if (aNeedsValue && !bNeedsValue) return -1;
|
||||
if (bNeedsValue && !aNeedsValue) return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
* Strip (OpenAPI) namespaces fom values.
|
||||
* @param value
|
||||
*/
|
||||
export function stripNamespace(value: string): string {
|
||||
export const stripNamespace = (value: string): string => {
|
||||
return value
|
||||
.trim()
|
||||
.replace(/^#\/definitions\//, '')
|
||||
.replace(/^#\/parameters\//, '')
|
||||
.replace(/^#\/responses\//, '')
|
||||
.replace(/^#\/securityDefinitions\//, '');
|
||||
}
|
||||
};
|
||||
|
||||
@ -10,11 +10,11 @@ import { getServiceVersion } from './parser/getServiceVersion';
|
||||
* all the models, services and schema's we should output.
|
||||
* @param openApi The OpenAPI spec that we have loaded from disk.
|
||||
*/
|
||||
export function parse(openApi: OpenApi): Client {
|
||||
export const parse = (openApi: OpenApi): Client => {
|
||||
const version = getServiceVersion(openApi.info.version);
|
||||
const server = getServer(openApi);
|
||||
const models = getModels(openApi);
|
||||
const services = getServices(openApi);
|
||||
|
||||
return { version, server, models, services };
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export function escapeName(value: string): string {
|
||||
export const escapeName = (value: string): string => {
|
||||
if (value) {
|
||||
const validName = /^[a-zA-Z_$][\w$]+$/g.test(value);
|
||||
if (!validName) {
|
||||
@ -6,4 +6,4 @@ export function escapeName(value: string): string {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,7 +7,7 @@ import type { WithEnumExtension } from '../interfaces/Extensions/WithEnumExtensi
|
||||
* @param enumerators
|
||||
* @param definition
|
||||
*/
|
||||
export function extendEnum(enumerators: Enum[], definition: WithEnumExtension): Enum[] {
|
||||
export const extendEnum = (enumerators: Enum[], definition: WithEnumExtension): Enum[] => {
|
||||
const names = definition['x-enum-varnames'];
|
||||
const descriptions = definition['x-enum-descriptions'];
|
||||
|
||||
@ -17,4 +17,4 @@ export function extendEnum(enumerators: Enum[], definition: WithEnumExtension):
|
||||
value: enumerator.value,
|
||||
type: enumerator.type,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
@ -21,7 +21,7 @@ const BASIC_MEDIA_TYPES = [
|
||||
'multipart/batch',
|
||||
];
|
||||
|
||||
export function getContent(openApi: OpenApi, content: Dictionary<OpenApiMediaType>): Content | null {
|
||||
export const getContent = (openApi: OpenApi, content: Dictionary<OpenApiMediaType>): Content | null => {
|
||||
const basicMediaTypeWithSchema = Object.keys(content)
|
||||
.filter(mediaType => {
|
||||
const cleanMediaType = mediaType.split(';')[0].trim();
|
||||
@ -43,4 +43,4 @@ export function getContent(openApi: OpenApi, content: Dictionary<OpenApiMediaTyp
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Enum } from '../../../client/interfaces/Enum';
|
||||
import { isDefined } from '../../../utils/isDefined';
|
||||
|
||||
export function getEnum(values?: (string | number)[]): Enum[] {
|
||||
export const getEnum = (values?: (string | number)[]): Enum[] => {
|
||||
if (Array.isArray(values)) {
|
||||
return values
|
||||
.filter((value, index, arr) => {
|
||||
@ -30,4 +30,4 @@ export function getEnum(values?: (string | number)[]): Enum[] {
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import type { Enum } from '../../../client/interfaces/Enum';
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export function getEnumFromDescription(description: string): Enum[] {
|
||||
export const getEnumFromDescription = (description: string): Enum[] => {
|
||||
// Check if we can find this special format string:
|
||||
// None=0,Something=1,AnotherThing=2
|
||||
if (/^(\w+=[0-9]+)/g.test(description)) {
|
||||
@ -36,4 +36,4 @@ export function getEnumFromDescription(description: string): Enum[] {
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
@ -24,9 +24,9 @@ const TYPE_MAPPINGS = new Map<string, string>([
|
||||
/**
|
||||
* Get mapped type for given type to any basic Typescript/Javascript type.
|
||||
*/
|
||||
export function getMappedType(type: string, format?: string): string | undefined {
|
||||
export const getMappedType = (type: string, format?: string): string | undefined => {
|
||||
if (format === 'binary') {
|
||||
return 'binary';
|
||||
}
|
||||
return TYPE_MAPPINGS.get(type);
|
||||
}
|
||||
};
|
||||
|
||||
@ -10,12 +10,12 @@ import { getModelDefault } from './getModelDefault';
|
||||
import { getModelProperties } from './getModelProperties';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getModel(
|
||||
export const getModel = (
|
||||
openApi: OpenApi,
|
||||
definition: OpenApiSchema,
|
||||
isDefinition: boolean = false,
|
||||
name: string = ''
|
||||
): Model {
|
||||
): Model => {
|
||||
const model: Model = {
|
||||
name,
|
||||
export: 'interface',
|
||||
@ -191,4 +191,4 @@ export function getModel(
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
};
|
||||
|
||||
@ -9,13 +9,13 @@ import { getRequiredPropertiesFromComposition } from './getRequiredPropertiesFro
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel;
|
||||
|
||||
export function getModelComposition(
|
||||
export const getModelComposition = (
|
||||
openApi: OpenApi,
|
||||
definition: OpenApiSchema,
|
||||
definitions: OpenApiSchema[],
|
||||
type: 'one-of' | 'any-of' | 'all-of',
|
||||
getModel: GetModelFn
|
||||
): ModelComposition {
|
||||
): ModelComposition => {
|
||||
const composition: ModelComposition = {
|
||||
type,
|
||||
imports: [],
|
||||
@ -87,4 +87,4 @@ export function getModelComposition(
|
||||
}
|
||||
|
||||
return composition;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Model } from '../../../client/interfaces/Model';
|
||||
import type { OpenApiSchema } from '../interfaces/OpenApiSchema';
|
||||
|
||||
export function getModelDefault(definition: OpenApiSchema, model?: Model): string | undefined {
|
||||
export const getModelDefault = (definition: OpenApiSchema, model?: Model): string | undefined => {
|
||||
if (definition.default === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -36,4 +36,4 @@ export function getModelDefault(definition: OpenApiSchema, model?: Model): strin
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@ -10,12 +10,12 @@ import { getType } from './getType';
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel;
|
||||
|
||||
export function getModelProperties(
|
||||
export const getModelProperties = (
|
||||
openApi: OpenApi,
|
||||
definition: OpenApiSchema,
|
||||
getModel: GetModelFn,
|
||||
parent?: Model
|
||||
): Model[] {
|
||||
): Model[] => {
|
||||
const models: Model[] = [];
|
||||
const discriminator = findOneOfParentDiscriminator(openApi, parent);
|
||||
for (const propertyName in definition.properties) {
|
||||
@ -104,4 +104,4 @@ export function getModelProperties(
|
||||
}
|
||||
|
||||
return models;
|
||||
}
|
||||
};
|
||||
|
||||
@ -6,6 +6,6 @@ import type { Type } from '../../../client/interfaces/Type';
|
||||
* @param modelClass The parsed model class type.
|
||||
* @returns The model template type (<T> or empty).
|
||||
*/
|
||||
export function getModelTemplate(modelClass: Type): string {
|
||||
export const getModelTemplate = (modelClass: Type): string => {
|
||||
return modelClass.template ? '<T>' : '';
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import type { OpenApi } from '../interfaces/OpenApi';
|
||||
import { getModel } from './getModel';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getModels(openApi: OpenApi): Model[] {
|
||||
export const getModels = (openApi: OpenApi): Model[] => {
|
||||
const models: Model[] = [];
|
||||
if (openApi.components) {
|
||||
for (const definitionName in openApi.components.schemas) {
|
||||
@ -16,4 +16,4 @@ export function getModels(openApi: OpenApi): Model[] {
|
||||
}
|
||||
}
|
||||
return models;
|
||||
}
|
||||
};
|
||||
|
||||
@ -6,7 +6,6 @@ import type { OpenApiRequestBody } from '../interfaces/OpenApiRequestBody';
|
||||
import { getOperationErrors } from './getOperationErrors';
|
||||
import { getOperationName } from './getOperationName';
|
||||
import { getOperationParameters } from './getOperationParameters';
|
||||
import { getOperationPath } from './getOperationPath';
|
||||
import { getOperationRequestBody } from './getOperationRequestBody';
|
||||
import { getOperationResponseHeader } from './getOperationResponseHeader';
|
||||
import { getOperationResponses } from './getOperationResponses';
|
||||
@ -15,18 +14,16 @@ import { getRef } from './getRef';
|
||||
import { getServiceName } from './getServiceName';
|
||||
import { sortByRequired } from './sortByRequired';
|
||||
|
||||
export function getOperation(
|
||||
export const getOperation = (
|
||||
openApi: OpenApi,
|
||||
url: string,
|
||||
method: string,
|
||||
tag: string,
|
||||
op: OpenApiOperation,
|
||||
pathParams: OperationParameters
|
||||
): Operation {
|
||||
): Operation => {
|
||||
const serviceName = getServiceName(tag);
|
||||
const operationNameFallback = `${method}${serviceName}`;
|
||||
const operationName = getOperationName(op.operationId || operationNameFallback);
|
||||
const operationPath = getOperationPath(url);
|
||||
const operationName = getOperationName(op.operationId || `${method}`);
|
||||
|
||||
// Create a new operation object for this method.
|
||||
const operation: Operation = {
|
||||
@ -36,7 +33,7 @@ export function getOperation(
|
||||
description: op.description || null,
|
||||
deprecated: op.deprecated === true,
|
||||
method: method.toUpperCase(),
|
||||
path: operationPath,
|
||||
path: url,
|
||||
parameters: [...pathParams.parameters],
|
||||
parametersPath: [...pathParams.parametersPath],
|
||||
parametersQuery: [...pathParams.parametersQuery],
|
||||
@ -87,4 +84,4 @@ export function getOperation(
|
||||
operation.parameters = operation.parameters.sort(sortByRequired);
|
||||
|
||||
return operation;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { OperationError } from '../../../client/interfaces/OperationError';
|
||||
import type { OperationResponse } from '../../../client/interfaces/OperationResponse';
|
||||
|
||||
export function getOperationErrors(operationResponses: OperationResponse[]): OperationError[] {
|
||||
export const getOperationErrors = (operationResponses: OperationResponse[]): OperationError[] => {
|
||||
return operationResponses
|
||||
.filter(operationResponse => {
|
||||
return operationResponse.code >= 300 && operationResponse.description;
|
||||
@ -10,4 +10,4 @@ export function getOperationErrors(operationResponses: OperationResponse[]): Ope
|
||||
code: response.code,
|
||||
description: response.description!,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
@ -5,10 +5,10 @@ import camelCase from 'camelcase';
|
||||
* This converts the input string to camelCase, so the method name follows
|
||||
* the most popular Javascript and Typescript writing style.
|
||||
*/
|
||||
export function getOperationName(value: string): string {
|
||||
export const getOperationName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim();
|
||||
return camelCase(clean);
|
||||
}
|
||||
};
|
||||
|
||||
@ -9,7 +9,7 @@ import { getOperationParameterName } from './getOperationParameterName';
|
||||
import { getRef } from './getRef';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParameter): OperationParameter {
|
||||
export const getOperationParameter = (openApi: OpenApi, parameter: OpenApiParameter): OperationParameter => {
|
||||
const operationParameter: OperationParameter = {
|
||||
in: parameter.in,
|
||||
prop: parameter.name,
|
||||
@ -89,4 +89,4 @@ export function getOperationParameter(openApi: OpenApi, parameter: OpenApiParame
|
||||
}
|
||||
|
||||
return operationParameter;
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,10 +7,10 @@ const reservedWords =
|
||||
* Replaces any invalid characters from a parameter name.
|
||||
* For example: 'filter.someProperty' becomes 'filterSomeProperty'.
|
||||
*/
|
||||
export function getOperationParameterName(value: string): string {
|
||||
export const getOperationParameterName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim();
|
||||
return camelCase(clean).replace(reservedWords, '_$1');
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import type { OpenApiParameter } from '../interfaces/OpenApiParameter';
|
||||
import { getOperationParameter } from './getOperationParameter';
|
||||
import { getRef } from './getRef';
|
||||
|
||||
export function getOperationParameters(openApi: OpenApi, parameters: OpenApiParameter[]): OperationParameters {
|
||||
export const getOperationParameters = (openApi: OpenApi, parameters: OpenApiParameter[]): OperationParameters => {
|
||||
const operationParameters: OperationParameters = {
|
||||
imports: [],
|
||||
parameters: [],
|
||||
@ -58,4 +58,4 @@ export function getOperationParameters(openApi: OpenApi, parameters: OpenApiPara
|
||||
}
|
||||
});
|
||||
return operationParameters;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import { getOperationPath } from './getOperationPath';
|
||||
|
||||
describe('getOperationPath', () => {
|
||||
it('should produce correct result', () => {
|
||||
expect(getOperationPath('/api/v{api-version}/list/{id}/{type}')).toEqual(
|
||||
'/api/v${OpenAPI.VERSION}/list/${id}/${type}'
|
||||
);
|
||||
expect(getOperationPath('/api/v{api-version}/list/{id}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}');
|
||||
expect(getOperationPath('/api/v1/list/{id}')).toEqual('/api/v1/list/${id}');
|
||||
expect(getOperationPath('/api/{foobar}')).toEqual('/api/${foobar}');
|
||||
expect(getOperationPath('/api/{fooBar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{foo-bar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{foo_bar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{foo.bar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{Foo-Bar}')).toEqual('/api/${fooBar}');
|
||||
expect(getOperationPath('/api/{FOO-BAR}')).toEqual('/api/${fooBar}');
|
||||
});
|
||||
});
|
||||
@ -1,16 +0,0 @@
|
||||
import { getOperationParameterName } from './getOperationParameterName';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Plus we return the correct parameter names to replace in the URL.
|
||||
* @param path
|
||||
*/
|
||||
export function getOperationPath(path: string): string {
|
||||
return path
|
||||
.replace(/\{(.*?)\}/g, (_, w: string) => {
|
||||
return `\${${getOperationParameterName(w)}}`;
|
||||
})
|
||||
.replace('${apiVersion}', '${OpenAPI.VERSION}');
|
||||
}
|
||||
@ -6,7 +6,7 @@ import { getContent } from './getContent';
|
||||
import { getModel } from './getModel';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getOperationRequestBody(openApi: OpenApi, body: OpenApiRequestBody): OperationParameter {
|
||||
export const getOperationRequestBody = (openApi: OpenApi, body: OpenApiRequestBody): OperationParameter => {
|
||||
const requestBody: OperationParameter = {
|
||||
in: 'body',
|
||||
export: 'interface',
|
||||
@ -83,4 +83,4 @@ export function getOperationRequestBody(openApi: OpenApi, body: OpenApiRequestBo
|
||||
}
|
||||
|
||||
return requestBody;
|
||||
}
|
||||
};
|
||||
|
||||
@ -8,11 +8,11 @@ import { getModel } from './getModel';
|
||||
import { getRef } from './getRef';
|
||||
import { getType } from './getType';
|
||||
|
||||
export function getOperationResponse(
|
||||
export const getOperationResponse = (
|
||||
openApi: OpenApi,
|
||||
response: OpenApiResponse,
|
||||
responseCode: number
|
||||
): OperationResponse {
|
||||
): OperationResponse => {
|
||||
const operationResponse: OperationResponse = {
|
||||
in: 'response',
|
||||
name: '',
|
||||
@ -95,4 +95,4 @@ export function getOperationResponse(
|
||||
}
|
||||
|
||||
return operationResponse;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
export function getOperationResponseCode(value: string | 'default'): number | null {
|
||||
export const getOperationResponseCode = (value: string | 'default'): number | null => {
|
||||
// You can specify a "default" response, this is treated as HTTP code 200
|
||||
if (value === 'default') {
|
||||
return 200;
|
||||
@ -13,4 +13,4 @@ export function getOperationResponseCode(value: string | 'default'): number | nu
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { OperationResponse } from '../../../client/interfaces/OperationResponse';
|
||||
|
||||
export function getOperationResponseHeader(operationResponses: OperationResponse[]): string | null {
|
||||
export const getOperationResponseHeader = (operationResponses: OperationResponse[]): string | null => {
|
||||
const header = operationResponses.find(operationResponses => {
|
||||
return operationResponses.in === 'header';
|
||||
});
|
||||
@ -8,4 +8,4 @@ export function getOperationResponseHeader(operationResponses: OperationResponse
|
||||
return header.name;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@ -6,7 +6,7 @@ import { getOperationResponse } from './getOperationResponse';
|
||||
import { getOperationResponseCode } from './getOperationResponseCode';
|
||||
import { getRef } from './getRef';
|
||||
|
||||
export function getOperationResponses(openApi: OpenApi, responses: OpenApiResponses): OperationResponse[] {
|
||||
export const getOperationResponses = (openApi: OpenApi, responses: OpenApiResponses): OperationResponse[] => {
|
||||
const operationResponses: OperationResponse[] = [];
|
||||
|
||||
// Iterate over each response code and get the
|
||||
@ -28,4 +28,4 @@ export function getOperationResponses(openApi: OpenApi, responses: OpenApiRespon
|
||||
return operationResponses.sort((a, b): number => {
|
||||
return a.code < b.code ? -1 : a.code > b.code ? 1 : 0;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import type { Model } from '../../../client/interfaces/Model';
|
||||
import type { OperationResponse } from '../../../client/interfaces/OperationResponse';
|
||||
|
||||
function areEqual(a: Model, b: Model): boolean {
|
||||
const areEqual = (a: Model, b: Model): boolean => {
|
||||
const equal = a.type === b.type && a.base === b.base && a.template === b.template;
|
||||
if (equal && a.link && b.link) {
|
||||
return areEqual(a.link, b.link);
|
||||
}
|
||||
return equal;
|
||||
}
|
||||
};
|
||||
|
||||
export function getOperationResults(operationResponses: OperationResponse[]): OperationResponse[] {
|
||||
export const getOperationResults = (operationResponses: OperationResponse[]): OperationResponse[] => {
|
||||
const operationResults: OperationResponse[] = [];
|
||||
|
||||
// Filter out success response codes, but skip "204 No Content"
|
||||
@ -49,4 +49,4 @@ export function getOperationResults(operationResponses: OperationResponse[]): Op
|
||||
}) === index
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import type { OpenApiReference } from '../interfaces/OpenApiReference';
|
||||
const ESCAPED_REF_SLASH = /~1/g;
|
||||
const ESCAPED_REF_TILDE = /~0/g;
|
||||
|
||||
export function getRef<T>(openApi: OpenApi, item: T & OpenApiReference): T {
|
||||
export const getRef = <T>(openApi: OpenApi, item: T & OpenApiReference): T => {
|
||||
if (item.$ref) {
|
||||
// Fetch the paths to the definitions, this converts:
|
||||
// "#/components/schemas/Form" to ["components", "schemas", "Form"]
|
||||
@ -29,4 +29,4 @@ export function getRef<T>(openApi: OpenApi, item: T & OpenApiReference): T {
|
||||
return result as T;
|
||||
}
|
||||
return item as T;
|
||||
}
|
||||
};
|
||||
|
||||
@ -7,12 +7,12 @@ import { getRef } from './getRef';
|
||||
// Fix for circular dependency
|
||||
export type GetModelFn = typeof getModel;
|
||||
|
||||
export function getRequiredPropertiesFromComposition(
|
||||
export const getRequiredPropertiesFromComposition = (
|
||||
openApi: OpenApi,
|
||||
required: string[],
|
||||
definitions: OpenApiSchema[],
|
||||
getModel: GetModelFn
|
||||
): Model[] {
|
||||
): Model[] => {
|
||||
return definitions
|
||||
.reduce((properties, definition) => {
|
||||
if (definition.$ref) {
|
||||
@ -30,4 +30,4 @@ export function getRequiredPropertiesFromComposition(
|
||||
isRequired: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { OpenApi } from '../interfaces/OpenApi';
|
||||
|
||||
export function getServer(openApi: OpenApi): string {
|
||||
export const getServer = (openApi: OpenApi): string => {
|
||||
const server = openApi.servers?.[0];
|
||||
const variables = server?.variables || {};
|
||||
let url = server?.url || '';
|
||||
@ -10,4 +10,4 @@ export function getServer(openApi: OpenApi): string {
|
||||
}
|
||||
}
|
||||
return url.replace(/\/$/g, '');
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,10 +4,10 @@ import camelCase from 'camelcase';
|
||||
* Convert the input value to a correct service name. This converts
|
||||
* the input string to PascalCase.
|
||||
*/
|
||||
export function getServiceName(value: string): string {
|
||||
export const getServiceName = (value: string): string => {
|
||||
const clean = value
|
||||
.replace(/^[^a-zA-Z]+/g, '')
|
||||
.replace(/[^\w\-]+/g, '-')
|
||||
.trim();
|
||||
return camelCase(clean, { pascalCase: true });
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
* This basically removes any "v" prefix from the version string.
|
||||
* @param version
|
||||
*/
|
||||
export function getServiceVersion(version = '1.0'): string {
|
||||
export const getServiceVersion = (version = '1.0'): string => {
|
||||
return String(version).replace(/^v/gi, '');
|
||||
}
|
||||
};
|
||||
|
||||
@ -26,6 +26,6 @@ describe('getServices', () => {
|
||||
});
|
||||
|
||||
expect(services).toHaveLength(1);
|
||||
expect(services[0].name).toEqual('');
|
||||
expect(services[0].name).toEqual('Default');
|
||||
});
|
||||
});
|
||||
|
||||
@ -7,7 +7,7 @@ import { getOperationParameters } from './getOperationParameters';
|
||||
/**
|
||||
* Get the OpenAPI services
|
||||
*/
|
||||
export function getServices(openApi: OpenApi): Service[] {
|
||||
export const getServices = (openApi: OpenApi): Service[] => {
|
||||
const services = new Map<string, Service>();
|
||||
for (const url in openApi.paths) {
|
||||
if (openApi.paths.hasOwnProperty(url)) {
|
||||
@ -28,7 +28,7 @@ export function getServices(openApi: OpenApi): Service[] {
|
||||
case 'patch':
|
||||
// Each method contains an OpenAPI operation, we parse the operation
|
||||
const op = path[method]!;
|
||||
const tags = op.tags?.length ? op.tags.filter(unique) : [''];
|
||||
const tags = op.tags?.length ? op.tags.filter(unique) : ['Default'];
|
||||
tags.forEach(tag => {
|
||||
const operation = getOperation(openApi, url, method, tag, op, pathParams);
|
||||
|
||||
@ -52,4 +52,4 @@ export function getServices(openApi: OpenApi): Service[] {
|
||||
}
|
||||
}
|
||||
return Array.from(services.values());
|
||||
}
|
||||
};
|
||||
|
||||
@ -3,16 +3,16 @@ import { isDefined } from '../../../utils/isDefined';
|
||||
import { getMappedType } from './getMappedType';
|
||||
import { stripNamespace } from './stripNamespace';
|
||||
|
||||
function encode(value: string): string {
|
||||
const encode = (value: string): string => {
|
||||
return value.replace(/^[^a-zA-Z_$]+/g, '').replace(/[^\w$]+/g, '_');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse any string value into a type object.
|
||||
* @param type String or String[] value like "integer", "Link[Model]" or ["string", "null"].
|
||||
* @param format String value like "binary" or "date".
|
||||
*/
|
||||
export function getType(type: string | string[] = 'any', format?: string): Type {
|
||||
export const getType = (type: string | string[] = 'any', format?: string): Type => {
|
||||
const result: Type = {
|
||||
type: 'any',
|
||||
base: 'any',
|
||||
@ -79,4 +79,4 @@ export function getType(type: string | string[] = 'any', format?: string): Type
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { OperationParameter } from '../../../client/interfaces/OperationParameter';
|
||||
|
||||
export function sortByRequired(a: OperationParameter, b: OperationParameter): number {
|
||||
export const sortByRequired = (a: OperationParameter, b: OperationParameter): number => {
|
||||
const aNeedsValue = a.isRequired && a.default === undefined;
|
||||
const bNeedsValue = b.isRequired && b.default === undefined;
|
||||
if (aNeedsValue && !bNeedsValue) return -1;
|
||||
if (bNeedsValue && !aNeedsValue) return 1;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
* Strip (OpenAPI) namespaces fom values.
|
||||
* @param value
|
||||
*/
|
||||
export function stripNamespace(value: string): string {
|
||||
export const stripNamespace = (value: string): string => {
|
||||
return value
|
||||
.trim()
|
||||
.replace(/^#\/components\/schemas\//, '')
|
||||
@ -14,4 +14,4 @@ export function stripNamespace(value: string): string {
|
||||
.replace(/^#\/components\/securitySchemes\//, '')
|
||||
.replace(/^#\/components\/links\//, '')
|
||||
.replace(/^#\/components\/callbacks\//, '');
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
export default {
|
||||
compiler: [8, '>= 4.3.0'],
|
||||
useData: true,
|
||||
main: function () {
|
||||
main: () => {
|
||||
return '';
|
||||
},
|
||||
};
|
||||
|
||||
40
src/templates/client.hbs
Normal file
40
src/templates/client.hbs
Normal file
@ -0,0 +1,40 @@
|
||||
{{>header}}
|
||||
|
||||
import type { BaseHttpRequest } from './core/BaseHttpRequest';
|
||||
import type { OpenAPIConfig } from './core/OpenAPI';
|
||||
import { {{{httpRequest}}} } from './core/{{{httpRequest}}}';
|
||||
|
||||
{{#if services}}
|
||||
{{#each services}}
|
||||
import { {{{name}}}{{{@root.postfix}}} } from './services/{{{name}}}{{{@root.postfix}}}';
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest;
|
||||
|
||||
export class {{{clientName}}} {
|
||||
|
||||
{{#each services}}
|
||||
public readonly {{{camelCase name}}}: {{{name}}}{{{@root.postfix}}};
|
||||
{{/each}}
|
||||
|
||||
private readonly request: BaseHttpRequest;
|
||||
|
||||
constructor(config?: Partial<OpenAPIConfig>, HttpRequest: HttpRequestConstructor = {{{httpRequest}}}) {
|
||||
this.request = new HttpRequest({
|
||||
BASE: config?.BASE ?? '{{{server}}}',
|
||||
VERSION: config?.VERSION ?? '{{{version}}}',
|
||||
WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false,
|
||||
CREDENTIALS: config?.CREDENTIALS ?? 'include',
|
||||
TOKEN: config?.TOKEN,
|
||||
USERNAME: config?.USERNAME,
|
||||
PASSWORD: config?.PASSWORD,
|
||||
HEADERS: config?.HEADERS,
|
||||
ENCODE_PATH: config?.ENCODE_PATH,
|
||||
});
|
||||
|
||||
{{#each services}}
|
||||
this.{{{camelCase name}}} = new {{{name}}}{{{@root.postfix}}}(this.request);
|
||||
{{/each}}
|
||||
}
|
||||
}
|
||||
@ -3,18 +3,18 @@
|
||||
import type { ApiResult } from './ApiResult';
|
||||
|
||||
export class ApiError extends Error {
|
||||
public readonly url: string;
|
||||
public readonly status: number;
|
||||
public readonly statusText: string;
|
||||
public readonly body: any;
|
||||
public readonly url: string;
|
||||
public readonly status: number;
|
||||
public readonly statusText: string;
|
||||
public readonly body: any;
|
||||
|
||||
constructor(response: ApiResult, message: string) {
|
||||
super(message);
|
||||
constructor(response: ApiResult, message: string) {
|
||||
super(message);
|
||||
|
||||
this.name = 'ApiError';
|
||||
this.url = response.url;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
this.body = response.body;
|
||||
}
|
||||
this.name = 'ApiError';
|
||||
this.url = response.url;
|
||||
this.status = response.status;
|
||||
this.statusText = response.statusText;
|
||||
this.body = response.body;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
{{>header}}
|
||||
|
||||
export type ApiRequestOptions = {
|
||||
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
|
||||
readonly path: string;
|
||||
readonly cookies?: Record<string, any>;
|
||||
readonly headers?: Record<string, any>;
|
||||
readonly query?: Record<string, any>;
|
||||
readonly formData?: Record<string, any>;
|
||||
readonly body?: any;
|
||||
readonly mediaType?: string;
|
||||
readonly responseHeader?: string;
|
||||
readonly errors?: Record<number, string>;
|
||||
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
|
||||
readonly url: string;
|
||||
readonly path?: Record<string, any>;
|
||||
readonly cookies?: Record<string, any>;
|
||||
readonly headers?: Record<string, any>;
|
||||
readonly query?: Record<string, any>;
|
||||
readonly formData?: Record<string, any>;
|
||||
readonly body?: any;
|
||||
readonly mediaType?: string;
|
||||
readonly responseHeader?: string;
|
||||
readonly errors?: Record<number, string>;
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
{{>header}}
|
||||
|
||||
export type ApiResult = {
|
||||
readonly url: string;
|
||||
readonly ok: boolean;
|
||||
readonly status: number;
|
||||
readonly statusText: string;
|
||||
readonly body: any;
|
||||
readonly url: string;
|
||||
readonly ok: boolean;
|
||||
readonly status: number;
|
||||
readonly statusText: string;
|
||||
readonly body: any;
|
||||
};
|
||||
|
||||
18
src/templates/core/BaseHttpRequest.hbs
Normal file
18
src/templates/core/BaseHttpRequest.hbs
Normal file
@ -0,0 +1,18 @@
|
||||
{{>header}}
|
||||
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { CancelablePromise } from './CancelablePromise';
|
||||
import type { OpenAPIConfig } from './OpenAPI';
|
||||
|
||||
export class BaseHttpRequest {
|
||||
|
||||
protected readonly config: OpenAPIConfig;
|
||||
|
||||
constructor(config: OpenAPIConfig) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
throw new Error('Not Implemented');
|
||||
}
|
||||
}
|
||||
@ -2,126 +2,126 @@
|
||||
|
||||
export class CancelError extends Error {
|
||||
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'CancelError';
|
||||
}
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'CancelError';
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return true;
|
||||
}
|
||||
public get isCancelled(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export interface OnCancel {
|
||||
readonly isResolved: boolean;
|
||||
readonly isRejected: boolean;
|
||||
readonly isCancelled: boolean;
|
||||
readonly isResolved: boolean;
|
||||
readonly isRejected: boolean;
|
||||
readonly isCancelled: boolean;
|
||||
|
||||
(cancelHandler: () => void): void;
|
||||
(cancelHandler: () => void): void;
|
||||
}
|
||||
|
||||
export class CancelablePromise<T> implements Promise<T> {
|
||||
readonly [Symbol.toStringTag]: string;
|
||||
readonly [Symbol.toStringTag]: string;
|
||||
|
||||
#isResolved: boolean;
|
||||
#isRejected: boolean;
|
||||
#isCancelled: boolean;
|
||||
readonly #cancelHandlers: (() => void)[];
|
||||
readonly #promise: Promise<T>;
|
||||
#resolve?: (value: T | PromiseLike<T>) => void;
|
||||
#reject?: (reason?: any) => void;
|
||||
#isResolved: boolean;
|
||||
#isRejected: boolean;
|
||||
#isCancelled: boolean;
|
||||
readonly #cancelHandlers: (() => void)[];
|
||||
readonly #promise: Promise<T>;
|
||||
#resolve?: (value: T | PromiseLike<T>) => void;
|
||||
#reject?: (reason?: any) => void;
|
||||
|
||||
constructor(
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason?: any) => void,
|
||||
onCancel: OnCancel
|
||||
) => void
|
||||
) {
|
||||
this.#isResolved = false;
|
||||
this.#isRejected = false;
|
||||
this.#isCancelled = false;
|
||||
this.#cancelHandlers = [];
|
||||
this.#promise = new Promise<T>((resolve, reject) => {
|
||||
this.#resolve = resolve;
|
||||
this.#reject = reject;
|
||||
constructor(
|
||||
executor: (
|
||||
resolve: (value: T | PromiseLike<T>) => void,
|
||||
reject: (reason?: any) => void,
|
||||
onCancel: OnCancel
|
||||
) => void
|
||||
) {
|
||||
this.#isResolved = false;
|
||||
this.#isRejected = false;
|
||||
this.#isCancelled = false;
|
||||
this.#cancelHandlers = [];
|
||||
this.#promise = new Promise<T>((resolve, reject) => {
|
||||
this.#resolve = resolve;
|
||||
this.#reject = reject;
|
||||
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isResolved = true;
|
||||
this.#resolve?.(value);
|
||||
};
|
||||
const onResolve = (value: T | PromiseLike<T>): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isResolved = true;
|
||||
this.#resolve?.(value);
|
||||
};
|
||||
|
||||
const onReject = (reason?: any): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isRejected = true;
|
||||
this.#reject?.(reason);
|
||||
};
|
||||
const onReject = (reason?: any): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isRejected = true;
|
||||
this.#reject?.(reason);
|
||||
};
|
||||
|
||||
const onCancel = (cancelHandler: () => void): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#cancelHandlers.push(cancelHandler);
|
||||
};
|
||||
const onCancel = (cancelHandler: () => void): void => {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#cancelHandlers.push(cancelHandler);
|
||||
};
|
||||
|
||||
Object.defineProperty(onCancel, 'isResolved', {
|
||||
get: (): boolean => this.#isResolved,
|
||||
});
|
||||
Object.defineProperty(onCancel, 'isResolved', {
|
||||
get: (): boolean => this.#isResolved,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isRejected', {
|
||||
get: (): boolean => this.#isRejected,
|
||||
});
|
||||
Object.defineProperty(onCancel, 'isRejected', {
|
||||
get: (): boolean => this.#isRejected,
|
||||
});
|
||||
|
||||
Object.defineProperty(onCancel, 'isCancelled', {
|
||||
get: (): boolean => this.#isCancelled,
|
||||
});
|
||||
Object.defineProperty(onCancel, 'isCancelled', {
|
||||
get: (): boolean => this.#isCancelled,
|
||||
});
|
||||
|
||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||
});
|
||||
}
|
||||
return executor(onResolve, onReject, onCancel as OnCancel);
|
||||
});
|
||||
}
|
||||
|
||||
public then<TResult1 = T, TResult2 = never>(
|
||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this.#promise.then(onFulfilled, onRejected);
|
||||
}
|
||||
public then<TResult1 = T, TResult2 = never>(
|
||||
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
|
||||
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
|
||||
): Promise<TResult1 | TResult2> {
|
||||
return this.#promise.then(onFulfilled, onRejected);
|
||||
}
|
||||
|
||||
public catch<TResult = never>(
|
||||
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
|
||||
): Promise<T | TResult> {
|
||||
return this.#promise.catch(onRejected);
|
||||
}
|
||||
public catch<TResult = never>(
|
||||
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
|
||||
): Promise<T | TResult> {
|
||||
return this.#promise.catch(onRejected);
|
||||
}
|
||||
|
||||
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||
return this.#promise.finally(onFinally);
|
||||
}
|
||||
public finally(onFinally?: (() => void) | null): Promise<T> {
|
||||
return this.#promise.finally(onFinally);
|
||||
}
|
||||
|
||||
public cancel(): void {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isCancelled = true;
|
||||
if (this.#cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this.#cancelHandlers) {
|
||||
cancelHandler();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Cancellation threw an error', error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.#cancelHandlers.length = 0;
|
||||
this.#reject?.(new CancelError('Request aborted'));
|
||||
}
|
||||
public cancel(): void {
|
||||
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
|
||||
return;
|
||||
}
|
||||
this.#isCancelled = true;
|
||||
if (this.#cancelHandlers.length) {
|
||||
try {
|
||||
for (const cancelHandler of this.#cancelHandlers) {
|
||||
cancelHandler();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Cancellation threw an error', error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.#cancelHandlers.length = 0;
|
||||
this.#reject?.(new CancelError('Request aborted'));
|
||||
}
|
||||
|
||||
public get isCancelled(): boolean {
|
||||
return this.#isCancelled;
|
||||
}
|
||||
public get isCancelled(): boolean {
|
||||
return this.#isCancelled;
|
||||
}
|
||||
}
|
||||
|
||||
24
src/templates/core/HttpRequest.hbs
Normal file
24
src/templates/core/HttpRequest.hbs
Normal file
@ -0,0 +1,24 @@
|
||||
{{>header}}
|
||||
|
||||
import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import { BaseHttpRequest } from './BaseHttpRequest';
|
||||
import type { CancelablePromise } from './CancelablePromise';
|
||||
import type { OpenAPIConfig } from './OpenAPI';
|
||||
import { request as __request } from './request';
|
||||
|
||||
export class {{httpRequest}} extends BaseHttpRequest {
|
||||
|
||||
constructor(config: OpenAPIConfig) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request method
|
||||
* @param options The request options from the service
|
||||
* @returns CancelablePromise<T>
|
||||
* @throws ApiError
|
||||
*/
|
||||
public request<T>(options: ApiRequestOptions): CancelablePromise<T> {
|
||||
return __request(this.config, options);
|
||||
}
|
||||
}
|
||||
@ -5,26 +5,26 @@ import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
|
||||
type Headers = Record<string, string>;
|
||||
|
||||
type Config = {
|
||||
BASE: string;
|
||||
VERSION: string;
|
||||
WITH_CREDENTIALS: boolean;
|
||||
CREDENTIALS: 'include' | 'omit' | 'same-origin';
|
||||
TOKEN?: string | Resolver<string>;
|
||||
USERNAME?: string | Resolver<string>;
|
||||
PASSWORD?: string | Resolver<string>;
|
||||
HEADERS?: Headers | Resolver<Headers>;
|
||||
ENCODE_PATH?: (path: string) => string;
|
||||
export type OpenAPIConfig = {
|
||||
BASE: string;
|
||||
VERSION: string;
|
||||
WITH_CREDENTIALS: boolean;
|
||||
CREDENTIALS: 'include' | 'omit' | 'same-origin';
|
||||
TOKEN?: string | Resolver<string>;
|
||||
USERNAME?: string | Resolver<string>;
|
||||
PASSWORD?: string | Resolver<string>;
|
||||
HEADERS?: Headers | Resolver<Headers>;
|
||||
ENCODE_PATH?: (path: string) => string;
|
||||
};
|
||||
|
||||
export const OpenAPI: Config = {
|
||||
BASE: '{{{server}}}',
|
||||
VERSION: '{{{version}}}',
|
||||
WITH_CREDENTIALS: false,
|
||||
CREDENTIALS: 'include',
|
||||
TOKEN: undefined,
|
||||
USERNAME: undefined,
|
||||
PASSWORD: undefined,
|
||||
HEADERS: undefined,
|
||||
ENCODE_PATH: undefined,
|
||||
export const OpenAPI: OpenAPIConfig = {
|
||||
BASE: '{{{server}}}',
|
||||
VERSION: '{{{version}}}',
|
||||
WITH_CREDENTIALS: false,
|
||||
CREDENTIALS: 'include',
|
||||
TOKEN: undefined,
|
||||
USERNAME: undefined,
|
||||
PASSWORD: undefined,
|
||||
HEADERS: undefined,
|
||||
ENCODE_PATH: undefined,
|
||||
};
|
||||
|
||||
@ -1,42 +1,42 @@
|
||||
async function getHeaders(options: ApiRequestOptions): Promise<HttpHeaders> {
|
||||
const token = await resolve(options, OpenAPI.TOKEN);
|
||||
const username = await resolve(options, OpenAPI.USERNAME);
|
||||
const password = await resolve(options, OpenAPI.PASSWORD);
|
||||
const additionalHeaders = await resolve(options, OpenAPI.HEADERS);
|
||||
const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise<Headers> => {
|
||||
const token = await resolve(options, config.TOKEN);
|
||||
const username = await resolve(options, config.USERNAME);
|
||||
const password = await resolve(options, config.PASSWORD);
|
||||
const additionalHeaders = await resolve(options, config.HEADERS);
|
||||
|
||||
const defaultHeaders = Object.entries({
|
||||
Accept: 'application/json',
|
||||
...additionalHeaders,
|
||||
...options.headers,
|
||||
})
|
||||
.filter(([_, value]) => isDefined(value))
|
||||
.reduce((headers, [key, value]) => ({
|
||||
...headers,
|
||||
[key]: String(value),
|
||||
}), {} as Record<string, string>);
|
||||
const defaultHeaders = Object.entries({
|
||||
Accept: 'application/json',
|
||||
...additionalHeaders,
|
||||
...options.headers,
|
||||
})
|
||||
.filter(([_, value]) => isDefined(value))
|
||||
.reduce((headers, [key, value]) => ({
|
||||
...headers,
|
||||
[key]: String(value),
|
||||
}), {} as Record<string, string>);
|
||||
|
||||
const headers = new HttpHeaders(defaultHeaders);
|
||||
const headers = new Headers(defaultHeaders);
|
||||
|
||||
if (isStringWithValue(token)) {
|
||||
headers.append('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
if (isStringWithValue(token)) {
|
||||
headers.append('Authorization', `Bearer ${token}`);
|
||||
}
|
||||
|
||||
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||
const credentials = base64(`${username}:${password}`);
|
||||
headers.append('Authorization', `Basic ${credentials}`);
|
||||
}
|
||||
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||
const credentials = base64(`${username}:${password}`);
|
||||
headers.append('Authorization', `Basic ${credentials}`);
|
||||
}
|
||||
|
||||
if (options.body) {
|
||||
if (options.mediaType) {
|
||||
headers.append('Content-Type', options.mediaType);
|
||||
} else if (isBlob(options.body)) {
|
||||
headers.append('Content-Type', options.body.type || 'application/octet-stream');
|
||||
} else if (isString(options.body)) {
|
||||
headers.append('Content-Type', 'text/plain');
|
||||
} else if (!isFormData(options.body)) {
|
||||
headers.append('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
if (options.body) {
|
||||
if (options.mediaType) {
|
||||
headers.append('Content-Type', options.mediaType);
|
||||
} else if (isBlob(options.body)) {
|
||||
headers.append('Content-Type', options.body.type || 'application/octet-stream');
|
||||
} else if (isString(options.body)) {
|
||||
headers.append('Content-Type', 'text/plain');
|
||||
} else if (!isFormData(options.body)) {
|
||||
headers.append('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
return headers;
|
||||
};
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
|
||||
if (options.body) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
} else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
|
||||
return options.body;
|
||||
} else {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const getRequestBody = (options: ApiRequestOptions): BodyInit | undefined => {
|
||||
if (options.body) {
|
||||
if (options.mediaType?.includes('/json')) {
|
||||
return JSON.stringify(options.body)
|
||||
} else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
|
||||
return options.body;
|
||||
} else {
|
||||
return JSON.stringify(options.body);
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
async function getResponseBody(response: Response): Promise<any> {
|
||||
if (response.status !== 204) {
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
const isJSON = contentType.toLowerCase().startsWith('application/json');
|
||||
if (isJSON) {
|
||||
return await response.json();
|
||||
} else {
|
||||
return await response.text();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const getResponseBody = async (response: Response): Promise<any> => {
|
||||
if (response.status !== 204) {
|
||||
try {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType) {
|
||||
const isJSON = contentType.toLowerCase().startsWith('application/json');
|
||||
if (isJSON) {
|
||||
return await response.json();
|
||||
} else {
|
||||
return await response.text();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
function getResponseHeader(response: Response, responseHeader?: string): string | undefined {
|
||||
if (responseHeader) {
|
||||
const content = response.headers.get(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => {
|
||||
if (responseHeader) {
|
||||
const content = response.headers.get(responseHeader);
|
||||
if (isString(content)) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
@ -8,7 +8,7 @@ import type { ApiRequestOptions } from './ApiRequestOptions';
|
||||
import type { ApiResult } from './ApiResult';
|
||||
import { CancelablePromise } from './CancelablePromise';
|
||||
import type { OnCancel } from './CancelablePromise';
|
||||
import { OpenAPI } from './OpenAPI';
|
||||
import type { OpenAPIConfig } from './OpenAPI';
|
||||
|
||||
{{>functions/isDefined}}
|
||||
|
||||
@ -59,39 +59,39 @@ import { OpenAPI } from './OpenAPI';
|
||||
|
||||
|
||||
/**
|
||||
* Request using fetch client
|
||||
* @param http The Angular HTTP client
|
||||
* Request method
|
||||
* @param config The OpenAPI configuration object
|
||||
* @param options The request options from the service
|
||||
* @returns CancelablePromise<T>
|
||||
* @throws ApiError
|
||||
*/
|
||||
export function request<T>(http: HttpClient, options: ApiRequestOptions): Observable<T> {
|
||||
return new CancelablePromise<T>(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(options);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(options);
|
||||
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
|
||||
return new CancelablePromise(async (resolve, reject, onCancel) => {
|
||||
try {
|
||||
const url = getUrl(config, options);
|
||||
const formData = getFormData(options);
|
||||
const body = getRequestBody(options);
|
||||
const headers = await getHeaders(config, options);
|
||||
|
||||
if (!onCancel.isCancelled) {
|
||||
const response = await sendRequest(options, url, formData, body, headers, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
if (!onCancel.isCancelled) {
|
||||
const response = await sendRequest(config, options, url, formData, body, headers, onCancel);
|
||||
const responseBody = await getResponseBody(response);
|
||||
const responseHeader = getResponseHeader(response, options.responseHeader);
|
||||
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader || responseBody,
|
||||
};
|
||||
const result: ApiResult = {
|
||||
url,
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: responseHeader ?? responseBody,
|
||||
};
|
||||
|
||||
catchErrors(options, result);
|
||||
catchErrors(options, result);
|
||||
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
resolve(result.body);
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,25 +1,26 @@
|
||||
async function sendRequest(
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
formData: FormData | undefined,
|
||||
body: BodyInit | undefined,
|
||||
headers: Headers,
|
||||
onCancel: OnCancel
|
||||
): Promise<Response> {
|
||||
const controller = new AbortController();
|
||||
export const sendRequest = async (
|
||||
config: OpenAPIConfig,
|
||||
options: ApiRequestOptions,
|
||||
url: string,
|
||||
formData: FormData | undefined,
|
||||
body: BodyInit | undefined,
|
||||
headers: Headers,
|
||||
onCancel: OnCancel
|
||||
): Promise<Response> => {
|
||||
const controller = new AbortController();
|
||||
|
||||
const request: RequestInit = {
|
||||
headers,
|
||||
body: body || formData,
|
||||
method: options.method,
|
||||
signal: controller.signal,
|
||||
};
|
||||
const request: RequestInit = {
|
||||
headers,
|
||||
body: body || formData,
|
||||
method: options.method,
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
if (OpenAPI.WITH_CREDENTIALS) {
|
||||
request.credentials = OpenAPI.CREDENTIALS;
|
||||
}
|
||||
if (config.WITH_CREDENTIALS) {
|
||||
request.credentials = config.CREDENTIALS;
|
||||
}
|
||||
|
||||
onCancel(() => controller.abort());
|
||||
onCancel(() => controller.abort());
|
||||
|
||||
return await fetch(url, request);
|
||||
}
|
||||
return await fetch(url, request);
|
||||
};
|
||||
|
||||
@ -1,30 +1,30 @@
|
||||
async function getHeaders(options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> {
|
||||
const token = await resolve(options, OpenAPI.TOKEN);
|
||||
const username = await resolve(options, OpenAPI.USERNAME);
|
||||
const password = await resolve(options, OpenAPI.PASSWORD);
|
||||
const additionalHeaders = await resolve(options, OpenAPI.HEADERS);
|
||||
const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
|
||||
const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise<Record<string, string>> => {
|
||||
const token = await resolve(options, config.TOKEN);
|
||||
const username = await resolve(options, config.USERNAME);
|
||||
const password = await resolve(options, config.PASSWORD);
|
||||
const additionalHeaders = await resolve(options, config.HEADERS);
|
||||
const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {}
|
||||
|
||||
const headers = Object.entries({
|
||||
Accept: 'application/json',
|
||||
...additionalHeaders,
|
||||
...options.headers,
|
||||
...formHeaders,
|
||||
})
|
||||
.filter(([_, value]) => isDefined(value))
|
||||
.reduce((headers, [key, value]) => ({
|
||||
...headers,
|
||||
[key]: String(value),
|
||||
}), {} as Record<string, string>);
|
||||
const headers = Object.entries({
|
||||
Accept: 'application/json',
|
||||
...additionalHeaders,
|
||||
...options.headers,
|
||||
...formHeaders,
|
||||
})
|
||||
.filter(([_, value]) => isDefined(value))
|
||||
.reduce((headers, [key, value]) => ({
|
||||
...headers,
|
||||
[key]: String(value),
|
||||
}), {} as Record<string, string>);
|
||||
|
||||
if (isStringWithValue(token)) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
if (isStringWithValue(token)) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||
const credentials = base64(`${username}:${password}`);
|
||||
headers['Authorization'] = `Basic ${credentials}`;
|
||||
}
|
||||
if (isStringWithValue(username) && isStringWithValue(password)) {
|
||||
const credentials = base64(`${username}:${password}`);
|
||||
headers['Authorization'] = `Basic ${credentials}`;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
return headers;
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
function getRequestBody(options: ApiRequestOptions): any {
|
||||
if (options.body) {
|
||||
return options.body;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const getRequestBody = (options: ApiRequestOptions): any => {
|
||||
if (options.body) {
|
||||
return options.body;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
function getResponseBody(response: AxiosResponse<any>): any {
|
||||
if (response.status !== 204) {
|
||||
return response.data;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const getResponseBody = (response: AxiosResponse<any>): any => {
|
||||
if (response.status !== 204) {
|
||||
return response.data;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user