Skip to content

Commit

Permalink
feat: Support parsing top level media types (#4909)
Browse files Browse the repository at this point in the history
* support text/plain + */*

* support applicatoin/octet-stream

* imperative changelog

* modify getType function

* add test case

* refactor

* whoops

* revert

* Add test case

* remove comment

* Revert type-mapping

* remove tests

* cleanup

---------

Co-authored-by: Tom Frenken <[email protected]>
Co-authored-by: Marika Marszalkowski <[email protected]>
  • Loading branch information
3 people authored Sep 6, 2024
1 parent f94f899 commit 89f77cd
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .changeset/spotty-bulldogs-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sap-cloud-sdk/openapi-generator': minor
---

[Improvement] Configure generated OpenAPI clients to handle `text/plain`, `application/octet-stream`, and wildcard `*/*` content types in response headers.
68 changes: 56 additions & 12 deletions packages/openapi-generator/src/parser/media-type.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createTestRefs, emptyObjectSchema } from '../../test/test-util';
import { parseApplicationJsonMediaType, parseMediaType } from './media-type';
import { parseTopLevelMediaType, parseMediaType } from './media-type';

const defaultOptions = { strictNaming: true };
describe('parseApplicationJsonMediaType', () => {
describe('parseTopLevelMediaType', () => {
it('returns undefined if the media type is not supported', async () => {
expect(
parseApplicationJsonMediaType(
parseTopLevelMediaType(
{
content: { 'application/xml': { schema: { type: 'string' } } }
},
Expand All @@ -15,9 +15,9 @@ describe('parseApplicationJsonMediaType', () => {
).toBeUndefined();
});

it('returns parsed media type for supported media type application/json', async () => {
it('returns parsed schema for supported media type application/json', async () => {
expect(
parseApplicationJsonMediaType(
parseTopLevelMediaType(
{
content: { 'application/json': { schema: { type: 'object' } } }
},
Expand All @@ -27,9 +27,9 @@ describe('parseApplicationJsonMediaType', () => {
).toEqual(emptyObjectSchema);
});

it('returns parsed media type for supported media type application/merge-patch+json', async () => {
it('returns parsed schema for supported media type application/merge-patch+json', async () => {
expect(
parseApplicationJsonMediaType(
parseTopLevelMediaType(
{
content: {
'application/merge-patch+json': { schema: { type: 'object' } }
Expand All @@ -40,6 +40,36 @@ describe('parseApplicationJsonMediaType', () => {
)
).toEqual(emptyObjectSchema);
});

it('returns parsed schema for supported media type text/plain', async () => {
expect(
parseTopLevelMediaType(
{
content: {
'text/plain': { schema: { type: 'integer' } }
}
},
await createTestRefs(),
defaultOptions
)
).toEqual({ type: 'number' });
});

it('returns parsed schema for supported media type application/octet-stream', async () => {
expect(
parseTopLevelMediaType(
{
content: {
'application/octet-stream': {
schema: { type: 'string', format: 'binary' }
}
}
},
await createTestRefs(),
defaultOptions
)
).toEqual({ type: 'string' });
});
});

describe('parseMediaType', () => {
Expand All @@ -49,7 +79,7 @@ describe('parseMediaType', () => {
).toBeUndefined();
});

it('returns any schema if there are other schemas', async () => {
it('returns type `any` if there is an unsupported media type', async () => {
expect(
parseMediaType(
{
Expand All @@ -61,7 +91,7 @@ describe('parseMediaType', () => {
).toEqual({ type: 'any' });
});

it('returns parsed media type if there is only application/json', async () => {
it('returns parsed schema if there is only application/json', async () => {
expect(
parseMediaType(
{
Expand All @@ -73,7 +103,7 @@ describe('parseMediaType', () => {
).toEqual(emptyObjectSchema);
});

it('returns parsed media type if there is only application/merge-patch+json', async () => {
it('returns parsed schema if there is only application/merge-patch+json', async () => {
expect(
parseMediaType(
{
Expand All @@ -87,7 +117,21 @@ describe('parseMediaType', () => {
).toEqual(emptyObjectSchema);
});

it('returns parsed media type if there is both application/json and application/merge-patch+json', async () => {
it('returns parsed schema if there is only wildcard media type */*', async () => {
expect(
parseMediaType(
{
content: {
'*/*': { schema: { type: 'string' } }
}
},
await createTestRefs(),
defaultOptions
)
).toEqual({ type: 'string' });
});

it('returns parsed schema if there is both application/json and application/merge-patch+json', async () => {
expect(
parseMediaType(
{
Expand All @@ -102,7 +146,7 @@ describe('parseMediaType', () => {
).toEqual(emptyObjectSchema);
});

it('returns anyOf schema if there are other schemas and application/json', async () => {
it('returns anyOf schema if there are unsupported media type and supported media type', async () => {
expect(
parseMediaType(
{
Expand Down
23 changes: 13 additions & 10 deletions packages/openapi-generator/src/parser/media-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import { parseSchema } from './schema';
import { ParserOptions } from './options';

const logger = createLogger('openapi-generator');
const allowedJsonMediaTypes = [
const allowedMediaTypes = [
'application/json',
'application/merge-patch+json'
'application/merge-patch+json',
'application/octet-stream',
'text/plain',
'*/*'
];
/**
* Parse the type of a resolved request body or response object.
Expand All @@ -18,7 +21,7 @@ const allowedJsonMediaTypes = [
* @returns The type name of the request body if there is one.
* @internal
*/
export function parseApplicationJsonMediaType(
export function parseTopLevelMediaType(
bodyOrResponseObject:
| OpenAPIV3.RequestBodyObject
| OpenAPIV3.ResponseObject
Expand All @@ -29,7 +32,7 @@ export function parseApplicationJsonMediaType(
if (bodyOrResponseObject) {
const mediaType = getMediaTypeObject(
bodyOrResponseObject,
allowedJsonMediaTypes
allowedMediaTypes
);
const schema = mediaType?.schema;
if (schema) {
Expand All @@ -51,13 +54,13 @@ export function parseMediaType(
): OpenApiSchema | undefined {
const allMediaTypes = getMediaTypes(bodyOrResponseObject);
if (allMediaTypes.length) {
const jsonMediaType = parseApplicationJsonMediaType(
const parsedMediaType = parseTopLevelMediaType(
bodyOrResponseObject,
refs,
options
);

if (!jsonMediaType) {
if (!parsedMediaType) {
logger.warn(
`Could not parse '${allMediaTypes}', because it is not supported. Generation will continue with 'any'. This might lead to errors at runtime.`
);
Expand All @@ -66,12 +69,12 @@ export function parseMediaType(

// There is only one media type
if (allMediaTypes.length === 1) {
return jsonMediaType;
return parsedMediaType;
}

return allMediaTypes.every(type => allowedJsonMediaTypes.includes(type))
? jsonMediaType
: { anyOf: [jsonMediaType, { type: 'any' }] };
return allMediaTypes.every(type => allowedMediaTypes.includes(type))
? parsedMediaType
: { anyOf: [parsedMediaType, { type: 'any' }] };
}
}
/**
Expand Down

0 comments on commit 89f77cd

Please sign in to comment.