Skip to content

Commit 25af774

Browse files
committed
Define type of Response, per implementation
1 parent cace20c commit 25af774

Some content is hidden

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

48 files changed

+3817
-1
lines changed

richtest/fetch/core/ApiError.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
import type { ApiResult } from './ApiResult';
5+
6+
export class ApiError extends Error {
7+
public readonly url: string;
8+
public readonly status: number;
9+
public readonly statusText: string;
10+
public readonly body: any;
11+
12+
constructor(response: ApiResult, message: string) {
13+
super(message);
14+
15+
this.url = response.url;
16+
this.status = response.status;
17+
this.statusText = response.statusText;
18+
this.body = response.body;
19+
}
20+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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 path: string;
7+
readonly cookies?: Record<string, any>;
8+
readonly headers?: Record<string, any>;
9+
readonly query?: Record<string, any>;
10+
readonly formData?: Record<string, any>;
11+
readonly body?: any;
12+
readonly responseHeader?: string;
13+
readonly errors?: Record<number, string>;
14+
}

richtest/fetch/core/ApiResult.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
}

richtest/fetch/core/OpenAPI.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
5+
6+
import { Response as ResponseImplementation } from 'node-fetch';
7+
8+
type Resolver<T> = () => Promise<T>;
9+
type Headers = Record<string, string>;
10+
11+
import { ApiResult } from './ApiResult';
12+
import { ApiRequestOptions } from './ApiRequestOptions';
13+
14+
export interface RequestHookParams {
15+
url: string;
16+
options: ApiRequestOptions;
17+
}
18+
19+
export interface ResponseHookParams {
20+
url: string;
21+
result: ApiResult;
22+
response?: ResponseImplementation;
23+
}
24+
25+
type Config = {
26+
BASE: string;
27+
VERSION: string;
28+
WITH_CREDENTIALS: boolean;
29+
REQUEST_HOOK?(params: RequestHookParams): Promise<RequestHookParams>;
30+
RESPONSE_HOOK?(result: ResponseHookParams): Promise<ApiResult>
31+
TOKEN?: string | Resolver<string>;
32+
USERNAME?: string | Resolver<string>;
33+
PASSWORD?: string | Resolver<string>;
34+
HEADERS?: Headers | Resolver<Headers>;
35+
}
36+
37+
export const OpenAPI: Config = {
38+
BASE: 'http://localhost:8081',
39+
VERSION: '0.1',
40+
WITH_CREDENTIALS: false,
41+
TOKEN: undefined,
42+
USERNAME: undefined,
43+
PASSWORD: undefined,
44+
HEADERS: undefined,
45+
};

