diff --git a/src/client/interfaces/Operation.d.ts b/src/client/interfaces/Operation.d.ts index b27f1abb..59fb7371 100644 --- a/src/client/interfaces/Operation.d.ts +++ b/src/client/interfaces/Operation.d.ts @@ -12,4 +12,5 @@ export interface Operation extends OperationParameters { path: string; errors: OperationError[]; results: OperationResponse[]; + responseHeader: string | null; } diff --git a/src/openApi/v2/parser/getOperation.ts b/src/openApi/v2/parser/getOperation.ts index 1f8d3990..ca5481a8 100644 --- a/src/openApi/v2/parser/getOperation.ts +++ b/src/openApi/v2/parser/getOperation.ts @@ -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); diff --git a/src/openApi/v2/parser/getOperationResponse.ts b/src/openApi/v2/parser/getOperationResponse.ts index 9c0b7d85..4b980225 100644 --- a/src/openApi/v2/parser/getOperationResponse.ts +++ b/src/openApi/v2/parser/getOperationResponse.ts @@ -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; } diff --git a/src/openApi/v2/parser/getOperationResponseHeader.ts b/src/openApi/v2/parser/getOperationResponseHeader.ts new file mode 100644 index 00000000..efd4e8eb --- /dev/null +++ b/src/openApi/v2/parser/getOperationResponseHeader.ts @@ -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; +} diff --git a/src/openApi/v3/parser/getOperation.ts b/src/openApi/v3/parser/getOperation.ts index 71e33c78..6bf1ce9b 100644 --- a/src/openApi/v3/parser/getOperation.ts +++ b/src/openApi/v3/parser/getOperation.ts @@ -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); diff --git a/src/openApi/v3/parser/getOperationResponse.ts b/src/openApi/v3/parser/getOperationResponse.ts index 4f469680..bc042d2f 100644 --- a/src/openApi/v3/parser/getOperationResponse.ts +++ b/src/openApi/v3/parser/getOperationResponse.ts @@ -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; } diff --git a/src/openApi/v3/parser/getOperationResponseHeader.ts b/src/openApi/v3/parser/getOperationResponseHeader.ts new file mode 100644 index 00000000..efd4e8eb --- /dev/null +++ b/src/openApi/v3/parser/getOperationResponseHeader.ts @@ -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; +} diff --git a/src/templates/core/RequestOptions.ts b/src/templates/core/RequestOptions.ts index e077369c..6f48a90f 100644 --- a/src/templates/core/RequestOptions.ts +++ b/src/templates/core/RequestOptions.ts @@ -11,4 +11,5 @@ export interface RequestOptions { query?: { [key: string]: any }; formData?: { [key: string]: any }; body?: any; + responseHeader?: string; } diff --git a/src/templates/core/request.ts b/src/templates/core/request.ts index 00d504dc..2662a1eb 100644 --- a/src/templates/core/request.ts +++ b/src/templates/core/request.ts @@ -66,9 +66,9 @@ export async function request(options: Readonly): Promise { 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): Promise { +export async function requestUsingFetch(url: string, request: Readonly, responseHeader?: string): Promise { // 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, }; } diff --git a/src/templates/core/requestUsingXHR.ts b/src/templates/core/requestUsingXHR.ts index a3445740..7d9fe0b6 100644 --- a/src/templates/core/requestUsingXHR.ts +++ b/src/templates/core/requestUsingXHR.ts @@ -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): Promise { +export async function requestUsingXHR(url: string, request: Readonly, responseHeader?: string): Promise { return new Promise(resolve => { const xhr = new XMLHttpRequest(); @@ -57,16 +73,19 @@ export async function requestUsingXHR(url: string, request: Readonly { 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); } diff --git a/src/templates/service.hbs b/src/templates/service.hbs index 0db0a6f9..ec2f3c5b 100644 --- a/src/templates/service.hbs +++ b/src/templates/service.hbs @@ -71,6 +71,9 @@ export class {{{name}}} { {{#if parametersBody~}} body: {{{parametersBody.name}}}, {{/if}} + {{#if responseHeader~}} + responseHeader: '{{{responseHeader}}}', + {{/if}} }); {{#if errors}} diff --git a/src/utils/postProcessServiceOperations.ts b/src/utils/postProcessServiceOperations.ts index 7963b5df..1fec8f0b 100644 --- a/src/utils/postProcessServiceOperations.ts +++ b/src/utils/postProcessServiceOperations.ts @@ -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) {