· 4 years ago · May 05, 2021, 04:38 PM
1import { paths } from "../types/generated-schema";
2
3/**
4 * Utility types
5 */
6// Filtre un objet en retirant les clefs qui ne satisfont pas la condition C
7type Filter<T, C> = Pick<
8 T,
9 {
10 [Key in keyof T]: T[Key] extends C ? Key : never;
11 }[keyof T]
12>;
13// Trouve les valeurs qui sont dans tous les ensembles T
14type KeysOfUnion<T> = T extends T ? keyof T : never;
15// Trouve le type d'une valeur en profondeur dans un object Get<obj, ["player", "firstname"]>
16type Get<T extends any, K extends any[], D = never> = K extends []
17 ? T
18 : K extends [infer A, ...infer B]
19 ? A extends keyof T
20 ? Get<T[A], B>
21 : D
22 : D;
23// Extrait la liste des clefs requises
24type RequiredKeys<T> = {
25 [K in keyof T]-?: T extends Record<K, T[K]> ? K : never;
26}[keyof T];
27// Vérifie si toutes les sous clef sont requise dans T, renvoie never si ce n'est pas le cas
28type AllRequiredKey<T> = {
29 [K in keyof T]: RequiredKeys<T[K]> extends never ? K : never;
30}[keyof T];
31// Rend certaines clefs optionnelles
32type Optional<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>;
33// Rend les clefs, qui n'ont que des valeurs optionnelles, optionnelles
34type OptionalDeep<T> = Optional<T, AllRequiredKey<T>>;
35// Retire les valeur "never" des clefs d'un objet
36type PickDefined<T> = Pick<
37 T,
38 { [K in keyof T]: T[K] extends never ? never : K }[keyof T]
39>;
40
41/*
42 * API Related types
43 */
44export type Paths = keyof paths;
45type PathMethods<Path extends Paths> = keyof paths[Path];
46type HTTPSuccess = 200 | 201 | 204;
47export type Methods = KeysOfUnion<paths[keyof paths]>;
48export type ApiResponse<Path, Method, Type = "application/json"> = Get<
49 paths,
50 [Path, Method, "responses", HTTPSuccess, "content", Type]
51>;
52type ApiParam<Path, Method, Parameter> = Get<
53 paths,
54 [Path, Method, "parameters", Parameter]
55>;
56type ApiRequestBody<Path, Method, Type = "application/json"> = Get<
57 paths,
58 [Path, Method, "requestBody", "content", Type]
59>;
60export type FetchOptions<Path, Method> = RequestInit & {
61 method?: Method;
62 headers?: Record<string, string>;
63} & OptionalDeep<
64 PickDefined<{
65 query: ApiParam<Path, Method, "query">;
66 params: ApiParam<Path, Method, "path">;
67 json: ApiRequestBody<Path, Method, "application/json">;
68 }>
69 >;
70
71export function fetchApi<Path extends Paths, Method extends Methods = "get">(
72 baseUrl: string,
73 path: Path,
74 options?: FetchOptions<Path, Method>
75): Promise<ApiResponse<Path, Method>> {
76 const o = {
77 headers: {},
78 ...options,
79 } as RequestInit & {
80 json?: object;
81 headers: Record<string, string>;
82 query?: Record<string, any>;
83 params?: Record<string, any>;
84 };
85 const query = o.query;
86 const params = o.params;
87 let url = baseUrl + path;
88 o.headers["Accept"] = "application/json";
89 // Si on a une clef json, alors la requête aura un body json
90 if (o.json) {
91 o.body = JSON.stringify(o.json);
92 o.headers["Content-Type"] = "application/json";
93 }
94 // On ajoute les query parameters à l'URL
95 if (query) {
96 const params = new URLSearchParams();
97 Object.keys(query).forEach((k: string) => params.set(k, query[k]));
98 url += `?${params.toString()}`;
99 }
100 // On remplace les paramètres dans l'url ("/path/{id}" par exemple)
101 if (params) {
102 Object.keys(params).forEach(
103 (k) => (url = url.replace(`{${k}}`, params[k]))
104 );
105 }
106 return fetch(url, o).then((r) => {
107 if (r.status === 204) {
108 return null;
109 }
110 if (r.status >= 200 && r.status < 300) {
111 return r.json();
112 }
113 throw r;
114 });
115}
116