Skip to content

Add facility to intercept requests and responses in host application. #434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
21 changes: 21 additions & 0 deletions src/templates/core/OpenAPI.hbs
Original file line number Diff line number Diff line change
@@ -1,12 +1,33 @@
{{>header}}


{{#equals @root.httpClient 'fetch'}}{{>fetch/responseType}}{{/equals}}
{{#equals @root.httpClient 'xhr'}}{{>xhr/responseType}}{{/equals}}
{{#equals @root.httpClient 'node'}}{{>node/responseType}}{{/equals}}

type Resolver<T> = () => Promise<T>;
type Headers = Record<string, string>;

import { ApiResult } from './ApiResult';
import { ApiRequestOptions } from './ApiRequestOptions';

export interface RequestHookParams {
url: string;
options: ApiRequestOptions;
}

export interface ResponseHookParams {
url: string;
result: ApiResult;
response?: ResponseImplementation;
}

type Config = {
BASE: string;
VERSION: string;
WITH_CREDENTIALS: boolean;
REQUEST_HOOK?(params: RequestHookParams): Promise<RequestHookParams>;
RESPONSE_HOOK?(result: ResponseHookParams): Promise<ApiResult>
TOKEN?: string | Resolver<string>;
USERNAME?: string | Resolver<string>;
PASSWORD?: string | Resolver<string>;
Expand Down
20 changes: 16 additions & 4 deletions src/templates/core/fetch/request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,30 @@ import { OpenAPI } from './OpenAPI';
*/
export async function request(options: ApiRequestOptions): Promise<ApiResult> {
const url = getUrl(options);
const response = await sendRequest(options, url);

// Pre-hook on request if a function is provided.
const requestHookResult = OpenAPI.REQUEST_HOOK ?
(await OpenAPI.REQUEST_HOOK({ url, options})) : { url, options };


const response = await sendRequest(requestHookResult.options, requestHookResult.url);
const responseBody = await getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
const responseHeader = getResponseHeader(response, requestHookResult.options.responseHeader);


const result: ApiResult = {
let result: ApiResult = {
url,
ok: response.ok,
status: response.status,
statusText: response.statusText,
body: responseHeader || responseBody,
body: responseHeader || responseBody
};

// Post-request Hook if provided
result = OpenAPI.RESPONSE_HOOK ? await OpenAPI.RESPONSE_HOOK({url, result, response}) : result;

catchErrors(options, result);


return result;
}
2 changes: 2 additions & 0 deletions src/templates/core/fetch/responseType.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
type ResponseImplementation = Response;

14 changes: 11 additions & 3 deletions src/templates/core/node/request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -59,18 +59,26 @@ import { OpenAPI } from './OpenAPI';
*/
export async function request(options: ApiRequestOptions): Promise<ApiResult> {
const url = getUrl(options);
const response = await sendRequest(options, url);

// Pre-hook on request if a function is provided.
const requestHookResult = OpenAPI.REQUEST_HOOK ?
(await OpenAPI.REQUEST_HOOK({ url, options})) : { url, options };

const response = await sendRequest(requestHookResult.options, requestHookResult.url);
const responseBody = await getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
const responseHeader = getResponseHeader(response, requestHookResult.options.responseHeader);

const result: ApiResult = {
url,
ok: response.ok,
status: response.status,
statusText: response.statusText,
body: responseHeader || responseBody,
body: responseHeader || responseBody
};

// Post-request Hook if provided
result = OpenAPI.RESPONSE_HOOK ? await OpenAPI.RESPONSE_HOOK({url, result, response}) : result;

catchErrors(options, result);
return result;
}
1 change: 1 addition & 0 deletions src/templates/core/node/responseType.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import { Response as ResponseImplementation } from 'node-fetch';
17 changes: 13 additions & 4 deletions src/templates/core/xhr/request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,27 @@ import { OpenAPI } from './OpenAPI';
*/
export async function request(options: ApiRequestOptions): Promise<ApiResult> {
const url = getUrl(options);
const response = await sendRequest(options, url);
const responseBody = getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);

// Pre-hook on request if a function is provided.
const requestHookResult = OpenAPI.REQUEST_HOOK ?
(await OpenAPI.REQUEST_HOOK({ url, options})) : { url, options };

const response = await sendRequest(requestHookResult.options, requestHookResult.url);
const responseBody = await getResponseBody(response);
const responseHeader = getResponseHeader(response, requestHookResult.options.responseHeader);


const result: ApiResult = {
url,
ok: isSuccess(response.status),
status: response.status,
statusText: response.statusText,
body: responseHeader || responseBody,
body: responseHeader || responseBody
};

// Post-request Hook if provided
result = OpenAPI.RESPONSE_HOOK ? await OpenAPI.RESPONSE_HOOK({url, result, response}) : result;

catchErrors(options, result);
return result;
}
1 change: 1 addition & 0 deletions src/templates/core/xhr/responseType.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type ResponseImplementation = XMLHttpRequest;
6 changes: 6 additions & 0 deletions src/utils/registerHandlebarTemplates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import fetchGetResponseBody from '../templates/core/fetch/getResponseBody.hbs';
import fetchGetResponseHeader from '../templates/core/fetch/getResponseHeader.hbs';
import fetchRequest from '../templates/core/fetch/request.hbs';
import fetchSendRequest from '../templates/core/fetch/sendRequest.hbs';
import fetchResponseType from '../templates/core/fetch/responseType.hbs';
import functionCatchErrors from '../templates/core/functions/catchErrors.hbs';
import functionGetFormData from '../templates/core/functions/getFormData.hbs';
import functionGetQueryString from '../templates/core/functions/getQueryString.hbs';
Expand All @@ -26,6 +27,7 @@ import nodeGetResponseBody from '../templates/core/node/getResponseBody.hbs';
import nodeGetResponseHeader from '../templates/core/node/getResponseHeader.hbs';
import nodeRequest from '../templates/core/node/request.hbs';
import nodeSendRequest from '../templates/core/node/sendRequest.hbs';
import nodeResponseType from '../templates/core/node/responseType.hbs';
import templateCoreSettings from '../templates/core/OpenAPI.hbs';
import templateCoreRequest from '../templates/core/request.hbs';
import xhrGetHeaders from '../templates/core/xhr/getHeaders.hbs';
Expand All @@ -34,6 +36,7 @@ import xhrGetResponseBody from '../templates/core/xhr/getResponseBody.hbs';
import xhrGetResponseHeader from '../templates/core/xhr/getResponseHeader.hbs';
import xhrRequest from '../templates/core/xhr/request.hbs';
import xhrSendRequest from '../templates/core/xhr/sendRequest.hbs';
import xhrResponseType from '../templates/core/xhr/responseType.hbs';
import templateExportModel from '../templates/exportModel.hbs';
import templateExportSchema from '../templates/exportSchema.hbs';
import templateExportService from '../templates/exportService.hbs';
Expand Down Expand Up @@ -154,6 +157,7 @@ export function registerHandlebarTemplates(): Templates {
Handlebars.registerPartial('fetch/getResponseHeader', Handlebars.template(fetchGetResponseHeader));
Handlebars.registerPartial('fetch/sendRequest', Handlebars.template(fetchSendRequest));
Handlebars.registerPartial('fetch/request', Handlebars.template(fetchRequest));
Handlebars.registerPartial('fetch/responseType', Handlebars.template(fetchResponseType));

// Specific files for the xhr client implementation
Handlebars.registerPartial('xhr/getHeaders', Handlebars.template(xhrGetHeaders));
Expand All @@ -162,6 +166,7 @@ export function registerHandlebarTemplates(): Templates {
Handlebars.registerPartial('xhr/getResponseHeader', Handlebars.template(xhrGetResponseHeader));
Handlebars.registerPartial('xhr/sendRequest', Handlebars.template(xhrSendRequest));
Handlebars.registerPartial('xhr/request', Handlebars.template(xhrRequest));
Handlebars.registerPartial('xhr/responseType', Handlebars.template(xhrResponseType));

// Specific files for the node client implementation
Handlebars.registerPartial('node/getHeaders', Handlebars.template(nodeGetHeaders));
Expand All @@ -170,6 +175,7 @@ export function registerHandlebarTemplates(): Templates {
Handlebars.registerPartial('node/getResponseHeader', Handlebars.template(nodeGetResponseHeader));
Handlebars.registerPartial('node/sendRequest', Handlebars.template(nodeSendRequest));
Handlebars.registerPartial('node/request', Handlebars.template(nodeRequest));
Handlebars.registerPartial('node/responseType', Handlebars.template(nodeResponseType));

return templates;
}
82 changes: 74 additions & 8 deletions test/__snapshots__/index.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,34 @@ exports[`v2 should generate: ./test/generated/v2/core/OpenAPI.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */

type ResponseImplementation = Response;



type Resolver<T> = () => Promise<T>;
type Headers = Record<string, string>;

import { ApiResult } from './ApiResult';
import { ApiRequestOptions } from './ApiRequestOptions';

export interface RequestHookParams {
url: string;
options: ApiRequestOptions;
}

export interface ResponseHookParams {
url: string;
result: ApiResult;
response?: ResponseImplementation;
}

type Config = {
BASE: string;
VERSION: string;
WITH_CREDENTIALS: boolean;
REQUEST_HOOK?(params: RequestHookParams): Promise<RequestHookParams>;
RESPONSE_HOOK?(result: ResponseHookParams): Promise<ApiResult>
TOKEN?: string | Resolver<string>;
USERNAME?: string | Resolver<string>;
PASSWORD?: string | Resolver<string>;
Expand Down Expand Up @@ -268,19 +289,31 @@ function catchErrors(options: ApiRequestOptions, result: ApiResult): void {
*/
export async function request(options: ApiRequestOptions): Promise<ApiResult> {
const url = getUrl(options);
const response = await sendRequest(options, url);

// Pre-hook on request if a function is provided.
const requestHookResult = OpenAPI.REQUEST_HOOK ?
(await OpenAPI.REQUEST_HOOK({ url, options})) : { url, options };


const response = await sendRequest(requestHookResult.options, requestHookResult.url);
const responseBody = await getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
const responseHeader = getResponseHeader(response, requestHookResult.options.responseHeader);

const result: ApiResult = {

let result: ApiResult = {
url,
ok: response.ok,
status: response.status,
statusText: response.statusText,
body: responseHeader || responseBody,
body: responseHeader || responseBody
};

// Post-request Hook if provided
result = OpenAPI.RESPONSE_HOOK ? await OpenAPI.RESPONSE_HOOK({url, result, response}) : result;

catchErrors(options, result);


return result;
}

Expand Down Expand Up @@ -2298,13 +2331,34 @@ exports[`v3 should generate: ./test/generated/v3/core/OpenAPI.ts 1`] = `
"/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */

type ResponseImplementation = Response;



type Resolver<T> = () => Promise<T>;
type Headers = Record<string, string>;

import { ApiResult } from './ApiResult';
import { ApiRequestOptions } from './ApiRequestOptions';

export interface RequestHookParams {
url: string;
options: ApiRequestOptions;
}

export interface ResponseHookParams {
url: string;
result: ApiResult;
response?: ResponseImplementation;
}

type Config = {
BASE: string;
VERSION: string;
WITH_CREDENTIALS: boolean;
REQUEST_HOOK?(params: RequestHookParams): Promise<RequestHookParams>;
RESPONSE_HOOK?(result: ResponseHookParams): Promise<ApiResult>
TOKEN?: string | Resolver<string>;
USERNAME?: string | Resolver<string>;
PASSWORD?: string | Resolver<string>;
Expand Down Expand Up @@ -2509,19 +2563,31 @@ function catchErrors(options: ApiRequestOptions, result: ApiResult): void {
*/
export async function request(options: ApiRequestOptions): Promise<ApiResult> {
const url = getUrl(options);
const response = await sendRequest(options, url);

// Pre-hook on request if a function is provided.
const requestHookResult = OpenAPI.REQUEST_HOOK ?
(await OpenAPI.REQUEST_HOOK({ url, options})) : { url, options };


const response = await sendRequest(requestHookResult.options, requestHookResult.url);
const responseBody = await getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
const responseHeader = getResponseHeader(response, requestHookResult.options.responseHeader);

const result: ApiResult = {

let result: ApiResult = {
url,
ok: response.ok,
status: response.status,
statusText: response.statusText,
body: responseHeader || responseBody,
body: responseHeader || responseBody
};

// Post-request Hook if provided
result = OpenAPI.RESPONSE_HOOK ? await OpenAPI.RESPONSE_HOOK({url, result, response}) : result;

catchErrors(options, result);


return result;
}

Expand Down