diff --git a/packages/insomnia/src/common/__tests__/insomnia-v5.test.ts b/packages/insomnia/src/common/__tests__/insomnia-v5.test.ts index af102b1cbd..7d2f936ddf 100644 --- a/packages/insomnia/src/common/__tests__/insomnia-v5.test.ts +++ b/packages/insomnia/src/common/__tests__/insomnia-v5.test.ts @@ -425,6 +425,47 @@ spec: expect(parsed.collection[0].children[0].children[0].name).toBe('Selected'); }); + it('keeps empty folders when every request is selected', async () => { + const workspace = await services.workspace.create({ + _id: 'wrk_all_selected', + name: 'All Selected Workspace', + parentId: 'proj_test', + scope: 'collection', + }); + await services.environment.create({ + _id: 'env_all_selected', + name: 'Base Env', + parentId: workspace._id, + data: {}, + }); + await services.requestGroup.create({ + _id: 'fld_empty', + name: 'Empty', + parentId: workspace._id, + }); + const folder = await services.requestGroup.create({ + _id: 'fld_with_request', + name: 'With Request', + parentId: workspace._id, + }); + const request = await services.request.create({ + _id: 'req_all_selected', + name: 'Only Request', + parentId: folder._id, + url: 'https://api.example.com/only', + method: 'GET', + }); + + const result = await getInsomniaV5DataExport({ + workspaceId: workspace._id, + includePrivateEnvironments: false, + requestIds: [request._id], + }); + + const names = YAML.parse(result).collection.map((item: { name: string }) => item.name); + expect(names).toEqual(expect.arrayContaining(['Empty', 'With Request'])); + }); + it('omits cookies and environments when excluded', async () => { const workspace = await services.workspace.create({ _id: 'wrk_exclude_test', diff --git a/packages/insomnia/src/common/insomnia-v5.ts b/packages/insomnia/src/common/insomnia-v5.ts index 76c8346dde..ac646c3a57 100644 --- a/packages/insomnia/src/common/insomnia-v5.ts +++ b/packages/insomnia/src/common/insomnia-v5.ts @@ -1089,9 +1089,23 @@ export async function getInsomniaV5DataExport({ exportableResources.filter(models.webSocketPayload.isWebSocketPayload).map(payload => [payload.parentId, payload]), ); - // When exporting a subset of requests, only keep the folders that contain at least one of them + // When exporting a subset of requests, only keep the folders that contain at least one of them. + // Selecting every request is not a subset, so all folders (including empty ones) are kept. + const selectedRequestIds = new Set(requestIds); + const isSubsetExport = + !!requestIds && + requestIds.length > 0 && + !exportableResources + .filter( + resource => + models.request.isRequest(resource) || + models.grpcRequest.isGrpcRequest(resource) || + models.webSocketRequest.isWebSocketRequest(resource) || + models.socketIORequest.isSocketIORequest(resource), + ) + .every(resource => selectedRequestIds.has(resource._id)); const requestGroupIdsToKeep = new Set(); - if (requestIds && requestIds.length > 0) { + if (requestIds && isSubsetExport) { const parentIdById = new Map(exportableResources.map(resource => [resource._id, resource.parentId])); for (const requestId of requestIds) { let parentId = parentIdById.get(requestId); @@ -1125,7 +1139,7 @@ export async function getInsomniaV5DataExport({ return true; } if (models.requestGroup.isRequestGroup(resource)) { - return requestGroupIdsToKeep.has(resource._id); + return !isSubsetExport || requestGroupIdsToKeep.has(resource._id); } return requestIds.includes(resource._id); diff --git a/packages/insomnia/src/ui/components/settings/import-export.tsx b/packages/insomnia/src/ui/components/settings/import-export.tsx index 60513d3d78..0ea4b375c2 100644 --- a/packages/insomnia/src/ui/components/settings/import-export.tsx +++ b/packages/insomnia/src/ui/components/settings/import-export.tsx @@ -1,5 +1,8 @@ import { format } from 'date-fns'; -import { convertApiSpecSyntax, detectApiSpecSyntax } from 'insomnia/src/common/api-specs'; +import { + convertApiSpecSyntax, + detectApiSpecSyntax, +} from 'insomnia/src/common/api-specs'; import { getProductName } from 'insomnia/src/common/constants'; import { getWorkspaceLabel } from 'insomnia/src/common/get-workspace-label'; import { getInsomniaV5DataExport } from 'insomnia/src/common/insomnia-v5'; @@ -761,7 +764,8 @@ export const ImportExport: FC = ({ hideSettingsModal, onModalChange }) => const activeProject = projectLoaderData?.activeProject; const projectName = activeProject?.name ?? getProductName(); const projects = projectLoaderData?.projects || []; - const organizationName = organizations.find(org => org.id === organizationId)?.name || 'Organization'; + const organizationName = + organizations.find(org => org.id === organizationId)?.name || 'Organization'; const [isImportModalOpen, setIsImportModalOpen] = useState(false); const [isImportProjectsModalOpen, setIsImportProjectsModalOpen] = useState(false);