mirror of
https://github.com/Kong/insomnia.git
synced 2026-06-03 13:47:23 -04:00
Move insomnia-data models, services, database code, and common utilities into a dedicated workspace package. Update consumers to import from the new package entrypoints and declare workspace dependencies for the extracted package.
150 lines
3.8 KiB
TypeScript
150 lines
3.8 KiB
TypeScript
import { promisify } from 'node:util';
|
|
import zlib from 'node:zlib';
|
|
|
|
import deepEqual from 'deep-equal';
|
|
import type {
|
|
GrpcRequest,
|
|
McpRequest,
|
|
Request,
|
|
RequestVersion,
|
|
SocketIORequest,
|
|
WebSocketRequest,
|
|
} from 'insomnia-data';
|
|
import { database, database as db, models } from 'insomnia-data';
|
|
|
|
import * as requestHelpers from './helpers/request-operations';
|
|
|
|
const { isRequest } = models.request;
|
|
const { isWebSocketRequest } = models.webSocketRequest;
|
|
const { isSocketIORequest } = models.socketIORequest;
|
|
const { type } = models.requestVersion;
|
|
|
|
const FIELDS_TO_IGNORE = [
|
|
'_id',
|
|
'type',
|
|
'created',
|
|
'modified',
|
|
'metaSortKey',
|
|
'description',
|
|
'parentId',
|
|
'name',
|
|
] as const;
|
|
|
|
export function getById(id: string) {
|
|
return db.findOne<RequestVersion>(type, { _id: id });
|
|
}
|
|
|
|
export function findByParentId(parentId: string) {
|
|
return db.find<RequestVersion>(type, { parentId });
|
|
}
|
|
|
|
function compressObject(obj: any) {
|
|
const compressed = zlib.gzipSync(JSON.stringify(obj));
|
|
return compressed.toString('base64');
|
|
}
|
|
|
|
export async function decompressObject<ObjectType>(input: string | null): Promise<ObjectType | null> {
|
|
if (typeof input !== 'string') {
|
|
return null;
|
|
}
|
|
|
|
const jsonBuffer = await promisify(zlib.gunzip)(Buffer.from(input, 'base64'));
|
|
return JSON.parse(jsonBuffer.toString('utf8')) as ObjectType;
|
|
}
|
|
|
|
export async function getRequest<T extends Request | WebSocketRequest | SocketIORequest>(
|
|
requestVersion: RequestVersion,
|
|
) {
|
|
return await decompressObject<T>(requestVersion.compressedRequest);
|
|
}
|
|
|
|
export async function create(request: Request | WebSocketRequest | GrpcRequest | SocketIORequest | McpRequest) {
|
|
if (
|
|
!isRequest(request) &&
|
|
!isWebSocketRequest(request) &&
|
|
!isSocketIORequest(request) &&
|
|
!models.mcpRequest.isMcpRequest(request)
|
|
) {
|
|
throw new Error(`New ${type} was not given a valid ${request.type} instance`);
|
|
}
|
|
|
|
const parentId = request._id;
|
|
const latestRequestVersion = await database.findOne<RequestVersion>(
|
|
type,
|
|
{
|
|
parentId,
|
|
},
|
|
{ modified: -1 },
|
|
);
|
|
const latestRequest = latestRequestVersion ? await getRequest(latestRequestVersion) : null;
|
|
|
|
const hasChanged = _diffRequests(latestRequest, request);
|
|
|
|
if (hasChanged || !latestRequestVersion) {
|
|
// Create a new version if the request has been modified
|
|
const compressedRequest = compressObject(request);
|
|
return db.docCreate<RequestVersion>(type, {
|
|
parentId,
|
|
compressedRequest,
|
|
});
|
|
}
|
|
// Re-use the latest version if not modified since
|
|
return latestRequestVersion;
|
|
}
|
|
|
|
export async function restore(requestVersionId: string) {
|
|
const requestVersion = await getById(requestVersionId);
|
|
|
|
// Older responses won't have versions saved with them
|
|
if (!requestVersion) {
|
|
return null;
|
|
}
|
|
|
|
const requestPatch = await decompressObject<Request | WebSocketRequest | GrpcRequest>(
|
|
requestVersion.compressedRequest,
|
|
);
|
|
|
|
if (!requestPatch) {
|
|
return null;
|
|
}
|
|
|
|
const originalRequest = await requestHelpers.getRequestById(requestPatch._id);
|
|
|
|
if (!originalRequest) {
|
|
return null;
|
|
}
|
|
|
|
// Only restore fields that aren't blacklisted
|
|
for (const field of FIELDS_TO_IGNORE) {
|
|
if (field in requestPatch) {
|
|
delete requestPatch[field];
|
|
}
|
|
}
|
|
|
|
return requestHelpers.updateRequest(originalRequest, requestPatch);
|
|
}
|
|
function _diffRequests(
|
|
rOld: Request | WebSocketRequest | SocketIORequest | McpRequest | null,
|
|
rNew: Request | WebSocketRequest | SocketIORequest | McpRequest,
|
|
) {
|
|
if (!rOld) {
|
|
return true;
|
|
}
|
|
|
|
for (const key of Object.keys(rOld) as (keyof typeof rOld)[]) {
|
|
// Skip fields that aren't useful
|
|
if (FIELDS_TO_IGNORE.find(field => field === key)) {
|
|
continue;
|
|
}
|
|
if (!deepEqual(rOld[key], rNew[key])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
export function all() {
|
|
return db.find<RequestVersion>(type);
|
|
}
|