Skip to content

Commit a5aecd2

Browse files
committed
fix: improve logic for and tests
1 parent c205b0c commit a5aecd2

File tree

3 files changed

+632
-43
lines changed

3 files changed

+632
-43
lines changed

packages/bruno-converters/src/openapi/openapi-to-bruno.js

Lines changed: 119 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,33 @@ const getExampleFromSchema = (schema) => {
161161
}
162162
};
163163

164+
/**
165+
* Populates request body in Bruno example from a value
166+
* @param {Object} body - The Bruno request body object to populate
167+
* @param {*} requestBodyValue - The request body value to set
168+
* @param {string} contentType - Content type (e.g., 'application/json')
169+
*/
170+
const populateRequestBody = (body, requestBodyValue, contentType) => {
171+
if (!requestBodyValue) return;
172+
173+
if (contentType?.includes('application/json')) {
174+
body.mode = 'json';
175+
body.json = typeof requestBodyValue === 'object' ? JSON.stringify(requestBodyValue, null, 2) : requestBodyValue;
176+
} else if (contentType?.includes('application/x-www-form-urlencoded')) {
177+
body.mode = 'formUrlEncoded';
178+
// Handle form data if needed
179+
} else if (contentType?.includes('multipart/form-data')) {
180+
body.mode = 'multipartForm';
181+
// Handle multipart form data if needed
182+
} else if (contentType?.includes('text/plain')) {
183+
body.mode = 'text';
184+
body.text = typeof requestBodyValue === 'object' ? JSON.stringify(requestBodyValue) : String(requestBodyValue);
185+
} else if (contentType?.includes('text/xml') || contentType?.includes('application/xml')) {
186+
body.mode = 'xml';
187+
body.xml = typeof requestBodyValue === 'object' ? JSON.stringify(requestBodyValue) : String(requestBodyValue);
188+
}
189+
};
190+
164191
/**
165192
* Creates a Bruno example from OpenAPI example data
166193
* @param {Object} brunoRequestItem - The base Bruno request item
@@ -169,11 +196,11 @@ const getExampleFromSchema = (schema) => {
169196
* @param {string} exampleDescription - Description of the example
170197
* @param {string|number} statusCode - HTTP status code (for response examples)
171198
* @param {string} contentType - Content type (e.g., 'application/json')
172-
* @param {Object} options - Optional configuration
173-
* @param {boolean} options.isRequestBodyExample - If true, populate request body instead of response
199+
* @param {*} requestBodyValue - Optional request body value to populate in the example
200+
* @param {string} requestBodyContentType - Optional request body content type
174201
* @returns {Object} Bruno example object
175202
*/
176-
const createBrunoExample = (brunoRequestItem, exampleValue, exampleName, exampleDescription, statusCode, contentType) => {
203+
const createBrunoExample = (brunoRequestItem, exampleValue, exampleName, exampleDescription, statusCode, contentType, requestBodyValue = null, requestBodyContentType = null) => {
177204
const brunoExample = {
178205
uid: uuid(),
179206
itemUid: brunoRequestItem.uid,
@@ -206,6 +233,11 @@ const createBrunoExample = (brunoRequestItem, exampleValue, exampleName, example
206233
}
207234
};
208235

236+
// Populate request body if provided
237+
if (requestBodyValue !== null) {
238+
populateRequestBody(brunoExample.request.body, requestBodyValue, requestBodyContentType);
239+
}
240+
209241
return brunoExample;
210242
};
211243

@@ -482,9 +514,87 @@ const transformOpenapiRequestItem = (request, usedNames = new Set()) => {
482514
}
483515

484516
// Handle OpenAPI examples from responses and request body
485-
if (_operationObject.responses || _operationObject.requestBody) {
517+
if (_operationObject.responses) {
486518
const examples = [];
487519

520+
// Extract request body examples if they exist
521+
// Unified structure: all request body data is stored as examples with contentType
522+
const requestBodyExamples = [];
523+
524+
/**
525+
* Helper function to create examples with appropriate request body handling
526+
* @param {*} responseExampleValue - The response example value
527+
* @param {string} exampleName - Name of the example
528+
* @param {string} exampleDescription - Description of the example
529+
* @param {string|number} statusCode - HTTP status code
530+
* @param {string} responseContentType - Response content type
531+
* @param {string} [responseExampleKey] - Optional response example key for matching
532+
*/
533+
const createExamplesWithRequestBody = (responseExampleValue, exampleName, exampleDescription, statusCode, responseContentType, responseExampleKey = null) => {
534+
const requestBodyExamplesWithKeys = requestBodyExamples.filter((rb) => rb.key !== null);
535+
const requestBodyExamplesWithoutKeys = requestBodyExamples.filter((rb) => rb.key === null);
536+
537+
// Check if there's a matching request body example by key
538+
const matchingRequestBodyExample = responseExampleKey
539+
? requestBodyExamplesWithKeys.find((rb) => rb.key === responseExampleKey)
540+
: null;
541+
542+
if (matchingRequestBodyExample) {
543+
// Use the matching request body example
544+
examples.push(createBrunoExample(brunoRequestItem, responseExampleValue, exampleName, exampleDescription, statusCode, responseContentType, matchingRequestBodyExample.value, matchingRequestBodyExample.contentType));
545+
} else if (requestBodyExamplesWithKeys.length > 0) {
546+
// No match found, create all combinations with request body examples that have keys
547+
requestBodyExamplesWithKeys.forEach((rbExample) => {
548+
const combinedExampleName = `${exampleName} (${rbExample.summary || rbExample.key})`;
549+
const combinedExampleDescription = exampleDescription || rbExample.description || '';
550+
examples.push(createBrunoExample(brunoRequestItem, responseExampleValue, combinedExampleName, combinedExampleDescription, statusCode, responseContentType, rbExample.value, rbExample.contentType));
551+
});
552+
} else if (requestBodyExamplesWithoutKeys.length > 0) {
553+
// Single example or schema - use the first one for all response examples
554+
const rbExample = requestBodyExamplesWithoutKeys[0];
555+
examples.push(createBrunoExample(brunoRequestItem, responseExampleValue, exampleName, exampleDescription, statusCode, responseContentType, rbExample.value, rbExample.contentType));
556+
} else {
557+
// No request body, create example without request body
558+
examples.push(createBrunoExample(brunoRequestItem, responseExampleValue, exampleName, exampleDescription, statusCode, responseContentType));
559+
}
560+
};
561+
562+
if (_operationObject.requestBody && _operationObject.requestBody.content) {
563+
Object.entries(_operationObject.requestBody.content).forEach(([contentType, content]) => {
564+
if (content.examples) {
565+
// Multiple request body examples
566+
Object.entries(content.examples).forEach(([exampleKey, example]) => {
567+
requestBodyExamples.push({
568+
key: exampleKey,
569+
value: example.value !== undefined ? example.value : example,
570+
summary: example.summary,
571+
description: example.description,
572+
contentType: contentType
573+
});
574+
});
575+
} else if (content.example !== undefined) {
576+
// Single request body example - convert to unified structure
577+
requestBodyExamples.push({
578+
key: null, // No key for single example
579+
value: content.example,
580+
summary: null,
581+
description: null,
582+
contentType: contentType
583+
});
584+
} else if (content.schema) {
585+
// Schema-based request body - convert to unified structure
586+
requestBodyExamples.push({
587+
key: null, // No key for schema
588+
value: getExampleFromSchema(content.schema),
589+
summary: null,
590+
description: null,
591+
contentType: contentType,
592+
isSchema: true
593+
});
594+
}
595+
});
596+
}
597+
488598
// Handle response examples
489599
if (_operationObject.responses) {
490600
Object.entries(_operationObject.responses).forEach(([statusCode, response]) => {
@@ -497,51 +607,23 @@ const transformOpenapiRequestItem = (request, usedNames = new Set()) => {
497607
const exampleDescription = example.description || '';
498608
const exampleValue = example.value !== undefined ? example.value : example;
499609

500-
examples.push(createBrunoExample(brunoRequestItem, exampleValue, exampleName, exampleDescription, statusCode, contentType));
610+
createExamplesWithRequestBody(exampleValue, exampleName, exampleDescription, statusCode, contentType, exampleKey);
501611
});
502612
} else if (content.example !== undefined) {
503613
// Handle example (singular) at content level
504614
const exampleName = `${statusCode} Response`;
505615
const exampleDescription = response.description || '';
506-
examples.push(createBrunoExample(brunoRequestItem, content.example, exampleName, exampleDescription, statusCode, contentType));
616+
617+
createExamplesWithRequestBody(content.example, exampleName, exampleDescription, statusCode, contentType);
507618
} else if (content.schema) {
508619
// Handle schema - extract or generate example from schema
509620
const exampleValue = getExampleFromSchema(content.schema);
510621
const exampleName = `${statusCode} Response`;
511622
const exampleDescription = response.description || '';
512-
examples.push(createBrunoExample(brunoRequestItem, exampleValue, exampleName, exampleDescription, statusCode, contentType));
513-
}
514-
});
515-
} else {
516-
// Create empty response example when no content is defined
517-
examples.push(createBrunoExample(brunoRequestItem, '', `${statusCode} Response`, response.description || '', statusCode, ''));
518-
}
519-
});
520-
}
521623

