Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/hip-times-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sap-cloud-sdk/openapi': minor
'@sap-cloud-sdk/openapi-generator': minor
---

[New Functionality] Support request bodies with content type "multipart/form-data".
2 changes: 2 additions & 0 deletions packages/openapi-generator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,14 @@
"@sap-cloud-sdk/generator-common": "^4.4.0",
"@sap-cloud-sdk/openapi": "^4.4.0",
"@sap-cloud-sdk/util": "^4.4.0",
"content-type": "^1.0.5",
"js-yaml": "^4.1.1",
"openapi-types": "^12.1.3",
"swagger2openapi": "^7.0.4"
},
"devDependencies": {
"@apidevtools/json-schema-ref-parser": "^14.1.1",
"@types/content-type": "^1.1.9",
"@types/js-yaml": "^4.0.9",
"mock-fs": "^5.5.0",
"prettier": "^3.8.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export const TestApi = {
"/test/{id}",
{
pathParameters: { id },
queryParameters,
headerParameters
headerParameters,
queryParameters
},
TestApi._defaultBasePath
),
Expand Down Expand Up @@ -91,8 +91,8 @@ export const TestApi = {
"/test/{id}",
{
pathParameters: { id },
queryParameters,
headerParameters
headerParameters,
queryParameters
},
TestApi._defaultBasePath
),
Expand Down Expand Up @@ -133,8 +133,8 @@ export const TestApi = {
"/test/{id}",
{
pathParameters: { id },
queryParameters,
headerParameters
headerParameters,
queryParameters
},
TestApi._defaultBasePath
),
Expand Down
166 changes: 83 additions & 83 deletions packages/openapi-generator/src/file-serializer/operation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,25 @@ describe('serializeOperation', () => {
pathPattern: '/test/{id}/{subId}'
};
expect(serializeOperation(operation, apiName)).toMatchInlineSnapshot(`
"/**
* Create a request builder for execution of get requests to the '/test/{id}/{subId}' endpoint.
* @param id - Path parameter.
* @param subId - Path parameter.
* @param queryParameters - Object containing the following keys: limit.
* @param headerParameters - Object containing the following keys: resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (id: string, subId: string, queryParameters?: {'limit'?: number}, headerParameters?: {'resource-group'?: string}) => new OpenApiRequestBuilder<string>(
'get',
"/test/{id}/{subId}",
{
pathParameters: { id, subId },
queryParameters,
headerParameters
},
TestApi._defaultBasePath
)"
`);
"/**
* Create a request builder for execution of get requests to the '/test/{id}/{subId}' endpoint.
* @param id - Path parameter.
* @param subId - Path parameter.
* @param queryParameters - Object containing the following keys: limit.
* @param headerParameters - Object containing the following keys: resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (id: string, subId: string, queryParameters?: {'limit'?: number}, headerParameters?: {'resource-group'?: string}) => new OpenApiRequestBuilder<string>(
'get',
"/test/{id}/{subId}",
{
pathParameters: { id, subId },
headerParameters,
queryParameters
},
TestApi._defaultBasePath
)"
`);
});

it('serializes operation with path and header parameters', () => {
Expand Down Expand Up @@ -236,22 +236,22 @@ describe('serializeOperation', () => {
};

expect(serializeOperation(operation, apiName)).toMatchInlineSnapshot(`
"/**
* Create a request builder for execution of get requests to the '/test' endpoint.
* @param queryParameters - Object containing the following keys: limit, page.
* @param headerParameters - Object containing the following keys: resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (queryParameters: {'limit'?: number, 'page'?: number}, headerParameters: {'resource-group': string}) => new OpenApiRequestBuilder<any>(
'get',
"/test",
{
queryParameters,
headerParameters
},
TestApi._defaultBasePath
)"
`);
"/**
* Create a request builder for execution of get requests to the '/test' endpoint.
* @param queryParameters - Object containing the following keys: limit, page.
* @param headerParameters - Object containing the following keys: resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (queryParameters: {'limit'?: number, 'page'?: number}, headerParameters: {'resource-group': string}) => new OpenApiRequestBuilder<any>(
'get',
"/test",
{
headerParameters,
queryParameters
},
TestApi._defaultBasePath
)"
`);
});

