Skip to content

Commit

Permalink
feat(client): add header to generated files
Browse files Browse the repository at this point in the history
  • Loading branch information
mlankamp committed Mar 27, 2024
1 parent b202265 commit ec490bf
Show file tree
Hide file tree
Showing 241 changed files with 4,842 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/moody-olives-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/openapi-ts": minor
---

Possibility to add header to generated files
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,22 @@ export const FooEnum = {
} as const;
```

### File headers

To add a header in the generated files, set the `header` parameter in your configuration file

```mjs
/** @type {import('@hey-api/openapi-ts').UserConfig} */
export default {
input: 'path/to/openapi.json',
output: 'src/client',
header: `/**
* generated using openapi-ts -- do no edit
* Licensed under the MIT License.
*/`
}
```

### Config API

```sh
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const getConfig = async (userConfig: UserConfig, dependencies: Dependencies) =>
exportServices = true,
format = true,
input,
header,
lint = false,
name,
operationId = true,
Expand Down Expand Up @@ -149,6 +150,7 @@ const getConfig = async (userConfig: UserConfig, dependencies: Dependencies) =>
exportServices,
format,
input,
header,
lint,
name,
operationId,
Expand Down
1 change: 1 addition & 0 deletions src/templates/client.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
{{#equals @root.$config.client 'angular'}}
import { NgModule} from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
Expand Down
1 change: 1 addition & 0 deletions src/templates/core/ApiError.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
import type { ApiRequestOptions } from './ApiRequestOptions';
import type { ApiResult } from './ApiResult';

Expand Down
1 change: 1 addition & 0 deletions src/templates/core/ApiRequestOptions.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
export type ApiRequestOptions = {
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
readonly url: string;
Expand Down
1 change: 1 addition & 0 deletions src/templates/core/ApiResult.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
export type ApiResult<TData = any> = {
readonly body: TData;
readonly ok: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/templates/core/BaseHttpRequest.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
{{#equals @root.$config.client 'angular'}}
import type { HttpClient } from '@angular/common/http';
import type { Observable } from 'rxjs';
Expand Down
1 change: 1 addition & 0 deletions src/templates/core/CancelablePromise.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
export class CancelError extends Error {

constructor(message: string) {
Expand Down
1 change: 1 addition & 0 deletions src/templates/core/HttpRequest.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
{{#equals @root.$config.client 'angular'}}
import { Inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
Expand Down
1 change: 1 addition & 0 deletions src/templates/core/OpenAPI.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
import type { ApiRequestOptions } from './ApiRequestOptions';
import type { TConfig, TResult } from './types';

Expand Down
1 change: 1 addition & 0 deletions src/templates/core/request.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
{{~#equals @root.$config.client 'angular'}}{{>angular/request}}{{/equals~}}
{{~#equals @root.$config.client 'axios'}}{{>axios/request}}{{/equals~}}
{{~#equals @root.$config.client 'fetch'}}{{>fetch/request}}{{/equals~}}
Expand Down
1 change: 1 addition & 0 deletions src/templates/core/types.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
import type { ApiResult } from './ApiResult';

export type TResult = 'body' | 'raw';
Expand Down
1 change: 1 addition & 0 deletions src/templates/exportModel.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
{{{modelImports this './'}}}

{{#equals export 'interface'}}
Expand Down
1 change: 1 addition & 0 deletions src/templates/exportSchema.hbs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
{{>header}}
export const ${{{name}}} = {{>schema}} as const;
1 change: 1 addition & 0 deletions src/templates/exportService.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
{{#equals @root.$config.client 'angular'}}
{{#if @root.$config.name}}
import { Injectable } from '@angular/core';
Expand Down
1 change: 1 addition & 0 deletions src/templates/index.hbs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{{>header}}
{{#if @root.$config.name}}
export { {{{@root.$config.name}}} } from './{{{@root.$config.name}}}';
{{/if}}
Expand Down
3 changes: 3 additions & 0 deletions src/templates/partials/header.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{#if @root.$config.header}}
{{{@root.$config.header}}}
{{/if}}
8 changes: 6 additions & 2 deletions src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,16 @@ export interface UserConfig {
* @default false
*/
useLegacyEnums?: boolean;
/**
* Header to include in the generated files
*/
header?: string;
/**
* Write the files to disk (true or false)
* @default true
*/
write?: boolean;
}

export type Config = Omit<Required<UserConfig>, 'base' | 'name' | 'request'> &
Pick<UserConfig, 'base' | 'name' | 'request'>;
export type Config = Omit<Required<UserConfig>, 'base' | 'name' | 'request' | 'header'> &
Pick<UserConfig, 'base' | 'name' | 'request' | 'header'>;
2 changes: 2 additions & 0 deletions src/utils/handlebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ import partialExportComposition from '../templates/partials/exportComposition.hb
import partialExportEnum from '../templates/partials/exportEnum.hbs';
import partialExportInterface from '../templates/partials/exportInterface.hbs';
import partialExportType from '../templates/partials/exportType.hbs';
import partialHeader from '../templates/partials/header.hbs';
import partialIsNullable from '../templates/partials/isNullable.hbs';
import partialIsReadOnly from '../templates/partials/isReadOnly.hbs';
import partialIsRequired from '../templates/partials/isRequired.hbs';
Expand Down Expand Up @@ -306,6 +307,7 @@ export const registerHandlebarTemplates = (config: Config, client: Client): Temp
Handlebars.registerPartial('exportEnum', Handlebars.template(partialExportEnum));
Handlebars.registerPartial('exportInterface', Handlebars.template(partialExportInterface));
Handlebars.registerPartial('exportType', Handlebars.template(partialExportType));
Handlebars.registerPartial('header', Handlebars.template(partialHeader));
Handlebars.registerPartial('isNullable', Handlebars.template(partialIsNullable));
Handlebars.registerPartial('isReadOnly', Handlebars.template(partialIsReadOnly));
Handlebars.registerPartial('isRequired', Handlebars.template(partialIsRequired));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* generated using openapi-ts -- do no edit
*/
import type { ApiRequestOptions } from './ApiRequestOptions';
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: unknown;
public readonly request: ApiRequestOptions;

constructor(request: ApiRequestOptions, 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.request = request;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* generated using openapi-ts -- do no edit
*/
export type ApiRequestOptions = {
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
readonly url: string;
readonly path?: Record<string, unknown>;
readonly cookies?: Record<string, unknown>;
readonly headers?: Record<string, unknown>;
readonly query?: Record<string, unknown>;
readonly formData?: Record<string, unknown>;
readonly body?: any;
readonly mediaType?: string;
readonly responseHeader?: string;
readonly errors?: Record<number, string>;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* generated using openapi-ts -- do no edit
*/
export type ApiResult<TData = any> = {
readonly body: TData;
readonly ok: boolean;
readonly status: number;
readonly statusText: string;
readonly url: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* generated using openapi-ts -- do no edit
*/
export class CancelError extends Error {
constructor(message: string) {
super(message);
this.name = 'CancelError';
}

public get isCancelled(): boolean {
return true;
}
}

export interface OnCancel {
readonly isResolved: boolean;
readonly isRejected: boolean;
readonly isCancelled: boolean;

(cancelHandler: () => void): void;
}

export class CancelablePromise<T> implements Promise<T> {
#isResolved: boolean;
#isRejected: boolean;
#isCancelled: boolean;
readonly #cancelHandlers: (() => void)[];
readonly #promise: Promise<T>;
#resolve?: (value: T | PromiseLike<T>) => void;
#reject?: (reason?: unknown) => void;

constructor(
executor: (
resolve: (value: T | PromiseLike<T>) => void,
reject: (reason?: unknown) => 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;
if (this.#resolve) this.#resolve(value);
};

const onReject = (reason?: unknown): void => {
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
return;
}
this.#isRejected = true;
if (this.#reject) this.#reject(reason);
};

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, 'isRejected', {
get: (): boolean => this.#isRejected,
});

Object.defineProperty(onCancel, 'isCancelled', {
get: (): boolean => this.#isCancelled,
});

return executor(onResolve, onReject, onCancel as OnCancel);
});
}

get [Symbol.toStringTag]() {
return 'Cancellable Promise';
}

public then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null
): Promise<TResult1 | TResult2> {
return this.#promise.then(onFulfilled, onRejected);
}

public catch<TResult = never>(
onRejected?: ((reason: unknown) => 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 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;
if (this.#reject) this.#reject(new CancelError('Request aborted'));
}

public get isCancelled(): boolean {
return this.#isCancelled;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* generated using openapi-ts -- do no edit
*/
import type { ApiRequestOptions } from './ApiRequestOptions';
import type { TConfig, TResult } from './types';

type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
type Headers = Record<string, string>;

export type OpenAPIConfig = {
BASE: string;
CREDENTIALS: 'include' | 'omit' | 'same-origin';
ENCODE_PATH?: ((path: string) => string) | undefined;
HEADERS?: Headers | Resolver<Headers> | undefined;
PASSWORD?: string | Resolver<string> | undefined;
RESULT?: TResult;
TOKEN?: string | Resolver<string> | undefined;
USERNAME?: string | Resolver<string> | undefined;
VERSION: string;
WITH_CREDENTIALS: boolean;
};

export const OpenAPI: OpenAPIConfig = {
BASE: 'http://localhost:3000/base',
CREDENTIALS: 'include',
ENCODE_PATH: undefined,
HEADERS: undefined,
PASSWORD: undefined,
RESULT: 'body',
TOKEN: undefined,
USERNAME: undefined,
VERSION: '1.0',
WITH_CREDENTIALS: false,
};

export const mergeOpenApiConfig = <T extends TResult>(config: OpenAPIConfig, overrides: TConfig<T>) => {
const merged = { ...config };
Object.entries(overrides)
.filter(([key]) => key.startsWith('_'))
.forEach(([key, value]) => {
const k = key.slice(1).toLocaleUpperCase() as keyof typeof merged;
if (merged.hasOwnProperty(k)) {
// @ts-ignore
merged[k] = value;
}
});
return merged;
};
Loading

0 comments on commit ec490bf

Please sign in to comment.