Merge pull request #6083 from dawidgora/bugfix/5939_curl-import-fails-for-custom-content-types

bugfix(#5939): curl import fails for custom content-types
This commit is contained in:
Bijin A B
2025-11-13 17:26:49 +05:30
committed by GitHub
4 changed files with 73 additions and 7 deletions

View File

@@ -0,0 +1,29 @@
const normalizeContentType = (contentType) => {
if (!contentType || typeof contentType !== 'string') {
return '';
}
return contentType.toLowerCase();
};
export const isJsonLikeContentType = (contentType) => {
const normalized = normalizeContentType(contentType);
return normalized.includes('application/json') || normalized.includes('+json');
};
export const isXmlLikeContentType = (contentType) => {
const normalized = normalizeContentType(contentType);
return normalized.includes('application/xml') || normalized.includes('+xml') || normalized.includes('text/xml');
};
export const isPlainTextContentType = (contentType) => {
const normalized = normalizeContentType(contentType);
return normalized.includes('text/plain');
};
export const isStructuredContentType = (contentType) => {
return isJsonLikeContentType(contentType) || isXmlLikeContentType(contentType) || isPlainTextContentType(contentType);
};

View File

@@ -10,6 +10,7 @@ import parseCurlCommand from './parse-curl';
import * as querystring from 'query-string';
import * as jsesc from 'jsesc';
import { buildQueryString } from '@usebruno/common/utils';
import { isStructuredContentType } from './content-type';
function getContentType(headers = {}) {
const contentType = Object.keys(headers).find((key) => key.toLowerCase() === 'content-type');
@@ -34,7 +35,7 @@ function getDataString(request) {
const contentType = getContentType(request.headers);
if (contentType && (contentType.includes('application/json') || contentType.includes('application/xml') || contentType.includes('text/plain'))) {
if (isStructuredContentType(contentType)) {
return { data: request.data };
}

View File

@@ -120,4 +120,37 @@ describe('curlToJson', () => {
]
});
});
it('should parse custom json content-types', () => {
const curlCommand = `curl 'https://api.example.com/test'
-H 'content-type: application/x.custom+json;version=1'
--data-raw '{"test":"data"}'
`;
const result = curlToJson(curlCommand);
expect(result).toEqual({
url: 'https://api.example.com/test',
raw_url: 'https://api.example.com/test',
method: 'post',
headers: {
'content-type': 'application/x.custom+json;version=1'
},
data: '{"test":"data"}'
});
});
it('should parse vendor tree json content-types', () => {
const curlCommand = `curl --request POST \\
--url https://api.example.com/orders/42/preferences \\
--header 'accept: */*' \\
--header 'content-type: application/vnd.vendor+json' \\
--data '{\\n "data": {\\n "type": "order-preferences",\\n "attributes": {\\n "notes": "Leave at door",\\n "priority": true\\n }\\n }\\n}'`;
const result = curlToJson(curlCommand);
expect(result.data).toContain('"type": "order-preferences"');
expect(result.data).toContain('"notes": "Leave at door"');
expect(result.data).toContain('"priority": true');
expect(result.headers['content-type']).toBe('application/vnd.vendor+json');
});
});

View File

@@ -1,6 +1,7 @@
import { forOwn } from 'lodash';
import curlToJson from './curl-to-json';
import { prettifyJsonString } from 'utils/common/index';
import { isJsonLikeContentType, isPlainTextContentType, isXmlLikeContentType } from './content-type';
export const getRequestFromCurlCommand = (curlCommand, requestType = 'http-request') => {
const parseFormData = (parsedBody) => {
@@ -59,25 +60,27 @@ export const getRequestFromCurlCommand = (curlCommand, requestType = 'http-reque
};
if (parsedBody && contentType && typeof contentType === 'string') {
if (requestType === 'graphql-request' && (contentType.includes('application/json') || contentType.includes('application/graphql'))) {
const normalizedContentType = contentType.toLowerCase();
if (requestType === 'graphql-request' && (isJsonLikeContentType(contentType) || normalizedContentType.includes('application/graphql'))) {
body.mode = 'graphql';
body.graphql = parseGraphQL(parsedBody);
} else if (requestType === 'http-request' && request.isDataBinary) {
body.mode = 'file';
body.file = parsedBody;
}else if (contentType.includes('application/json')) {
} else if (isJsonLikeContentType(contentType)) {
body.mode = 'json';
body.json = prettifyJsonString(parsedBody);
} else if (contentType.includes('xml')) {
} else if (isXmlLikeContentType(contentType) || normalizedContentType.includes('xml')) {
body.mode = 'xml';
body.xml = parsedBody;
} else if (contentType.includes('application/x-www-form-urlencoded')) {
} else if (normalizedContentType.includes('application/x-www-form-urlencoded')) {
body.mode = 'formUrlEncoded';
body.formUrlEncoded = parseFormData(parsedBody);
} else if (contentType.includes('multipart/form-data')) {
} else if (normalizedContentType.includes('multipart/form-data')) {
body.mode = 'multipartForm';
body.multipartForm = parsedBody;
} else if (contentType.includes('text/plain')) {
} else if (isPlainTextContentType(contentType)) {
body.mode = 'text';
body.text = parsedBody;
}