522-
// Handle request body examples
523-
if (_operationObject.requestBody && _operationObject.requestBody.content) {
524-
Object.entries(_operationObject.requestBody.content).forEach(([contentType, content]) => {
525-
// Handle examples (plural) in request body
526-
if (content.examples) {
527-
Object.entries(content.examples).forEach(([exampleKey, example]) => {
528-
const exampleName = example.summary || exampleKey || 'Request Example';
529-
const exampleDescription = example.description || '';
530-
const exampleValue = example.value !== undefined ? example.value : example;
531-
532-
examples.push(createBrunoExample(brunoRequestItem, exampleValue, exampleName, exampleDescription, 200, contentType));
624+
createExamplesWithRequestBody(exampleValue, exampleName, exampleDescription, statusCode, contentType);
625+
}
533626
});
534-
} else if (content.example !== undefined) {
535-
// Handle example (singular) at content level in request body
536-
const exampleName = 'Request Example';
537-
const exampleDescription = '';
538-
examples.push(createBrunoExample(brunoRequestItem, content.example, exampleName, exampleDescription, 200, contentType));
539-
} else if (content.schema) {
540-
// Handle schema in request body - extract or generate example from schema
541-
const exampleValue = getExampleFromSchema(content.schema);
542-
const exampleName = 'Request Example';
543-
const exampleDescription = '';
544-
examples.push(createBrunoExample(brunoRequestItem, exampleValue, exampleName, exampleDescription, 200, contentType));
545627
}
546628
});
547629
}

0 commit comments

Comments
 (0)