it('serializes operation with optional query and optional + required header parameters', () => {
Expand Down Expand Up @@ -302,22 +302,22 @@ describe('serializeOperation', () => {
};

expect(serializeOperation(operation, apiName)).toMatchInlineSnapshot(`
"/**
* Create a request builder for execution of get requests to the '/test' endpoint.
* @param queryParameters - Object containing the following keys: limit, page.
* @param headerParameters - Object containing the following keys: authentication, resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (queryParameters: {'limit'?: number, 'page'?: number}, headerParameters: {'authentication': string, 'resource-group'?: string}) => new OpenApiRequestBuilder<any>(
'get',
"/test",
{
queryParameters,
headerParameters
},
TestApi._defaultBasePath
)"
`);
"/**
* Create a request builder for execution of get requests to the '/test' endpoint.
* @param queryParameters - Object containing the following keys: limit, page.
* @param headerParameters - Object containing the following keys: authentication, resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (queryParameters: {'limit'?: number, 'page'?: number}, headerParameters: {'authentication': string, 'resource-group'?: string}) => new OpenApiRequestBuilder<any>(
'get',
"/test",
{
headerParameters,
queryParameters
},
TestApi._defaultBasePath
)"
`);
});

it('serializes operation with required query and optional header parameters', () => {
Expand Down Expand Up @@ -352,22 +352,22 @@ describe('serializeOperation', () => {
};

expect(serializeOperation(operation, apiName)).toMatchInlineSnapshot(`
"/**
* Create a request builder for execution of get requests to the '/test' endpoint.
* @param queryParameters - Object containing the following keys: limit.
* @param headerParameters - Object containing the following keys: resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (queryParameters: {'limit': number}, headerParameters?: {'resource-group'?: string}) => new OpenApiRequestBuilder<any>(
'get',
"/test",
{
queryParameters,
headerParameters
},
TestApi._defaultBasePath
)"
`);
"/**
* Create a request builder for execution of get requests to the '/test' endpoint.
* @param queryParameters - Object containing the following keys: limit.
* @param headerParameters - Object containing the following keys: resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (queryParameters: {'limit': number}, headerParameters?: {'resource-group'?: string}) => new OpenApiRequestBuilder<any>(
'get',
"/test",
{
headerParameters,
queryParameters
},
TestApi._defaultBasePath
)"
`);
});

it('serializes operation with required query and required header parameters', () => {
Expand Down Expand Up @@ -410,22 +410,22 @@ describe('serializeOperation', () => {
};

expect(serializeOperation(operation, apiName)).toMatchInlineSnapshot(`
"/**
* Create a request builder for execution of get requests to the '/test' endpoint.
* @param queryParameters - Object containing the following keys: limit, page.
* @param headerParameters - Object containing the following keys: resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (queryParameters: {'limit': number, 'page'?: number}, headerParameters: {'resource-group': string}) => new OpenApiRequestBuilder<any>(
'get',
"/test",
{
queryParameters,
headerParameters
},
TestApi._defaultBasePath
)"
`);
"/**
* Create a request builder for execution of get requests to the '/test' endpoint.
* @param queryParameters - Object containing the following keys: limit, page.
* @param headerParameters - Object containing the following keys: resource-group.
* @returns The request builder, use the \`execute()\` method to trigger the request.
*/
getFn: (queryParameters: {'limit': number, 'page'?: number}, headerParameters: {'resource-group': string}) => new OpenApiRequestBuilder<any>(
'get',
"/test",
{
headerParameters,
queryParameters
},
TestApi._defaultBasePath
)"
`);
});

it('serializes operation with only query parameters', () => {
Expand Down
29 changes: 26 additions & 3 deletions packages/openapi-generator/src/file-serializer/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ function serializeParamsForSignature(
}
}

function getHeaderParameters(operation: OpenApiOperation): string | undefined {
if (operation.requestBody?.mediaType) {
const contentTypeStr = `'content-type': '${operation.requestBody.mediaType}'`;
return operation.headerParameters.length
? `headerParameters: {${contentTypeStr}, ...headerParameters}`
: `headerParameters: {${contentTypeStr}}`;
}
if (operation.headerParameters.length) {
return 'headerParameters';
}
}

function serializeParamsForRequestBuilder(
operation: OpenApiOperation
): string | undefined {
Expand All @@ -121,13 +133,24 @@ function serializeParamsForRequestBuilder(
}
if (operation.requestBody) {
params.push('body');
if (
operation.requestBody.encoding &&
Object.keys(operation.requestBody.encoding).length
) {
params.push(
`_encoding: ${JSON.stringify(operation.requestBody.encoding)}`
);
}
}

const headerParam = getHeaderParameters(operation);
if (headerParam) {
params.push(headerParam);
}
if (operation.queryParameters.length) {
params.push('queryParameters');
}
if (operation.headerParameters.length) {
params.push('headerParameters');
}

if (params.length) {
return codeBlock`{
${params.join(',\n')}
Expand Down
29 changes: 29 additions & 0 deletions packages/openapi-generator/src/openapi-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,41 @@ export interface OpenApiRequestBody {
*/
schema: OpenApiSchema;

/**
* Media type of the body.
*/
mediaType: string;

/**
* Description of the body.
*/
description?: string;

/**
* Encoding options for multipart/form-data properties.
* Maps property names to their encoding configuration (e.g., contentType).
*/
encoding?: Record<
string,
{
contentType: string;
isImplicit: boolean;
parsedContentTypes: {
type: string;
parameters: { [key: string]: string };
}[];
}
>;
}

/**
* Representation of a media type.
* @internal
*/
export type OpenApiMediaTypeObject = OpenAPIV3.MediaTypeObject & {
mediaType: string;
};

/**
* Represents all possible Types of schemas.
* @internal
Expand Down
4 changes: 4 additions & 0 deletions packages/openapi-generator/src/parser/document.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import type { OpenAPIV3 } from 'openapi-types';

const options = { strictNaming: true, schemaPrefix: '', resolveExternal: true };
describe('parseOpenApiDocument()', () => {
afterEach(() => {
jest.restoreAllMocks();
});

it('does not modify input service specification', () => {
const input: OpenAPIV3.Document = {
...emptyDocument,
Expand Down
Loading
Loading