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
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,20 @@ function generateFunctionImplementation(params: {
? `const body = context.payload?.marshal();`
: `const body = undefined;`;

// Generate response parsing
// Use unmarshalByStatusCode if the payload is a union type with status code support
// Generate response parsing.
// Use unmarshalByStatusCode if the payload is a union type with status code support.
// unmarshal receives the raw JSON text (JSON.stringify(rawData)) rather than the
// parsed object: object/array models accept either, but primitive-typed payloads
// (e.g. `type X = string`) generate `unmarshal(json: string)` which JSON.parses its
// argument, so passing the already-parsed value would both fail to type-check and
// throw at runtime.
let responseParseCode: string;
if (replyMessageModule) {
responseParseCode = includesStatusCodes
? `const responseData = ${replyMessageModule}.unmarshalByStatusCode(rawData, response.status);`
: `const responseData = ${replyMessageModule}.unmarshal(rawData);`;
? `const responseData = ${replyMessageModule}.unmarshalByStatusCode(JSON.stringify(rawData), response.status);`
: `const responseData = ${replyMessageModule}.unmarshal(JSON.stringify(rawData));`;
} else {
responseParseCode = `const responseData = ${replyMessageType}.unmarshal(rawData);`;
responseParseCode = `const responseData = ${replyMessageType}.unmarshal(JSON.stringify(rawData));`;
}

// Generate default context for optional context parameter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ async function addPet(context: AddPetContext): Promise<HttpClientResponse<Pet>>

// Parse response
const rawData = await response.json();
const responseData = Pet.unmarshal(rawData);
const responseData = Pet.unmarshal(JSON.stringify(rawData));

// Extract response metadata
const responseHeaders = extractHeaders(response);
Expand Down Expand Up @@ -1216,7 +1216,7 @@ async function updatePet(context: UpdatePetContext): Promise<HttpClientResponse<

// Parse response
const rawData = await response.json();
const responseData = Pet.unmarshal(rawData);
const responseData = Pet.unmarshal(JSON.stringify(rawData));

// Extract response metadata
const responseHeaders = extractHeaders(response);
Expand Down Expand Up @@ -1339,7 +1339,7 @@ async function findPetsByStatusAndCategory(context: FindPetsByStatusAndCategoryC

// Parse response
const rawData = await response.json();
const responseData = FindPetsByStatusAndCategoryResponseModule.unmarshal(rawData);
const responseData = FindPetsByStatusAndCategoryResponseModule.unmarshal(JSON.stringify(rawData));

// Extract response metadata
const responseHeaders = extractHeaders(response);
Expand Down Expand Up @@ -3038,7 +3038,7 @@ async function getPingRequest(context: GetPingRequestContext = {}): Promise<Http

// Parse response
const rawData = await response.json();
const responseData = TestPayloadModelModule.unmarshal(rawData);
const responseData = TestPayloadModelModule.unmarshal(JSON.stringify(rawData));

// Extract response metadata
const responseHeaders = extractHeaders(response);
Expand Down
7 changes: 7 additions & 0 deletions test/codegen/generators/typescript/channels.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,13 @@ describe('channels', () => {
expect(generatedChannels.files.length).toBeGreaterThan(0);
expect(generatedChannels.result).toMatchSnapshot('openapi-http_client-index');
expect(generatedChannels.protocolFiles['http_client']).toMatchSnapshot('openapi-http_client-protocol-code');

// Responses are unmarshalled from the raw JSON text, not the parsed
// object, so primitive-typed payloads whose unmarshal JSON.parses its
// argument keep working. See fix/openapi-http-client-primitive-response.
const httpProtocolCode = generatedChannels.protocolFiles['http_client'];
expect(httpProtocolCode).toContain('unmarshal(JSON.stringify(rawData))');
expect(httpProtocolCode).not.toMatch(/unmarshal\(rawData\)/);
});

it('should skip generation when http_client is not in protocols', async () => {
Expand Down
42 changes: 42 additions & 0 deletions test/runtime/openapi-primitive.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"openapi": "3.1.0",
"info": {
"title": "Primitive Response API",
"version": "1.0.0",
"description": "Minimal spec whose operations return primitive (non-object) payloads, used to prove the HTTP client unmarshals string/number bodies correctly."
},
"paths": {
"/echo": {
"get": {
"operationId": "getEcho",
"summary": "Return a plain string body",
"responses": {
"200": {
"description": "A plain string",
"content": {
"application/json": {
"schema": { "type": "string" }
}
}
}
}
}
},
"/count": {
"get": {
"operationId": "getCount",
"summary": "Return a plain number body",
"responses": {
"200": {
"description": "A plain number",
"content": {
"application/json": {
"schema": { "type": "number" }
}
}
}
}
}
}
}
}
30 changes: 30 additions & 0 deletions test/runtime/typescript/codegen-openapi-primitive.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/** @type {import("../../../dist").TheCodegenConfiguration} **/
export default {
inputType: 'openapi',
inputPath: '../openapi-primitive.json',
language: 'typescript',
generators: [
{
preset: 'payloads',
outputPath: './src/openapi-primitive/payloads',
serializationType: 'json',
},
{
preset: 'parameters',
outputPath: './src/openapi-primitive/parameters',
},
{
preset: 'headers',
outputPath: './src/openapi-primitive/headers',
},
{
preset: 'types',
outputPath: './src/openapi-primitive',
},
{
preset: 'channels',
outputPath: './src/openapi-primitive/channels',
protocols: ['http_client']
}
]
};
3 changes: 2 additions & 1 deletion test/runtime/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
"test:websocket": "jest -- ./test/channels/regular/websocket.spec.ts",
"test:http": "jest -- ./test/channels/request_reply/http_client/",
"test:payload-types": "jest -- ./test/payload-types/",
"generate": "npm run generate:regular && npm run generate:request:reply && npm run generate:openapi && npm run generate:payload-types",
"generate": "npm run generate:regular && npm run generate:request:reply && npm run generate:openapi && npm run generate:openapi-primitive && npm run generate:payload-types",
"generate:request:reply": "cross-env CODEGEN_TELEMETRY_DISABLED=1 node ../../../bin/run.mjs generate ./codegen-request-reply.mjs",
"generate:regular": "cross-env CODEGEN_TELEMETRY_DISABLED=1 node ../../../bin/run.mjs generate ./codegen-regular.mjs",
"generate:openapi": "cross-env CODEGEN_TELEMETRY_DISABLED=1 node ../../../bin/run.mjs generate ./codegen-openapi.mjs",
"generate:openapi-primitive": "cross-env CODEGEN_TELEMETRY_DISABLED=1 node ../../../bin/run.mjs generate ./codegen-openapi-primitive.mjs",
"generate:payload-types": "cross-env CODEGEN_TELEMETRY_DISABLED=1 node ../../../bin/run.mjs generate ./codegen-payload-types.mjs",
"debug:generate": "node --inspect-brk ../../../bin/run.mjs generate"
},
Expand Down
26 changes: 26 additions & 0 deletions test/runtime/typescript/src/openapi-primitive/Types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export type Paths = '/echo' | '/count';
export type OperationIds = 'getEcho' | 'getCount';
export function ToPath(operationId: OperationIds): Paths {
switch (operationId) {
case 'getEcho':
return '/echo';
case 'getCount':
return '/count';
default:
throw new Error('Unknown operation ID: ' + operationId);
}
}
export function ToOperationIds(path: Paths): OperationIds[] {
switch (path) {
case '/echo':
return ['getEcho'];
case '/count':
return ['getCount'];
default:
throw new Error('Unknown path: ' + path);
}
}
export const PathsMap: Record<OperationIds, Paths> = {
'getEcho': '/echo',
'getCount': '/count'
};
Loading
Loading