- Working on formatter for spacing

This commit is contained in:
Ferdi Koomen 2022-01-25 00:00:47 +01:00
parent ad854a9738
commit 6f2a714a15
60 changed files with 860 additions and 843 deletions

View File

@ -7,3 +7,6 @@ trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
[*.hbs]
indent_style = tab

View File

@ -1,4 +1,5 @@
dist
samples
test/generated
test/e2e/generated
node_modules

View File

@ -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;
}
}

View File

@ -1,14 +1,14 @@
{{>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 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>;
};

View File

@ -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;
};

View File

@ -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;
}
}

View File

@ -6,25 +6,25 @@ 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;
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,
BASE: '{{{server}}}',
VERSION: '{{{version}}}',
WITH_CREDENTIALS: false,
CREDENTIALS: 'include',
TOKEN: undefined,
USERNAME: undefined,
PASSWORD: undefined,
HEADERS: undefined,
ENCODE_PATH: undefined,
};

View File

@ -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 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 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;
}

View File

@ -1,6 +1,6 @@
function getRequestBody(options: ApiRequestOptions): any {
if (options.body) {
return options.body;
}
return;
if (options.body) {
return options.body;
}
return;
}

View File

@ -1,6 +1,6 @@
function getResponseBody(response: AxiosResponse<any>): any {
if (response.status !== 204) {
return response.data;
}
return;
if (response.status !== 204) {
return response.data;
}
return;
}

View File

@ -1,9 +1,9 @@
function getResponseHeader(response: AxiosResponse<any>, responseHeader?: string): string | undefined {
if (responseHeader) {
const content = response.headers[responseHeader];
if (isString(content)) {
return content;
}
}
return;
if (responseHeader) {
const content = response.headers[responseHeader];
if (isString(content)) {
return content;
}
}
return;
}

View File

@ -65,33 +65,33 @@ import { OpenAPI } from './OpenAPI';
* @throws ApiError
*/
export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(options, formData);
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(options, formData);
if (!onCancel.isCancelled) {
const response = await sendRequest(options, url, formData, body, headers, onCancel);
const responseBody = getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
if (!onCancel.isCancelled) {
const response = await sendRequest(options, url, formData, body, headers, onCancel);
const responseBody = getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
const result: ApiResult = {
url,
ok: isSuccess(response.status),
status: response.status,
statusText: response.statusText,
body: responseHeader || responseBody,
};
const result: ApiResult = {
url,
ok: isSuccess(response.status),
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);
}
});
}

View File

@ -1,31 +1,31 @@
async function sendRequest(
options: ApiRequestOptions,
url: string,
formData: FormData | undefined,
body: any,
headers: Record<string, string>,
onCancel: OnCancel
options: ApiRequestOptions,
url: string,
formData: FormData | undefined,
body: any,
headers: Record<string, string>,
onCancel: OnCancel
): Promise<AxiosResponse<any>> {
const source = axios.CancelToken.source();
const source = axios.CancelToken.source();
const config: AxiosRequestConfig = {
url,
headers,
data: body || formData,
method: options.method,
withCredentials: OpenAPI.WITH_CREDENTIALS,
cancelToken: source.token,
};
const config: AxiosRequestConfig = {
url,
headers,
data: body || formData,
method: options.method,
withCredentials: OpenAPI.WITH_CREDENTIALS,
cancelToken: source.token,
};
onCancel(() => source.cancel('The user aborted a request.'));
onCancel(() => source.cancel('The user aborted a request.'));
try {
return await axios.request(config);
} catch (error) {
const axiosError = error as AxiosError;
if (axiosError.response) {
return axiosError.response;
}
throw error;
}
try {
return await axios.request(config);
} catch (error) {
const axiosError = error as AxiosError;
if (axiosError.response) {
return axiosError.response;
}
throw error;
}
}

View File

@ -1,42 +1,42 @@
async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
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 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 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 Headers(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;
}

View File

@ -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;
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;
}

View File

@ -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;
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;
}

View File

@ -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;
if (responseHeader) {
const content = response.headers.get(responseHeader);
if (isString(content)) {
return content;
}
}
return;
}

View File

