From 9e192446651fbbc707e7b24588e3429d4c886047 Mon Sep 17 00:00:00 2001 From: Pooja Date: Wed, 12 Nov 2025 11:36:21 +0530 Subject: [PATCH] move: import setting into import collection modal (#5929) --- .../Sidebar/ImportCollection/index.js | 59 ++--- .../StyledWrapper.js | 0 .../Sidebar/ImportCollectionLocation/index.js | 228 ++++++++++++++---- .../Sidebar/ImportSettings/index.js | 84 ------- .../src/components/Sidebar/TitleBar/index.js | 17 +- .../bruno-app/src/components/Welcome/index.js | 17 +- .../src/utils/importers/bruno-collection.js | 24 ++ .../src/utils/importers/postman-collection.js | 16 +- ...port-bruno-missing-required-schema.spec.ts | 2 +- .../bruno/import-bruno-testbench.spec.ts | 14 +- .../insomnia/import-insomnia-v4.spec.ts | 15 +- .../insomnia/import-insomnia-v5.spec.ts | 15 +- .../invalid-missing-collection.spec.ts | 2 +- .../duplicate-operation-names-fix.spec.ts | 12 - .../openapi/import-openapi-json.spec.ts | 23 +- .../openapi/import-openapi-yaml.spec.ts | 23 +- tests/import/openapi/missing-info.spec.ts | 2 +- .../operation-name-with-newlines-fix.spec.ts | 10 - .../openapi/path-based-grouping.spec.ts | 18 +- .../import/postman/import-postman-v20.spec.ts | 16 +- .../import/postman/import-postman-v21.spec.ts | 16 +- tests/import/postman/invalid-json.spec.ts | 3 +- .../postman/invalid-missing-info.spec.ts | 2 +- tests/import/postman/invalid-schema.spec.ts | 2 +- .../postman/malformed-structure.spec.ts | 2 +- tests/import/wsdl/import-wsdl.spec.ts | 4 +- 26 files changed, 344 insertions(+), 282 deletions(-) rename packages/bruno-app/src/components/Sidebar/{ImportSettings => ImportCollectionLocation}/StyledWrapper.js (100%) delete mode 100644 packages/bruno-app/src/components/Sidebar/ImportSettings/index.js diff --git a/packages/bruno-app/src/components/Sidebar/ImportCollection/index.js b/packages/bruno-app/src/components/Sidebar/ImportCollection/index.js index 13f93a402..f0760f7e4 100644 --- a/packages/bruno-app/src/components/Sidebar/ImportCollection/index.js +++ b/packages/bruno-app/src/components/Sidebar/ImportCollection/index.js @@ -3,13 +3,11 @@ import { IconFileImport } from '@tabler/icons'; import { toastError } from 'utils/common/error'; import Modal from 'components/Modal'; import jsyaml from 'js-yaml'; -import { postmanToBruno, isPostmanCollection } from 'utils/importers/postman-collection'; -import { convertInsomniaToBruno, isInsomniaCollection } from 'utils/importers/insomnia-collection'; -import { convertOpenapiToBruno, isOpenApiSpec } from 'utils/importers/openapi-collection'; +import { isPostmanCollection } from 'utils/importers/postman-collection'; +import { isInsomniaCollection } from 'utils/importers/insomnia-collection'; +import { isOpenApiSpec } from 'utils/importers/openapi-collection'; import { isWSDLCollection } from 'utils/importers/wsdl-collection'; -import { processBrunoCollection } from 'utils/importers/bruno-collection'; -import { wsdlToBruno } from '@usebruno/converters'; -import ImportSettings from 'components/Sidebar/ImportSettings'; +import { isBrunoCollection } from 'utils/importers/bruno-collection'; import FullscreenLoader from './FullscreenLoader/index'; const convertFileToObject = async (file) => { @@ -38,9 +36,6 @@ const convertFileToObject = async (file) => { const ImportCollection = ({ onClose, handleSubmit }) => { const [isLoading, setIsLoading] = useState(false); const [dragActive, setDragActive] = useState(false); - const [showImportSettings, setShowImportSettings] = useState(false); - const [openApiData, setOpenApiData] = useState(null); - const [groupingType, setGroupingType] = useState('tags'); const fileInputRef = useRef(null); const handleDrag = (e) => { @@ -58,16 +53,6 @@ const ImportCollection = ({ onClose, handleSubmit }) => { } }; - const handleImportSettings = () => { - try { - const collection = convertOpenapiToBruno(openApiData, { groupBy: groupingType }); - handleSubmit({ collection }); - } catch (err) { - console.error(err); - toastError(err, 'Failed to process OpenAPI specification'); - } - }; - const processFile = async (file) => { setIsLoading(true); try { @@ -77,26 +62,23 @@ const ImportCollection = ({ onClose, handleSubmit }) => { throw new Error('Failed to parse file content'); } - // Check if it's an OpenAPI spec and show settings + let type = null; + if (isOpenApiSpec(data)) { - setOpenApiData(data); - setIsLoading(false); - setShowImportSettings(true); - return; - } - - let collection; - if (isWSDLCollection(data)) { - collection = await wsdlToBruno(data); + type = 'openapi'; + } else if (isWSDLCollection(data)) { + type = 'wsdl'; } else if (isPostmanCollection(data)) { - collection = await postmanToBruno(data); + type = 'postman'; } else if (isInsomniaCollection(data)) { - collection = convertInsomniaToBruno(data); + type = 'insomnia'; + } else if (isBrunoCollection(data)) { + type = 'bruno'; } else { - collection = await processBrunoCollection(data); + throw new Error('Unsupported collection format'); } - handleSubmit({ collection }); + handleSubmit({ rawData: data, type }); } catch (err) { toastError(err, 'Import collection failed'); } finally { @@ -140,17 +122,6 @@ const ImportCollection = ({ onClose, handleSubmit }) => { 'application/xml' ]; - if (showImportSettings) { - return ( - - ); - } - return (
diff --git a/packages/bruno-app/src/components/Sidebar/ImportSettings/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/StyledWrapper.js similarity index 100% rename from packages/bruno-app/src/components/Sidebar/ImportSettings/StyledWrapper.js rename to packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/StyledWrapper.js diff --git a/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js b/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js index 243a51f56..ffd543bf6 100644 --- a/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js +++ b/packages/bruno-app/src/components/Sidebar/ImportCollectionLocation/index.js @@ -1,15 +1,94 @@ -import React, { useRef, useEffect, useState } from 'react'; +import React, { useRef, useEffect, useState, forwardRef } from 'react'; import { useDispatch } from 'react-redux'; import { useFormik } from 'formik'; import * as Yup from 'yup'; +import { IconCaretDown } from '@tabler/icons'; import { browseDirectory } from 'providers/ReduxStore/slices/collections/actions'; +import { postmanToBruno } from 'utils/importers/postman-collection'; +import { convertInsomniaToBruno } from 'utils/importers/insomnia-collection'; +import { convertOpenapiToBruno } from 'utils/importers/openapi-collection'; +import { processBrunoCollection } from 'utils/importers/bruno-collection'; +import { wsdlToBruno } from '@usebruno/converters'; +import { toastError } from 'utils/common/error'; import Modal from 'components/Modal'; import Help from 'components/Help'; +import Dropdown from 'components/Dropdown'; +import StyledWrapper from './StyledWrapper'; +// Extract collection name from raw data +const getCollectionName = (format, rawData) => { + if (!rawData) return 'Collection'; -const ImportCollectionLocation = ({ onClose, handleSubmit, collectionName }) => { + switch (format) { + case 'openapi': + return rawData.info?.title || 'OpenAPI Collection'; + case 'postman': + return rawData.info?.name || rawData.collection?.info?.name || 'Postman Collection'; + case 'insomnia': + // For Insomnia v4 format, name is in the workspace resource + if (rawData.resources && Array.isArray(rawData.resources)) { + const workspace = rawData.resources.find((r) => r._type === 'workspace'); + if (workspace?.name) { + return workspace.name; + } + } + // Fallback to root name property + return rawData.name || 'Insomnia Collection'; + case 'bruno': + return rawData.name || 'Bruno Collection'; + case 'wsdl': + return 'WSDL Collection'; + default: + return 'Collection'; + } +}; + +// Convert raw data to Bruno collection format +const convertCollection = async (format, rawData, groupingType) => { + try { + let collection; + + switch (format) { + case 'openapi': + collection = convertOpenapiToBruno(rawData, { groupBy: groupingType }); + break; + case 'wsdl': + collection = await wsdlToBruno(rawData); + break; + case 'postman': + collection = await postmanToBruno(rawData); + break; + case 'insomnia': + collection = convertInsomniaToBruno(rawData); + break; + case 'bruno': + collection = await processBrunoCollection(rawData); + break; + default: + throw new Error('Unknown collection format'); + } + + return collection; + } catch (err) { + console.error('Conversion error:', err); + toastError(err, 'Failed to convert collection'); + throw err; + } +}; + +const groupingOptions = [ + { value: 'tags', label: 'Tags', description: 'Group requests by OpenAPI tags', testId: 'grouping-option-tags' }, + { value: 'path', label: 'Paths', description: 'Group requests by URL path structure', testId: 'grouping-option-path' } +]; + +const ImportCollectionLocation = ({ onClose, handleSubmit, rawData, format }) => { const inputRef = useRef(); const dispatch = useDispatch(); + const [groupingType, setGroupingType] = useState('tags'); + const dropdownTippyRef = useRef(); + const isOpenApi = format === 'openapi'; + + const collectionName = getCollectionName(format, rawData); const formik = useFormik({ enableReinitialize: true, @@ -22,10 +101,27 @@ const ImportCollectionLocation = ({ onClose, handleSubmit, collectionName }) => .max(500, 'must be 500 characters or less') .required('Location is required') }), - onSubmit: (values) => { - handleSubmit(values.collectionLocation); + onSubmit: async (values) => { + const convertedCollection = await convertCollection(format, rawData, groupingType); + handleSubmit(convertedCollection, values.collectionLocation); } }); + + const onDropdownCreate = (ref) => { + dropdownTippyRef.current = ref; + }; + + const GroupingDropdownIcon = forwardRef((props, ref) => { + const selectedOption = groupingOptions.find((option) => option.value === groupingType); + return ( +
+
+
{selectedOption.label}
+
+ +
+ ); + }); const browse = () => { dispatch(browseDirectory()) .then((dirPath) => { @@ -48,53 +144,89 @@ const ImportCollectionLocation = ({ onClose, handleSubmit, collectionName }) => const onSubmit = () => formik.handleSubmit(); return ( - -
e.preventDefault()}> -
- -
{collectionName}
- <> -