Skip to content

Commit

Permalink
Support binary responses
Browse files Browse the repository at this point in the history
By default this library adds a `Accept: application/json` header to
requests.
We want to signal that other content types are accepted also, eg
'application/pdf'.

Furthermore, Axios won't handle binary response data without the
`responseType` parameter set to something other than `json` (which is
the default).

Therefore, this library now set both a non-`Accept: application/json`
header (if one is not explicit defined in the spec)
and `responseType: arraybuffer` parameter for binary responses.

See https://axios-http.com/docs/req_config.
  • Loading branch information
rbruggem committed Dec 24, 2024
1 parent 1c3cb8e commit 392bf49
Show file tree
Hide file tree
Showing 14 changed files with 291 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/client/interfaces/Operation.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export interface Operation extends OperationParameters {
errors: OperationError[];
results: OperationResponse[];
responseHeader: string | null;
responseType: 'arraybuffer' | 'document' | 'json' | 'text' | 'stream' | 'blob' | null;
}
1 change: 1 addition & 0 deletions src/client/interfaces/OperationResponse.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import type { Model } from './Model';
export interface OperationResponse extends Model {
in: 'response' | 'header';
code: number;
mediaType: string | null;
}
1 change: 1 addition & 0 deletions src/openApi/v2/parser/getOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export const getOperation = (
errors: [],
results: [],
responseHeader: null,
responseType: 'json',
};

// Parse the operation parameters (path, query, body, etc).
Expand Down
1 change: 1 addition & 0 deletions src/openApi/v2/parser/getOperationResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const getOperationResponse = (
enum: [],
enums: [],
properties: [],
mediaType: null,
};

// If this response has a schema, then we need to check two things:
Expand Down
1 change: 1 addition & 0 deletions src/openApi/v2/parser/getOperationResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const getOperationResults = (operationResponses: OperationResponse[]): Op
enum: [],
enums: [],
properties: [],
mediaType: null,
});
}

Expand Down
34 changes: 34 additions & 0 deletions src/openApi/v3/parser/getOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import type { OpenApi } from '../interfaces/OpenApi';
import type { OpenApiOperation } from '../interfaces/OpenApiOperation';
import type { OpenApiRequestBody } from '../interfaces/OpenApiRequestBody';
import { getOperationErrors } from './getOperationErrors';
import { getOperationExplicitAcceptHeader } from './getOperationExplicitAcceptHeader';
import { getOperationImplicitAcceptHeader } from './getOperationImplicitAcceptHeader';
import { getOperationName } from './getOperationName';
import { getOperationParameters } from './getOperationParameters';
import { getOperationRequestBody } from './getOperationRequestBody';
import { getOperationResponseHeader } from './getOperationResponseHeader';
import { getOperationResponses } from './getOperationResponses';
import { getOperationResponseType } from './getOperationResponseType';
import { getOperationResults } from './getOperationResults';
import { getRef } from './getRef';
import { getServiceName } from './getServiceName';
Expand Down Expand Up @@ -47,6 +50,7 @@ export const getOperation = (
errors: [],
results: [],
responseHeader: null,
responseType: null,
};

// We want to treat query parameters and body request as a single object with all the parameters
Expand Down Expand Up @@ -125,6 +129,36 @@ export const getOperation = (
const operationResults = getOperationResults(operationResponses);
operation.errors = getOperationErrors(operationResponses);
operation.responseHeader = getOperationResponseHeader(operationResults);
operation.responseType = getOperationResponseType(operationResults);

// Add 'Accept' header, if not set
const explicitAcceptHeader = parameters ? getOperationExplicitAcceptHeader(parameters.parametersHeader) : null;
if (!explicitAcceptHeader) {
const acceptHeader = getOperationImplicitAcceptHeader(operationResults);
if (acceptHeader) {
const acceptHeaderOperationParameter: OperationParameter = {
in: 'header',
prop: 'Accept',
export: 'interface',
name: `'${acceptHeader}'`,
type: 'any',
base: 'any',
template: null,
link: null,
description: null,
isDefinition: false,
isReadOnly: false,
isRequired: true,
isNullable: false,
imports: [],
enum: [],
enums: [],
properties: [],
mediaType: null,
};
operation.parametersHeader.push(acceptHeaderOperationParameter);
}
}

operationResults.forEach(operationResult => {
operation.results.push(operationResult);
Expand Down
11 changes: 11 additions & 0 deletions src/openApi/v3/parser/getOperationExplicitAcceptHeader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { OperationParameter } from '../../../client/interfaces/OperationParameter';

export const getOperationExplicitAcceptHeader = (operationParameters: OperationParameter[]): string | null => {
const header = operationParameters.find(operationParameter => {
return operationParameter.in === 'header' && operationParameter.name.toLowerCase() === 'accept';
});
if (header) {
return header.name;
}
return null;
};
11 changes: 11 additions & 0 deletions src/openApi/v3/parser/getOperationImplicitAcceptHeader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { OperationResponse } from '../../../client/interfaces/OperationResponse';

export const getOperationImplicitAcceptHeader = (operationResponses: OperationResponse[]): string | null => {
const operationResponse = operationResponses.find(operationResponses => {
return operationResponses.in === 'response';
});
if (operationResponse) {
return operationResponse.mediaType;
}
return null;
};
2 changes: 2 additions & 0 deletions src/openApi/v3/parser/getOperationResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ export const getOperationResponse = (
enum: [],
enums: [],
properties: [],
mediaType: null,
};

if (response.content) {
const content = getContent(openApi, response.content);
if (content) {
operationResponse.mediaType = content.mediaType;
if (content.schema.$ref?.startsWith('#/components/responses/')) {
content.schema = getRef<OpenApiSchema>(openApi, content.schema);
}
Expand Down
13 changes: 13 additions & 0 deletions src/openApi/v3/parser/getOperationResponseType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { OperationResponse } from '../../../client/interfaces/OperationResponse';

export const getOperationResponseType = (
operationResponses: OperationResponse[]
): 'arraybuffer' | 'document' | 'json' | 'text' | 'stream' | 'blob' | null => {
const operationResponse = operationResponses.find(operationResponses => {
return operationResponses.in === 'response';
});
if (operationResponse) {
return operationResponse.format === 'binary' ? 'blob' : null; // Other cases: not implemented
}
return null;
};
1 change: 1 addition & 0 deletions src/openApi/v3/parser/getOperationResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const getOperationResults = (operationResponses: OperationResponse[]): Op
enum: [],
enums: [],
properties: [],
mediaType: null,
});
}

Expand Down
3 changes: 3 additions & 0 deletions src/templates/exportService.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ export abstract class {{{name}}}{{{@root.postfix}}} {
{{#if responseHeader}}
responseHeader: '{{{responseHeader}}}',
{{/if}}
{{#if responseType}}
responseType: '{{{responseType}}}',
{{/if}}
{{#if errors}}
errors: {
{{#each errors}}
Expand Down
Loading

0 comments on commit 392bf49

Please sign in to comment.