From a69a185fb64d65be731828dee03fb8d45e47736d Mon Sep 17 00:00:00 2001 From: Hexxa Date: Mon, 18 Mar 2024 18:30:11 +0800 Subject: [PATCH] chore(pre-req): improve the snippets menu for the pre-request script - INS-3319 (#7173) * chore: add more helper snippets * chore: add more helper snippets for pre-req scripting * fix: use correct icons * feat: enable basic auto code completing * feedback --------- Co-authored-by: jackkav --- packages/insomnia/src/sdk/objects/insomnia.ts | 11 +- packages/insomnia/src/sdk/objects/request.ts | 6 +- .../editors/pre-request-script-editor.tsx | 204 ++++++++++++++++-- .../src/ui/components/panes/request-pane.tsx | 1 + 4 files changed, 200 insertions(+), 22 deletions(-) diff --git a/packages/insomnia/src/sdk/objects/insomnia.ts b/packages/insomnia/src/sdk/objects/insomnia.ts index b95be3904d..b407e7970c 100644 --- a/packages/insomnia/src/sdk/objects/insomnia.ts +++ b/packages/insomnia/src/sdk/objects/insomnia.ts @@ -15,12 +15,12 @@ export class InsomniaObject { public baseEnvironment: Environment; public variables: Variables; public request: ScriptRequest; - private settings: Settings; private clientCertificates: ClientCertificate[]; // TODO: follows will be enabled after Insomnia supports them private _globals: Environment; private _iterationData: Environment; + private _settings: Settings; constructor( rawObj: { @@ -41,7 +41,7 @@ export class InsomniaObject { this._iterationData = rawObj.iterationData; this.variables = rawObj.variables; this.request = rawObj.request; - this.settings = rawObj.settings; + this._settings = rawObj.settings; this.clientCertificates = rawObj.clientCertificates; } @@ -50,7 +50,7 @@ export class InsomniaObject { cb: (error?: string, response?: ScriptResponse) => void ) { // TODO: hook to settings later - return sendRequest(request, cb, this.settings); + return sendRequest(request, cb, this._settings); } // TODO: remove this after enabled globals @@ -63,6 +63,11 @@ export class InsomniaObject { throw unsupportedError('iterationData', 'environment'); } + // TODO: remove this after enabled iterationData + get settings() { + return undefined; + } + toObject = () => { return { globals: this._globals.toObject(), diff --git a/packages/insomnia/src/sdk/objects/request.ts b/packages/insomnia/src/sdk/objects/request.ts index 3ce09a08b9..7c82bbb061 100644 --- a/packages/insomnia/src/sdk/objects/request.ts +++ b/packages/insomnia/src/sdk/objects/request.ts @@ -333,7 +333,7 @@ export class Request extends Property { return headersObj; } - removeHeader(toRemove: string | Header, options: { ignoreCase: boolean }) { + removeHeader(toRemove: string | Header, options?: { ignoreCase: boolean }) { const filteredHeaders = this.headers.filter( header => { if (!header.key) { @@ -341,7 +341,7 @@ export class Request extends Property { } if (typeof toRemove === 'string') { - return options.ignoreCase ? + return options != null && options.ignoreCase ? header.key.toLocaleLowerCase() !== toRemove.toLocaleLowerCase() : header.key !== toRemove; } else if (toRemove instanceof Header) { @@ -349,7 +349,7 @@ export class Request extends Property { return false; } - return options.ignoreCase ? + return options != null && options.ignoreCase ? header.key.toLocaleLowerCase() !== toRemove.key.toLocaleLowerCase() : header.key !== toRemove.key; } else { diff --git a/packages/insomnia/src/ui/components/editors/pre-request-script-editor.tsx b/packages/insomnia/src/ui/components/editors/pre-request-script-editor.tsx index c089f9981a..a6d12ae8f1 100644 --- a/packages/insomnia/src/ui/components/editors/pre-request-script-editor.tsx +++ b/packages/insomnia/src/ui/components/editors/pre-request-script-editor.tsx @@ -1,5 +1,11 @@ +import { Snippet } from 'codemirror'; import React, { FC, Fragment, useRef } from 'react'; +import { Settings } from '../../../models/settings'; +import { Environment, Variables } from '../../../sdk/objects/environments'; +import { InsomniaObject } from '../../../sdk/objects/insomnia'; +import { Request as ScriptRequest } from '../../../sdk/objects/request'; +import { Url } from '../../../sdk/objects/urls'; import { Dropdown, DropdownButton, DropdownItem, ItemContent } from '../base/dropdown'; import { CodeEditor, CodeEditorHandle } from '../codemirror/code-editor'; @@ -8,19 +14,20 @@ interface Props { defaultValue: string; uniquenessKey: string; className?: string; + settings: Settings; } -const getEnvVar = 'insomnia.environment.get("variable_name");\n'; -// const getGlbVar = 'insomnia.globals.get("variable_name");\n'; -const getVar = 'insomnia.variables.get("variable_name");\n'; -const getCollectionVar = 'insomnia.collectionVariables.get("variable_name");\n'; -const setEnvVar = 'insomnia.environment.set("variable_name", "variable_value");\n'; -// const setGlbVar = 'insomnia.globals.set("variable_name", "variable_value");\n'; -const setVar = 'insomnia.variables.set("variable_name", "variable_value");\n'; -const setCollectionVar = 'insomnia.collectionVariables.set("variable_name", "variable_value");\n'; -const unsetEnvVar = 'insomnia.environment.unset("variable_name");\n'; -// const unsetGlbVar = 'insomnia.globals.unset("variable_name");\n'; -const unsetCollectionVar = 'insomnia.collectionVariables.unset("variable_name");\n'; +const getEnvVar = 'insomnia.environment.get("variable_name");'; +// const getGlbVar = 'insomnia.globals.get("variable_name");'; +const getVar = 'insomnia.variables.get("variable_name");'; +const getCollectionVar = 'insomnia.collectionVariables.get("variable_name");'; +const setEnvVar = 'insomnia.environment.set("variable_name", "variable_value");'; +// const setGlbVar = 'insomnia.globals.set("variable_name", "variable_value");'; +const setVar = 'insomnia.variables.set("variable_name", "variable_value");'; +const setCollectionVar = 'insomnia.collectionVariables.set("variable_name", "variable_value");'; +const unsetEnvVar = 'insomnia.environment.unset("variable_name");'; +// const unsetGlbVar = 'insomnia.globals.unset("variable_name");'; +const unsetCollectionVar = 'insomnia.collectionVariables.unset("variable_name");'; const sendReq = `const resp = await new Promise((resolve, reject) => { insomnia.sendRequest( @@ -29,8 +36,28 @@ const sendReq = err != null ? reject(err) : resolve(resp); } ); -});\n`; -const logValue = 'console.log("log", variableName);\n'; +});`; +const logValue = 'console.log("log", variableName);'; +const addHeader = "insomnia.request.addHeader({key: 'X-Header-Name', value: 'header_value' });"; +const removeHeader = "insomnia.request.removeHeader('X-Header-Name');"; +const setMethod = "insomnia.request.method = 'GET';"; +const addQueryParams = "insomnia.request.url.addQueryParams('k1=v1');"; +const updateRequestBody = + `insomnia.request.body.update({ + mode: 'raw', + raw: 'rawContent', +});`; + +const updateRequestAuth = + `insomnia.request.auth.update( + { + type: 'bearer', + bearer: [ + {key: 'token', value: 'tokenValue'}, + ], + }, + 'bearer' +);`; const lintOptions = { globals: { @@ -48,11 +75,64 @@ const lintOptions = { esversion: 8, // ES8 syntax (async/await, etc) }; +// TODO: We probably don't want to expose every property like .toObject() so we need a way to filter those out +// or make those properties private +// TODO: introduce this functionality for other objects, such as Url, UrlMatchPattern and so on +// TODO: introduce function arguments +// TODO: provide snippets for environment keys if possible +function getPreRequestScriptSnippets(insomniaObject: InsomniaObject, path: string): Snippet[] { + let snippets: Snippet[] = []; + + const refs = new Set(); + const insomniaRecords = insomniaObject as Record; + + for (const key in insomniaObject) { + const isPrivate = typeof key === 'string' && key.startsWith('_'); + if (isPrivate) { + continue; + } + + const value = insomniaRecords[key]; + + if (typeof key === 'object') { + if (refs.has(value)) { + // avoid cyclic referring + continue; + } else { + refs.add(value); + } + } + + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + snippets.push({ + displayValue: `${path}.${value}`, + name: `${path}.${key}`, + value: `${path}.${key}`, + }); + } else if (typeof value === 'function') { + snippets.push({ + displayValue: `${path}.${key}()`, + name: `${path}.${key}()`, + value: `${path}.${key}()`, + }); + } else if (Array.isArray(value)) { + for (const item of value) { + snippets = snippets.concat(getPreRequestScriptSnippets(item, `${path}.${key}`)); + } + } else { + snippets = snippets.concat(getPreRequestScriptSnippets(value, `${path}.${key}`)); + } + } + + return snippets; +} + export const PreRequestScriptEditor: FC = ({ className, defaultValue, onChange, uniquenessKey, + settings, }) => { const editorRef = useRef(null); @@ -65,6 +145,7 @@ export const PreRequestScriptEditor: FC = ({ editorRef.current?.setValue([ ...value.split('\n').slice(0, nextRow), snippet, + '\n', ...value.split('\n').slice(nextRow), ].join('\n')); @@ -72,6 +153,28 @@ export const PreRequestScriptEditor: FC = ({ editorRef.current?.setCursorLine(cursorRow + snippet.split('\n').length); }; + // TODO(george): Add more to this object to provide improved autocomplete + const preRequestScriptSnippets = getPreRequestScriptSnippets( + new InsomniaObject({ + globals: new Environment({}), + iterationData: new Environment({}), + environment: new Environment({}), + baseEnvironment: new Environment({}), + variables: new Variables({ + globals: new Environment({}), + environment: new Environment({}), + collection: new Environment({}), + data: new Environment({}), + }), + request: new ScriptRequest({ + url: new Url('http://placeholder.com'), + }), + settings, + clientCertificates: [], + }), + 'insomnia', + ); + return (
@@ -88,18 +191,18 @@ export const PreRequestScriptEditor: FC = ({ placeholder="..." lintOptions={lintOptions} ref={editorRef} + getAutocompleteSnippets={() => preRequestScriptSnippets} />
addSnippet(getEnvVar)} + label='Variable Snippets' /> } @@ -181,6 +284,75 @@ export const PreRequestScriptEditor: FC = ({ onClick={() => addSnippet(unsetCollectionVar)} /> + + + + + + } + > + + addSnippet(addQueryParams)} + /> + + + addSnippet(setMethod)} + /> + + + addSnippet(addHeader)} + /> + + + addSnippet(removeHeader)} + /> + + + addSnippet(updateRequestBody)} + /> + + + addSnippet(updateRequestAuth)} + /> + + + + + + } + > = ({ uniquenessKey={uniqueKey} defaultValue={activeRequest.preRequestScript || ''} onChange={preRequestScript => patchRequest(requestId, { preRequestScript })} + settings={settings} />