- Supporting response header parsing

This commit is contained in:
Ferdi Koomen 2020-04-27 01:23:48 +02:00
parent de79de4f84
commit 1859e60416
13 changed files with 110 additions and 26 deletions

View File

@ -12,4 +12,5 @@ export interface Operation extends OperationParameters {
path: string;
errors: OperationError[];
results: OperationResponse[];
responseHeader: string | null;
}

View File

@ -6,6 +6,7 @@ import { getOperationErrors } from './getOperationErrors';
import { getOperationName } from './getOperationName';
import { getOperationParameters } from './getOperationParameters';
import { getOperationPath } from './getOperationPath';
import { getOperationResponseHeader } from './getOperationResponseHeader';
import { getOperationResponses } from './getOperationResponses';
import { getOperationResults } from './getOperationResults';
import { getServiceClassName } from './getServiceClassName';
@ -36,6 +37,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
imports: [],
errors: [],
results: [],
responseHeader: null,
};
// Parse the operation parameters (path, query, body, etc).
@ -56,6 +58,8 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
const operationResponses = getOperationResponses(openApi, op.responses);
const operationResults = getOperationResults(operationResponses);
operation.errors = getOperationErrors(operationResponses);
operation.responseHeader = getOperationResponseHeader(operationResults);
operationResults.forEach(operationResult => {
operation.results.push(operationResult);
operation.imports.push(...operationResult.imports);

View File

@ -28,6 +28,20 @@ export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse
properties: [],
};
// We support basic properties from response headers, since both
// fetch and XHR client just support string types.
if (response.headers) {
for (const name in response.headers) {
if (response.headers.hasOwnProperty(name)) {
operationResponse.in = 'header';
operationResponse.name = name;
operationResponse.type = PrimaryType.STRING;
operationResponse.base = PrimaryType.STRING;
return operationResponse;
}
}
}
// If this response has a schema, then we need to check two things:
// if this is a reference then the parameter is just the 'name' of
// this reference type. Otherwise it might be a complex schema and
@ -74,14 +88,5 @@ export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse
}
}
if (response.headers) {
for (const name in response.headers) {
if (response.headers.hasOwnProperty(name)) {
// console.log(name, response.headers[name]);
// const header = response.headers[name];
}
}
}
return operationResponse;
}

View File

@ -0,0 +1,11 @@
import { OperationResponse } from '../../../client/interfaces/OperationResponse';
export function getOperationResponseHeader(operationResponses: OperationResponse[]): string | null {
const header = operationResponses.find(operationResponses => {
return operationResponses.in === 'header';
});
if (header) {
return header.name;
}
return null;
}

View File

