irongit

Django + Next + Postgres + Docker + Nginx

automatically generate api

FIRST_NAME LAST_NAMEauthored
parent 80e7d52commit bf96e8719cc4d929b0074dbfe02e9f59ccceaa80Browse files

16 files changed, +1182 -0

+24-0frontend/api/core/ApiError.ts
@@ -0,0 +1,24 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+import type { ApiRequestOptions } from './ApiRequestOptions';
5+import type { ApiResult } from './ApiResult';
6+
7+export class ApiError extends Error {
8+ public readonly url: string;
9+ public readonly status: number;
10+ public readonly statusText: string;
11+ public readonly body: any;
12+ public readonly request: ApiRequestOptions;
13+
14+ constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
15+ super(message);
16+
17+ this.name = 'ApiError';
18+ this.url = response.url;
19+ this.status = response.status;
20+ this.statusText = response.statusText;
21+ this.body = response.body;
22+ this.request = request;
23+ }
24+}
+16-0frontend/api/core/ApiRequestOptions.ts
@@ -0,0 +1,16 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+export type ApiRequestOptions = {
5+ readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
6+ readonly url: string;
7+ readonly path?: Record<string, any>;
8+ readonly cookies?: Record<string, any>;
9+ readonly headers?: Record<string, any>;
10+ readonly query?: Record<string, any>;
11+ readonly formData?: Record<string, any>;
12+ readonly body?: any;
13+ readonly mediaType?: string;
14+ readonly responseHeader?: string;
15+ readonly errors?: Record<number, string>;
16+};
+10-0frontend/api/core/ApiResult.ts
@@ -0,0 +1,10 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+export type ApiResult = {
5+ readonly url: string;
6+ readonly ok: boolean;
7+ readonly status: number;
8+ readonly statusText: string;
9+ readonly body: any;
10+};
+128-0frontend/api/core/CancelablePromise.ts
@@ -0,0 +1,128 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+export class CancelError extends Error {
5+
6+ constructor(message: string) {
7+ super(message);
8+ this.name = 'CancelError';
9+ }
10+
11+ public get isCancelled(): boolean {
12+ return true;
13+ }
14+}
15+
16+export interface OnCancel {
17+ readonly isResolved: boolean;
18+ readonly isRejected: boolean;
19+ readonly isCancelled: boolean;
20+
21+ (cancelHandler: () => void): void;
22+}
23+
24+export class CancelablePromise<T> implements Promise<T> {
25+ readonly [Symbol.toStringTag]!: string;
26+
27+ private _isResolved: boolean;
28+ private _isRejected: boolean;
29+ private _isCancelled: boolean;
30+ private readonly _cancelHandlers: (() => void)[];
31+ private readonly _promise: Promise<T>;
32+ private _resolve?: (value: T | PromiseLike<T>) => void;
33+ private _reject?: (reason?: any) => void;
34+
35+ constructor(
36+ executor: (
37+ resolve: (value: T | PromiseLike<T>) => void,
38+ reject: (reason?: any) => void,
39+ onCancel: OnCancel
40+ ) => void
41+ ) {
42+ this._isResolved = false;
43+ this._isRejected = false;
44+ this._isCancelled = false;
45+ this._cancelHandlers = [];
46+ this._promise = new Promise<T>((resolve, reject) => {
47+ this._resolve = resolve;
48+ this._reject = reject;
49+
50+ const onResolve = (value: T | PromiseLike<T>): void => {
51+ if (this._isResolved || this._isRejected || this._isCancelled) {
52+ return;
53+ }
54+ this._isResolved = true;
55+ this._resolve?.(value);
56+ };
57+
58+ const onReject = (reason?: any): void => {
59+ if (this._isResolved || this._isRejected || this._isCancelled) {
60+ return;
61+ }
62+ this._isRejected = true;
63+ this._reject?.(reason);
64+ };
65+
66+ const onCancel = (cancelHandler: () => void): void => {
67+ if (this._isResolved || this._isRejected || this._isCancelled) {
68+ return;
69+ }
70+ this._cancelHandlers.push(cancelHandler);
71+ };
72+
73+ Object.defineProperty(onCancel, 'isResolved', {
74+ get: (): boolean => this._isResolved,
75+ });
76+
77+ Object.defineProperty(onCancel, 'isRejected', {
78+ get: (): boolean => this._isRejected,
79+ });
80+
81+ Object.defineProperty(onCancel, 'isCancelled', {
82+ get: (): boolean => this._isCancelled,
83+ });
84+
85+ return executor(onResolve, onReject, onCancel as OnCancel);
86+ });
87+ }
88+
89+ public then<TResult1 = T, TResult2 = never>(
90+ onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
91+ onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
92+ ): Promise<TResult1 | TResult2> {
93+ return this._promise.then(onFulfilled, onRejected);
94+ }
95+
96+ public catch<TResult = never>(
97+ onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
98+ ): Promise<T | TResult> {
99+ return this._promise.catch(onRejected);
100+ }
101+
102+ public finally(onFinally?: (() => void) | null): Promise<T> {
103+ return this._promise.finally(onFinally);
104+ }
105+
106+ public cancel(): void {
107+ if (this._isResolved || this._isRejected || this._isCancelled) {
108+ return;
109+ }
110+ this._isCancelled = true;
111+ if (this._cancelHandlers.length) {
112+ try {
113+ for (const cancelHandler of this._cancelHandlers) {
114+ cancelHandler();
115+ }
116+ } catch (error) {
117+ console.warn('Cancellation threw an error', error);
118+ return;
119+ }
120+ }
121+ this._cancelHandlers.length = 0;
122+ this._reject?.(new CancelError('Request aborted'));
123+ }
124+
125+ public get isCancelled(): boolean {
126+ return this._isCancelled;
127+ }
128+}
+31-0frontend/api/core/OpenAPI.ts
@@ -0,0 +1,31 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+import type { ApiRequestOptions } from './ApiRequestOptions';
5+
6+type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
7+type Headers = Record<string, string>;
8+
9+export type OpenAPIConfig = {
10+ BASE: string;
11+ VERSION: string;
12+ WITH_CREDENTIALS: boolean;
13+ CREDENTIALS: 'include' | 'omit' | 'same-origin';
14+ TOKEN?: string | Resolver<string>;
15+ USERNAME?: string | Resolver<string>;
16+ PASSWORD?: string | Resolver<string>;
17+ HEADERS?: Headers | Resolver<Headers>;
18+ ENCODE_PATH?: (path: string) => string;
19+};
20+
21+export const OpenAPI: OpenAPIConfig = {
22+ BASE: '',
23+ VERSION: '',
24+ WITH_CREDENTIALS: false,
25+ CREDENTIALS: 'include',
26+ TOKEN: undefined,
27+ USERNAME: undefined,
28+ PASSWORD: undefined,
29+ HEADERS: undefined,
30+ ENCODE_PATH: undefined,
31+};
+306-0frontend/api/core/request.ts
@@ -0,0 +1,306 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+import { ApiError } from './ApiError';
5+import type { ApiRequestOptions } from './ApiRequestOptions';
6+import type { ApiResult } from './ApiResult';
7+import { CancelablePromise } from './CancelablePromise';
8+import type { OnCancel } from './CancelablePromise';
9+import type { OpenAPIConfig } from './OpenAPI';
10+
11+const isDefined = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => {
12+ return value !== undefined && value !== null;
13+};
14+
15+const isString = (value: any): value is string => {
16+ return typeof value === 'string';
17+};
18+
19+const isStringWithValue = (value: any): value is string => {
20+ return isString(value) && value !== '';
21+};
22+
23+const isBlob = (value: any): value is Blob => {
24+ return (
25+ typeof value === 'object' &&
26+ typeof value.type === 'string' &&
27+ typeof value.stream === 'function' &&
28+ typeof value.arrayBuffer === 'function' &&
29+ typeof value.constructor === 'function' &&
30+ typeof value.constructor.name === 'string' &&
31+ /^(Blob|File)$/.test(value.constructor.name) &&
32+ /^(Blob|File)$/.test(value[Symbol.toStringTag])
33+ );
34+};
35+
36+const isFormData = (value: any): value is FormData => {
37+ return value instanceof FormData;
38+};
39+
40+const base64 = (str: string): string => {
41+ try {
42+ return btoa(str);
43+ } catch (err) {
44+ // @ts-ignore
45+ return Buffer.from(str).toString('base64');
46+ }
47+};
48+
49+const getQueryString = (params: Record<string, any>): string => {
50+ const qs: string[] = [];
51+
52+ const append = (key: string, value: any) => {
53+ qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
54+ };
55+
56+ const process = (key: string, value: any) => {
57+ if (isDefined(value)) {
58+ if (Array.isArray(value)) {
59+ value.forEach(v => {
60+ process(key, v);
61+ });
62+ } else if (typeof value === 'object') {
63+ Object.entries(value).forEach(([k, v]) => {
64+ process(`${key}[${k}]`, v);
65+ });
66+ } else {
67+ append(key, value);
68+ }
69+ }
70+ };
71+
72+ Object.entries(params).forEach(([key, value]) => {
73+ process(key, value);
74+ });
75+
76+ if (qs.length > 0) {
77+ return `?${qs.join('&')}`;
78+ }
79+
80+ return '';
81+};
82+
83+const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
84+ const encoder = config.ENCODE_PATH || encodeURI;
85+
86+ const path = options.url
87+ .replace('{api-version}', config.VERSION)
88+ .replace(/{(.*?)}/g, (substring: string, group: string) => {
89+ if (options.path?.hasOwnProperty(group)) {
90+ return encoder(String(options.path[group]));
91+ }
92+ return substring;
93+ });
94+
95+ const url = `${config.BASE}${path}`;
96+ if (options.query) {
97+ return `${url}${getQueryString(options.query)}`;
98+ }
99+ return url;
100+};
101+
102+const getFormData = (options: ApiRequestOptions): FormData | undefined => {
103+ if (options.formData) {
104+ const formData = new FormData();
105+
106+ const process = (key: string, value: any) => {
107+ if (isString(value) || isBlob(value)) {
108+ formData.append(key, value);
109+ } else {
110+ formData.append(key, JSON.stringify(value));
111+ }
112+ };
113+
114+ Object.entries(options.formData)
115+ .filter(([_, value]) => isDefined(value))
116+ .forEach(([key, value]) => {
117+ if (Array.isArray(value)) {
118+ value.forEach(v => process(key, v));
119+ } else {
120+ process(key, value);
121+ }
122+ });
123+
124+ return formData;
125+ }
126+ return undefined;
127+};
128+
129+type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;
130+
131+const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => {
132+ if (typeof resolver === 'function') {
133+ return (resolver as Resolver<T>)(options);
134+ }
135+ return resolver;
136+};
137+
138+const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise<Headers> => {
139+ const token = await resolve(options, config.TOKEN);
140+ const username = await resolve(options, config.USERNAME);
141+ const password = await resolve(options, config.PASSWORD);
142+ const additionalHeaders = await resolve(options, config.HEADERS);
143+
144+ const headers = Object.entries({
145+ Accept: 'application/json',
146+ ...additionalHeaders,
147+ ...options.headers,
148+ })
149+ .filter(([_, value]) => isDefined(value))
150+ .reduce((headers, [key, value]) => ({
151+ ...headers,
152+ [key]: String(value),
153+ }), {} as Record<string, string>);
154+
155+ if (isStringWithValue(token)) {
156+ headers['Authorization'] = `Bearer ${token}`;
157+ }
158+
159+ if (isStringWithValue(username) && isStringWithValue(password)) {
160+ const credentials = base64(`${username}:${password}`);
161+ headers['Authorization'] = `Basic ${credentials}`;
162+ }
163+
164+ if (options.body) {
165+ if (options.mediaType) {
166+ headers['Content-Type'] = options.mediaType;
167+ } else if (isBlob(options.body)) {
168+ headers['Content-Type'] = options.body.type || 'application/octet-stream';
169+ } else if (isString(options.body)) {
170+ headers['Content-Type'] = 'text/plain';
171+ } else if (!isFormData(options.body)) {
172+ headers['Content-Type'] = 'application/json';
173+ }
174+ }
175+
176+ return new Headers(headers);
177+};
178+
179+const getRequestBody = (options: ApiRequestOptions): any => {
180+ if (options.body) {
181+ if (options.mediaType?.includes('/json')) {
182+ return JSON.stringify(options.body)
183+ } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
184+ return options.body;
185+ } else {
186+ return JSON.stringify(options.body);
187+ }
188+ }
189+ return undefined;
190+};
191+
192+export const sendRequest = async (
193+ config: OpenAPIConfig,
194+ options: ApiRequestOptions,
195+ url: string,
196+ body: any,
197+ formData: FormData | undefined,
198+ headers: Headers,
199+ onCancel: OnCancel
200+): Promise<Response> => {
201+ const controller = new AbortController();
202+
203+ const request: RequestInit = {
204+ headers,
205+ body: body ?? formData,
206+ method: options.method,
207+ signal: controller.signal,
208+ };
209+
210+ if (config.WITH_CREDENTIALS) {
211+ request.credentials = config.CREDENTIALS;
212+ }
213+
214+ onCancel(() => controller.abort());
215+
216+ return await fetch(url, request);
217+};
218+
219+const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => {
220+ if (responseHeader) {
221+ const content = response.headers.get(responseHeader);
222+ if (isString(content)) {
223+ return content;
224+ }
225+ }
226+ return undefined;
227+};
228+
229+const getResponseBody = async (response: Response): Promise<any> => {
230+ if (response.status !== 204) {
231+ try {
232+ const contentType = response.headers.get('Content-Type');
233+ if (contentType) {
234+ const isJSON = contentType.toLowerCase().startsWith('application/json');
235+ if (isJSON) {
236+ return await response.json();
237+ } else {
238+ return await response.text();
239+ }
240+ }
241+ } catch (error) {
242+ console.error(error);
243+ }
244+ }
245+ return undefined;
246+};
247+
248+const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
249+ const errors: Record<number, string> = {
250+ 400: 'Bad Request',
251+ 401: 'Unauthorized',
252+ 403: 'Forbidden',
253+ 404: 'Not Found',
254+ 500: 'Internal Server Error',
255+ 502: 'Bad Gateway',
256+ 503: 'Service Unavailable',
257+ ...options.errors,
258+ }
259+
260+ const error = errors[result.status];
261+ if (error) {
262+ throw new ApiError(options, result, error);
263+ }
264+
265+ if (!result.ok) {
266+ throw new ApiError(options, result, 'Generic Error');
267+ }
268+};
269+
270+/**
271+ * Request method
272+ * @param config The OpenAPI configuration object
273+ * @param options The request options from the service
274+ * @returns CancelablePromise<T>
275+ * @throws ApiError
276+ */
277+export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => {
278+ return new CancelablePromise(async (resolve, reject, onCancel) => {
279+ try {
280+ const url = getUrl(config, options);
281+ const formData = getFormData(options);
282+ const body = getRequestBody(options);
283+ const headers = await getHeaders(config, options);
284+
285+ if (!onCancel.isCancelled) {
286+ const response = await sendRequest(config, options, url, body, formData, headers, onCancel);
287+ const responseBody = await getResponseBody(response);
288+ const responseHeader = getResponseHeader(response, options.responseHeader);
289+
290+ const result: ApiResult = {
291+ url,
292+ ok: response.ok,
293+ status: response.status,
294+ statusText: response.statusText,
295+ body: responseHeader ?? responseBody,
296+ };
297+
298+ catchErrorCodes(options, result);
299+
300+ resolve(result.body);
301+ }
302+ } catch (error) {
303+ reject(error);
304+ }
305+ });
306+};
+15-0frontend/api/index.ts
@@ -0,0 +1,15 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+export { ApiError } from './core/ApiError';
5+export { CancelablePromise, CancelError } from './core/CancelablePromise';
6+export { OpenAPI } from './core/OpenAPI';
7+export type { OpenAPIConfig } from './core/OpenAPI';
8+
9+export type { logged_in } from './models/logged_in';
10+export type { login } from './models/login';
11+export type { logout } from './models/logout';
12+export type { user } from './models/user';
13+
14+export { AuthService } from './services/AuthService';
15+export { UserService } from './services/UserService';
+7-0frontend/api/models/logged_in.ts
@@ -0,0 +1,7 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+
5+export type logged_in = {
6+ readonly detail?: boolean;
7+};
+9-0frontend/api/models/login.ts
@@ -0,0 +1,9 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+
5+export type login = {
6+ username?: string;
7+ email?: string;
8+ password: string;
9+};
+7-0frontend/api/models/logout.ts
@@ -0,0 +1,7 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+
5+export type logout = {
6+ readonly detail?: string;
7+};
+16-0frontend/api/models/user.ts
@@ -0,0 +1,16 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+
5+export type user = {
6+ readonly url?: string;
7+ readonly id?: number;
8+ /**
9+ * Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.
10+ */
11+ username: string;
12+ first_name?: string;
13+ last_name?: string;
14+ email?: string;
15+ password: string;
16+};
+254-0frontend/api/schema.yml
@@ -0,0 +1,254 @@
1+openapi: 3.0.2
2+info:
3+ title: ''
4+ version: ''
5+paths:
6+ /api/user/:
7+ get:
8+ operationId: listUsers
9+ description: API endpoint that allows users to be viewed or edited.
10+ parameters: []
11+ responses:
12+ '200':
13+ content:
14+ application/json:
15+ schema:
16+ type: array
17+ items:
18+ $ref: '#/components/schemas/user'
19+ description: ''
20+ tags:
21+ - User
22+ post:
23+ operationId: createUser
24+ description: API endpoint that allows users to be viewed or edited.
25+ parameters: []
26+ requestBody:
27+ content:
28+ application/json:
29+ schema:
30+ $ref: '#/components/schemas/user'
31+ application/x-www-form-urlencoded:
32+ schema:
33+ $ref: '#/components/schemas/user'
34+ multipart/form-data:
35+ schema:
36+ $ref: '#/components/schemas/user'
37+ responses:
38+ '201':
39+ content:
40+ application/json:
41+ schema:
42+ $ref: '#/components/schemas/user'
43+ description: ''
44+ tags:
45+ - User
46+ /api/user/{id}/:
47+ get:
48+ operationId: retrieveUser
49+ description: API endpoint that allows users to be viewed or edited.
50+ parameters:
51+ - name: id
52+ in: path
53+ required: true
54+ description: A unique integer value identifying this user.
55+ schema:
56+ type: string
57+ responses:
58+ '200':
59+ content:
60+ application/json:
61+ schema:
62+ $ref: '#/components/schemas/user'
63+ description: ''
64+ tags:
65+ - User
66+ put:
67+ operationId: updateUser
68+ description: API endpoint that allows users to be viewed or edited.
69+ parameters:
70+ - name: id
71+ in: path
72+ required: true
73+ description: A unique integer value identifying this user.
74+ schema:
75+ type: string
76+ requestBody:
77+ content:
78+ application/json:
79+ schema:
80+ $ref: '#/components/schemas/user'
81+ application/x-www-form-urlencoded:
82+ schema:
83+ $ref: '#/components/schemas/user'
84+ multipart/form-data:
85+ schema:
86+ $ref: '#/components/schemas/user'
87+ responses:
88+ '200':
89+ content:
90+ application/json:
91+ schema:
92+ $ref: '#/components/schemas/user'
93+ description: ''
94+ tags:
95+ - User
96+ patch:
97+ operationId: partialUpdateUser
98+ description: API endpoint that allows users to be viewed or edited.
99+ parameters:
100+ - name: id
101+ in: path
102+ required: true
103+ description: A unique integer value identifying this user.
104+ schema:
105+ type: string
106+ requestBody:
107+ content:
108+ application/json:
109+ schema:
110+ $ref: '#/components/schemas/user'
111+ application/x-www-form-urlencoded:
112+ schema:
113+ $ref: '#/components/schemas/user'
114+ multipart/form-data:
115+ schema:
116+ $ref: '#/components/schemas/user'
117+ responses:
118+ '200':
119+ content:
120+ application/json:
121+ schema:
122+ $ref: '#/components/schemas/user'
123+ description: ''
124+ tags:
125+ - User
126+ delete:
127+ operationId: destroyUser
128+ description: API endpoint that allows users to be viewed or edited.
129+ parameters:
130+ - name: id
131+ in: path
132+ required: true
133+ description: A unique integer value identifying this user.
134+ schema:
135+ type: string
136+ responses:
137+ '204':
138+ description: ''
139+ tags:
140+ - User
141+ /api/auth/logout:
142+ get:
143+ operationId: retrieveLogout
144+ description: ''
145+ parameters: []
146+ responses:
147+ '200':
148+ content:
149+ application/json:
150+ schema:
151+ $ref: '#/components/schemas/logout'
152+ description: ''
153+ tags:
154+ - Auth
155+ /api/auth/logged-in:
156+ get:
157+ operationId: retrieveLoggedIn
158+ description: ''
159+ parameters: []
160+ responses:
161+ '200':
162+ content:
163+ application/json:
164+ schema:
165+ $ref: '#/components/schemas/logged-in'
166+ description: ''
167+ tags:
168+ - Auth
169+ /api/auth/login:
170+ post:
171+ operationId: createLogin
172+ description: ''
173+ parameters: []
174+ requestBody:
175+ content:
176+ application/json:
177+ schema:
178+ $ref: '#/components/schemas/login'
179+ application/x-www-form-urlencoded:
180+ schema:
181+ $ref: '#/components/schemas/login'
182+ multipart/form-data:
183+ schema:
184+ $ref: '#/components/schemas/login'
185+ responses:
186+ '201':
187+ content:
188+ application/json:
189+ schema:
190+ $ref: '#/components/schemas/login'
191+ description: ''
192+ tags:
193+ - Auth
194+components:
195+ schemas:
196+ user:
197+ type: object
198+ properties:
199+ url:
200+ type: string
201+ readOnly: true
202+ id:
203+ type: integer
204+ readOnly: true
205+ username:
206+ type: string
207+ description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_
208+ only.
209+ pattern: ^[\w.@+-]+\z
210+ maxLength: 150
211+ first_name:
212+ type: string
213+ maxLength: 150
214+ last_name:
215+ type: string
216+ maxLength: 150
217+ email:
218+ type: string
219+ format: email
220+ maxLength: 254
221+ password:
222+ type: string
223+ writeOnly: true
224+ maxLength: 128
225+ required:
226+ - username
227+ - password
228+ logout:
229+ type: object
230+ properties:
231+ detail:
232+ type: string
233+ readOnly: true
234+ default: Success
235+ logged-in:
236+ type: object
237+ properties:
238+ detail:
239+ type: boolean
240+ readOnly: true
241+ default: false
242+ login:
243+ type: object
244+ properties:
245+ username:
246+ type: string
247+ email:
248+ type: string
249+ format: email
250+ password:
251+ type: string
252+ writeOnly: true
253+ required:
254+ - password
+52-0frontend/api/services/AuthService.ts
@@ -0,0 +1,52 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+import type { logged_in } from '../models/logged_in';
5+import type { login } from '../models/login';
6+import type { logout } from '../models/logout';
7+
8+import type { CancelablePromise } from '../core/CancelablePromise';
9+import { OpenAPI } from '../core/OpenAPI';
10+import { request as __request } from '../core/request';
11+
12+export class AuthService {
13+
14+ /**
15+ * @returns logout
16+ * @throws ApiError
17+ */
18+ public static retrieveLogout(): CancelablePromise<logout> {
19+ return __request(OpenAPI, {
20+ method: 'GET',
21+ url: '/api/auth/logout',
22+ });
23+ }
24+
25+ /**
26+ * @returns logged_in
27+ * @throws ApiError
28+ */
29+ public static retrieveLoggedIn(): CancelablePromise<logged_in> {
30+ return __request(OpenAPI, {
31+ method: 'GET',
32+ url: '/api/auth/logged-in',
33+ });
34+ }
35+
36+ /**
37+ * @param requestBody
38+ * @returns login
39+ * @throws ApiError
40+ */
41+ public static createLogin(
42+requestBody?: login,
43+): CancelablePromise<login> {
44+ return __request(OpenAPI, {
45+ method: 'POST',
46+ url: '/api/auth/login',
47+ body: requestBody,
48+ mediaType: 'application/json',
49+ });
50+ }
51+
52+}
+121-0frontend/api/services/UserService.ts
@@ -0,0 +1,121 @@
1+/* istanbul ignore file */
2+/* tslint:disable */
3+/* eslint-disable */
4+import type { user } from '../models/user';
5+
6+import type { CancelablePromise } from '../core/CancelablePromise';
7+import { OpenAPI } from '../core/OpenAPI';
8+import { request as __request } from '../core/request';
9+
10+export class UserService {
11+
12+ /**
13+ * API endpoint that allows users to be viewed or edited.
14+ * @returns user
15+ * @throws ApiError
16+ */
17+ public static listUsers(): CancelablePromise<Array<user>> {
18+ return __request(OpenAPI, {
19+ method: 'GET',
20+ url: '/api/user/',
21+ });
22+ }
23+
24+ /**
25+ * API endpoint that allows users to be viewed or edited.
26+ * @param requestBody
27+ * @returns user
28+ * @throws ApiError
29+ */
30+ public static createUser(
31+requestBody?: user,
32+): CancelablePromise<user> {
33+ return __request(OpenAPI, {
34+ method: 'POST',
35+ url: '/api/user/',
36+ body: requestBody,
37+ mediaType: 'application/json',
38+ });
39+ }
40+
41+ /**
42+ * API endpoint that allows users to be viewed or edited.
43+ * @param id A unique integer value identifying this user.
44+ * @returns user
45+ * @throws ApiError
46+ */
47+ public static retrieveUser(
48+id: string,
49+): CancelablePromise<user> {
50+ return __request(OpenAPI, {
51+ method: 'GET',
52+ url: '/api/user/{id}/',
53+ path: {
54+ 'id': id,
55+ },
56+ });
57+ }
58+
59+ /**
60+ * API endpoint that allows users to be viewed or edited.
61+ * @param id A unique integer value identifying this user.
62+ * @param requestBody
63+ * @returns user
64+ * @throws ApiError
65+ */
66+ public static updateUser(
67+id: string,
68+requestBody?: user,
69+): CancelablePromise<user> {
70+ return __request(OpenAPI, {
71+ method: 'PUT',
72+ url: '/api/user/{id}/',
73+ path: {
74+ 'id': id,
75+ },
76+ body: requestBody,
77+ mediaType: 'application/json',
78+ });
79+ }
80+
81+ /**
82+ * API endpoint that allows users to be viewed or edited.
83+ * @param id A unique integer value identifying this user.
84+ * @param requestBody
85+ * @returns user
86+ * @throws ApiError
87+ */
88+ public static partialUpdateUser(
89+id: string,
90+requestBody?: user,
91+): CancelablePromise<user> {
92+ return __request(OpenAPI, {
93+ method: 'PATCH',
94+ url: '/api/user/{id}/',
95+ path: {
96+ 'id': id,
97+ },
98+ body: requestBody,
99+ mediaType: 'application/json',
100+ });
101+ }
102+
103+ /**
104+ * API endpoint that allows users to be viewed or edited.
105+ * @param id A unique integer value identifying this user.
106+ * @returns void
107+ * @throws ApiError
108+ */
109+ public static destroyUser(
110+id: string,
111+): CancelablePromise<void> {
112+ return __request(OpenAPI, {
113+ method: 'DELETE',
114+ url: '/api/user/{id}/',
115+ path: {
116+ 'id': id,
117+ },
118+ });
119+ }
120+
121+}
+184-0frontend/package-lock.json
@@ -13,11 +13,28 @@
1313 "@types/react": "18.0.26",
1414 "@types/react-dom": "18.0.10",
1515 "next": "13.1.1",
16+ "openapi-typescript-codegen": "^0.23.0",
1617 "react": "18.2.0",
1718 "react-dom": "18.2.0",
1819 "typescript": "4.9.4"
1920 }
2021 },
22+ "node_modules/@apidevtools/json-schema-ref-parser": {
23+ "version": "9.0.9",
24+ "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz",
25+ "integrity": "sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==",
26+ "dependencies": {
27+ "@jsdevtools/ono": "^7.1.3",
28+ "@types/json-schema": "^7.0.6",
29+ "call-me-maybe": "^1.0.1",
30+ "js-yaml": "^4.1.0"
31+ }
32+ },
33+ "node_modules/@jsdevtools/ono": {
34+ "version": "7.1.3",
35+ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
36+ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="
37+ },
2138 "node_modules/@next/env": {
2239 "version": "13.1.1",
2340 "resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.1.tgz",
@@ -231,6 +248,11 @@
231248 "tslib": "^2.4.0"
232249 }
233250 },
251+ "node_modules/@types/json-schema": {
252+ "version": "7.0.11",
253+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
254+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
255+ },
234256 "node_modules/@types/node": {
235257 "version": "18.11.18",
236258 "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz",
@@ -264,6 +286,27 @@
264286 "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
265287 "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
266288 },
289+ "node_modules/argparse": {
290+ "version": "2.0.1",
291+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
292+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
293+ },
294+ "node_modules/call-me-maybe": {
295+ "version": "1.0.2",
296+ "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
297+ "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ=="
298+ },
299+ "node_modules/camelcase": {
300+ "version": "6.3.0",
301+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
302+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
303+ "engines": {
304+ "node": ">=10"
305+ },
306+ "funding": {
307+ "url": "https://github.com/sponsors/sindresorhus"
308+ }
309+ },
267310 "node_modules/caniuse-lite": {
268311 "version": "1.0.30001442",
269312 "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz",
@@ -284,16 +327,96 @@
284327 "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
285328 "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
286329 },
330+ "node_modules/commander": {
331+ "version": "9.5.0",
332+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
333+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
334+ "engines": {
335+ "node": "^12.20.0 || >=14"
336+ }
337+ },
287338 "node_modules/csstype": {
288339 "version": "3.1.1",
289340 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
290341 "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
291342 },
343+ "node_modules/fs-extra": {
344+ "version": "10.1.0",
345+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
346+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
347+ "dependencies": {
348+ "graceful-fs": "^4.2.0",
349+ "jsonfile": "^6.0.1",
350+ "universalify": "^2.0.0"
351+ },
352+ "engines": {
353+ "node": ">=12"
354+ }
355+ },
356+ "node_modules/graceful-fs": {
357+ "version": "4.2.10",
358+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
359+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
360+ },
361+ "node_modules/handlebars": {
362+ "version": "4.7.7",
363+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
364+ "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
365+ "dependencies": {
366+ "minimist": "^1.2.5",
367+ "neo-async": "^2.6.0",
368+ "source-map": "^0.6.1",
369+ "wordwrap": "^1.0.0"
370+ },
371+ "bin": {
372+ "handlebars": "bin/handlebars"
373+ },
374+ "engines": {
375+ "node": ">=0.4.7"
376+ },
377+ "optionalDependencies": {
378+ "uglify-js": "^3.1.4"
379+ }
380+ },
292381 "node_modules/js-tokens": {
293382 "version": "4.0.0",
294383 "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
295384 "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
296385 },
386+ "node_modules/js-yaml": {
387+ "version": "4.1.0",
388+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
389+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
390+ "dependencies": {
391+ "argparse": "^2.0.1"
392+ },
393+ "bin": {
394+ "js-yaml": "bin/js-yaml.js"
395+ }
396+ },
397+ "node_modules/json-schema-ref-parser": {
398+ "version": "9.0.9",
399+ "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz",
400+ "integrity": "sha512-qcP2lmGy+JUoQJ4DOQeLaZDqH9qSkeGCK3suKWxJXS82dg728Mn3j97azDMaOUmJAN4uCq91LdPx4K7E8F1a7Q==",
401+ "deprecated": "Please switch to @apidevtools/json-schema-ref-parser",
402+ "dependencies": {
403+ "@apidevtools/json-schema-ref-parser": "9.0.9"
404+ },
405+ "engines": {
406+ "node": ">=10"
407+ }
408+ },
409+ "node_modules/jsonfile": {
410+ "version": "6.1.0",
411+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
412+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
413+ "dependencies": {
414+ "universalify": "^2.0.0"
415+ },
416+ "optionalDependencies": {
417+ "graceful-fs": "^4.1.6"
418+ }
419+ },
297420 "node_modules/loose-envify": {
298421 "version": "1.4.0",
299422 "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -305,6 +428,14 @@
305428 "loose-envify": "cli.js"
306429 }
307430 },
431+ "node_modules/minimist": {
432+ "version": "1.2.7",
433+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
434+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
435+ "funding": {
436+ "url": "https://github.com/sponsors/ljharb"
437+ }
438+ },
308439 "node_modules/nanoid": {
309440 "version": "3.3.4",
310441 "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
@@ -316,6 +447,11 @@
316447 "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
317448 }
318449 },
450+ "node_modules/neo-async": {
451+ "version": "2.6.2",
452+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
453+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
454+ },
319455 "node_modules/next": {
320456 "version": "13.1.1",
321457 "resolved": "https://registry.npmjs.org/next/-/next-13.1.1.tgz",
@@ -367,6 +503,21 @@
367503 }
368504 }
369505 },
506+ "node_modules/openapi-typescript-codegen": {
507+ "version": "0.23.0",
508+ "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.23.0.tgz",
509+ "integrity": "sha512-gOJXy5g3H3HlLpVNN+USrNK2i2KYBmDczk9Xk34u6JorwrGiDJZUj+al4S+i9TXdfUQ/ZaLxE59Xf3wqkxGfqA==",
510+ "dependencies": {
511+ "camelcase": "^6.3.0",
512+ "commander": "^9.3.0",
513+ "fs-extra": "^10.1.0",
514+ "handlebars": "^4.7.7",
515+ "json-schema-ref-parser": "^9.0.9"
516+ },
517+ "bin": {
518+ "openapi": "bin/index.js"
519+ }
520+ },
370521 "node_modules/picocolors": {
371522 "version": "1.0.0",
372523 "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -426,6 +577,14 @@
426577 "loose-envify": "^1.1.0"
427578 }
428579 },
580+ "node_modules/source-map": {
581+ "version": "0.6.1",
582+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
583+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
584+ "engines": {
585+ "node": ">=0.10.0"
586+ }
587+ },
429588 "node_modules/source-map-js": {
430589 "version": "1.0.2",
431590 "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
@@ -472,6 +631,31 @@
472631 "engines": {
473632 "node": ">=4.2.0"
474633 }
634+ },
635+ "node_modules/uglify-js": {
636+ "version": "3.17.4",
637+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
638+ "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==",
639+ "optional": true,
640+ "bin": {
641+ "uglifyjs": "bin/uglifyjs"
642+ },
643+ "engines": {
644+ "node": ">=0.8.0"
645+ }
646+ },
647+ "node_modules/universalify": {
648+ "version": "2.0.0",
649+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
650+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
651+ "engines": {
652+ "node": ">= 10.0.0"
653+ }
654+ },
655+ "node_modules/wordwrap": {
656+ "version": "1.0.0",
657+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
658+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="
475659 }
476660 }
477661 }
+2-0frontend/package.json
@@ -4,6 +4,7 @@
44 "private": true,
55 "scripts": {
66 "dev": "next dev",
7+ "syncapi": "cd ../backend && python manage.py generateschema --file ../frontend/api/schema.yml && cd ../frontend && npx openapi-typescript-codegen --input api/schema.yml --output api --indent 2",
78 "build": "next build",
89 "start": "next start",
910 "lint": "next lint"
@@ -14,6 +15,7 @@
1415 "@types/react": "18.0.26",
1516 "@types/react-dom": "18.0.10",
1617 "next": "13.1.1",
18+ "openapi-typescript-codegen": "^0.23.0",
1719 "react": "18.2.0",
1820 "react-dom": "18.2.0",
1921 "typescript": "4.9.4"