mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-22 04:05:42 +00:00
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:
@@ -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: []
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user