Browse Source

🎨 Run biome after OpenAPI client generation (#1226)

pull/13907/head
Tomer Barletz 1 year ago
committed by GitHub
parent
commit
960a38aeb6
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      frontend/biome.json
  2. 2
      frontend/package.json
  3. 38
      frontend/src/client/core/ApiError.ts
  4. 31
      frontend/src/client/core/ApiRequestOptions.ts
  5. 12
      frontend/src/client/core/ApiResult.ts
  6. 236
      frontend/src/client/core/CancelablePromise.ts
  7. 77
      frontend/src/client/core/OpenAPI.ts
  8. 638
      frontend/src/client/core/request.ts
  9. 18
      frontend/src/client/core/types.ts
  10. 15
      frontend/src/client/index.ts
  11. 165
      frontend/src/client/models.ts
  12. 696
      frontend/src/client/schemas.ts
  13. 988
      frontend/src/client/services.ts

2
frontend/biome.json

@ -4,7 +4,7 @@
"enabled": true "enabled": true
}, },
"files": { "files": {
"ignore": ["node_modules", "src/client/", "src/routeTree.gen.ts"] "ignore": ["node_modules", "src/routeTree.gen.ts"]
}, },
"linter": { "linter": {
"enabled": true, "enabled": true,

2
frontend/package.json

@ -8,7 +8,7 @@
"build": "tsc && vite build", "build": "tsc && vite build",
"lint": "biome check --apply-unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./", "lint": "biome check --apply-unsafe --no-errors-on-unmatched --files-ignore-unknown=true ./",
"preview": "vite preview", "preview": "vite preview",
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios --exportSchemas true" "generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios --exportSchemas true && biome format --write ./src/client"
}, },
"dependencies": { "dependencies": {
"@chakra-ui/icons": "2.1.1", "@chakra-ui/icons": "2.1.1",

38
frontend/src/client/core/ApiError.ts

@ -1,21 +1,25 @@
import type { ApiRequestOptions } from './ApiRequestOptions'; import type { ApiRequestOptions } from "./ApiRequestOptions"
import type { ApiResult } from './ApiResult'; import type { ApiResult } from "./ApiResult"
export class ApiError extends Error { export class ApiError extends Error {
public readonly url: string; public readonly url: string
public readonly status: number; public readonly status: number
public readonly statusText: string; public readonly statusText: string
public readonly body: unknown; public readonly body: unknown
public readonly request: ApiRequestOptions; public readonly request: ApiRequestOptions
constructor(request: ApiRequestOptions, response: ApiResult, message: string) { constructor(
super(message); request: ApiRequestOptions,
response: ApiResult,
message: string,
) {
super(message)
this.name = 'ApiError'; this.name = "ApiError"
this.url = response.url; this.url = response.url
this.status = response.status; this.status = response.status
this.statusText = response.statusText; this.statusText = response.statusText
this.body = response.body; this.body = response.body
this.request = request; this.request = request
} }
} }

31
frontend/src/client/core/ApiRequestOptions.ts

@ -1,13 +1,20 @@
export type ApiRequestOptions = { export type ApiRequestOptions = {
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH'; readonly method:
readonly url: string; | "GET"
readonly path?: Record<string, unknown>; | "PUT"
readonly cookies?: Record<string, unknown>; | "POST"
readonly headers?: Record<string, unknown>; | "DELETE"
readonly query?: Record<string, unknown>; | "OPTIONS"
readonly formData?: Record<string, unknown>; | "HEAD"
readonly body?: any; | "PATCH"
readonly mediaType?: string; readonly url: string
readonly responseHeader?: string; readonly path?: Record<string, unknown>
readonly errors?: Record<number, string>; 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>
}

12
frontend/src/client/core/ApiResult.ts

@ -1,7 +1,7 @@
export type ApiResult<TData = any> = { export type ApiResult<TData = any> = {
readonly body: TData; readonly body: TData
readonly ok: boolean; readonly ok: boolean
readonly status: number; readonly status: number
readonly statusText: string; readonly statusText: string
readonly url: string; readonly url: string
}; }

236
frontend/src/client/core/CancelablePromise.ts

@ -1,126 +1,126 @@
export class CancelError extends Error { export class CancelError extends Error {
constructor(message: string) { constructor(message: string) {
super(message); super(message)
this.name = 'CancelError'; this.name = "CancelError"
} }
public get isCancelled(): boolean { public get isCancelled(): boolean {
return true; return true
} }
} }
export interface OnCancel { export interface OnCancel {
readonly isResolved: boolean; readonly isResolved: boolean
readonly isRejected: boolean; readonly isRejected: boolean
readonly isCancelled: boolean; readonly isCancelled: boolean
(cancelHandler: () => void): void; (cancelHandler: () => void): void
} }
export class CancelablePromise<T> implements Promise<T> { export class CancelablePromise<T> implements Promise<T> {
private _isResolved: boolean; private _isResolved: boolean
private _isRejected: boolean; private _isRejected: boolean
private _isCancelled: boolean; private _isCancelled: boolean
readonly cancelHandlers: (() => void)[]; readonly cancelHandlers: (() => void)[]
readonly promise: Promise<T>; readonly promise: Promise<T>
private _resolve?: (value: T | PromiseLike<T>) => void; private _resolve?: (value: T | PromiseLike<T>) => void
private _reject?: (reason?: unknown) => void; private _reject?: (reason?: unknown) => void
constructor( constructor(
executor: ( executor: (
resolve: (value: T | PromiseLike<T>) => void, resolve: (value: T | PromiseLike<T>) => void,
reject: (reason?: unknown) => void, reject: (reason?: unknown) => void,
onCancel: OnCancel onCancel: OnCancel,
) => void ) => void,
) { ) {
this._isResolved = false; this._isResolved = false
this._isRejected = false; this._isRejected = false
this._isCancelled = false; this._isCancelled = false
this.cancelHandlers = []; this.cancelHandlers = []
this.promise = new Promise<T>((resolve, reject) => { this.promise = new Promise<T>((resolve, reject) => {
this._resolve = resolve; this._resolve = resolve
this._reject = reject; this._reject = reject
const onResolve = (value: T | PromiseLike<T>): void => { const onResolve = (value: T | PromiseLike<T>): void => {
if (this._isResolved || this._isRejected || this._isCancelled) { if (this._isResolved || this._isRejected || this._isCancelled) {
return; return
} }
this._isResolved = true; this._isResolved = true
if (this._resolve) this._resolve(value); if (this._resolve) this._resolve(value)
}; }
const onReject = (reason?: unknown): void => { const onReject = (reason?: unknown): void => {
if (this._isResolved || this._isRejected || this._isCancelled) { if (this._isResolved || this._isRejected || this._isCancelled) {
return; return
} }
this._isRejected = true; this._isRejected = true
if (this._reject) this._reject(reason); if (this._reject) this._reject(reason)
}; }
const onCancel = (cancelHandler: () => void): void => { const onCancel = (cancelHandler: () => void): void => {
if (this._isResolved || this._isRejected || this._isCancelled) { if (this._isResolved || this._isRejected || this._isCancelled) {
return; return
} }
this.cancelHandlers.push(cancelHandler); this.cancelHandlers.push(cancelHandler)
}; }
Object.defineProperty(onCancel, 'isResolved', { Object.defineProperty(onCancel, "isResolved", {
get: (): boolean => this._isResolved, get: (): boolean => this._isResolved,
}); })
Object.defineProperty(onCancel, 'isRejected', { Object.defineProperty(onCancel, "isRejected", {
get: (): boolean => this._isRejected, get: (): boolean => this._isRejected,
}); })
Object.defineProperty(onCancel, 'isCancelled', { Object.defineProperty(onCancel, "isCancelled", {
get: (): boolean => this._isCancelled, get: (): boolean => this._isCancelled,
}); })
return executor(onResolve, onReject, onCancel as OnCancel); return executor(onResolve, onReject, onCancel as OnCancel)
}); })
} }
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
return "Cancellable Promise"; return "Cancellable Promise"
} }
public then<TResult1 = T, TResult2 = never>( public then<TResult1 = T, TResult2 = never>(
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null onRejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
): Promise<TResult1 | TResult2> { ): Promise<TResult1 | TResult2> {
return this.promise.then(onFulfilled, onRejected); return this.promise.then(onFulfilled, onRejected)
} }
public catch<TResult = never>( public catch<TResult = never>(
onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null onRejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
): Promise<T | TResult> { ): Promise<T | TResult> {
return this.promise.catch(onRejected); return this.promise.catch(onRejected)
} }
public finally(onFinally?: (() => void) | null): Promise<T> { public finally(onFinally?: (() => void) | null): Promise<T> {
return this.promise.finally(onFinally); return this.promise.finally(onFinally)
} }
public cancel(): void { public cancel(): void {
if (this._isResolved || this._isRejected || this._isCancelled) { if (this._isResolved || this._isRejected || this._isCancelled) {
return; return
} }
this._isCancelled = true; this._isCancelled = true
if (this.cancelHandlers.length) { if (this.cancelHandlers.length) {
try { try {
for (const cancelHandler of this.cancelHandlers) { for (const cancelHandler of this.cancelHandlers) {
cancelHandler(); cancelHandler()
} }
} catch (error) { } catch (error) {
console.warn('Cancellation threw an error', error); console.warn("Cancellation threw an error", error)
return; return
} }
} }
this.cancelHandlers.length = 0; this.cancelHandlers.length = 0
if (this._reject) this._reject(new CancelError('Request aborted')); if (this._reject) this._reject(new CancelError("Request aborted"))
} }
public get isCancelled(): boolean { public get isCancelled(): boolean {
return this._isCancelled; return this._isCancelled
} }
} }

77
frontend/src/client/core/OpenAPI.ts

@ -1,58 +1,57 @@
import type { AxiosRequestConfig, AxiosResponse } from 'axios';import type { ApiRequestOptions } from './ApiRequestOptions'; import type { AxiosRequestConfig, AxiosResponse } from "axios"
import type { TResult } from './types'; import type { ApiRequestOptions } from "./ApiRequestOptions"
import type { TResult } from "./types"
type Headers = Record<string, string>; type Headers = Record<string, string>
type Middleware<T> = (value: T) => T | Promise<T>; type Middleware<T> = (value: T) => T | Promise<T>
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>; type Resolver<T> = (options: ApiRequestOptions) => Promise<T>
export class Interceptors<T> { export class Interceptors<T> {
_fns: Middleware<T>[]; _fns: Middleware<T>[]
constructor() { constructor() {
this._fns = []; this._fns = []
} }
eject(fn: Middleware<T>) { eject(fn: Middleware<T>) {
const index = this._fns.indexOf(fn); const index = this._fns.indexOf(fn)
if (index !== -1) { if (index !== -1) {
this._fns = [ this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)]
...this._fns.slice(0, index),
...this._fns.slice(index + 1),
];
} }
} }
use(fn: Middleware<T>) { use(fn: Middleware<T>) {
this._fns = [...this._fns, fn]; this._fns = [...this._fns, fn]
} }
} }
export type OpenAPIConfig = { export type OpenAPIConfig = {
BASE: string; BASE: string
CREDENTIALS: 'include' | 'omit' | 'same-origin'; CREDENTIALS: "include" | "omit" | "same-origin"
ENCODE_PATH?: ((path: string) => string) | undefined; ENCODE_PATH?: ((path: string) => string) | undefined
HEADERS?: Headers | Resolver<Headers> | undefined; HEADERS?: Headers | Resolver<Headers> | undefined
PASSWORD?: string | Resolver<string> | undefined; PASSWORD?: string | Resolver<string> | undefined
RESULT?: TResult; RESULT?: TResult
TOKEN?: string | Resolver<string> | undefined; TOKEN?: string | Resolver<string> | undefined
USERNAME?: string | Resolver<string> | undefined; USERNAME?: string | Resolver<string> | undefined
VERSION: string; VERSION: string
WITH_CREDENTIALS: boolean; WITH_CREDENTIALS: boolean
interceptors: {request: Interceptors<AxiosRequestConfig>; interceptors: {
response: Interceptors<AxiosResponse>;}; request: Interceptors<AxiosRequestConfig>
}; response: Interceptors<AxiosResponse>
}
}
export const OpenAPI: OpenAPIConfig = { export const OpenAPI: OpenAPIConfig = {
BASE: '', BASE: "",
CREDENTIALS: 'include', CREDENTIALS: "include",
ENCODE_PATH: undefined, ENCODE_PATH: undefined,
HEADERS: undefined, HEADERS: undefined,
PASSWORD: undefined, PASSWORD: undefined,
RESULT: 'body', RESULT: "body",
TOKEN: undefined, TOKEN: undefined,
USERNAME: undefined, USERNAME: undefined,
VERSION: '0.1.0', VERSION: "0.1.0",
WITH_CREDENTIALS: false, WITH_CREDENTIALS: false,
interceptors: {request: new Interceptors(),response: new Interceptors(), interceptors: { request: new Interceptors(), response: new Interceptors() },
}, }
};