richtest/fetch/core/request.ts

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
2+
3+
/* istanbul ignore file */
4+
/* tslint:disable */
5+
/* eslint-disable */
6+
import FormData from 'form-data';
7+
import fetch, { BodyInit, Headers, RequestInit, Response } from 'node-fetch';
8+
import { types } from 'util';
9+
10+
import { ApiError } from './ApiError';
11+
import type { ApiRequestOptions } from './ApiRequestOptions';
12+
import type { ApiResult } from './ApiResult';
13+
import { OpenAPI } from './OpenAPI';
14+
15+
function isDefined<T>(value: T | null | undefined): value is Exclude<T, null | undefined> {
16+
return value !== undefined && value !== null;
17+
}
18+
19+
function isString(value: any): value is string {
20+
return typeof value === 'string';
21+
}
22+
23+
function isStringWithValue(value: any): value is string {
24+
return isString(value) && value !== '';
25+
}
26+
27+
function isBinary(value: any): value is Buffer | ArrayBuffer | ArrayBufferView {
28+
const isBuffer = Buffer.isBuffer(value);
29+
const isArrayBuffer = types.isArrayBuffer(value);
30+
const isArrayBufferView = types.isArrayBufferView(value);
31+
return isBuffer || isArrayBuffer || isArrayBufferView;
32+
}
33+
34+
function getQueryString(params: Record<string, any>): string {
35+
const qs: string[] = [];
36+
Object.keys(params).forEach(key => {
37+
const value = params[key];
38+
if (isDefined(value)) {
39+
if (Array.isArray(value)) {
40+
value.forEach(value => {
41+
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
42+
});
43+
} else {
44+
qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
45+
}
46+
}
47+
});
48+
if (qs.length > 0) {
49+
return `?${qs.join('&')}`;
50+
}
51+
return '';
52+
}
53+
54+
function getUrl(options: ApiRequestOptions): string {
55+
const path = options.path.replace(/[:]/g, '_');
56+
const url = `${OpenAPI.BASE}${path}`;
57+
58+
if (options.query) {
59+
return `${url}${getQueryString(options.query)}`;
60+
}
61+
return url;
62+
}
63+
64+
function getFormData(params: Record<string, any>): FormData {
65+
const formData = new FormData();
66+
Object.keys(params).forEach(key => {
67+
const value = params[key];
68+
if (isDefined(value)) {
69+
formData.append(key, value);
70+
}
71+
});
72+
return formData;
73+
}
74+
75+
type Resolver<T> = () => Promise<T>;
76+
77+
async function resolve<T>(resolver?: T | Resolver<T>): Promise<T | undefined> {
78+
if (typeof resolver === 'function') {
79+
return (resolver as Resolver<T>)();
80+
}
81+
return resolver;
82+
}
83+
84+
async function getHeaders(options: ApiRequestOptions): Promise<Headers> {
85+
const headers = new Headers({
86+
Accept: 'application/json',
87+
...OpenAPI.HEADERS,
88+
...options.headers,
89+
});
90+
91+
const token = await resolve(OpenAPI.TOKEN);
92+
const username = await resolve(OpenAPI.USERNAME);
93+
const password = await resolve(OpenAPI.PASSWORD);
94+
95+
if (isStringWithValue(token)) {
96+
headers.append('Authorization', `Bearer ${token}`);
97+
}
98+
99+
if (isStringWithValue(username) && isStringWithValue(password)) {
100+
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
101+
headers.append('Authorization', `Basic ${credentials}`);
102+
}
103+
104+
if (options.body) {
105+
if (isBinary(options.body)) {
106+
headers.append('Content-Type', 'application/octet-stream');
107+
} else if (isString(options.body)) {
108+
headers.append('Content-Type', 'text/plain');
109+
} else {
110+
headers.append('Content-Type', 'application/json');
111+
}
112+
}
113+
return headers;
114+
}
115+
116+
function getRequestBody(options: ApiRequestOptions): BodyInit | undefined {
117+
if (options.formData) {
118+
return getFormData(options.formData);
119+
}
120+
if (options.body) {
121+
if (isString(options.body) || isBinary(options.body)) {
122+
return options.body;
123+
} else {
124+
return JSON.stringify(options.body);
125+
}
126+
}
127+
return undefined;
128+
}
129+
130+
async function sendRequest(options: ApiRequestOptions, url: string): Promise<Response> {
131+
const request: RequestInit = {
132+
method: options.method,
133+
headers: await getHeaders(options),
134+
body: getRequestBody(options),
135+
};
136+
return await fetch(url, request);
137+
}
138+
139+
function getResponseHeader(response: Response, responseHeader?: string): string | null {
140+
if (responseHeader) {
141+
const content = response.headers.get(responseHeader);
142+
if (isString(content)) {
143+
return content;
144+
}
145+
}
146+
return null;
147+
}
148+
149+
async function getResponseBody(response: Response): Promise<any> {
150+
try {
151+
const contentType = response.headers.get('Content-Type');
152+
if (contentType) {
153+
const isJSON = contentType.toLowerCase().startsWith('application/json');
154+
if (isJSON) {
155+
return await response.json();
156+
} else {
157+
return await response.text();
158+
}
159+
}
160+
} catch (error) {
161+
console.error(error);
162+
}
163+
return null;
164+
}
165+
166+
function catchErrors(options: ApiRequestOptions, result: ApiResult): void {
167+
const errors: Record<number, string> = {
168+
400: 'Bad Request',
169+
401: 'Unauthorized',
170+
403: 'Forbidden',
171+
404: 'Not Found',
172+
500: 'Internal Server Error',
173+
502: 'Bad Gateway',
174+
503: 'Service Unavailable',
175+
...options.errors,
176+
}
177+
178+
const error = errors[result.status];
179+
if (error) {
180+
throw new ApiError(result, error);
181+
}
182+
183+
if (!result.ok) {
184+
throw new ApiError(result, 'Generic Error');
185+
}
186+
}
187+
188+
/**
189+
* Request using node-fetch client
190+
* @param options The request options from the the service
191+
* @result ApiResult
192+
* @throws ApiError
193+
*/
194+
export async function request(options: ApiRequestOptions): Promise<ApiResult> {
195+
const url = getUrl(options);
196+
197+
// Pre-hook on request if a function is provided.
198+
const requestHookResult = OpenAPI.REQUEST_HOOK ?
199+
(await OpenAPI.REQUEST_HOOK({ url, options})) : { url, options };
200+
201+
const response = await sendRequest(requestHookResult.options, requestHookResult.url);
202+
const responseBody = await getResponseBody(response);
203+
const responseHeader = getResponseHeader(response, requestHookResult.options.responseHeader);
204+
205+
const result: ApiResult = {
206+
url,
207+
ok: response.ok,
208+
status: response.status,
209+
statusText: response.statusText,
210+
body: responseHeader || responseBody
211+
};
212+
213+
// Post-request Hook if provided
214+
result = OpenAPI.RESPONSE_HOOK ? await OpenAPI.RESPONSE_HOOK({url, result, response}) : result;
215+
216+
catchErrors(options, result);
217+
return result;
218+
}

