fix: openapi body import (#6288)

* fix: openapi body import

* add: unit test

* fix

* fix

* Revert "fix"

This reverts commit 3219e8af8e.

* fix: we need the same check here too!

* fix: handle number type

* fix: correct empty securitySchemes check

---------

Co-authored-by: Taylore Thornton <tthornton3@chewy.com>
This commit is contained in:
Pooja
2025-12-16 17:23:22 +05:30
committed by GitHub
parent dc111ecce2
commit dbd966850c
2 changed files with 241 additions and 7 deletions

View File

@@ -128,15 +128,15 @@ const buildEmptyJsonBody = (bodySchema, visited = new Map()) => {
let _jsonBody = {};
each(bodySchema.properties || {}, (prop, name) => {
if (prop.type === 'object') {
if (prop.type === 'object' || prop.properties) {
_jsonBody[name] = buildEmptyJsonBody(prop, visited);
} else if (prop.type === 'array') {
if (prop.items && prop.items.type === 'object') {
if (prop.items && (prop.items.type === 'object' || prop.items.properties)) {
_jsonBody[name] = [buildEmptyJsonBody(prop.items, visited)];
} else {
_jsonBody[name] = [];
}
} else if (prop.type === 'integer') {
} else if (prop.type === 'integer' || prop.type === 'number') {
_jsonBody[name] = 0;
} else if (prop.type === 'boolean') {
_jsonBody[name] = false;
@@ -489,7 +489,7 @@ const transformOpenapiRequestItem = (request, usedNames = new Set()) => {
if (CONTENT_TYPE_PATTERNS.JSON.test(normalizedMimeType)) {
brunoRequestItem.request.body.mode = 'json';
if (bodySchema && bodySchema.type === 'object') {
if (bodySchema && (bodySchema.type === 'object' || bodySchema.properties)) {
let _jsonBody = buildEmptyJsonBody(bodySchema);
brunoRequestItem.request.body.json = JSON.stringify(_jsonBody, null, 2);
}
@@ -498,7 +498,7 @@ const transformOpenapiRequestItem = (request, usedNames = new Set()) => {
}
} else if (normalizedMimeType === 'application/x-www-form-urlencoded') {
brunoRequestItem.request.body.mode = 'formUrlEncoded';
if (bodySchema && bodySchema.type === 'object') {
if (bodySchema && (bodySchema.type === 'object' || bodySchema.properties)) {
each(bodySchema.properties || {}, (prop, name) => {
brunoRequestItem.request.body.formUrlEncoded.push({
uid: uuid(),
@@ -511,7 +511,7 @@ const transformOpenapiRequestItem = (request, usedNames = new Set()) => {
}
} else if (normalizedMimeType === 'multipart/form-data') {
brunoRequestItem.request.body.mode = 'multipartForm';
if (bodySchema && bodySchema.type === 'object') {
if (bodySchema && (bodySchema.type === 'object' || bodySchema.properties)) {
each(bodySchema.properties || {}, (prop, name) => {
brunoRequestItem.request.body.multipartForm.push({
uid: uuid(),
@@ -927,7 +927,7 @@ const getSecurity = (apiSpec) => {
let defaultSchemes = apiSpec.security || [];
let securitySchemes = get(apiSpec, 'components.securitySchemes', {});
if (Object.keys(securitySchemes) === 0) {
if (Object.keys(securitySchemes).length === 0) {
return {
supported: []
};

View File

@@ -0,0 +1,234 @@
import { describe, it, expect } from '@jest/globals';
import openApiToBruno from '../../../src/openapi/openapi-to-bruno';
describe('openapi requestBody with $ref', () => {
it('should import body fields when requestBody uses $ref to components/requestBodies with inline schema (no explicit type: object)', () => {
const openApiSpec = `
openapi: "3.0.0"
info:
version: "1.0.0"
title: "RequestBody Ref Inline Schema Test"
servers:
- url: "https://api.example.com"
paths:
/salesInvoices:
post:
summary: "Creates a salesInvoice"
operationId: "postSalesInvoice"
requestBody:
$ref: '#/components/requestBodies/salesInvoice'
responses:
'201':
description: "A new salesInvoice has been successfully created"
components:
requestBodies:
salesInvoice:
required: true
content:
application/json:
schema:
properties:
id:
type: string
format: uuid
number:
type: string
maxLength: 20
externalDocumentNumber:
type: string
maxLength: 35
invoiceDate:
type: string
format: date-time
dueDate:
type: string
format: date-time
fees:
type: array
items:
$ref: '#/components/requestBodies/fees'
fees:
properties:
id:
type: string
format: uuid
name:
type: string
maxLength: 50
amount:
type: number
required:
- id
- amount
`;
const result = openApiToBruno(openApiSpec);
// Should have one request item
expect(result.items.length).toBe(1);
const request = result.items[0];
// Body mode should be json
expect(request.request.body.mode).toBe('json');
// Body should contain the properties from the schema
expect(request.request.body.json).not.toBeNull();
const bodyJson = JSON.parse(request.request.body.json);
expect(bodyJson).toHaveProperty('id');
expect(bodyJson).toHaveProperty('number');
expect(bodyJson).toHaveProperty('externalDocumentNumber');
expect(bodyJson).toHaveProperty('invoiceDate');
expect(bodyJson).toHaveProperty('dueDate');
expect(bodyJson).toHaveProperty('fees');
expect(bodyJson['fees'][0]).toHaveProperty('id');
});
it('should import formUrlEncoded body when requestBody uses $ref with inline schema', () => {
const openApiSpec = `
openapi: "3.0.0"
info:
version: "1.0.0"
title: "Form URL Encoded Ref Test"
servers:
- url: "https://api.example.com"
paths:
/login:
post:
summary: "Login"
operationId: "login"
requestBody:
$ref: '#/components/requestBodies/loginForm'
responses:
'200':
description: "Login successful"
components:
requestBodies:
loginForm:
required: true
content:
application/x-www-form-urlencoded:
schema:
properties:
username:
type: string
password:
type: string
`;
const result = openApiToBruno(openApiSpec);
expect(result.items.length).toBe(1);
const request = result.items[0];
expect(request.request.body.mode).toBe('formUrlEncoded');
expect(request.request.body.formUrlEncoded.length).toBe(2);
const fieldNames = request.request.body.formUrlEncoded.map((f) => f.name);
expect(fieldNames).toContain('username');
expect(fieldNames).toContain('password');
});
it('should import multipartForm body when requestBody uses $ref with inline schema', () => {
const openApiSpec = `
openapi: "3.0.0"
info:
version: "1.0.0"
title: "Multipart Form Ref Test"
servers:
- url: "https://api.example.com"
paths:
/upload:
post:
summary: "Upload file"
operationId: "uploadFile"
requestBody:
$ref: '#/components/requestBodies/fileUpload'
responses:
'200':
description: "Upload successful"
components:
requestBodies:
fileUpload:
required: true
content:
multipart/form-data:
schema:
properties:
file:
type: string
format: binary
description:
type: string
`;
const result = openApiToBruno(openApiSpec);
expect(result.items.length).toBe(1);
const request = result.items[0];
expect(request.request.body.mode).toBe('multipartForm');
expect(request.request.body.multipartForm.length).toBe(2);
const fieldNames = request.request.body.multipartForm.map((f) => f.name);
expect(fieldNames).toContain('file');
expect(fieldNames).toContain('description');
});
it('should handle number and integer types with correct default values', () => {
const openApiSpec = `
openapi: "3.0.0"
info:
version: "1.0.0"
title: "Number Type Test"
servers:
- url: "https://api.example.com"
paths:
/orders:
post:
summary: "Create order"
operationId: "createOrder"
requestBody:
content:
application/json:
schema:
properties:
quantity:
type: integer
price:
type: number
discount:
type: number
name:
type: string
active:
type: boolean
responses:
'201':
description: "Order created"
`;
const result = openApiToBruno(openApiSpec);
expect(result.items.length).toBe(1);
const request = result.items[0];
expect(request.request.body.mode).toBe('json');
expect(request.request.body.json).not.toBeNull();
const bodyJson = JSON.parse(request.request.body.json);
// integer type should be 0
expect(bodyJson.quantity).toBe(0);
// number type should be 0 (not empty string)
expect(bodyJson.price).toBe(0);
expect(bodyJson.discount).toBe(0);
// string type should be empty string
expect(bodyJson.name).toBe('');
// boolean type should be false
expect(bodyJson.active).toBe(false);
});
});