mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-11 09:51:30 +00:00
fix: enhance tag handling and validation in collection import/export (#7107)
- Added collection format handling in Tags component. - Updated convertCollection function to accept collectionFormat parameter. - Improved tag validation logic in TagList component based on collection format. - Adjusted OpenAPI transformation functions to support collection format options. - Enhanced schema validation for tags to allow spaces and underscores.
This commit is contained in:
@@ -58,6 +58,7 @@ const Tags = ({ item, collection }) => {
|
||||
handleRemoveTag={handleRemove}
|
||||
tags={tags}
|
||||
onSave={handleRequestSave}
|
||||
collectionFormat={collection.format}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -51,13 +51,13 @@ const getCollectionName = (format, rawData) => {
|
||||
};
|
||||
|
||||
// Convert raw data to Bruno collection format
|
||||
const convertCollection = async (format, rawData, groupingType) => {
|
||||
const convertCollection = async (format, rawData, groupingType, collectionFormat) => {
|
||||
try {
|
||||
let collection;
|
||||
|
||||
switch (format) {
|
||||
case 'openapi':
|
||||
collection = convertOpenapiToBruno(rawData, { groupBy: groupingType });
|
||||
collection = convertOpenapiToBruno(rawData, { groupBy: groupingType, collectionFormat });
|
||||
break;
|
||||
case 'wsdl':
|
||||
collection = await wsdlToBruno(rawData);
|
||||
@@ -127,7 +127,7 @@ const ImportCollectionLocation = ({ onClose, handleSubmit, rawData, format }) =>
|
||||
.required('Location is required')
|
||||
}),
|
||||
onSubmit: async (values) => {
|
||||
const convertedCollection = await convertCollection(format, rawData, groupingType);
|
||||
const convertedCollection = await convertCollection(format, rawData, groupingType, collectionFormat);
|
||||
handleSubmit(convertedCollection, values.collectionLocation, { format: collectionFormat });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,9 +4,10 @@ import StyledWrapper from './StyledWrapper';
|
||||
import SingleLineEditor from 'components/SingleLineEditor/index';
|
||||
import { useTheme } from 'providers/Theme/index';
|
||||
|
||||
const TagList = ({ tagsHintList = [], handleAddTag, tags, handleRemoveTag, onSave, handleValidation }) => {
|
||||
const TagList = ({ tagsHintList = [], handleAddTag, tags, handleRemoveTag, onSave, handleValidation, collectionFormat }) => {
|
||||
const { displayedTheme } = useTheme();
|
||||
const tagNameRegex = /^[\w-]+$/;
|
||||
const isBruFormat = collectionFormat === 'bru';
|
||||
const tagNameRegex = isBruFormat ? /^[\w-]+$/ : /^[\w-][\w\s-]*[\w-]$|^[\w-]+$/;
|
||||
const [text, setText] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
|
||||
@@ -16,8 +17,14 @@ const TagList = ({ tagsHintList = [], handleAddTag, tags, handleRemoveTag, onSav
|
||||
};
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (!text.trim()) {
|
||||
return;
|
||||
}
|
||||
if (!tagNameRegex.test(text)) {
|
||||
setError('Tags must only contain alpha-numeric characters, "-", "_"');
|
||||
setError(isBruFormat
|
||||
? 'Tags in BRU format must only contain alpha-numeric characters, "-", "_".'
|
||||
: 'Tags must only contain alpha-numeric characters, spaces, "-", "_"'
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (tags.includes(text)) {
|
||||
@@ -28,7 +35,6 @@ const TagList = ({ tagsHintList = [], handleAddTag, tags, handleRemoveTag, onSav
|
||||
const error = handleValidation(text);
|
||||
if (error) {
|
||||
setError(error);
|
||||
setText('');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +494,7 @@ const createBrunoExample = ({ brunoRequestItem, exampleValue, exampleName, examp
|
||||
return brunoExample;
|
||||
};
|
||||
|
||||
const transformOpenapiRequestItem = (request, usedNames = new Set()) => {
|
||||
const transformOpenapiRequestItem = (request, usedNames = new Set(), options = {}) => {
|
||||
let _operationObject = request.operationObject;
|
||||
|
||||
let operationName = _operationObject.summary || _operationObject.operationId || _operationObject.description;
|
||||
@@ -530,6 +530,15 @@ const transformOpenapiRequestItem = (request, usedNames = new Set()) => {
|
||||
uid: uuid(),
|
||||
name: operationName,
|
||||
type: 'http-request',
|
||||
tags: [...new Set(
|
||||
(request.operationObject.tags || []).map((tag) => {
|
||||
let sanitized = tag.trim();
|
||||
if (options.collectionFormat !== 'yml') {
|
||||
sanitized = sanitized.replace(/\s+/g, '_');
|
||||
}
|
||||
return sanitized;
|
||||
}).filter((tag) => tag.trim())
|
||||
)],
|
||||
request: {
|
||||
url: ensureUrl(request.global.server + path),
|
||||
method: request.method.toUpperCase(),
|
||||
@@ -1050,7 +1059,7 @@ const groupRequestsByTags = (requests) => {
|
||||
return [groups, ungrouped];
|
||||
};
|
||||
|
||||
const groupRequestsByPath = (requests) => {
|
||||
const groupRequestsByPath = (requests, options = {}) => {
|
||||
const pathGroups = {};
|
||||
|
||||
// Group requests by their path segments
|
||||
@@ -1110,7 +1119,7 @@ const groupRequestsByPath = (requests) => {
|
||||
const buildFolderStructure = (group) => {
|
||||
// Create a new usedNames set for each folder/subfolder scope
|
||||
const localUsedNames = new Set();
|
||||
const items = group.requests.map((req) => transformOpenapiRequestItem(req, localUsedNames));
|
||||
const items = group.requests.map((req) => transformOpenapiRequestItem(req, localUsedNames, options));
|
||||
|
||||
// Add sub-folders
|
||||
const subFolders = [];
|
||||
@@ -1258,7 +1267,7 @@ export const parseOpenApiCollection = (data, options = {}) => {
|
||||
const groupingType = options.groupBy || 'tags';
|
||||
|
||||
if (groupingType === 'path') {
|
||||
brunoCollection.items = groupRequestsByPath(allRequests);
|
||||
brunoCollection.items = groupRequestsByPath(allRequests, options);
|
||||
} else {
|
||||
// Default tag-based grouping
|
||||
let [groups, ungroupedRequests] = groupRequestsByTags(allRequests);
|
||||
@@ -1282,11 +1291,11 @@ export const parseOpenApiCollection = (data, options = {}) => {
|
||||
name: group.name
|
||||
}
|
||||
},
|
||||
items: group.requests.map((req) => transformOpenapiRequestItem(req, usedNames))
|
||||
items: group.requests.map((req) => transformOpenapiRequestItem(req, usedNames, options))
|
||||
};
|
||||
});
|
||||
|
||||
let ungroupedItems = ungroupedRequests.map((req) => transformOpenapiRequestItem(req, usedNames));
|
||||
let ungroupedItems = ungroupedRequests.map((req) => transformOpenapiRequestItem(req, usedNames, options));
|
||||
let brunoCollectionItems = brunoFolders.concat(ungroupedItems);
|
||||
brunoCollection.items = brunoCollectionItems;
|
||||
}
|
||||
|
||||
@@ -545,7 +545,7 @@ const itemSchema = Yup.object({
|
||||
type: Yup.string().oneOf(['http-request', 'graphql-request', 'folder', 'js', 'grpc-request', 'ws-request']).required('type is required'),
|
||||
seq: Yup.number().min(1),
|
||||
name: Yup.string().min(1, 'name must be at least 1 character').required('name is required'),
|
||||
tags: Yup.array().of(Yup.string().matches(/^[\w-]+$/, 'tag must be alphanumeric')),
|
||||
tags: Yup.array().of(Yup.string().matches(/^[\w-][\w\s-]*[\w-]$|^[\w-]+$/, 'tag must contain only alphanumeric characters, spaces, hyphens, or underscores')),
|
||||
request: Yup.mixed().when('type', {
|
||||
is: (type) => type === 'grpc-request',
|
||||
then: grpcRequestSchema.required('request is required when item-type is grpc-request'),
|
||||
|
||||
Reference in New Issue
Block a user