richtest/fetch/index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* istanbul ignore file */
2+
/* tslint:disable */
3+
/* eslint-disable */
4+
export { ApiError } from './core/ApiError';
5+
export { OpenAPI } from './core/OpenAPI';
6+
7+
export type { AddressRecordItem } from './models/AddressRecordItem';
8+
export type { BioRecordItem } from './models/BioRecordItem';
9+
export type { DiagnosisDomainItem } from './models/DiagnosisDomainItem';
10+
export type { DiagnosisDomainRecordItem } from './models/DiagnosisDomainRecordItem';
11+
export type { DiagnosisGroupItem } from './models/DiagnosisGroupItem';
12+
export type { DiagnosisGroupRecordItem } from './models/DiagnosisGroupRecordItem';
13+
export type { DiagnosisRecordItem } from './models/DiagnosisRecordItem';
14+
export type { exception } from './models/exception';
15+
export type { GrantItem } from './models/GrantItem';
16+
export type { GrantPartialItem } from './models/GrantPartialItem';
17+
export { GrantRecordItem } from './models/GrantRecordItem';
18+
export type { Login } from './models/Login';
19+
export type { MembershipCensusItem } from './models/MembershipCensusItem';
20+
export { MembershipCensusRecordItem } from './models/MembershipCensusRecordItem';
21+
export type { MembershipOrganizationRecordItem } from './models/MembershipOrganizationRecordItem';
22+
export type { MembershipPlanItem } from './models/MembershipPlanItem';
23+
export type { MembershipPlanList } from './models/MembershipPlanList';
24+
export type { MembershipPlanPartialItem } from './models/MembershipPlanPartialItem';
25+
export { MembershipPlanRecordItem } from './models/MembershipPlanRecordItem';
26+
export type { meta } from './models/meta';
27+
export { PhoneRecordItem } from './models/PhoneRecordItem';
28+
export type { PoolItem } from './models/PoolItem';
29+
export type { PoolRecordItem } from './models/PoolRecordItem';
30+
export { QueryHeaderPartialItem } from './models/QueryHeaderPartialItem';
31+
export type { QueryHeaderPartialList } from './models/QueryHeaderPartialList';
32+
export { QueryHeaderRecordItem } from './models/QueryHeaderRecordItem';
33+
export { QueryHeaderStatusCountItem } from './models/QueryHeaderStatusCountItem';
34+
export type { UserItem } from './models/UserItem';
35+
export { UserPartialItem } from './models/UserPartialItem';
36+
export { UserRecordItem } from './models/UserRecordItem';
37+
38+
export { AuthService } from './services/AuthService';
39+
export { DiagnosisService } from './services/DiagnosisService';
40+
export { GrantService } from './services/GrantService';
41+
export { MembershipService } from './services/MembershipService';
42+
export { PoolService } from './services/PoolService';
43+
export { QueryService } from './services/QueryService';
44+
export { UserService } from './services/UserService';

0 commit comments

Comments
 (0)