638
frontend/src/client/core/request.ts

@ -1,295 +1,319 @@
import axios from 'axios'; import axios from "axios"
import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'; import type {
AxiosError,
import { ApiError } from './ApiError'; AxiosRequestConfig,
import type { ApiRequestOptions } from './ApiRequestOptions'; AxiosResponse,
import type { ApiResult } from './ApiResult'; AxiosInstance,
import { CancelablePromise } from './CancelablePromise'; } from "axios"
import type { OnCancel } from './CancelablePromise';
import type { OpenAPIConfig } from './OpenAPI'; import { ApiError } from "./ApiError"
import type { ApiRequestOptions } from "./ApiRequestOptions"
import type { ApiResult } from "./ApiResult"
import { CancelablePromise } from "./CancelablePromise"
import type { OnCancel } from "./CancelablePromise"
import type { OpenAPIConfig } from "./OpenAPI"
export const isString = (value: unknown): value is string => { export const isString = (value: unknown): value is string => {
return typeof value === 'string'; return typeof value === "string"
}; }
export const isStringWithValue = (value: unknown): value is string => { export const isStringWithValue = (value: unknown): value is string => {
return isString(value) && value !== ''; return isString(value) && value !== ""
}; }
export const isBlob = (value: any): value is Blob => { export const isBlob = (value: any): value is Blob => {
return value instanceof Blob; return value instanceof Blob
}; }
export const isFormData = (value: unknown): value is FormData => { export const isFormData = (value: unknown): value is FormData => {
return value instanceof FormData; return value instanceof FormData
}; }
export const isSuccess = (status: number): boolean => { export const isSuccess = (status: number): boolean => {
return status >= 200 && status < 300; return status >= 200 && status < 300
}; }
export const base64 = (str: string): string => { export const base64 = (str: string): string => {
try { try {
return btoa(str); return btoa(str)
} catch (err) { } catch (err) {
// @ts-ignore // @ts-ignore
return Buffer.from(str).toString('base64'); return Buffer.from(str).toString("base64")
} }
}; }
export const getQueryString = (params: Record<string, unknown>): string => { export const getQueryString = (params: Record<string, unknown>): string => {
const qs: string[] = []; const qs: string[] = []
const append = (key: string, value: unknown) => { const append = (key: string, value: unknown) => {
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
}; }
const encodePair = (key: string, value: unknown) => { const encodePair = (key: string, value: unknown) => {
if (value === undefined || value === null) { if (value === undefined || value === null) {
return; return
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
value.forEach(v => encodePair(key, v)); value.forEach((v) => encodePair(key, v))
} else if (typeof value === 'object') { } else if (typeof value === "object") {
Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v)); Object.entries(value).forEach(([k, v]) => encodePair(`${key}[${k}]`, v))
} else { } else {
append(key, value); append(key, value)
} }
}; }
Object.entries(params).forEach(([key, value]) => encodePair(key, value)); Object.entries(params).forEach(([key, value]) => encodePair(key, value))
return qs.length ? `?${qs.join('&')}` : ''; return qs.length ? `?${qs.join("&")}` : ""
}; }
const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
const encoder = config.ENCODE_PATH || encodeURI; const encoder = config.ENCODE_PATH || encodeURI
const path = options.url const path = options.url
.replace('{api-version}', config.VERSION) .replace("{api-version}", config.VERSION)
.replace(/{(.*?)}/g, (substring: string, group: string) => { .replace(/{(.*?)}/g, (substring: string, group: string) => {
if (options.path?.hasOwnProperty(group)) { if (options.path?.hasOwnProperty(group)) {
return encoder(String(options.path[group])); return encoder(String(options.path[group]))
} }
return substring; return substring
}); })
const url = config.BASE + path; const url = config.BASE + path
return options.query ? url + getQueryString(options.query) : url; return options.query ? url + getQueryString(options.query) : url
}; }
export const getFormData = (options: ApiRequestOptions): FormData | undefined => { export const getFormData = (
if (options.formData) { options: ApiRequestOptions,
const formData = new FormData(); ): FormData | undefined => {
if (options.formData) {
const process = (key: string, value: unknown) => { const formData = new FormData()
if (isString(value) || isBlob(value)) {
formData.append(key, value); const process = (key: string, value: unknown) => {
} else { if (isString(value) || isBlob(value)) {
formData.append(key, JSON.stringify(value)); formData.append(key, value)
} } else {
}; formData.append(key, JSON.stringify(value))
}
Object.entries(options.formData) }
.filter(([, value]) => value !== undefined && value !== null)
.forEach(([key, value]) => { Object.entries(options.formData)
if (Array.isArray(value)) { .filter(([, value]) => value !== undefined && value !== null)
value.forEach(v => process(key, v)); .forEach(([key, value]) => {
} else { if (Array.isArray(value)) {
process(key, value); value.forEach((v) => process(key, v))
} } else {
}); process(key, value)
}
return formData; })
}
return undefined; return formData
}; }
return undefined
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>; }
export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => { type Resolver<T> = (options: ApiRequestOptions) => Promise<T>
if (typeof resolver === 'function') {
return (resolver as Resolver<T>)(options); export const resolve = async <T>(
} options: ApiRequestOptions,
return resolver; resolver?: T | Resolver<T>,
}; ): Promise<T | undefined> => {
if (typeof resolver === "function") {
export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise<Record<string, string>> => { return (resolver as Resolver<T>)(options)
const [token, username, password, additionalHeaders] = await Promise.all([ }
resolve(options, config.TOKEN), return resolver
resolve(options, config.USERNAME), }
resolve(options, config.PASSWORD),
resolve(options, config.HEADERS), export const getHeaders = async (
]); config: OpenAPIConfig,
options: ApiRequestOptions,
const headers = Object.entries({ ): Promise<Record<string, string>> => {
Accept: 'application/json', const [token, username, password, additionalHeaders] = await Promise.all([
...additionalHeaders, resolve(options, config.TOKEN),
...options.headers, resolve(options, config.USERNAME),
}) resolve(options, config.PASSWORD),
.filter(([, value]) => value !== undefined && value !== null) resolve(options, config.HEADERS),
.reduce((headers, [key, value]) => ({ ])
...headers,
[key]: String(value), const headers = Object.entries({
}), {} as Record<string, string>); Accept: "application/json",
...additionalHeaders,
if (isStringWithValue(token)) { ...options.headers,
headers['Authorization'] = `Bearer ${token}`; })
} .filter(([, value]) => value !== undefined && value !== null)
.reduce(
if (isStringWithValue(username) && isStringWithValue(password)) { (headers, [key, value]) => ({
const credentials = base64(`${username}:${password}`); ...headers,
headers['Authorization'] = `Basic ${credentials}`; [key]: String(value),
} }),
{} as Record<string, string>,
if (options.body !== undefined) { )
if (options.mediaType) {
headers['Content-Type'] = options.mediaType; if (isStringWithValue(token)) {
} else if (isBlob(options.body)) { headers["Authorization"] = `Bearer ${token}`
headers['Content-Type'] = options.body.type || 'application/octet-stream'; }
} else if (isString(options.body)) {
headers['Content-Type'] = 'text/plain'; if (isStringWithValue(username) && isStringWithValue(password)) {
} else if (!isFormData(options.body)) { const credentials = base64(`${username}:${password}`)
headers['Content-Type'] = 'application/json'; headers["Authorization"] = `Basic ${credentials}`
} }
} else if (options.formData !== undefined) {
if (options.mediaType) { if (options.body !== undefined) {
headers['Content-Type'] = options.mediaType; if (options.mediaType) {
} headers["Content-Type"] = options.mediaType
} } else if (isBlob(options.body)) {
headers["Content-Type"] = options.body.type || "application/octet-stream"
return headers; } else if (isString(options.body)) {
}; headers["Content-Type"] = "text/plain"
} else if (!isFormData(options.body)) {
headers["Content-Type"] = "application/json"
}
} else if (options.formData !== undefined) {
if (options.mediaType) {
headers["Content-Type"] = options.mediaType
}
}
return headers
}
export const getRequestBody = (options: ApiRequestOptions): unknown => { export const getRequestBody = (options: ApiRequestOptions): unknown => {
if (options.body) { if (options.body) {
return options.body; return options.body
} }
return undefined; return undefined
}; }
export const sendRequest = async <T>( export const sendRequest = async <T>(
config: OpenAPIConfig, config: OpenAPIConfig,
options: ApiRequestOptions, options: ApiRequestOptions,
url: string, url: string,
body: unknown, body: unknown,
formData: FormData | undefined, formData: FormData | undefined,
headers: Record<string, string>, headers: Record<string, string>,
onCancel: OnCancel, onCancel: OnCancel,
axiosClient: AxiosInstance axiosClient: AxiosInstance,
): Promise<AxiosResponse<T>> => { ): Promise<AxiosResponse<T>> => {
const controller = new AbortController(); const controller = new AbortController()
let requestConfig: AxiosRequestConfig = { let requestConfig: AxiosRequestConfig = {
data: body ?? formData, data: body ?? formData,
headers, headers,
method: options.method, method: options.method,
signal: controller.signal, signal: controller.signal,
url, url,
withCredentials: config.WITH_CREDENTIALS, withCredentials: config.WITH_CREDENTIALS,
}; }
onCancel(() => controller.abort()); onCancel(() => controller.abort())
for (const fn of config.interceptors.request._fns) { for (const fn of config.interceptors.request._fns) {
requestConfig = await fn(requestConfig) requestConfig = await fn(requestConfig)
} }
try { try {
return await axiosClient.request(requestConfig); return await axiosClient.request(requestConfig)
} catch (error) { } catch (error) {
const axiosError = error as AxiosError<T>; const axiosError = error as AxiosError<T>
if (axiosError.response) { if (axiosError.response) {
return axiosError.response; return axiosError.response
} }
throw error; throw error
} }
}; }
export const getResponseHeader = (response: AxiosResponse<unknown>, responseHeader?: string): string | undefined => { export const getResponseHeader = (
if (responseHeader) { response: AxiosResponse<unknown>,
const content = response.headers[responseHeader]; responseHeader?: string,
if (isString(content)) { ): string | undefined => {
return content; if (responseHeader) {
} const content = response.headers[responseHeader]
} if (isString(content)) {
return undefined; return content
}; }
}
return undefined
}
export const getResponseBody = (response: AxiosResponse<unknown>): unknown => { export const getResponseBody = (response: AxiosResponse<unknown>): unknown => {
if (response.status !== 204) { if (response.status !== 204) {
return response.data; return response.data
} }
return undefined; return undefined
}; }
export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { export const catchErrorCodes = (
const errors: Record<number, string> = { options: ApiRequestOptions,
400: 'Bad Request', result: ApiResult,
401: 'Unauthorized', ): void => {
402: 'Payment Required', const errors: Record<number, string> = {
403: 'Forbidden', 400: "Bad Request",
404: 'Not Found', 401: "Unauthorized",
405: 'Method Not Allowed', 402: "Payment Required",
406: 'Not Acceptable', 403: "Forbidden",
407: 'Proxy Authentication Required', 404: "Not Found",
408: 'Request Timeout', 405: "Method Not Allowed",
409: 'Conflict', 406: "Not Acceptable",
410: 'Gone', 407: "Proxy Authentication Required",
411: 'Length Required', 408: "Request Timeout",
412: 'Precondition Failed', 409: "Conflict",
413: 'Payload Too Large', 410: "Gone",
414: 'URI Too Long', 411: "Length Required",
415: 'Unsupported Media Type', 412: "Precondition Failed",
416: 'Range Not Satisfiable', 413: "Payload Too Large",
417: 'Expectation Failed', 414: "URI Too Long",
418: 'Im a teapot', 415: "Unsupported Media Type",
421: 'Misdirected Request', 416: "Range Not Satisfiable",
422: 'Unprocessable Content', 417: "Expectation Failed",
423: 'Locked', 418: "Im a teapot",
424: 'Failed Dependency', 421: "Misdirected Request",
425: 'Too Early', 422: "Unprocessable Content",
426: 'Upgrade Required', 423: "Locked",
428: 'Precondition Required', 424: "Failed Dependency",
429: 'Too Many Requests', 425: "Too Early",
431: 'Request Header Fields Too Large', 426: "Upgrade Required",
451: 'Unavailable For Legal Reasons', 428: "Precondition Required",
500: 'Internal Server Error', 429: "Too Many Requests",
501: 'Not Implemented', 431: "Request Header Fields Too Large",
502: 'Bad Gateway', 451: "Unavailable For Legal Reasons",
503: 'Service Unavailable', 500: "Internal Server Error",
504: 'Gateway Timeout', 501: "Not Implemented",
505: 'HTTP Version Not Supported', 502: "Bad Gateway",
506: 'Variant Also Negotiates', 503: "Service Unavailable",
507: 'Insufficient Storage', 504: "Gateway Timeout",
508: 'Loop Detected', 505: "HTTP Version Not Supported",
510: 'Not Extended', 506: "Variant Also Negotiates",
511: 'Network Authentication Required', 507: "Insufficient Storage",
...options.errors, 508: "Loop Detected",
} 510: "Not Extended",
511: "Network Authentication Required",
const error = errors[result.status]; ...options.errors,
if (error) { }
throw new ApiError(options, result, error);
} const error = errors[result.status]
if (error) {
if (!result.ok) { throw new ApiError(options, result, error)
const errorStatus = result.status ?? 'unknown'; }
const errorStatusText = result.statusText ?? 'unknown';
const errorBody = (() => { if (!result.ok) {
try { const errorStatus = result.status ?? "unknown"
return JSON.stringify(result.body, null, 2); const errorStatusText = result.statusText ?? "unknown"
} catch (e) { const errorBody = (() => {
return undefined; try {
} return JSON.stringify(result.body, null, 2)
})(); } catch (e) {
return undefined
throw new ApiError(options, result, }
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` })()
);
} throw new ApiError(
}; options,
result,
`Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`,
)
}
}
/** /**
* Request method * Request method
@ -299,38 +323,54 @@ export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult):
* @returns CancelablePromise<T> * @returns CancelablePromise<T>
* @throws ApiError * @throws ApiError
*/ */
export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise<T> => { export const request = <T>(
return new CancelablePromise(async (resolve, reject, onCancel) => { config: OpenAPIConfig,
try { options: ApiRequestOptions,
const url = getUrl(config, options); axiosClient: AxiosInstance = axios,
const formData = getFormData(options); ): CancelablePromise<T> => {
const body = getRequestBody(options); return new CancelablePromise(async (resolve, reject, onCancel) => {
const headers = await getHeaders(config, options); try {
const url = getUrl(config, options)
if (!onCancel.isCancelled) { const formData = getFormData(options)
let response = await sendRequest<T>(config, options, url, body, formData, headers, onCancel, axiosClient); const body = getRequestBody(options)
const headers = await getHeaders(config, options)
for (const fn of config.interceptors.response._fns) {
response = await fn(response) if (!onCancel.isCancelled) {
} let response = await sendRequest<T>(
config,
const responseBody = getResponseBody(response); options,
const responseHeader = getResponseHeader(response, options.responseHeader); url,
body,
const result: ApiResult = { formData,
url, headers,
ok: isSuccess(response.status), onCancel,
status: response.status, axiosClient,
statusText: response.statusText, )
body: responseHeader ?? responseBody,
}; for (const fn of config.interceptors.response._fns) {
response = await fn(response)
catchErrorCodes(options, result); }
resolve(result.body); const responseBody = getResponseBody(response)
} const responseHeader = getResponseHeader(
} catch (error) { response,
reject(error); options.responseHeader,
} )
});
}; const result: ApiResult = {
url,
ok: isSuccess(response.status),
status: response.status,
statusText: response.statusText,
body: responseHeader ?? responseBody,
}
catchErrorCodes(options, result)
resolve(result.body)
}
} catch (error) {
reject(error)
}
})
}

18
frontend/src/client/core/types.ts

@ -1,12 +1,14 @@
import type { ApiResult } from './ApiResult'; import type { ApiResult } from "./ApiResult"
export type TResult = 'body' | 'raw'; export type TResult = "body" | "raw"
export type TApiResponse<T extends TResult, TData> = export type TApiResponse<T extends TResult, TData> = Exclude<
Exclude<T, 'raw'> extends never T,
? ApiResult<TData> "raw"
: ApiResult<TData>['body']; > extends never
? ApiResult<TData>
: ApiResult<TData>["body"]
export type TConfig<T extends TResult> = { export type TConfig<T extends TResult> = {
_result?: T; _result?: T
}; }

15
frontend/src/client/index.ts

@ -1,9 +1,8 @@
export { ApiError } from "./core/ApiError"
export { CancelablePromise, CancelError } from "./core/CancelablePromise"
export { OpenAPI } from "./core/OpenAPI"
export type { OpenAPIConfig } from "./core/OpenAPI"
export { ApiError } from './core/ApiError'; export * from "./models"
export { CancelablePromise, CancelError } from './core/CancelablePromise'; export * from "./schemas"
export { OpenAPI } from './core/OpenAPI'; export * from "./services"
export type { OpenAPIConfig } from './core/OpenAPI';
export * from './models'
export * from './schemas'
export * from './services'

165
frontend/src/client/models.ts

@ -1,132 +1,99 @@
export type Body_login_login_access_token = { export type Body_login_login_access_token = {
grant_type?: string | null; grant_type?: string | null
username: string; username: string
password: string; password: string
scope?: string; scope?: string
client_id?: string | null; client_id?: string | null
client_secret?: string | null; client_secret?: string | null
}; }
export type HTTPValidationError = { export type HTTPValidationError = {
detail?: Array<ValidationError>; detail?: Array<ValidationError>
}; }
export type ItemCreate = { export type ItemCreate = {
title: string; title: string
description?: string | null; description?: string | null
}; }
export type ItemPublic = { export type ItemPublic = {
title: string; title: string
description?: string | null; description?: string | null
id: number; id: number
owner_id: number; owner_id: number
}; }
export type ItemUpdate = { export type ItemUpdate = {
title?: string | null; title?: string | null
description?: string | null; description?: string | null
}; }
export type ItemsPublic = { export type ItemsPublic = {
data: Array<ItemPublic>; data: Array<ItemPublic>
count: number; count: number
}; }
export type Message = { export type Message = {
message: string; message: string
}; }
export type NewPassword = { export type NewPassword = {
token: string; token: string
new_password: string; new_password: string
}; }
export type Token = { export type Token = {
access_token: string; access_token: string
token_type?: string; token_type?: string
}; }
export type UpdatePassword = { export type UpdatePassword = {
current_password: string; current_password: string
new_password: string; new_password: string
}; }
export type UserCreate = { export type UserCreate = {
email: string; email: string
is_active?: boolean; is_active?: boolean
is_superuser?: boolean; is_superuser?: boolean
full_name?: string | null; full_name?: string | null
password: string; password: string
}; }
export type UserPublic = { export type UserPublic = {
email: string; email: string
is_active?: boolean; is_active?: boolean
is_superuser?: boolean; is_superuser?: boolean
full_name?: string | null; full_name?: string | null
id: number; id: number
}; }
export type UserRegister = { export type UserRegister = {
email: string; email: string
password: string; password: string
full_name?: string | null; full_name?: string | null
}; }
export type UserUpdate = { export type UserUpdate = {
email?: string | null; email?: string | null
is_active?: boolean; is_active?: boolean
is_superuser?: boolean; is_superuser?: boolean
full_name?: string | null; full_name?: string | null
password?: string | null; password?: string | null
}; }
export type UserUpdateMe = { export type UserUpdateMe = {
full_name?: string | null; full_name?: string | null
email?: string | null; email?: string | null
}; }
export type UsersPublic = { export type UsersPublic = {
data: Array<UserPublic>; data: Array<UserPublic>
count: number; count: number
}; }
export type ValidationError = { export type ValidationError = {
loc: Array<string | number>; loc: Array<string | number>
msg: string; msg: string
type: string; type: string
}; }

696
frontend/src/client/schemas.ts

@ -1,357 +1,405 @@
export const $Body_login_login_access_token = { export const $Body_login_login_access_token = {
properties: { properties: {
grant_type: { grant_type: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
pattern: 'password', type: "string",
}, { pattern: "password",
type: 'null', },
}], {
}, type: "null",
username: { },
type: 'string', ],
isRequired: true, },
}, username: {
password: { type: "string",
type: 'string', isRequired: true,
isRequired: true, },
}, password: {
scope: { type: "string",
type: 'string', isRequired: true,
default: '', },
}, scope: {
client_id: { type: "string",
type: 'any-of', default: "",
contains: [{ },
type: 'string', client_id: {
}, { type: "any-of",
type: 'null', contains: [
}], {
}, type: "string",
client_secret: { },
type: 'any-of', {
contains: [{ type: "null",
type: 'string', },
}, { ],
type: 'null', },
}], client_secret: {
}, type: "any-of",
}, contains: [
} as const; {
type: "string",
},
{
type: "null",
},
],
},
},
} as const
export const $HTTPValidationError = { export const $HTTPValidationError = {
properties: { properties: {
detail: { detail: {
type: 'array', type: "array",
contains: { contains: {
type: 'ValidationError', type: "ValidationError",
}, },
}, },
}, },
} as const; } as const
export const $ItemCreate = { export const $ItemCreate = {
properties: { properties: {
title: { title: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
description: { description: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'null', },
}], {
}, type: "null",
}, },
} as const; ],
},
},
} as const
export const $ItemPublic = { export const $ItemPublic = {
properties: { properties: {
title: { title: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
description: { description: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'null', },
}], {
}, type: "null",
id: { },
type: 'number', ],
isRequired: true, },
}, id: {
owner_id: { type: "number",
type: 'number', isRequired: true,
isRequired: true, },
}, owner_id: {
}, type: "number",
} as const; isRequired: true,
},
},
} as const
export const $ItemUpdate = { export const $ItemUpdate = {
properties: { properties: {
title: { title: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'null', },
}], {
}, type: "null",
description: { },
type: 'any-of', ],
contains: [{ },
type: 'string', description: {
}, { type: "any-of",
type: 'null', contains: [
}], {
}, type: "string",
}, },
} as const; {
type: "null",
},
],
},
},
} as const
export const $ItemsPublic = { export const $ItemsPublic = {
properties: { properties: {
data: { data: {
type: 'array', type: "array",
contains: { contains: {
type: 'ItemPublic', type: "ItemPublic",
}, },
isRequired: true, isRequired: true,
}, },
count: { count: {
type: 'number', type: "number",
isRequired: true, isRequired: true,
}, },
}, },
} as const; } as const
export const $Message = { export const $Message = {
properties: { properties: {
message: { message: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
}, },
} as const; } as const
export const $NewPassword = { export const $NewPassword = {
properties: { properties: {
token: { token: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
new_password: { new_password: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
}, },
} as const; } as const
export const $Token = { export const $Token = {
properties: { properties: {
access_token: { access_token: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
token_type: { token_type: {
type: 'string', type: "string",
default: 'bearer', default: "bearer",
}, },
}, },
} as const; } as const
export const $UpdatePassword = { export const $UpdatePassword = {
properties: { properties: {
current_password: { current_password: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
new_password: { new_password: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
}, },
} as const; } as const
export const $UserCreate = { export const $UserCreate = {
properties: { properties: {
email: { email: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
is_active: { is_active: {
type: 'boolean', type: "boolean",
default: true, default: true,
}, },
is_superuser: { is_superuser: {
type: 'boolean', type: "boolean",
default: false, default: false,
}, },
full_name: { full_name: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'null', },
}], {
}, type: "null",
password: { },
type: 'string', ],
isRequired: true, },
}, password: {
}, type: "string",
} as const; isRequired: true,
},
},
} as const
export const $UserPublic = { export const $UserPublic = {
properties: { properties: {
email: { email: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
is_active: { is_active: {
type: 'boolean', type: "boolean",
default: true, default: true,
}, },
is_superuser: { is_superuser: {
type: 'boolean', type: "boolean",
default: false, default: false,
}, },
full_name: { full_name: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'null', },
}], {
}, type: "null",
id: { },
type: 'number', ],
isRequired: true, },
}, id: {
}, type: "number",
} as const; isRequired: true,
},
},
} as const
export const $UserRegister = { export const $UserRegister = {
properties: { properties: {
email: { email: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
password: { password: {
type: 'string', type: "string",
isRequired: true, isRequired: true,
}, },
full_name: { full_name: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'null', },
}], {
}, type: "null",
}, },
} as const; ],
},
},
} as const
export const $UserUpdate = { export const $UserUpdate = {
properties: { properties: {
email: { email: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'null', },
}], {
}, type: "null",
is_active: { },
type: 'boolean', ],
default: true, },
}, is_active: {
is_superuser: { type: "boolean",
type: 'boolean', default: true,
default: false, },
}, is_superuser: {
full_name: { type: "boolean",
type: 'any-of', default: false,
contains: [{ },
type: 'string', full_name: {
}, { type: "any-of",
type: 'null', contains: [
}], {
}, type: "string",
password: { },
type: 'any-of', {
contains: [{ type: "null",
type: 'string', },
}, { ],
type: 'null', },
}], password: {
}, type: "any-of",
}, contains: [
} as const; {
type: "string",
},
{
type: "null",
},
],
},
},
} as const
export const $UserUpdateMe = { export const $UserUpdateMe = {
properties: { properties: {
full_name: { full_name: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'null', },
}], {
}, type: "null",
email: { },
type: 'any-of', ],
contains: [{ },
type: 'string', email: {
}, { type: "any-of",
type: 'null', contains: [
}], {
}, type: "string",
}, },
} as const; {
type: "null",
},
],
},
},
} as const
export const $UsersPublic = { export const $UsersPublic = {
properties: { properties: {
data: { data: {
type: 'array', type: "array",
contains: { contains: {
type: 'UserPublic', type: "UserPublic",
}, },
isRequired: true, isRequired: true,
}, },
count: { count: {
type: 'number', type: "number",
isRequired: true, isRequired: true,
}, },
}, },
} as const; } as const
export const $ValidationError = { export const $ValidationError = {
properties: { properties: {
loc: { loc: {
type: 'array', type: "array",
contains: { contains: {
type: 'any-of', type: "any-of",
contains: [{ contains: [
type: 'string', {
}, { type: "string",
type: 'number', },
}], {
}, type: "number",
isRequired: true, },
}, ],
msg: { },
type: 'string', isRequired: true,
isRequired: true, },
}, msg: {
type: { type: "string",
type: 'string', isRequired: true,
isRequired: true, },
}, type: {
}, type: "string",
} as const; isRequired: true,
},
},
} as const

988
frontend/src/client/services.ts

File diff suppressed because it is too large
Loading…
Cancel
Save