Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ test('Command palette - can switch between requests and workspaces', async ({ ap
await page.getByPlaceholder('Search and switch between').press('ArrowUp');
await page.getByPlaceholder('Search and switch between').press('ArrowUp');
await page.getByPlaceholder('Search and switch between').press('ArrowUp');
await page.getByPlaceholder('Search and switch between').press('ArrowUp');
await page.getByPlaceholder('Search and switch between').press('Enter');
await expect
.soft(page.getByTestId('workspace-context-dropdown').locator('span'))
Expand Down
152 changes: 68 additions & 84 deletions packages/insomnia/src/common/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,12 @@ export async function scanResources(importEntries: ImportEntry[]): Promise<ScanR
let v5Error = null;

try {
const { data: insomnia5Import, error } = tryImportV5Data(contentStr);
v5Error = error;
let insomnia5Import: ExportedModel[] = [];
if (contentStr.startsWith('type: ')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this will be true for all yaml imports.
Maybe we can improve the error logging instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused why this is not a consistent convert function like the others, as there are already too many try catches here

const { data, error } = tryImportV5Data(contentStr);
insomnia5Import = data as ExportedModel[];
v5Error = error;
}
Comment on lines +176 to +181
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this because the error spam was killing my debugging experience

Comment on lines +176 to +181
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scanResources only attempts Insomnia v5 import when contentStr.startsWith('type: '). Valid v5 YAML may include a BOM, leading whitespace, --- document start, or comments before type:, which would skip v5 parsing and change behavior/error reporting. Consider using contentStr.trimStart() and/or a regex that tolerates YAML preambles (and ideally verifies the value matches *.insomnia.rest/5.0) rather than a strict prefix match.

Copilot uses AI. Check for mistakes.
if (insomnia5Import.length > 0) {
result = {
type: {
Expand All @@ -183,7 +187,6 @@ export async function scanResources(importEntries: ImportEntry[]): Promise<ScanR
description: 'Insomnia v5',
},
data: {
// @ts-expect-error -- TSCONVERSION
resources: insomnia5Import,
},
};
Expand Down Expand Up @@ -234,6 +237,7 @@ export async function scanResources(importEntries: ImportEntry[]): Promise<ScanR
});

const requests = resources.filter(isRequest);
const requestGroups = resources.filter(isRequestGroup);
const websocketRequests = resources.filter(isWebSocketRequest);
const grpcRequests = resources.filter(isGrpcRequest);
const socketIoRequests = resources.filter(isSocketIORequest);
Expand All @@ -251,6 +255,7 @@ export async function scanResources(importEntries: ImportEntry[]): Promise<ScanR
unitTests,
unitTestSuites,
requests: [...requests, ...websocketRequests, ...grpcRequests, ...socketIoRequests],
requestGroups,
Comment on lines 240 to 258
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was most of what was broken about imports before, if im not mistaken

workspaces,
Comment on lines 239 to 259
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requestGroups is added to the returned scan result, but ScanResult interface does not declare this property. This makes the new data invisible to typed consumers and encourages any usage. Add requestGroups?: RequestGroup[] (and necessary import/type) to ScanResult to keep the contract accurate.

Copilot uses AI. Check for mistakes.
environments,
apiSpecs,
Expand Down Expand Up @@ -598,21 +603,14 @@ export const importResourcesToNewWorkspace = async ({
const resources = resourceCacheItem.resources;
const ResourceIdMap = new Map();
let newWorkspace: Workspace;
// in order to support import from api spec yaml
if (resourceCacheItem?.importer?.id && isApiSpecImport(resourceCacheItem.importer)) {
// support import from both insomnia export and api spec yaml
if (resources.find(isApiSpec) || isApiSpecImport(resourceCacheItem.importer)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes the next code support both v5 and oas

newWorkspace = await models.workspace.create({
name: workspaceToImport?.name,
scope: 'design',
parentId: projectId,
});

if (isGitProject(project)) {
const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(newWorkspace._id);
await models.workspaceMeta.update(workspaceMeta, {
gitFilePath: `${newWorkspace.name}-${newWorkspace._id}.yaml`,
});
}

await models.apiSpec.updateOrCreateForParentId(newWorkspace._id, {
contents: resourceCacheItem.content as string | undefined,
contentType: 'yaml',
Expand All @@ -624,95 +622,81 @@ export const importResourcesToNewWorkspace = async ({
scope: workspaceToImport?.scope || 'collection',
parentId: projectId,
});
}

if (isGitProject(project)) {
const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(newWorkspace._id);
await models.workspaceMeta.update(workspaceMeta, {
gitFilePath: `${newWorkspace.name}-${newWorkspace._id}.yaml`,
});
}

const apiSpec = resources.find(r => r.type === 'ApiSpec' && r.parentId === workspaceToImport?._id) as ApiSpec;
const hasApiSpec = newWorkspace.scope === 'design' && isApiSpec(apiSpec);
// if workspace is not in the resources, there will be no apiSpec, if resource type is set to api spec this could cause a bug
if (hasApiSpec) {
// TODO: will overwrite existing api spec, not needed after migrate hack is removed
await models.apiSpec.updateOrCreateForParentId(newWorkspace._id, {
contents: apiSpec.contents,
contentType: apiSpec.contentType,
fileName: workspaceToImport?.name,
});
}

// If we're importing into a new workspace
// Map new IDs
ResourceIdMap.set('__WORKSPACE_ID__', newWorkspace._id);
workspaceToImport && ResourceIdMap.set(workspaceToImport._id, newWorkspace._id);
// If we're importing into a new workspace
// Map new IDs
ResourceIdMap.set('__WORKSPACE_ID__', newWorkspace._id);
workspaceToImport && ResourceIdMap.set(workspaceToImport._id, newWorkspace._id);

const resourcesWithoutWorkspaceAndApiSpec = resources.filter(
resource => !isWorkspace(resource) && !isApiSpec(resource),
);
const resourcesWithoutWorkspaceAndApiSpec = resources.filter(
resource => !isWorkspace(resource) && !isApiSpec(resource),
);

for (const resource of resourcesWithoutWorkspaceAndApiSpec) {
const model = getModel(resource.type);
model && ResourceIdMap.set(resource._id, generateId(model.prefix));
}
for (const resource of resourcesWithoutWorkspaceAndApiSpec) {
const model = getModel(resource.type);
model && ResourceIdMap.set(resource._id, generateId(model.prefix));
}

for (const resource of resourcesWithoutWorkspaceAndApiSpec) {
const model = getModel(resource.type);
for (const resource of resourcesWithoutWorkspaceAndApiSpec) {
const model = getModel(resource.type);

if (model) {
const newParentId = ResourceIdMap.get(resource.parentId);
if (!newParentId) {
console.warn(`Could not find new parent id for ${resource.name} ${resource._id}`);
continue;
}
if (isGrpcRequest(resource)) {
await models.grpcRequest.create({
...resource,
_id: ResourceIdMap.get(resource._id),
protoFileId: ResourceIdMap.get(resource.protoFileId),
parentId: newParentId,
});
} else if (isUnitTest(resource)) {
await models.unitTest.create({
...resource,
_id: ResourceIdMap.get(resource._id),
requestId: ResourceIdMap.get(resource.requestId),
parentId: newParentId,
});
} else if (isRequest(resource)) {
await models.request.create(importRequestWithNewIds(resource, ResourceIdMap));
} else {
await db.docCreate(model.type, {
...resource,
_id: ResourceIdMap.get(resource._id),
parentId: newParentId,
});
}
if (model) {
const newParentId = ResourceIdMap.get(resource.parentId);
if (!newParentId) {
console.warn(`Could not find new parent id for ${resource.name} ${resource._id}`);
continue;
}
if (isGrpcRequest(resource)) {
await models.grpcRequest.create({
...resource,
_id: ResourceIdMap.get(resource._id),
protoFileId: ResourceIdMap.get(resource.protoFileId),
parentId: newParentId,
});
} else if (isUnitTest(resource)) {
await models.unitTest.create({
...resource,
_id: ResourceIdMap.get(resource._id),
requestId: ResourceIdMap.get(resource.requestId),
parentId: newParentId,
});
} else if (isRequest(resource)) {
await models.request.create(importRequestWithNewIds(resource, ResourceIdMap));
} else {
await db.docCreate(model.type, {
...resource,
_id: ResourceIdMap.get(resource._id),
parentId: newParentId,
});
}
}
}

// Use the first sub environment as the active one
const subEnvironments = resources.filter(isEnvironment).filter(isSubEnvironmentResource) || [];
// Use the first sub environment as the active one
const subEnvironments = resources.filter(isEnvironment).filter(isSubEnvironmentResource) || [];

if (subEnvironments.length > 0) {
const firstSubEnvironment = subEnvironments[0];
if (subEnvironments.length > 0) {
const firstSubEnvironment = subEnvironments[0];

if (firstSubEnvironment) {
const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(newWorkspace._id);
if (firstSubEnvironment) {
const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(newWorkspace._id);

await models.workspaceMeta.update(workspaceMeta, {
activeEnvironmentId: ResourceIdMap.get(firstSubEnvironment._id),
});
}
await models.workspaceMeta.update(workspaceMeta, {
activeEnvironmentId: ResourceIdMap.get(firstSubEnvironment._id),
});
}
}

// Make sure the new workspace has required resources like base environment, cookie jar and workspaceMeta
await models.environment.getOrCreateForParentId(newWorkspace._id);
await models.workspaceMeta.getOrCreateByParentId(newWorkspace._id);
const workspaceMeta = await models.workspaceMeta.getOrCreateByParentId(newWorkspace._id);

if (isGitProject(project)) {
await models.workspaceMeta.update(workspaceMeta, {
gitFilePath: `${newWorkspace.name}-${newWorkspace._id}.yaml`,
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gitFilePath is derived from newWorkspace.name without sanitization. Workspace names can contain path separators or other unsafe characters, which can create invalid paths or unintended directories in git-backed projects. Use the existing safeToUseInsomniaFileNameWithExt() helper (and path.join if needed) to generate a safe .yaml file name.

Suggested change
gitFilePath: `${newWorkspace.name}-${newWorkspace._id}.yaml`,
gitFilePath: safeToUseInsomniaFileNameWithExt(`${newWorkspace.name}-${newWorkspace._id}`, 'yaml'),

Copilot uses AI. Check for mistakes.
});
}
Comment on lines +695 to +699
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved this down since it was duplicated

// we sync the new workspace to the cloud in workspaceLoader when user enters the workspace
// since we won't navigate to the workspace automatically after import
// here we push to the cloud programmatically
Expand Down
8 changes: 0 additions & 8 deletions packages/insomnia/src/routes/import.scan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,6 @@ export const scanImportResources = async (data: {
contentList.push({
contentStr: curl,
});
// const { data } = await window.main.parseImport(
// {
// contentStr: curl || '',
// },
// {
// importerId: 'curl',
// },
// );
} else if (source === 'file') {
let filePaths: string[];
try {
Expand Down
Loading