From d7a0bc1e5857b9fb63ba078cd07b2e1987ba7366 Mon Sep 17 00:00:00 2001 From: Hexxa Date: Fri, 23 Feb 2024 20:00:20 +0800 Subject: [PATCH] feat(hidden-window): enable baseEnvironment in the pre-request scripting (#7102) * feat(hidden-window): enable baseEnvironment in the pre-request scripting * fix: input empty selected environment data to avoid incorrect environment manipulation and overriding * test: add a test for folder environments overriding * fix: smoke tests failed because of env overriding --- .../fixtures/smoke-test-collection.yaml | 18 +++++-- .../tests/smoke/pre-request-script-ui.test.ts | 53 +++++++++++++++++++ packages/insomnia/src/common/render.ts | 7 ++- packages/insomnia/src/hidden-window.ts | 1 + packages/insomnia/src/main/window-utils.ts | 4 ++ packages/insomnia/src/network/cancellation.ts | 3 ++ packages/insomnia/src/network/network.ts | 29 ++++++++-- packages/insomnia/src/sdk/objects/insomnia.ts | 9 ++++ packages/insomnia/src/ui/routes/request.tsx | 13 ++++- 9 files changed, 126 insertions(+), 11 deletions(-) diff --git a/packages/insomnia-smoke-test/fixtures/smoke-test-collection.yaml b/packages/insomnia-smoke-test/fixtures/smoke-test-collection.yaml index a81408161..b9531d761 100644 --- a/packages/insomnia-smoke-test/fixtures/smoke-test-collection.yaml +++ b/packages/insomnia-smoke-test/fixtures/smoke-test-collection.yaml @@ -177,7 +177,7 @@ resources: settingFollowRedirects: global _type: request - _id: req_89dade2ee9ee42fbb22d588783a9df3c - parentId: wrk_5b5ab67830944ffcbec47528366ef403 + parentId: fld_01de564274824ecaad272330339ea6b2 modified: 1636707449231 created: 1636141014552 url: http://127.0.0.1:4010/echo @@ -202,7 +202,8 @@ resources: modified: 1636140994432 created: 1636140994432 name: Base Environment - data: {} + data: + customValue: "fromEnvManager" dataPropertyOrder: null color: null isPrivate: false @@ -264,8 +265,19 @@ resources: settingRebuildPath: true settingFollowRedirects: global _type: request - - _id: req_0769dca686df49358082b2183dfa073d + - _id: fld_01de564274824ecaad272330339ea6b2 parentId: wrk_5b5ab67830944ffcbec47528366ef403 + modified: 1668533312225 + created: 1668533312225 + name: FolderWithEnv + description: "" + environment: + customValue: "fromFolder" + environmentPropertyOrder: null + metaSortKey: -1668533312225 + _type: request_group + - _id: req_0769dca686df49358082b2183dfa073d + parentId: fld_01de564274824ecaad272330339ea6b2 modified: 1643892278711 created: 1643892270079 url: http://127.0.0.1:4010/echo diff --git a/packages/insomnia-smoke-test/tests/smoke/pre-request-script-ui.test.ts b/packages/insomnia-smoke-test/tests/smoke/pre-request-script-ui.test.ts index ac9217275..facc1e602 100644 --- a/packages/insomnia-smoke-test/tests/smoke/pre-request-script-ui.test.ts +++ b/packages/insomnia-smoke-test/tests/smoke/pre-request-script-ui.test.ts @@ -45,6 +45,59 @@ test.describe('pre-request UI tests', async () => { predefined: 'updatedByScript', }, }, + { + name: 'environments / populate environments', + preReqScript: ` + insomnia.baseEnvironment.set('fromBaseEnv', 'baseEnv'); + `, + body: `{ + "fromBaseEnv": "{{ _.fromBaseEnv }}" + }`, + expectedBody: { + fromBaseEnv: 'baseEnv', + }, + }, + { + name: 'environments / override base environments', + preReqScript: ` + insomnia.baseEnvironment.set('scriptValue', 'fromBase'); + insomnia.environment.set('scriptValue', 'fromEnv'); + `, + body: `{ + "scriptValue": "{{ _.scriptValue }}" + }`, + expectedBody: { + scriptValue: 'fromEnv', + }, + }, + { + name: 'environments / override predefined base environment in script', + preReqScript: ` + // "preDefinedValue" is already defined in the base environment modal. + // but it is rewritten here + insomnia.baseEnvironment.set('preDefinedValue', 'fromScript'); + `, + body: `{ + "preDefinedValue": "{{ _.preDefinedValue }}" + }`, + expectedBody: { + preDefinedValue: 'fromScript', + }, + }, + { + name: 'environments/ envrionment from script should be overidden by folder environment', + preReqScript: ` + // "customValue" is already defined in the folder environment. + // folder version will override the following wone + insomnia.baseEnvironment.set('customValue', 'fromScript'); + `, + body: `{ + "customValue": "{{ _.customValue }}" + }`, + expectedBody: { + customValue: 'fromFolder', + }, + }, ]; for (let i = 0; i < testCases.length; i++) { diff --git a/packages/insomnia/src/common/render.ts b/packages/insomnia/src/common/render.ts index 277bcf45b..26cdc533e 100644 --- a/packages/insomnia/src/common/render.ts +++ b/packages/insomnia/src/common/render.ts @@ -298,6 +298,7 @@ interface RenderRequest { interface BaseRenderContextOptions { environment?: string | Environment; + baseEnvironment?: Environment; purpose?: RenderPurpose; extraInfo?: ExtraRenderInfo; } @@ -309,6 +310,7 @@ export async function getRenderContext( { request, environment, + baseEnvironment, ancestors: _ancestors, purpose, extraInfo, @@ -322,7 +324,7 @@ export async function getRenderContext( throw new Error('Failed to render. Could not find workspace'); } - const rootEnvironment = await models.environment.getOrCreateForParentId( + const rootEnvironment = baseEnvironment || await models.environment.getOrCreateForParentId( workspace ? workspace._id : 'n/a', ); const subEnvironmentId = environment ? @@ -466,6 +468,7 @@ export async function getRenderedRequestAndContext( { request, environment, + baseEnvironment, extraInfo, purpose, }: RenderRequestOptions, @@ -474,7 +477,7 @@ export async function getRenderedRequestAndContext( const workspace = ancestors.find(isWorkspace); const parentId = workspace ? workspace._id : 'n/a'; const cookieJar = await models.cookieJar.getOrCreateForParentId(parentId); - const renderContext = await getRenderContext({ request, environment, ancestors, purpose, extraInfo }); + const renderContext = await getRenderContext({ request, environment, ancestors, purpose, extraInfo, baseEnvironment }); // HACK: Switch '#}' to '# }' to prevent Nunjucks from barfing // https://github.com/kong/insomnia/issues/895 diff --git a/packages/insomnia/src/hidden-window.ts b/packages/insomnia/src/hidden-window.ts index f7dd277af..a1155498a 100644 --- a/packages/insomnia/src/hidden-window.ts +++ b/packages/insomnia/src/hidden-window.ts @@ -53,5 +53,6 @@ const runPreRequestScript = async ( return { ...context, environment: mutatedContextObject.environment, + baseEnvironment: mutatedContextObject.baseEnvironment, }; }; diff --git a/packages/insomnia/src/main/window-utils.ts b/packages/insomnia/src/main/window-utils.ts index 813b7ceca..fa5d59e90 100644 --- a/packages/insomnia/src/main/window-utils.ts +++ b/packages/insomnia/src/main/window-utils.ts @@ -71,6 +71,10 @@ export async function createHiddenBrowserWindow(): Promise void>(); export async function cancelRequestById(requestId: string) { const cancel = cancelRequestFunctionMap.get(requestId); @@ -30,6 +31,7 @@ export const cancellableRunPreRequestScript = async (options: { script: string; return result as { request: Request; environment: object; + baseEnvironment: object; }; } catch (err) { if (err.name === 'AbortError') { @@ -62,6 +64,7 @@ export const cancellableCurlRequest = async (requestOptions: CurlRequestOptions) return { statusMessage: 'Error', error: err.message || 'Something went wrong' }; } }; + const cancellablePromise = ({ signal, fn }: { signal: AbortSignal; fn: Promise }) => { if (signal?.aborted) { return Promise.reject(new DOMException('Aborted', 'AbortError')); diff --git a/packages/insomnia/src/network/network.ts b/packages/insomnia/src/network/network.ts index d4ed52bef..8118c89f2 100644 --- a/packages/insomnia/src/network/network.ts +++ b/packages/insomnia/src/network/network.ts @@ -73,20 +73,31 @@ export const fetchRequestData = async (requestId: string) => { return { request, environment, settings, clientCertificates, caCert, activeEnvironmentId, timelinePath, responseId }; }; -export const tryToExecutePreRequestScript = async (request: Request, environment: Environment, timelinePath: string, responseId: string) => { +export const tryToExecutePreRequestScript = async ( + request: Request, + environment: Environment, + timelinePath: string, + responseId: string, + baseEnvironment: Environment, +) => { if (!request.preRequestScript) { return { request, environment: undefined, + baseEnvironment: undefined, }; } + try { const output = await cancellableRunPreRequestScript({ script: request.preRequestScript, context: { request, timelinePath, - environment: environment?.data || {}, + // it inputs empty environment data when active environment is the base environment + // this is more deterministic and avoids that script accidently manipulates baseEnvironment instead of environment + environment: environment._id === baseEnvironment._id ? {} : (environment?.data || {}), + baseEnvironment: baseEnvironment?.data || {}, }, }); console.log('[network] Pre-request script succeeded', output); @@ -98,10 +109,18 @@ export const tryToExecutePreRequestScript = async (request: Request, environment ); environment.data = output.environment; environment.dataPropertyOrder = envPropertyOrder.map; + const baseEnvPropertyOrder = orderedJSON.parse( + JSON.stringify(output.baseEnvironment), + JSON_ORDER_PREFIX, + JSON_ORDER_SEPARATOR, + ); + baseEnvironment.data = output.baseEnvironment; + baseEnvironment.dataPropertyOrder = baseEnvPropertyOrder.map; return { request: output.request, - environment: environment, + environment, + baseEnvironment, }; } catch (err) { await fs.promises.appendFile(timelinePath, JSON.stringify({ value: err.message, name: 'Text', timestamp: Date.now() }) + '\n'); @@ -126,12 +145,14 @@ export const tryToInterpolateRequest = async ( request: Request, environment: string | Environment, purpose?: RenderPurpose, - extraInfo?: ExtraRenderInfo + extraInfo?: ExtraRenderInfo, + baseEnvironment?: Environment, ) => { try { return await getRenderedRequestAndContext({ request: request, environment, + baseEnvironment, purpose, extraInfo, }); diff --git a/packages/insomnia/src/sdk/objects/insomnia.ts b/packages/insomnia/src/sdk/objects/insomnia.ts index 85592d33b..230aa29f9 100644 --- a/packages/insomnia/src/sdk/objects/insomnia.ts +++ b/packages/insomnia/src/sdk/objects/insomnia.ts @@ -5,22 +5,29 @@ export interface RequestContext { request: Request; timelinePath: string; environment?: object; + baseEnvironment?: object; } export class InsomniaObject { public environment: Environment; + public collectionVariables: Environment; + public baseEnvironment: Environment; constructor( rawObj: { environment: Environment; + baseEnvironment: Environment; }, ) { this.environment = rawObj.environment; + this.baseEnvironment = rawObj.baseEnvironment; + this.collectionVariables = this.baseEnvironment; // collectionVariables is mapped to baseEnvironment } toObject = () => { return { environment: this.environment.toObject(), + baseEnvironment: this.baseEnvironment.toObject(), }; }; } @@ -29,10 +36,12 @@ export function initInsomniaObject( rawObj: RequestContext, ) { const environment = new Environment(rawObj.environment); + const baseEnvironment = new Environment(rawObj.baseEnvironment); return new InsomniaObject( { environment, + baseEnvironment, }, ); }; diff --git a/packages/insomnia/src/ui/routes/request.tsx b/packages/insomnia/src/ui/routes/request.tsx index d87c37ecd..396367f03 100644 --- a/packages/insomnia/src/ui/routes/request.tsx +++ b/packages/insomnia/src/ui/routes/request.tsx @@ -369,15 +369,24 @@ export const sendAction: ActionFunction = async ({ request, params }) => { timelinePath, responseId, } = await fetchRequestData(requestId); + const baseEnvironment = await models.environment.getOrCreateForParentId(workspaceId); + try { const { shouldPromptForPathAfterResponse } = await request.json() as SendActionParams; - const mutatedContext = await tryToExecutePreRequestScript(req, environment, timelinePath, responseId); + const mutatedContext = await tryToExecutePreRequestScript(req, environment, timelinePath, responseId, baseEnvironment); if (!mutatedContext?.request) { // exiy early if there was a problem with the pre-request script // TODO: improve error message? return null; } - const renderedResult = await tryToInterpolateRequest(mutatedContext.request, mutatedContext.environment || environment._id, RENDER_PURPOSE_SEND); + + const renderedResult = await tryToInterpolateRequest( + mutatedContext.request, + mutatedContext.environment || environment._id, + RENDER_PURPOSE_SEND, + undefined, + mutatedContext.baseEnvironment, + ); const renderedRequest = await tryToTransformRequestWithPlugins(renderedResult); // TODO: remove this temporary hack to support GraphQL variables in the request body properly