mirror of
https://github.com/Kong/insomnia.git
synced 2026-11-03 11:35:20 -05:00
## Background Workspace `hasUncommittedChanges` / `hasUnpushedChanges` flags are written only by cloud sync. When a project stopped being a cloud project, those flags were never cleared, so the project overview cards kept showing "Uncommitted changes". Repro: 1. Open a cloud project whose workspaces have uncommitted changes. 2. Remove the project from the remote. 3. Open the project overview. The cards still show "Uncommitted changes" although the project is now local. A project converted from cloud to git carries the same residual flags, because the values come from its former cloud state. ## Changes - Clear both workspace sync flags when a project's `remoteId` becomes `null` in the project service. Remote deletion, conversion to local, cross-organization move, and conversion to git all update the project through this service. - Reuse the cleanup in the startup database repair to fix existing stale records. Only dirty workspace metas are written, and `modified` is preserved. - Add service and repair tests. ## Testing - `npm test -w insomnia-data` (538 tests) - `npm run type-check -w insomnia-data` - `npm run lint -w insomnia-data` ## References - INS-3899
142 lines
4.3 KiB
TypeScript
142 lines
4.3 KiB
TypeScript
import type { CookieJar, Environment, GitRepository, Workspace } from 'insomnia-data';
|
|
import { database, models } from 'insomnia-data';
|
|
|
|
import * as apiSpecServices from '../services/api-spec';
|
|
import * as projectServices from '../services/project';
|
|
|
|
/**
|
|
* Run various database repair scripts
|
|
*/
|
|
export async function repairDatabase() {
|
|
console.log('[fix] Running database repairs');
|
|
|
|
for (const project of await projectServices.list()) {
|
|
if (project.remoteId === null) {
|
|
await projectServices.clearWorkspaceSyncFlags(project._id);
|
|
}
|
|
}
|
|
|
|
for (const workspace of await database.find<Workspace>(models.workspace.type)) {
|
|
await _repairBaseEnvironments(workspace);
|
|
await _fixMultipleCookieJars(workspace);
|
|
await _applyApiSpecName(workspace);
|
|
}
|
|
|
|
for (const gitRepository of await database.find<GitRepository>(models.gitRepository.type)) {
|
|
await _fixOldGitURIs(gitRepository);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function ensures that apiSpec exists for each workspace
|
|
* If the filename on the apiSpec is not set or is the default initialized name
|
|
* It will apply the workspace name to it
|
|
*/
|
|
async function _applyApiSpecName(workspace: Workspace) {
|
|
const apiSpec = await apiSpecServices.getByParentId(workspace._id);
|
|
const existsAndFilenameIsDefaultOrMissing =
|
|
apiSpec && (!apiSpec.fileName || apiSpec.fileName === models.apiSpec.init().fileName);
|
|
// A backfill that would write the existing value changes nothing but would
|
|
// still restamp `modified`, which git sync serializes into YAML and shows up
|
|
// as a modified file. Skip no-op writes.
|
|
if (existsAndFilenameIsDefaultOrMissing && apiSpec.fileName !== workspace.name) {
|
|
await apiSpecServices.update(apiSpec, { fileName: workspace.name });
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function repairs workspaces that have multiple base environments. Since a workspace
|
|
* can only have one, this function walks over all base environments, merges the data, and
|
|
* moves all children as well.
|
|
*/
|
|
async function _repairBaseEnvironments(workspace: Workspace) {
|
|
const baseEnvironments = await database.find<Environment>(models.environment.type, {
|
|
parentId: workspace._id,
|
|
});
|
|
|
|
// Nothing to do here
|
|
if (baseEnvironments.length <= 1) {
|
|
return;
|
|
}
|
|
|
|
const chosenBase = baseEnvironments[0];
|
|
|
|
for (const baseEnvironment of baseEnvironments) {
|
|
if (baseEnvironment._id === chosenBase._id) {
|
|
continue;
|
|
}
|
|
|
|
chosenBase.data = Object.assign(baseEnvironment.data, chosenBase.data);
|
|
const subEnvironments = await database.find<Environment>(models.environment.type, {
|
|
parentId: baseEnvironment._id,
|
|
});
|
|
|
|
for (const subEnvironment of subEnvironments) {
|
|
await database.docUpdate(subEnvironment, {
|
|
parentId: chosenBase._id,
|
|
});
|
|
}
|
|
|
|
// Remove unnecessary base env
|
|
await database.remove(baseEnvironment);
|
|
}
|
|
|
|
// Update remaining base env
|
|
await database.update(chosenBase);
|
|
console.log(`[fix] Merged ${baseEnvironments.length} base environments under ${workspace.name}`);
|
|
}
|
|
|
|
/**
|
|
* This function repairs workspaces that have multiple cookie jars. Since a workspace
|
|
* can only have one, this function walks over all jars and merges them and their cookies
|
|
* together.
|
|
*/
|
|
async function _fixMultipleCookieJars(workspace: Workspace) {
|
|
const cookieJars = await database.find<CookieJar>(models.cookieJar.type, {
|
|
parentId: workspace._id,
|
|
});
|
|
|
|
// Nothing to do here
|
|
if (cookieJars.length <= 1) {
|
|
return;
|
|
}
|
|
|
|
const chosenJar = cookieJars[0];
|
|
|
|
for (const cookieJar of cookieJars) {
|
|
if (cookieJar._id === chosenJar._id) {
|
|
continue;
|
|
}
|
|
|
|
for (const cookie of cookieJar.cookies) {
|
|
if (chosenJar.cookies.find(c => c.id === cookie.id)) {
|
|
continue;
|
|
}
|
|
|
|
chosenJar.cookies.push(cookie);
|
|
}
|
|
|
|
// Remove unnecessary jar
|
|
await database.remove(cookieJar);
|
|
}
|
|
|
|
// Update remaining jar
|
|
await database.update(chosenJar);
|
|
console.log(`[fix] Merged ${cookieJars.length} cookie jars under ${workspace.name}`);
|
|
}
|
|
|
|
// Append .git to old git URIs to mimic previous isomorphic-git behavior
|
|
async function _fixOldGitURIs(doc: GitRepository) {
|
|
if (!doc.uriNeedsMigration) {
|
|
return;
|
|
}
|
|
|
|
if (!doc.uri.endsWith('.git')) {
|
|
doc.uri += '.git';
|
|
}
|
|
|
|
doc.uriNeedsMigration = false;
|
|
await database.update(doc);
|
|
console.log(`[fix] Fixed git URI for ${doc._id}`);
|
|
}
|