mirror of
https://github.com/Kong/insomnia.git
synced 2026-11-06 04:53:59 -05:00
## Background
In a Git-synced project, restarting Insomnia without any change marked every design document as modified in the Git staging list. The diff of each affected YAML contained exactly one line: the embedded ApiSpec `meta.modified` timestamp.
The v5 design-doc YAML does not serialize `ApiSpec.fileName` (the v5 schema has no such field), while v5 import parses a missing `fileName` as an empty string. Any git round-trip therefore reset `fileName` to empty in the database, and the startup repair (`_applyApiSpecName`) rewrote it via `docUpdate`, which unconditionally restamped `modified: Date.now()` for every design document in one burst. RepoFileWatcher then re-exported the database to disk and git status reported all files as modified, repeating on every startup until committed.
## Changes
- Import: fall back `ApiSpec.fileName` to the file top-level name when the spec uses the `{ contents }` shape, so a round-trip never stores an empty fileName.
- Repair: skip the fileName backfill when it would write the existing value; a no-op write only restamps `modified`.
- Regression tests for both behaviors.
Fixes INS-4058
135 lines
4.0 KiB
TypeScript
135 lines
4.0 KiB
TypeScript
import type { CookieJar, Environment, GitRepository, Workspace } from 'insomnia-data';
|
|
import { database, models } from 'insomnia-data';
|
|
|
|
import * as apiSpecServices from '../services/api-spec';
|
|
|
|
/**
|
|
* Run various database repair scripts
|
|
*/
|
|
export async function repairDatabase() {
|
|
console.log('[fix] Running database repairs');
|
|
|
|
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}`);
|
|
}
|