@ -62,32 +62,32 @@ import { OpenAPI } from './OpenAPI';
* @throws ApiError
*/
export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(options);
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(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(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);
}
});
}

View File

@ -1,25 +1,25 @@
async function sendRequest(
options: ApiRequestOptions,
url: string,
formData: FormData | undefined,
body: BodyInit | undefined,
headers: Headers,
onCancel: OnCancel
options: ApiRequestOptions,
url: string,
formData: FormData | undefined,
body: BodyInit | undefined,
headers: Headers,
onCancel: OnCancel
): Promise<Response> {
const controller = new AbortController();
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 (OpenAPI.WITH_CREDENTIALS) {
request.credentials = OpenAPI.CREDENTIALS;
}
onCancel(() => controller.abort());
onCancel(() => controller.abort());
return await fetch(url, request);
return await fetch(url, request);
}

View File

@ -1,8 +1,8 @@
function base64(str: string): string {
try {
return btoa(str);
} catch (err) {
// @ts-ignore
return Buffer.from(str).toString('base64');
}
try {
return btoa(str);
} catch (err) {
// @ts-ignore
return Buffer.from(str).toString('base64');
}
}

View File

@ -1,21 +1,21 @@
function catchErrors(options: ApiRequestOptions, result: ApiResult): void {
const errors: Record<number, string> = {
400: 'Bad Request',
401: 'Unauthorized',
403: 'Forbidden',
404: 'Not Found',
500: 'Internal Server Error',
502: 'Bad Gateway',
503: 'Service Unavailable',
...options.errors,
}
const errors: Record<number, string> = {
400: 'Bad Request',
401: 'Unauthorized',
403: 'Forbidden',
404: 'Not Found',
500: 'Internal Server Error',
502: 'Bad Gateway',
503: 'Service Unavailable',
...options.errors,
}
const error = errors[result.status];
if (error) {
throw new ApiError(result, error);
}
const error = errors[result.status];
if (error) {
throw new ApiError(result, error);
}
if (!result.ok) {
throw new ApiError(result, 'Generic Error');
}
if (!result.ok) {
throw new ApiError(result, 'Generic Error');
}
}

View File

@ -1,26 +1,26 @@
function getFormData(options: ApiRequestOptions): FormData | undefined {
if (options.formData) {
const formData = new FormData();
if (options.formData) {
const formData = new FormData();
const process = (key: string, value: any) => {
if (isString(value) || isBlob(value)) {
formData.append(key, value);
} else {
formData.append(key, JSON.stringify(value));
}
};
const process = (key: string, value: any) => {
if (isString(value) || isBlob(value)) {
formData.append(key, value);
} else {
formData.append(key, JSON.stringify(value));
}
};
Object.entries(options.formData)
.filter(([_, value]) => isDefined(value))
.forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach(v => process(key, v));
} else {
process(key, value);
}
});
Object.entries(options.formData)
.filter(([_, value]) => isDefined(value))
.forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach(v => process(key, v));
} else {
process(key, value);
}
});
return formData;
}
return;
return formData;
}
return;
}

View File

@ -1,30 +1,30 @@
function getQueryString(params: Record<string, any>): string {
const searchParams = new URLSearchParams();
const searchParams = new URLSearchParams();
const process = (key: string, value: any) => {
if (isDefined(value)) {
if (Array.isArray(value)) {
value.forEach(v => {
process(key, v);
});
} else if (typeof value === 'object') {
Object.entries(value).forEach(([k, v]) => {
process(`${key}[${k}]`, v);
});
} else {
searchParams.append(key, value);
}
}
};
const process = (key: string, value: any) => {
if (isDefined(value)) {
if (Array.isArray(value)) {
value.forEach(v => {
process(key, v);
});
} else if (typeof value === 'object') {
Object.entries(value).forEach(([k, v]) => {
process(`${key}[${k}]`, v);
});
} else {
searchParams.append(key, value);
}
}
};
Object.entries(params).forEach(([key, value]) => {
process(key, value);
});
Object.entries(params).forEach(([key, value]) => {
process(key, value);
});
const query = searchParams.toString();
if (query.length) {
return `?${query}`;
}
const query = searchParams.toString();
if (query.length) {
return `?${query}`;
}
return '';
return '';
}

