fix: circular recursion for openapi import (#4729)

This commit is contained in:
Pooja
2025-05-21 15:10:35 +05:30
committed by GitHub
parent e02c6c274b
commit 3696562414
3 changed files with 260 additions and 4 deletions

View File

@@ -8,14 +8,22 @@ const ensureUrl = (url) => {
return url.replace(/([^:])\/{2,}/g, '$1/');
};
const buildEmptyJsonBody = (bodySchema) => {
const buildEmptyJsonBody = (bodySchema, visited = new Map()) => {
// Check for circular references
if (visited.has(bodySchema)) {
return {};
}
// Add this schema to visited map
visited.set(bodySchema, true);
let _jsonBody = {};
each(bodySchema.properties || {}, (prop, name) => {
if (prop.type === 'object') {
_jsonBody[name] = buildEmptyJsonBody(prop);
_jsonBody[name] = buildEmptyJsonBody(prop, visited);
} else if (prop.type === 'array') {
if (prop.items && prop.items.type === 'object') {
_jsonBody[name] = [buildEmptyJsonBody(prop.items)];
_jsonBody[name] = [buildEmptyJsonBody(prop.items, visited)];
} else {
_jsonBody[name] = [];
}

View File

@@ -0,0 +1,248 @@
import { describe, it, expect } from '@jest/globals';
import openApiToBruno from '../../../src/openapi/openapi-to-bruno';
describe('openapi-circular-references', () => {
it('should handle simple circular references in schema correctly', async () => {
const brunoCollection = openApiToBruno(circularRefsData);
expect(brunoCollection).toMatchObject(circularRefsOutput);
});
it('should handle complex circular reference chains correctly', async () => {
const brunoCollection = openApiToBruno(complexCircularRefsData);
expect(brunoCollection).toMatchObject(circularRefsOutput);
});
});
const circularRefsData = {
"components": {
"schemas": {
"schema_1": {
"additionalProperties": false,
"description": "schema_1",
"properties": {
"conditions": {
"$ref": "#/components/schemas/schema_1"
}
},
"type": "object"
},
"schema_2": {
"additionalProperties": false,
"description": "schema_2",
"properties": {
"conditionGroup": {
"description": "nested schema_1",
"items": { "$ref": "#/components/schemas/schema_1" },
"type": "array"
},
"operation": {
"description": "operation",
"enum": ["ANY", "ALL"],
"type": "string"
}
},
"type": "object"
}
}
},
"info": {
"description": "circular reference openapi sample json spec",
"title": "circular reference openapi sample json spec",
"version": "0.1"
},
"openapi": "3.0.1",
"paths": {
"/": {
"post": {
"deprecated": false,
"description": "echo ping api",
"operationId": "echo ping",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/schema_1"
}
}
},
"description": "echo ping api",
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"example": "ping"
}
},
"description": "Returned if the request is successful."
}
}
}
}
},
"servers": [{ "url": "https://echo.usebruno.com" }]
};
// More complex circular reference test with a longer chain
const complexCircularRefsData = {
"components": {
"schemas": {
"schema_1": {
"additionalProperties": false,
"description": "schema_1",
"properties": {
"conditionGroup": {
"description": "nested schema_1",
"items": { "$ref": "#/components/schemas/schema_2" },
"type": "array"
}
},
"type": "object"
},
"schema_2": {
"additionalProperties": false,
"description": "schema_2",
"properties": {
"conditionGroup": {
"description": "nested schema_2",
"items": { "$ref": "#/components/schemas/schema_3" },
"type": "array"
}
},
"type": "object"
},
"schema_3": {
"additionalProperties": false,
"description": "schema_3",
"properties": {
"conditionGroup": {
"description": "nested schema_3",
"items": { "$ref": "#/components/schemas/schema_4" },
"type": "array"
}
},
"type": "object"
},
"schema_4": {
"additionalProperties": false,
"description": "schema_4",
"properties": {
"conditionGroup": {
"description": "nested schema_4",
"items": { "$ref": "#/components/schemas/schema_5" },
"type": "array"
}
},
"type": "object"
},
"schema_5": {
"additionalProperties": false,
"description": "schema_4",
"properties": {
"conditionGroup": {
"description": "nested schema_5",
"items": { "$ref": "#/components/schemas/schema_1" },
"type": "array"
}
},
"type": "object"
},
"schema_6": {
"additionalProperties": false,
"description": "schema_3",
"properties": {
"conditionGroup": {
"description": "nested schema_3",
"items": { "$ref": "#/components/schemas/schema_1" },
"type": "array"
},
"operation": {
"description": "operation",
"enum": ["ANY", "ALL"],
"type": "string"
}
},
"type": "object"
}
}
},
"info": {
"description": "circular reference openapi sample json spec",
"title": "circular reference openapi sample json spec",
"version": "0.1"
},
"openapi": "3.0.1",
"paths": {
"/": {
"post": {
"deprecated": false,
"description": "echo ping api",
"operationId": "echo ping",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/schema_1"
}
}
},
"description": "echo ping api",
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"example": "ping"
}
},
"description": "Returned if the request is successful."
}
}
}
}
},
"servers": [{ "url": "https://echo.usebruno.com" }]
};
const circularRefsOutput = {
"environments": [
{
"name": "Environment 1",
"variables": [
{
"enabled": true,
"name": "baseUrl",
"secret": false,
"type": "text",
"value": "https://echo.usebruno.com",
},
],
},
],
"items": [
{
"name": "echo ping",
"type": "http-request",
"request": {
"url": "{{baseUrl}}/",
"method": "POST",
"auth": {
"mode": "none",
},
"headers": [],
"params": [],
"body": {
"mode": "json",
}
},
},
],
"name": "circular reference openapi sample json spec",
"version": "1",
};

View File

@@ -1,5 +1,5 @@
import { describe, it, expect } from '@jest/globals';
import openApiToBruno from '../../src/openapi/openapi-to-bruno';
import openApiToBruno from '../../../src/openapi/openapi-to-bruno';
describe('openapi-collection', () => {
it('should correctly import a valid OpenAPI file', async () => {