@ -7,6 +7,7 @@ import { getOperationName } from './getOperationName';
import { getOperationParameters } from './getOperationParameters';
import { getOperationPath } from './getOperationPath';
import { getOperationRequestBody } from './getOperationRequestBody';
import { getOperationResponseHeader } from './getOperationResponseHeader';
import { getOperationResponses } from './getOperationResponses';
import { getOperationResults } from './getOperationResults';
import { getServiceClassName } from './getServiceClassName';
@ -38,6 +39,7 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
imports: [],
errors: [],
results: [],
responseHeader: null,
};
// Parse the operation parameters (path, query, body, etc).
@ -66,6 +68,8 @@ export function getOperation(openApi: OpenApi, url: string, method: string, op:
const operationResponses = getOperationResponses(openApi, op.responses);
const operationResults = getOperationResults(operationResponses);
operation.errors = getOperationErrors(operationResponses);
operation.responseHeader = getOperationResponseHeader(operationResults);
operationResults.forEach(operationResult => {
operation.results.push(operationResult);
operation.imports.push(...operationResult.imports);

View File

@ -29,6 +29,20 @@ export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse
properties: [],
};
// We support basic properties from response headers, since both
// fetch and XHR client just support string types.
if (response.headers) {
for (const name in response.headers) {
if (response.headers.hasOwnProperty(name)) {
operationResponse.in = 'header';
operationResponse.name = name;
operationResponse.type = PrimaryType.STRING;
operationResponse.base = PrimaryType.STRING;
return operationResponse;
}
}
}
if (response.content) {
const schema = getContent(openApi, response.content);
if (schema) {
@ -74,14 +88,5 @@ export function getOperationResponse(openApi: OpenApi, response: OpenApiResponse
}
}
if (response.headers) {
for (const name in response.headers) {
if (response.headers.hasOwnProperty(name)) {
// console.log(name, response.headers[name]);
// const header = response.headers[name];
}
}
}
return operationResponse;
}

View File

@ -0,0 +1,11 @@
import { OperationResponse } from '../../../client/interfaces/OperationResponse';
export function getOperationResponseHeader(operationResponses: OperationResponse[]): string | null {
const header = operationResponses.find(operationResponses => {
return operationResponses.in === 'header';
});
if (header) {
return header.name;
}
return null;
}

View File

@ -11,4 +11,5 @@ export interface RequestOptions {
query?: { [key: string]: any };
formData?: { [key: string]: any };
body?: any;
responseHeader?: string;
}

View File

@ -66,9 +66,9 @@ export async function request(options: Readonly<RequestOptions>): Promise<Result
try {
switch (OpenAPI.CLIENT) {
case 'xhr':
return await requestUsingXHR(url, request);
return await requestUsingXHR(url, request, options.responseHeader);
default:
return await requestUsingFetch(url, request);
return await requestUsingFetch(url, request, options.responseHeader);
}
} catch (error) {
return {

View File

@ -30,24 +30,44 @@ async function parseBody(response: Response): Promise<any> {
return null;
}
/**
* Fetch the response header (if specified)
* @param response Response object from fetch
* @param responseHeader The name of the header to fetch
*/
function parseHeader(response: Response, responseHeader?: string): string | null {
if (responseHeader) {
const content = response.headers.get(responseHeader);
if (typeof content === 'string') {
return content;
}
}
return null;
}
/**
* Request content using the new Fetch API. This is the default API that is used and
* is create for all JSON, XML and text objects. However it is limited to UTF-8.
* This is a problem for some of the Docs content, since that requires UTF-16!
* @param url The url to request.
* @param request The request object, containing method, headers, body, etc.
* @param responseHeader The header we want to parse.
*/
export async function requestUsingFetch(url: string, request: Readonly<RequestInit>): Promise<Result> {
export async function requestUsingFetch(url: string, request: Readonly<RequestInit>, responseHeader?: string): Promise<Result> {
// Fetch response using fetch API.
const response = await fetch(url, request);
// Get content of response header or response body
const contentHeader = parseHeader(response, responseHeader);
const contentBody = await parseBody(response);
// Create result object.
return {
url,
ok: response.ok,
status: response.status,
statusText: response.statusText,
body: await parseBody(response),
body: contentHeader || contentBody,
};
}

View File

@ -31,6 +31,21 @@ function parseBody(xhr: XMLHttpRequest): any {
return null;
}
/**
* Fetch the response header (if specified)
* @param xhr XHR request object
* @param responseHeader The name of the header to fetch
*/
function parseHeader(xhr: XMLHttpRequest, responseHeader?: string): string | null {
if (responseHeader) {
const content = xhr.getResponseHeader(responseHeader);
if (typeof content === 'string') {
return content;
}
}
return null;
}
/**
* Request content using the new legacy XMLHttpRequest API. This method is useful
* when we want to request UTF-16 content, since it natively supports loading UTF-16.
@ -38,8 +53,9 @@ function parseBody(xhr: XMLHttpRequest): any {
* content using JavaScript... And that is very very slow.
* @param url The url to request.
* @param request The request object, containing method, headers, body, etc.
* @param responseHeader The header we want to parse.
*/
export async function requestUsingXHR(url: string, request: Readonly<RequestInit>): Promise<Result> {
export async function requestUsingXHR(url: string, request: Readonly<RequestInit>, responseHeader?: string): Promise<Result> {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
@ -57,16 +73,19 @@ export async function requestUsingXHR(url: string, request: Readonly<RequestInit
xhr.onreadystatechange = () => {
if (xhr.readyState === XMLHttpRequest.DONE) {
// Get content of response header or response body
const contentHeader = parseHeader(xhr, responseHeader);
const contentBody = parseBody(xhr);
// Create result object.
const result: Result = {
url,
ok: isSuccess(xhr.status),
status: xhr.status,
statusText: xhr.statusText,
body: parseBody(xhr),
body: contentHeader || contentBody,
};
// Done!
resolve(result);
}

View File

@ -71,6 +71,9 @@ export class {{{name}}} {
{{#if parametersBody~}}
body: {{{parametersBody.name}}},
{{/if}}
{{#if responseHeader~}}
responseHeader: '{{{responseHeader}}}',
{{/if}}
});
{{#if errors}}

View File

@ -17,7 +17,7 @@ export function postProcessServiceOperations(service: Service, client: Client, u
clone.imports.push(...flatMap(clone.parameters, parameter => parameter.imports));
clone.imports.push(...flatMap(clone.results, result => result.imports));
// Check of the operation name
// Check if the operation name is unique, if not then prefix this with a number
const name = clone.name;
const index = names.get(name) || 0;
if (index > 0) {