View File

@ -1,9 +1,9 @@
function getUrl(options: ApiRequestOptions): string {
const path = OpenAPI.ENCODE_PATH ? OpenAPI.ENCODE_PATH(options.path) : options.path;
const url = `${OpenAPI.BASE}${path}`;
if (options.query) {
return `${url}${getQueryString(options.query)}`;
}
const path = OpenAPI.ENCODE_PATH ? OpenAPI.ENCODE_PATH(options.path) : options.path;
const url = `${OpenAPI.BASE}${path}`;
if (options.query) {
return `${url}${getQueryString(options.query)}`;
}
return url;
return url;
}

View File

@ -1,12 +1,12 @@
function isBlob(value: any): value is Blob {
return (
typeof value === 'object' &&
typeof value.type === 'string' &&
typeof value.stream === 'function' &&
typeof value.arrayBuffer === 'function' &&
typeof value.constructor === 'function' &&
typeof value.constructor.name === 'string' &&
/^(Blob|File)$/.test(value.constructor.name) &&
/^(Blob|File)$/.test(value[Symbol.toStringTag])
);
return (
typeof value === 'object' &&
typeof value.type === 'string' &&
typeof value.stream === 'function' &&
typeof value.arrayBuffer === 'function' &&
typeof value.constructor === 'function' &&
typeof value.constructor.name === 'string' &&
/^(Blob|File)$/.test(value.constructor.name) &&
/^(Blob|File)$/.test(value[Symbol.toStringTag])
);
}

View File

@ -1,3 +1,3 @@
function isDefined<T>(value: T | null | undefined): value is Exclude<T, null | undefined> {
return value !== undefined && value !== null;
return value !== undefined && value !== null;
}

View File

@ -1,3 +1,3 @@
function isFormData(value: any): value is FormData {
return value instanceof FormData;
}
return value instanceof FormData;
}

View File

@ -1,3 +1,3 @@
function isString(value: any): value is string {
return typeof value === 'string';
return typeof value === 'string';
}

View File

@ -1,3 +1,3 @@
function isStringWithValue(value: any): value is string {
return isString(value) && value !== '';
return isString(value) && value !== '';
}

View File

@ -1,3 +1,3 @@
function isSuccess(status: number): boolean {
return status >= 200 && status < 300;
return status >= 200 && status < 300;
}

View File

@ -1,8 +1,8 @@
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
async function resolve<T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> {
if (typeof resolver === 'function') {
return (resolver as Resolver<T>)(options);
}
return resolver;
if (typeof resolver === 'function') {
return (resolver as Resolver<T>)(options);
}
return resolver;
}

View File

@ -1,41 +1,41 @@
async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
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 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 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 Headers(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', '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;
if (options.body) {
if (options.mediaType) {
headers.append('Content-Type', options.mediaType);
} else if (isBlob(options.body)) {
headers.append('Content-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;
}

View File

@ -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 as any;
} else {
return JSON.stringify(options.body);
}
}
return;
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 as any;
} else {
return JSON.stringify(options.body);
}
}
return;
}

View File

@ -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;
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;
}

View File

@ -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;
if (responseHeader) {
const content = response.headers.get(responseHeader);
if (isString(content)) {
return content;
}
}
return;
}

View File

@ -66,32 +66,32 @@ import { OpenAPI } from './OpenAPI';
* @throws ApiError
*/
export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(options);
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(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(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);
}
});
}

View File

@ -1,21 +1,21 @@
async function sendRequest(
options: ApiRequestOptions,
url: string,
formData: FormData | undefined,
body: BodyInit | undefined,
headers: Headers,
onCancel: OnCancel
options: ApiRequestOptions,
url: string,
formData: FormData | undefined,
body: BodyInit | undefined,
headers: Headers,
onCancel: OnCancel
): Promise<Response> {
const controller = new AbortController();
const controller = new AbortController();
const request: RequestInit = {
headers,
method: options.method,
body: body || formData,
signal: controller.signal,
};
const request: RequestInit = {
headers,
method: options.method,
body: body || formData,
signal: controller.signal,
};
onCancel(() => controller.abort());
onCancel(() => controller.abort());
return await fetch(url, request);
return await fetch(url, request);
}

