Skip to content

Commit cabf093

Browse files
committed
feat(core): add bodyType to UrlMapValue for content type awareness
- Add optional bodyType?: 'json' | 'form' | 'multipart' field to UrlMapValue - Field is optional for backward compatibility - Enables runtime to determine request body serialization strategy
1 parent f9ac798 commit cabf093

File tree

6 files changed

+3721
-14
lines changed

6 files changed

+3721
-14
lines changed

packages/core/src/url-map.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export interface UrlMapValue {
22
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
33
url: string
4+
bodyType?: 'json' | 'form' | 'multipart'
45
}

packages/generator/src/__tests__/generate-schema.test.ts

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
extractParameters,
66
extractRequestBody,
77
formatTypeValue,
8+
getRequestBodyContent,
89
getTypeFromSchema,
910
} from '../generate-schema'
1011

@@ -1185,3 +1186,171 @@ test('extractRequestBody handles requestBody with empty content', () => {
11851186
} as OpenAPIV3_1.RequestBodyObject
11861187
expect(extractRequestBody(requestBody, createDocument())).toBeUndefined()
11871188
})
1189+
1190+
// Tests for form-urlencoded content type
1191+
test('extractRequestBody handles requestBody with application/x-www-form-urlencoded', () => {
1192+
const requestBody = {
1193+
content: {
1194+
'application/x-www-form-urlencoded': {
1195+
schema: {
1196+
type: 'object' as const,
1197+
properties: {
1198+
name: { type: 'string' as const },
1199+
email: { type: 'string' as const },
1200+
},
1201+
},
1202+
},
1203+
},
1204+
} as OpenAPIV3_1.RequestBodyObject
1205+
expect(extractRequestBody(requestBody, createDocument())).toMatchSnapshot()
1206+
})
1207+
1208+
test('extractRequestBody handles requestBody $ref with application/x-www-form-urlencoded', () => {
1209+
const requestBody = {
1210+
$ref: '#/components/requestBodies/SubscribeForm',
1211+
} as OpenAPIV3_1.ReferenceObject
1212+
const document = {
1213+
components: {
1214+
requestBodies: {
1215+
SubscribeForm: {
1216+
content: {
1217+
'application/x-www-form-urlencoded': {
1218+
schema: {
1219+
type: 'object' as const,
1220+
properties: {
1221+
name: { type: 'string' as const },
1222+
email: { type: 'string' as const },
1223+
},
1224+
},
1225+
},
1226+
},
1227+
},
1228+
},
1229+
},
1230+
}
1231+
expect(
1232+
extractRequestBody(requestBody, createDocument(document)),
1233+
).toMatchSnapshot()
1234+
})
1235+
1236+
// Tests for multipart/form-data content type
1237+
test('extractRequestBody handles requestBody with multipart/form-data', () => {
1238+
const requestBody = {
1239+
content: {
1240+
'multipart/form-data': {
1241+
schema: {
1242+
type: 'object' as const,
1243+
properties: {
1244+
name: { type: 'string' as const },
1245+
document: { type: 'string' as const, format: 'binary' },
1246+
},
1247+
},
1248+
},
1249+
},
1250+
} as OpenAPIV3_1.RequestBodyObject
1251+
expect(extractRequestBody(requestBody, createDocument())).toMatchSnapshot()
1252+
})
1253+
1254+
test('extractRequestBody handles requestBody $ref with multipart/form-data', () => {
1255+
const requestBody = {
1256+
$ref: '#/components/requestBodies/FileUpload',
1257+
} as OpenAPIV3_1.ReferenceObject
1258+
const document = {
1259+
components: {
1260+
requestBodies: {
1261+
FileUpload: {
1262+
content: {
1263+
'multipart/form-data': {
1264+
schema: {
1265+
type: 'object' as const,
1266+
properties: {
1267+
file: { type: 'string' as const, format: 'binary' },
1268+
},
1269+
},
1270+
},
1271+
},
1272+
},
1273+
},
1274+
},
1275+
}
1276+
expect(
1277+
extractRequestBody(requestBody, createDocument(document)),
1278+
).toMatchSnapshot()
1279+
})
1280+
1281+
// Tests for JSON priority over other content types
1282+
test('extractRequestBody prefers application/json over other content types', () => {
1283+
const requestBody = {
1284+
content: {
1285+
'multipart/form-data': {
1286+
schema: {
1287+
type: 'object' as const,
1288+
properties: {
1289+
file: { type: 'string' as const, format: 'binary' },
1290+
},
1291+
},
1292+
},
1293+
'application/json': {
1294+
schema: {
1295+
type: 'object' as const,
1296+
properties: {
1297+
name: { type: 'string' as const },
1298+
},
1299+
},
1300+
},
1301+
},
1302+
} as OpenAPIV3_1.RequestBodyObject
1303+
// Should pick JSON content (no binary field)
1304+
const result = extractRequestBody(requestBody, createDocument())
1305+
expect(result).toMatchSnapshot()
1306+
})
1307+
1308+
// Tests for getRequestBodyContent helper
1309+
test('getRequestBodyContent returns undefined for undefined content', () => {
1310+
expect(getRequestBodyContent(undefined)).toBeUndefined()
1311+
})
1312+
1313+
test('getRequestBodyContent returns json content when available', () => {
1314+
const content = {
1315+
'application/json': { schema: { type: 'object' as const } },
1316+
'multipart/form-data': { schema: { type: 'object' as const } },
1317+
}
1318+
expect(getRequestBodyContent(content)).toBe(content['application/json'])
1319+
})
1320+
1321+
test('getRequestBodyContent returns urlencoded content when no json', () => {
1322+
const content = {
1323+
'application/x-www-form-urlencoded': {
1324+
schema: { type: 'object' as const },
1325+
},
1326+
'multipart/form-data': { schema: { type: 'object' as const } },
1327+
}
1328+
expect(getRequestBodyContent(content)).toBe(
1329+
content['application/x-www-form-urlencoded'],
1330+
)
1331+
})
1332+
1333+
test('getRequestBodyContent returns multipart content when no json or urlencoded', () => {
1334+
const content = {
1335+
'multipart/form-data': { schema: { type: 'object' as const } },
1336+
}
1337+
expect(getRequestBodyContent(content)).toBe(content['multipart/form-data'])
1338+
})
1339+
1340+
test('getRequestBodyContent returns undefined for unsupported content types', () => {
1341+
const content = {
1342+
'application/xml': { schema: { type: 'object' as const } },
1343+
}
1344+
expect(getRequestBodyContent(content)).toBeUndefined()
1345+
})
1346+
1347+
// Tests for format: "binary" in getTypeFromSchema
1348+
test('getTypeFromSchema handles string with format binary', () => {
1349+
const schema = { type: 'string' as const, format: 'binary' }
1350+
expect(getTypeFromSchema(schema, createDocument())).toMatchSnapshot()
1351+
})
1352+
1353+
test('getTypeFromSchema handles nullable string with format binary', () => {
1354+
const schema = { type: ['string', 'null'] as any, format: 'binary' }
1355+
expect(getTypeFromSchema(schema, createDocument())).toMatchSnapshot()
1356+
})

0 commit comments

Comments
 (0)