Skip to content

Commit 2256cba

Browse files
committed
feat: add v3 OpenAPI client
1 parent 9215ed6 commit 2256cba

File tree

94 files changed

+2895
-29
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+2895
-29
lines changed

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"cypress-run-a11y": "yarn cypress run --config specPattern=cypress/e2e/accessibility/**/*.ts",
2222
"cypress-run-docs": "yarn cypress run --config specPattern=cypress/e2e/docs-links/**/*.ts ",
2323
"cypress-run": "yarn cypress run",
24+
"generate-api-client": "openapi --input http://localhost:5240/MAAS/a/openapi.json --output ./src/app/api/client --name ApiClient --exportSchemas=true --useOptions",
2425
"lint": "npmPkgJsonLint . && eslint src cypress && tsc --project tsconfig.json --noEmit && tsc --project cypress/tsconfig.json --noEmit",
2526
"link-components": "yarn link \"@canonical/react-components\" && yarn link \"react\" && yarn install",
2627
"percy": "./cypress/percy.sh",
@@ -153,6 +154,7 @@
153154
"nanoid": "5.0.7",
154155
"nodemon": "3.1.3",
155156
"npm-package-json-lint": "8.0.0",
157+
"openapi-typescript-codegen": "0.29.0",
156158
"postcss-normalize": "10.0.1",
157159
"prettier": "3.3.2",
158160
"redux-mock-store": "1.5.4",

src/app/api/api.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { getCookie } from "../utils";
2+
3+
import { ApiClient } from "@/app/api/client";
4+
5+
export const baseURL = import.meta.env.VITE_API_URL;
6+
7+
export const apiClient = new ApiClient({
8+
BASE: baseURL,
9+
TOKEN: getCookie("csrftoken") ?? "",
10+
});
11+
12+
export default apiClient;

src/app/api/client/ApiClient.ts

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* generated using openapi-typescript-codegen -- do not edit */
2+
/* istanbul ignore file */
3+
/* tslint:disable */
4+
/* eslint-disable */
5+
import type { BaseHttpRequest } from './core/BaseHttpRequest';
6+
import type { OpenAPIConfig } from './core/OpenAPI';
7+
import { FetchHttpRequest } from './core/FetchHttpRequest';
8+
import { AuthService } from './services/AuthService';
9+
import { FabricsService } from './services/FabricsService';
10+
import { MachineService } from './services/MachineService';
11+
import { MachinesService } from './services/MachinesService';
12+
import { ResourcePoolService } from './services/ResourcePoolService';
13+
import { SpacesService } from './services/SpacesService';
14+
import { SubnetsService } from './services/SubnetsService';
15+
import { VlansService } from './services/VlansService';
16+
import { ZonesService } from './services/ZonesService';
17+
type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest;
18+
export class ApiClient {
19+
public readonly auth: AuthService;
20+
public readonly fabrics: FabricsService;
21+
public readonly machine: MachineService;
22+
public readonly machines: MachinesService;
23+
public readonly resourcePool: ResourcePoolService;
24+
public readonly spaces: SpacesService;
25+
public readonly subnets: SubnetsService;
26+
public readonly vlans: VlansService;
27+
public readonly zones: ZonesService;
28+
public readonly request: BaseHttpRequest;
29+
constructor(config?: Partial<OpenAPIConfig>, HttpRequest: HttpRequestConstructor = FetchHttpRequest) {
30+
this.request = new HttpRequest({
31+
BASE: config?.BASE ?? '',
32+
VERSION: config?.VERSION ?? '0.1.0',
33+
WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false,
34+
CREDENTIALS: config?.CREDENTIALS ?? 'include',
35+
TOKEN: config?.TOKEN,
36+
USERNAME: config?.USERNAME,
37+
PASSWORD: config?.PASSWORD,
38+
HEADERS: config?.HEADERS,
39+
ENCODE_PATH: config?.ENCODE_PATH,
40+
});
41+
this.auth = new AuthService(this.request);
42+
this.fabrics = new FabricsService(this.request);
43+
this.machine = new MachineService(this.request);
44+
this.machines = new MachinesService(this.request);
45+
this.resourcePool = new ResourcePoolService(this.request);
46+
this.spaces = new SpacesService(this.request);
47+
this.subnets = new SubnetsService(this.request);
48+
this.vlans = new VlansService(this.request);
49+
this.zones = new ZonesService(this.request);
50+
}
51+
}
52+