View File

@ -1,41 +1,41 @@
async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
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 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 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 Headers(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');
}
}
return headers;
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;
}

View File

@ -1,13 +1,13 @@
function getRequestBody(options: ApiRequestOptions): any {
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);
}
}
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;
return;
}

View File

@ -1,18 +1,18 @@
function getResponseBody(xhr: XMLHttpRequest): any {
if (xhr.status !== 204) {
try {
const contentType = xhr.getResponseHeader('Content-Type');
if (contentType) {
const isJSON = contentType.toLowerCase().startsWith('application/json');
if (isJSON) {
return JSON.parse(xhr.responseText);
} else {
return xhr.responseText;
}
}
} catch (error) {
console.error(error);
}
}
return;
if (xhr.status !== 204) {
try {
const contentType = xhr.getResponseHeader('Content-Type');
if (contentType) {
const isJSON = contentType.toLowerCase().startsWith('application/json');
if (isJSON) {
return JSON.parse(xhr.responseText);
} else {
return xhr.responseText;
}
}
} catch (error) {
console.error(error);
}
}
return;
}

View File

@ -1,9 +1,9 @@
function getResponseHeader(xhr: XMLHttpRequest, responseHeader?: string): string | undefined {
if (responseHeader) {
const content = xhr.getResponseHeader(responseHeader);
if (isString(content)) {
return content;
}
}
return;
if (responseHeader) {
const content = xhr.getResponseHeader(responseHeader);
if (isString(content)) {
return content;
}
}
return;
}

View File

