mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-30 11:03:00 -04:00
151 lines
3.8 KiB
TypeScript
151 lines
3.8 KiB
TypeScript
import { promisify } from 'node:util';
|
|
import zlib from 'node:zlib';
|
|
|
|
import deepEqual from 'deep-equal';
|
|
import {
|
|
database as db,
|
|
type GrpcRequest,
|
|
type McpRequest,
|
|
models,
|
|
type Request,
|
|
type RequestVersion,
|
|
type SocketIORequest,
|
|
type WebSocketRequest,
|
|
} 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 db.findOne<RequestVersion>(
|
|
'RequestVersion',
|
|
{
|
|
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);
|
|
}
|