src/app/api/client/core/ApiError.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* generated using openapi-typescript-codegen -- do not edit */
2+
/* istanbul ignore file */
3+
/* tslint:disable */
4+
/* eslint-disable */
5+
import type { ApiRequestOptions } from './ApiRequestOptions';
6+
import type { ApiResult } from './ApiResult';
7+
8+
export class ApiError extends Error {
9+
public readonly url: string;
10+
public readonly status: number;
11+
public readonly statusText: string;
12+
public readonly body: any;
13+
public readonly request: ApiRequestOptions;
14+
15+
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
16+
super(message);
17+
18+
this.name = 'ApiError';
19+
this.url = response.url;
20+
this.status = response.status;
21+
this.statusText = response.statusText;
22+
this.body = response.body;
23+
this.request = request;
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* generated using openapi-typescript-codegen -- do not edit */
2+
/* istanbul ignore file */
3+
/* tslint:disable */
4+
/* eslint-disable */
5+
export type ApiRequestOptions = {
6+
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
7+
readonly url: string;
8+
readonly path?: Record<string, any>;
9+
readonly cookies?: Record<string, any>;
10+
readonly headers?: Record<string, any>;
11+
readonly query?: Record<string, any>;
12+
readonly formData?: Record<string, any>;
13+
readonly body?: any;
14+
readonly mediaType?: string;
15+
readonly responseHeader?: string;
16+
readonly errors?: Record<number, string>;
17+
};

src/app/api/client/core/ApiResult.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* generated using openapi-typescript-codegen -- do not edit */
2+
/* istanbul ignore file */
3+
/* tslint:disable */
4+
/* eslint-disable */
5+
export type ApiResult = {
6+
readonly url: string;
7+
readonly ok: boolean;
8+
readonly status: number;
9+
readonly statusText: string;
10+
readonly body: any;
11+
};
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* generated using openapi-typescript-codegen -- do not edit */
2+
/* istanbul ignore file */
3+
/* tslint:disable */
4+
/* eslint-disable */
5+
import type { ApiRequestOptions } from './ApiRequestOptions';
6+
import type { CancelablePromise } from './CancelablePromise';
7+
import type { OpenAPIConfig } from './OpenAPI';
8+
9+
export abstract class BaseHttpRequest {
10+
11+
constructor(public readonly config: OpenAPIConfig) {}
12+
13+
public abstract request<T>(options: ApiRequestOptions): CancelablePromise<T>;
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/* generated using openapi-typescript-codegen -- do not edit */
2+
/* istanbul ignore file */
3+
/* tslint:disable */
4+
/* eslint-disable */
5+
export class CancelError extends Error {
6+
7+
constructor(message: string) {
8+
super(message);
9+
this.name = 'CancelError';
10+
}
11+
12+
public get isCancelled(): boolean {
13+
return true;
14+
}
15+
}
16+
17+
export interface OnCancel {
18+
readonly isResolved: boolean;
19+
readonly isRejected: boolean;
20+
readonly isCancelled: boolean;
21+
22+
(cancelHandler: () => void): void;
23+
}
24+
25+
export class CancelablePromise<T> implements Promise<T> {
26+
#isResolved: boolean;
27+
#isRejected: boolean;
28+
#isCancelled: boolean;
29+
readonly #cancelHandlers: (() => void)[];
30+
readonly #promise: Promise<T>;
31+
#resolve?: (value: T | PromiseLike<T>) => void;
32+
#reject?: (reason?: any) => void;
33+
34+
constructor(
35+
executor: (
36+
resolve: (value: T | PromiseLike<T>) => void,
37+
reject: (reason?: any) => void,
38+
onCancel: OnCancel
39+
) => void
40+
) {
41+
this.#isResolved = false;
42+
this.#isRejected = false;
43+
this.#isCancelled = false;
44+
this.#cancelHandlers = [];
45+
this.#promise = new Promise<T>((resolve, reject) => {
46+
this.#resolve = resolve;
47+
this.#reject = reject;
48+
49+
const onResolve = (value: T | PromiseLike<T>): void => {
50+
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
51+
return;
52+
}
53+
this.#isResolved = true;
54+
if (this.#resolve) this.#resolve(value);
55+
};
56+
57+
const onReject = (reason?: any): void => {
58+
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
59+
return;
60+
}
61+
this.#isRejected = true;
62+
if (this.#reject) this.#reject(reason);
63+
};
64+
65+
const onCancel = (cancelHandler: () => void): void => {
66+
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
67+
return;
68+
}
69+
this.#cancelHandlers.push(cancelHandler);
70+
};
71+
72+
Object.defineProperty(onCancel, 'isResolved', {
73+
get: (): boolean => this.#isResolved,
74+
});
75+
76+
Object.defineProperty(onCancel, 'isRejected', {
77+
get: (): boolean => this.#isRejected,
78+
});
79+
80+
Object.defineProperty(onCancel, 'isCancelled', {
81+
get: (): boolean => this.#isCancelled,
82+
});
83+
84+
return executor(onResolve, onReject, onCancel as OnCancel);
85+
});
86+
}
87+
88+
get [Symbol.toStringTag]() {
89+
return "Cancellable Promise";
90+
}
91+
92+
public then<TResult1 = T, TResult2 = never>(
93+
onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
94+
onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
95+
): Promise<TResult1 | TResult2> {
96+
return this.#promise.then(onFulfilled, onRejected);
97+
}
98+
99+
public catch<TResult = never>(
100+
onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
101+
): Promise<T | TResult> {
102+
return this.#promise.catch(onRejected);
103+
}
104+
105+
public finally(onFinally?: (() => void) | null): Promise<T> {
106+
return this.#promise.finally(onFinally);
107+
}
108+
109+
public cancel(): void {
110+
if (this.#isResolved || this.#isRejected || this.#isCancelled) {
111+
return;
112+
}
113+
this.#isCancelled = true;
114+
if (this.#cancelHandlers.length) {
115+
try {
116+
for (const cancelHandler of this.#cancelHandlers) {
117+
cancelHandler();
118+
}
119+
} catch (error) {
120+
console.warn('Cancellation threw an error', error);
121+
return;
122+
}
123+
}
124+
this.#cancelHandlers.length = 0;
125+
if (this.#reject) this.#reject(new CancelError('Request aborted'));
126+
}
127+
128+
public get isCancelled(): boolean {
129+
return this.#isCancelled;
130+
}
131+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* generated using openapi-typescript-codegen -- do not edit */
2+
/* istanbul ignore file */
3+
/* tslint:disable */
4+
/* eslint-disable */
5+
import type { ApiRequestOptions } from './ApiRequestOptions';
6+
import { BaseHttpRequest } from './BaseHttpRequest';
7+
import type { CancelablePromise } from './CancelablePromise';
8+
import type { OpenAPIConfig } from './OpenAPI';
9+
import { request as __request } from './request';
10+
11+
export class FetchHttpRequest extends BaseHttpRequest {
12+
13+
constructor(config: OpenAPIConfig) {
14+
super(config);
15+
}
16+
17+
/**
18+
* Request method
19+
* @param options The request options from the service
20+
* @returns CancelablePromise<T>
21+
* @throws ApiError
22+
*/
23+
public override request<T>(options: ApiRequestOptions): CancelablePromise<T> {
24+
return __request(this.config, options);
25+
}
26+
}

src/app/api/client/core/OpenAPI.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* generated using openapi-typescript-codegen -- do not edit */
2+
/* istanbul ignore file */
3+
/* tslint:disable */
4+
/* eslint-disable */
5+
import type { ApiRequestOptions } from './ApiRequestOptions';
6+
7+
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
8+
type Headers = Record<string, string>;
9+
10+
export type OpenAPIConfig = {
11+
BASE: string;
12+
VERSION: string;
13+
WITH_CREDENTIALS: boolean;
14+
CREDENTIALS: 'include' | 'omit' | 'same-origin';
15+
TOKEN?: string | Resolver<string> | undefined;
16+
USERNAME?: string | Resolver<string> | undefined;
17+
PASSWORD?: string | Resolver<string> | undefined;
18+
HEADERS?: Headers | Resolver<Headers> | undefined;
19+
ENCODE_PATH?: ((path: string) => string) | undefined;
20+
};
21+
22+
export const OpenAPI: OpenAPIConfig = {
23+
BASE: '',
24+
VERSION: '0.1.0',
25+
WITH_CREDENTIALS: false,
26+
CREDENTIALS: 'include',
27+
TOKEN: undefined,
28+
USERNAME: undefined,
29+
PASSWORD: undefined,
30+
HEADERS: undefined,
31+
ENCODE_PATH: undefined,
32+
};

0 commit comments

Comments
 (0)