@ -65,32 +65,32 @@ import { OpenAPI } from './OpenAPI';
* @throws ApiError
*/
export function request<T>(options: ApiRequestOptions): CancelablePromise<T> {
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(options);
return new CancelablePromise(async (resolve, reject, onCancel) => {
try {
const url = getUrl(options);
const formData = getFormData(options);
const body = getRequestBody(options);
const headers = await getHeaders(options);
if (!onCancel.isCancelled) {
const response = await sendRequest(options, url, formData, body, headers, onCancel);
const responseBody = getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
if (!onCancel.isCancelled) {
const response = await sendRequest(options, url, formData, body, headers, onCancel);
const responseBody = getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
const result: ApiResult = {
url,
ok: isSuccess(response.status),
status: response.status,
statusText: response.statusText,
body: responseHeader || responseBody,
};
const result: ApiResult = {
url,
ok: isSuccess(response.status),
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);
}
});
}

View File

@ -1,25 +1,25 @@
async function sendRequest(
options: ApiRequestOptions,
url: string,
formData: FormData | undefined,
body: any,
headers: Headers,
onCancel: OnCancel
options: ApiRequestOptions,
url: string,
formData: FormData | undefined,
body: any,
headers: Headers,
onCancel: OnCancel
): Promise<XMLHttpRequest> {
const xhr = new XMLHttpRequest();
xhr.open(options.method, url, true);
xhr.withCredentials = OpenAPI.WITH_CREDENTIALS;
const xhr = new XMLHttpRequest();
xhr.open(options.method, url, true);
xhr.withCredentials = OpenAPI.WITH_CREDENTIALS;
headers.forEach((value, key) => {
xhr.setRequestHeader(key, value);
});
headers.forEach((value, key) => {
xhr.setRequestHeader(key, value);
});
return new Promise<XMLHttpRequest>((resolve, reject) => {
xhr.onload = () => resolve(xhr);
xhr.onabort = () => reject(new Error('Request aborted'));
xhr.onerror = () => reject(new Error('Network error'));
xhr.send(body || formData);
return new Promise<XMLHttpRequest>((resolve, reject) => {
xhr.onload = () => resolve(xhr);
xhr.onabort = () => reject(new Error('Request aborted'));
xhr.onerror = () => reject(new Error('Network error'));
xhr.send(body || formData);
onCancel(() => xhr.abort());
});
onCancel(() => xhr.abort());
});
}

View File

@ -13,84 +13,84 @@ import { OpenAPI } from '../core/OpenAPI';
export class {{{name}}}{{{@root.postfix}}} {
{{#each operations}}
/**
{{#if deprecated}}
* @deprecated
{{/if}}
{{#if summary}}
* {{{escapeComment summary}}}
{{/if}}
{{#if description}}
* {{{escapeComment description}}}
{{/if}}
{{#unless @root.useOptions}}
{{#if parameters}}
{{#each parameters}}
* @param {{{name}}} {{#if description}}{{{escapeComment description}}}{{/if}}
{{/each}}
{{/if}}
{{/unless}}
{{#each results}}
* @returns {{{type}}} {{#if description}}{{{escapeComment description}}}{{/if}}
{{/each}}
* @throws ApiError
*/
public static {{{name}}}({{>parameters}}): CancelablePromise<{{>result}}> {
return __request({
method: '{{{method}}}',
path: `{{{path}}}`,
{{#if parametersCookie}}
cookies: {
{{#each parametersCookie}}
'{{{prop}}}': {{{name}}},
{{/each}}
},
{{/if}}
{{#if parametersHeader}}
headers: {
{{#each parametersHeader}}
'{{{prop}}}': {{{name}}},
{{/each}}
},
{{/if}}
{{#if parametersQuery}}
query: {
{{#each parametersQuery}}
'{{{prop}}}': {{{name}}},
{{/each}}
},
{{/if}}
{{#if parametersForm}}
formData: {
{{#each parametersForm}}
'{{{prop}}}': {{{name}}},
{{/each}}
},
{{/if}}
{{#if parametersBody}}
{{#equals parametersBody.in 'formData'}}
formData: {{{parametersBody.name}}},
{{/equals}}
{{#equals parametersBody.in 'body'}}
body: {{{parametersBody.name}}},
{{/equals}}
{{#if parametersBody.mediaType}}
mediaType: '{{{parametersBody.mediaType}}}',
{{/if}}
{{/if}}
{{#if responseHeader}}
responseHeader: '{{{responseHeader}}}',
{{/if}}
{{#if errors}}
errors: {
{{#each errors}}
{{{code}}}: `{{{description}}}`,
{{/each}}
},
{{/if}}
});
}
{{#each operations}}
/**
{{#if deprecated}}
* @deprecated
{{/if}}
{{#if summary}}
* {{{escapeComment summary}}}
{{/if}}
{{#if description}}
* {{{escapeComment description}}}
{{/if}}
{{#unless @root.useOptions}}
{{#if parameters}}
{{#each parameters}}
* @param {{{name}}} {{#if description}}{{{escapeComment description}}}{{/if}}
{{/each}}
{{/if}}
{{/unless}}
{{#each results}}
* @returns {{{type}}} {{#if description}}{{{escapeComment description}}}{{/if}}
{{/each}}
* @throws ApiError
*/
public static {{{name}}}({{>parameters}}): CancelablePromise<{{>result}}> {
return __request({
method: '{{{method}}}',
path: `{{{path}}}`,
{{#if parametersCookie}}
cookies: {
{{#each parametersCookie}}
'{{{prop}}}': {{{name}}},
{{/each}}
},
{{/if}}
{{#if parametersHeader}}
headers: {
{{#each parametersHeader}}
'{{{prop}}}': {{{name}}},
{{/each}}
},
{{/if}}
{{#if parametersQuery}}
query: {
{{#each parametersQuery}}
'{{{prop}}}': {{{name}}},
{{/each}}
},
{{/if}}
{{#if parametersForm}}
formData: {
{{#each parametersForm}}
'{{{prop}}}': {{{name}}},
{{/each}}
},
{{/if}}
{{#if parametersBody}}
{{#equals parametersBody.in 'formData'}}
formData: {{{parametersBody.name}}},
{{/equals}}
{{#equals parametersBody.in 'body'}}
body: {{{parametersBody.name}}},
{{/equals}}
{{#if parametersBody.mediaType}}
mediaType: '{{{parametersBody.mediaType}}}',
{{/if}}
{{/if}}
{{#if responseHeader}}
responseHeader: '{{{responseHeader}}}',
{{/if}}
{{#if errors}}
errors: {
{{#each errors}}
{{{code}}}: `{{{description}}}`,
{{/each}}
},
{{/if}}
});
}
{{/each}}
{{/each}}
}

View File

@ -9,19 +9,19 @@ export type {{{name}}} = {{>type parent=name}};
export namespace {{{name}}} {
{{#each enums}}
{{#if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
export enum {{{name}}} {
{{#each enum}}
{{{name}}} = {{{value}}},
{{/each}}
}
{{#each enums}}
{{#if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
export enum {{{name}}} {
{{#each enum}}
{{{name}}} = {{{value}}},
{{/each}}
}
{{/each}}
{{/each}}
}
{{/unless}}

View File

@ -4,16 +4,16 @@
*/
{{/if}}
export enum {{{name}}} {
{{#each enum}}
{{#if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
{{#containsSpaces name}}
"{{{name}}}" = {{{value}}},
{{else}}
{{{name}}} = {{{value}}},
{{/containsSpaces}}
{{/each}}
{{#each enum}}
{{#if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
{{#containsSpaces name}}
"{{{name}}}" = {{{value}}},
{{else}}
{{{name}}} = {{{value}}},
{{/containsSpaces}}
{{/each}}
}

View File

@ -4,33 +4,33 @@
*/
{{/if}}
export type {{{name}}} = {
{{#each properties}}
{{#if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
{{>isReadOnly}}{{{name}}}{{>isRequired}}: {{>type parent=../name}};
{{/each}}
{{#each properties}}
{{#if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
{{>isReadOnly}}{{{name}}}{{>isRequired}}: {{>type parent=../name}};
{{/each}}
};
{{#if enums}}
{{#unless @root.useUnionTypes}}
export namespace {{{name}}} {
{{#each enums}}
{{#if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
export enum {{{name}}} {
{{#each enum}}
{{{name}}} = {{{value}}},
{{/each}}
}
{{#each enums}}
{{#if description}}
/**
* {{{escapeComment description}}}
*/
{{/if}}
export enum {{{name}}} {
{{#each enum}}
{{{name}}} = {{{value}}},
{{/each}}
}
{{/each}}
{{/each}}
}
{{/unless}}

View File

@ -1,19 +1,19 @@
{
type: 'array',
type: 'array',
{{#if link}}
contains: {{>schema link}},
contains: {{>schema link}},
{{else}}
contains: {
type: '{{{base}}}',
},
contains: {
type: '{{{base}}}',
},
{{/if}}
{{#if isReadOnly}}
isReadOnly: {{{isReadOnly}}},
isReadOnly: {{{isReadOnly}}},
{{/if}}
{{#if isRequired}}
isRequired: {{{isRequired}}},
isRequired: {{{isRequired}}},
{{/if}}
{{#if isNullable}}
isNullable: {{{isNullable}}},
isNullable: {{{isNullable}}},
{{/if}}
}

View File

@ -1,16 +1,16 @@
{
type: '{{export}}',
type: '{{export}}',
{{#if description}}
description: `{{{escapeDescription description}}}`,
description: `{{{escapeDescription description}}}`,
{{/if}}
contains: [{{#each properties}}{{>schema}}{{#unless @last}}, {{/unless}}{{/each}}],
contains: [{{#each properties}}{{>schema}}{{#unless @last}}, {{/unless}}{{/each}}],
{{#if isReadOnly}}
isReadOnly: {{{isReadOnly}}},
isReadOnly: {{{isReadOnly}}},
{{/if}}
{{#if isRequired}}
isRequired: {{{isRequired}}},
isRequired: {{{isRequired}}},
{{/if}}
{{#if isNullable}}
isNullable: {{{isNullable}}},
isNullable: {{{isNullable}}},
{{/if}}
}

View File

@ -1,19 +1,19 @@
{
type: 'dictionary',
type: 'dictionary',
{{#if link}}
contains: {{>schema link}},
contains: {{>schema link}},
{{else}}
contains: {
type: '{{{base}}}',
},
contains: {
type: '{{{base}}}',
},
{{/if}}
{{#if isReadOnly}}
isReadOnly: {{{isReadOnly}}},
isReadOnly: {{{isReadOnly}}},
{{/if}}
{{#if isRequired}}
isRequired: {{{isRequired}}},
isRequired: {{{isRequired}}},
{{/if}}
{{#if isNullable}}
isNullable: {{{isNullable}}},
isNullable: {{{isNullable}}},
{{/if}}
}

View File

@ -1,12 +1,12 @@
{
type: 'Enum',
type: 'Enum',
{{#if isReadOnly}}
isReadOnly: {{{isReadOnly}}},
isReadOnly: {{{isReadOnly}}},
{{/if}}
{{#if isRequired}}
isRequired: {{{isRequired}}},
isRequired: {{{isRequired}}},
{{/if}}
{{#if isNullable}}
isNullable: {{{isNullable}}},
isNullable: {{{isNullable}}},
{{/if}}
}

View File

@ -1,59 +1,59 @@
{
{{#if type}}
type: '{{{type}}}',
type: '{{{type}}}',
{{/if}}
{{#if description}}
description: `{{{escapeDescription description}}}`,
description: `{{{escapeDescription description}}}`,
{{/if}}
{{#if isReadOnly}}
isReadOnly: {{{isReadOnly}}},
isReadOnly: {{{isReadOnly}}},
{{/if}}
{{#if isRequired}}
isRequired: {{{isRequired}}},
isRequired: {{{isRequired}}},
{{/if}}
{{#if isNullable}}
isNullable: {{{isNullable}}},
isNullable: {{{isNullable}}},
{{/if}}
{{#if format}}
format: '{{{format}}}',
format: '{{{format}}}',
{{/if}}
{{#if maximum}}
maximum: {{{maximum}}},
maximum: {{{maximum}}},
{{/if}}
{{#if exclusiveMaximum}}
exclusiveMaximum: {{{exclusiveMaximum}}},
exclusiveMaximum: {{{exclusiveMaximum}}},
{{/if}}
{{#if minimum}}
minimum: {{{minimum}}},
minimum: {{{minimum}}},
{{/if}}
{{#if exclusiveMinimum}}
exclusiveMinimum: {{{exclusiveMinimum}}},
exclusiveMinimum: {{{exclusiveMinimum}}},
{{/if}}
{{#if multipleOf}}
multipleOf: {{{multipleOf}}},
multipleOf: {{{multipleOf}}},
{{/if}}
{{#if maxLength}}
maxLength: {{{maxLength}}},
maxLength: {{{maxLength}}},
{{/if}}
{{#if minLength}}
minLength: {{{minLength}}},
minLength: {{{minLength}}},
{{/if}}
{{#if pattern}}
pattern: '{{{pattern}}}',
pattern: '{{{pattern}}}',
{{/if}}
{{#if maxItems}}
maxItems: {{{maxItems}}},
maxItems: {{{maxItems}}},
{{/if}}
{{#if minItems}}
minItems: {{{minItems}}},
minItems: {{{minItems}}},
{{/if}}
{{#if uniqueItems}}
uniqueItems: {{{uniqueItems}}},
uniqueItems: {{{uniqueItems}}},
{{/if}}
{{#if maxProperties}}
maxProperties: {{{maxProperties}}},
maxProperties: {{{maxProperties}}},
{{/if}}
{{#if minProperties}}
minProperties: {{{minProperties}}},
minProperties: {{{minProperties}}},
{{/if}}
}

View File

@ -1,21 +1,21 @@
{
{{#if description}}
description: `{{{escapeDescription description}}}`,
description: `{{{escapeDescription description}}}`,
{{/if}}
properties: {
properties: {
{{#if properties}}
{{#each properties}}
{{{name}}}: {{>schema}},
{{/each}}
{{#each properties}}
{{{name}}}: {{>schema}},
{{/each}}
{{/if}}
},
},
{{#if isReadOnly}}
isReadOnly: {{{isReadOnly}}},
isReadOnly: {{{isReadOnly}}},
{{/if}}
{{#if isRequired}}
isRequired: {{{isRequired}}},
isRequired: {{{isRequired}}},
{{/if}}
{{#if isNullable}}
isNullable: {{{isNullable}}},
isNullable: {{{isNullable}}},
{{/if}}
}

View File

@ -14,8 +14,8 @@ bar: 123
}`;
const output3 = `{
foo: true,
bar: 123
\tfoo: true,
\tbar: 123
}`;
const input4 = `{
@ -24,8 +24,8 @@ const input4 = `{
}`;
const output4 = `{
foo: true,
bar: 123
\tfoo: true,
\tbar: 123
}`;
describe('format', () => {

View File

@ -13,7 +13,7 @@ export function format(s: string): string {
indent--;
i--;
}
const result = `${' '.repeat(i)}${line}`;
const result = `${'\t'.repeat(i)}${line}`;
if (result.trim() === '') {
return '';
}

9
src/utils/indent.ts Normal file
View File

@ -0,0 +1,9 @@
import { EOL } from 'os';
export function indent(s: string): string {
let lines = s.split(EOL);
lines = lines.map(line => {
return line.replace(/\t/g, ' ');
});
return lines.join(EOL);
}

View File

@ -3,6 +3,7 @@ import { resolve } from 'path';
import type { Client } from '../client/interfaces/Client';
import { HttpClient } from '../HttpClient';
import { copyFile, exists, writeFile } from './fileSystem';
import { indent } from './indent';
import { Templates } from './registerHandlebarTemplates';
/**
@ -26,12 +27,12 @@ export async function writeClientCore(
version: client.version,
};
await writeFile(resolve(outputPath, 'OpenAPI.ts'), templates.core.settings(context));
await writeFile(resolve(outputPath, 'ApiError.ts'), templates.core.apiError({}));
await writeFile(resolve(outputPath, 'ApiRequestOptions.ts'), templates.core.apiRequestOptions({}));
await writeFile(resolve(outputPath, 'ApiResult.ts'), templates.core.apiResult({}));
await writeFile(resolve(outputPath, 'CancelablePromise.ts'), templates.core.cancelablePromise({}));
await writeFile(resolve(outputPath, 'request.ts'), templates.core.request(context));
await writeFile(resolve(outputPath, 'OpenAPI.ts'), indent(templates.core.settings(context)));
await writeFile(resolve(outputPath, 'ApiError.ts'), indent(templates.core.apiError({})));
await writeFile(resolve(outputPath, 'ApiRequestOptions.ts'), indent(templates.core.apiRequestOptions({})));
await writeFile(resolve(outputPath, 'ApiResult.ts'), indent(templates.core.apiResult({})));
await writeFile(resolve(outputPath, 'CancelablePromise.ts'), indent(templates.core.cancelablePromise({})));
await writeFile(resolve(outputPath, 'request.ts'), indent(templates.core.request(context)));
if (request) {
const requestFile = resolve(process.cwd(), request);

View File

@ -4,6 +4,7 @@ import type { Model } from '../client/interfaces/Model';
import { HttpClient } from '../HttpClient';
import { writeFile } from './fileSystem';
import { format } from './format';
import { indent } from './indent';
import { Templates } from './registerHandlebarTemplates';
/**
@ -28,6 +29,6 @@ export async function writeClientModels(
httpClient,
useUnionTypes,
});
await writeFile(file, format(templateResult));
await writeFile(file, indent(format(templateResult)));
}
}

View File

@ -4,6 +4,7 @@ import type { Model } from '../client/interfaces/Model';
import { HttpClient } from '../HttpClient';
import { writeFile } from './fileSystem';
import { format } from './format';
import { indent } from './indent';
import { Templates } from './registerHandlebarTemplates';
/**
@ -28,6 +29,6 @@ export async function writeClientSchemas(
httpClient,
useUnionTypes,
});
await writeFile(file, format(templateResult));
await writeFile(file, indent(format(templateResult)));
}
}

View File

@ -4,6 +4,7 @@ import type { Service } from '../client/interfaces/Service';
import { HttpClient } from '../HttpClient';
import { writeFile } from './fileSystem';
import { format } from './format';
import { indent } from './indent';
import { Templates } from './registerHandlebarTemplates';
const VERSION_TEMPLATE_STRING = 'OpenAPI.VERSION';
@ -38,6 +39,6 @@ export async function writeClientServices(
useOptions,
postfix,
});
await writeFile(file, format(templateResult));
await writeFile(file, indent(format(templateResult)));
}
}