fix: some minor fixes for pre-request script sdk - INS-3379 (#7225)

* fix: try to make sendRequest compatible with the existing behavior by adding await

* fix: add _index for making method behavior (such as upsert) consistent with existing ones
This commit is contained in:
Hexxa 2024-04-09 21:00:09 +08:00 committed by GitHub
parent 836570604a
commit aaf76f755e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 240 additions and 64 deletions

View File

@ -796,3 +796,46 @@ resources:
text: |-
{}
_type: request
- _id: req_89dade2ee9ee42fbb22d588783a9df2
parentId: fld_01de564274824ecaad272330339ea6b2
modified: 1636707449231
created: 1636141014552
url: http://127.0.0.1:4010/echo
name: get sendRequest response through await or callback
description: ""
method: GET
parameters: []
headers:
- name: 'Content-Type'
value: 'application/json'
authentication: {}
metaSortKey: -1636141014553
isPrivate: false
settingStoreCookies: true
settingSendCookies: true
settingDisableRenderRequestBody: false
settingEncodeUrl: true
settingRebuildPath: true
settingFollowRedirects: global
preRequestScript: |-
let respFromCallback;
const respFromAwait = await insomnia.sendRequest(
'http://127.0.0.1:4010/echo',
(err, resp) => {
if (err != null) {
throw err;
} else {
respFromCallback = resp;
}
}
);
insomnia.environment.set('bodyFromAwait', respFromAwait.body);
insomnia.environment.set('bodyFromCallback', respFromCallback.body);
body:
mimeType: "application/json"
text: |-
{
"bodyFromAwait": {{ _.bodyFromAwait }},
"bodyFromCallback": {{ _.bodyFromCallback }}
}
_type: request

View File

@ -44,18 +44,16 @@ test.describe('pre-request features tests', async () => {
name: 'require / require classes from insomnia-collection module and init them',
expectedBody: {
propJson: {
'_kind': 'Property',
'disabled': false,
'id': 'pid',
'name': 'pname',
disabled: false,
id: 'pid',
name: 'pname',
},
headerJson: {
'_kind': 'Header',
'key': 'headerKey',
'value': 'headerValue',
'id': '',
'name': '',
'type': '',
key: 'headerKey',
value: 'headerValue',
id: '',
name: '',
type: '',
},
},
},
@ -126,6 +124,14 @@ test.describe('pre-request features tests', async () => {
events: true,
},
},
{
name: 'get sendRequest response through await or callback',
customVerify: (bodyJson: any) => {
const requestBody = JSON.parse(bodyJson.data);
expect(requestBody.bodyFromAwait.method).toEqual('GET');
expect(requestBody.bodyFromCallback.method).toEqual('GET');
},
},
];
for (let i = 0; i < testCases.length; i++) {

View File

@ -1,6 +1,6 @@
import { describe, expect, it } from '@jest/globals';
import { Cookie, CookieJar } from '../cookies';
import { Cookie, CookieJar, CookieList } from '../cookies';
describe('test Cookie object', () => {
it('test basic operations', () => {
@ -65,7 +65,6 @@ describe('test Cookie object', () => {
Cookie.unparseSingle(cookie1Opt)
).toEqual(expectedCookieString);
});
});
describe('test CookieJar', () => {
@ -142,4 +141,17 @@ describe('test CookieJar', () => {
expect(cookie).toBeUndefined();
});
});
it('CookieList operations', () => {
const cookieList = new CookieList(
[
new Cookie({ key: 'c1', value: 'v1' }),
new Cookie({ key: 'c2', value: 'v2' }),
]
);
const upsertedC1 = new Cookie({ key: 'c1', value: 'v1upserted' });
cookieList.upsert(upsertedC1);
expect(cookieList.one('c1')).toEqual(upsertedC1);
});
});

View File

@ -1,6 +1,6 @@
import { describe, expect, it } from '@jest/globals';
import { Header } from '../headers';
import { Header, HeaderList } from '../headers';
// import { QueryParam, setUrlParser, Url, UrlMatchPattern } from '../urls';
describe('test Header object', () => {
@ -17,4 +17,18 @@ describe('test Header object', () => {
Header.parse(Header.unparse(headerObjs))
).toEqual(headerObjs);
});
it('HeaderList operations', () => {
const headerList = new HeaderList(
undefined,
[
new Header({ key: 'h1', value: 'v1' }),
new Header({ key: 'h2', value: 'v2' }),
]
);
const upserted = new Header({ key: 'h1', value: 'v1upserted' });
headerList.upsert(upserted);
expect(headerList.one('h1')).toEqual(upserted);
});
});

View File

@ -8,11 +8,9 @@ describe('test Property objects', () => {
const pbase = new PropertyBase('my property');
expect(pbase.toJSON()).toEqual({
_kind: 'PropertyBase',
description: 'my property',
});
expect(pbase.toObject()).toEqual({
_kind: 'PropertyBase',
description: 'my property',
});
});
@ -26,14 +24,13 @@ describe('test Property objects', () => {
);
expect(prop.toJSON()).toEqual({
_kind: 'Property',
disabled: false,
id: 'real_id',
name: 'real_name',
});
});
it('PropertyList: basic operations', () => {
it('PropertyList: basic operations: add, append, count, all, clear', () => {
const propList = new PropertyList(
{},
undefined,
@ -47,19 +44,16 @@ describe('test Property objects', () => {
expect(propList.count()).toBe(3);
expect(propList.all()).toEqual([
{
_kind: 'Property',
disabled: false,
id: 'id1',
name: 'p1',
},
{
_kind: 'Property',
disabled: false,
id: 'id2',
name: 'p2',
},
{
_kind: 'Property',
disabled: false,
id: 'id3',
name: 'p3',
@ -67,6 +61,15 @@ describe('test Property objects', () => {
]);
propList.clear();
});
it('PropertyList: basic operations: assimilate, each, filter, find', () => {
const propList = new PropertyList<Property>(
Property,
undefined,
[],
);
propList.assimilate(
[
new Property('id1', 'p1'),
@ -96,25 +99,48 @@ describe('test Property objects', () => {
{},
) != null
).toBeTruthy();
});
it('PropertyList: basic operations: one, has, indexOf, insert, insertAfter, prepend, populate, map, reduce', () => {
const propList = new PropertyList<Property>(
Property,
undefined,
[
new Property('id1', 'p1'),
new Property('id2', 'p2'),
],
);
expect(propList.one('id1'))
.toEqual(new Property('id1', 'p1'));
expect(propList.has(new Property('id1', 'p1')))
.toBeTruthy();
expect(propList.indexOf(new Property('id1', 'p1')) >= 0).toBeTruthy();
expect(propList.indexOf(new Property('id1', 'p1')) === 0).toBeTruthy();
propList.clear();
propList.insert(new Property('id0', 'p0'), 0);
propList.insertAfter(new Property('id1', 'p1'), 1);
propList.prepend(new Property('id-1', 'p-1'));
propList.populate([new Property('id2', 'p2')]);
});
it('PropertyList: basic operations: one, has, indexOf, insert, insertAfter, prepend, populate, map, reduce', () => {
const propList = new PropertyList<Property>(
Property,
undefined,
[
new Property('id0', 'p0'),
new Property('id1', 'p1'),
new Property('id2', 'p2'),
],
);
expect(
propList.map(
prop => prop.id,
{},
)
).toEqual([
'id-1',
'id0',
'id1',
'id2',
@ -125,15 +151,27 @@ describe('test Property objects', () => {
'',
{},
),
).toEqual('id-1id0id1id2');
).toEqual('id0id1id2');
});
it('PropertyList: basic operations: remove, count, repopulate, toString, get, one, idx, upsert', () => {
const propList = new PropertyList<Property>(
Property,
undefined,
[
new Property('id0', 'p0'),
new Property('id1', 'p1'),
new Property('id2', 'p2'),
],
);
propList.remove(
prop => prop.id === 'id-1',
prop => prop.id === 'id0',
{},
);
expect(
propList.count(),
).toEqual(3);
).toEqual(2);
propList.repopulate([
new Property('id1', 'p1'),
@ -141,7 +179,20 @@ describe('test Property objects', () => {
]);
expect(propList.toString()).toEqual(
'[{"_kind":"Property","id":"id1","name":"p1","disabled":false}; {"_kind":"Property","id":"id2","name":"p2","disabled":false}]',
'[{"id":"id1","name":"p1","disabled":false}; {"id":"id2","name":"p2","disabled":false}]',
);
const expectedP1 = new Property('id1', 'p1');
const getP1 = propList.get('id1');
const oneP1 = propList.one('id1');
expect(getP1).toEqual(expectedP1);
expect(oneP1).toEqual(expectedP1);
const idxP1 = propList.idx(0);
expect(idxP1).toEqual(expectedP1);
const upsertedP2 = new Property('id2', 'upsertedP2');
propList.upsert(upsertedP2);
expect(propList.one('id2')).toEqual(upsertedP2);
});
});

View File

@ -50,6 +50,7 @@ describe('test Url object', () => {
query: [
new QueryParam({ key: 'key1', value: 'value1' }),
new QueryParam({ key: 'key2', value: 'value2' }),
new QueryParam({ key: 'key3', value: 'value3' }),
],
variables: [
new Variable({ key: 'varKey', value: 'varValue' }),
@ -58,14 +59,15 @@ describe('test Url object', () => {
expect(url.getHost()).toEqual('hostvalue.com');
expect(url.getPath()).toEqual('/pathLevel1/pathLevel2');
expect(url.getQueryString()).toEqual('key1=value1&key2=value2');
expect(url.getPathWithQuery()).toEqual('/pathLevel1/pathLevel2?key1=value1&key2=value2');
expect(url.getQueryString()).toEqual('key1=value1&key2=value2&key3=value3');
expect(url.getPathWithQuery()).toEqual('/pathLevel1/pathLevel2?key1=value1&key2=value2&key3=value3');
expect(url.getRemote(true)).toEqual('hostvalue.com:777');
expect(url.getRemote(false)).toEqual('hostvalue.com:777'); // TODO: add more cases
url.removeQueryParams([
new QueryParam({ key: 'key1', value: 'value1' }),
]);
url.removeQueryParams('key3');
expect(url.getQueryString()).toEqual('key2=value2');
expect(url.toString()).toEqual('https://usernameValue:passwordValue@hostvalue.com:777/pathLevel1/pathLevel2?key2=value2#hashValue');

View File

@ -1,6 +1,6 @@
import { describe, expect, it } from '@jest/globals';
import { Variable } from '../variables';
import { Variable, VariableList } from '../variables';
describe('test Variables object', () => {
it('test basic operations', () => {
@ -19,4 +19,18 @@ describe('test Variables object', () => {
expect(variable.get()).toBe('value2');
});
it('VariableList operations', () => {
const varList = new VariableList(
undefined,
[
new Variable({ key: 'h1', value: 'v1' }),
new Variable({ key: 'h2', value: 'v2' }),
]
);
const upserted = new Variable({ key: 'h1', value: 'v1upserted' });
varList.upsert(upserted);
expect(varList.one('h1')).toEqual(upserted);
});
});

View File

@ -48,6 +48,8 @@ export class Cookie extends Property {
this.cookie = cookie;
}
static _index = 'key';
static isCookie(obj: Property) {
return '_kind' in obj && obj._kind === 'Cookie';
}
@ -134,7 +136,7 @@ export class Cookie extends Property {
return this.cookie.toJSON().value;
};
key = () => {
get key() {
return this.cookie.toJSON().key;
};
@ -207,6 +209,7 @@ export class CookieObject extends CookieList {
super(cookies);
const scriptCookieJar = cookieJar ? new CookieJar(cookieJar.name, cookies) : new CookieJar('', []);
this.cookieJar = scriptCookieJar;
this.typeClass = Cookie;
}
jar() {

View File

@ -35,6 +35,8 @@ export class Header extends Property {
}
}
static _index: string = 'key';
static create(input?: { key: string; value: string } | string, name?: string): Header {
return new Header(input || { key: '', value: '' }, name);
}
@ -113,11 +115,6 @@ export class HeaderList<T extends Header> extends PropertyList<T> {
throw unsupportedError('eachParent');
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
toObject(_excludeDisabled?: boolean, _caseSensitive?: boolean, _multiValue?: boolean, _sanitizeKeys?: boolean) {
throw unsupportedError('toObject');
}
contentSize(): number {
return this.list
.map(header => header.toString())

View File

@ -123,7 +123,9 @@ export class PropertyBase {
const entriesToExport = Object
.entries(this)
.filter((kv: [string, any]) =>
typeof kv[1] !== 'function' && typeof kv[1] !== 'undefined'
typeof kv[1] !== 'function'
&& typeof kv[1] !== 'undefined'
&& kv[0] !== '_kind'
);
return Object.fromEntries(entriesToExport);
@ -158,6 +160,8 @@ export class Property extends PropertyBase {
}
static _index = 'id';
// TODO: unsupported yet
// eslint-disable-next-line @typescript-eslint/no-unused-vars
static replaceSubstitutions(_str: string, _variables: object): string {
@ -181,7 +185,7 @@ export class PropertyList<T extends Property> {
protected list: T[] = [];
constructor(
protected _typeClass: {}, // TODO: it is not used before collection is introduced
protected typeClass: { _index?: string },
protected parent: Property | PropertyList<any> | undefined,
populate: T[],
) {
@ -284,9 +288,18 @@ export class PropertyList<T extends Property> {
}
indexOf(item: string | T) {
const indexFieldName = this.typeClass._index || 'id';
for (let i = 0; i < this.list.length; i++) {
if (equal(item, this.list[i])) {
const record = this.list[i] as Record<string, any>;
if (typeof item === 'string' && record[indexFieldName] === item) {
return i;
} else {
const itemRecord = item as Record<string, any>;
if (record[indexFieldName] === itemRecord[indexFieldName]) {
return i;
}
}
}
return -1;
@ -320,8 +333,12 @@ export class PropertyList<T extends Property> {
}
one(id: string) {
const indexFieldName = this.typeClass._index || 'id';
for (let i = this.list.length - 1; i >= 0; i--) {
if (this.list[i].id === id) {
const record = this.list[i] as Record<string, any>;
if (record[indexFieldName] === id) {
return this.list[i];
}
}
@ -365,8 +382,9 @@ export class PropertyList<T extends Property> {
// TODO: unsupported yet
// eslint-disable-next-line @typescript-eslint/no-unused-vars
toObject(_excludeDisabled?: boolean, _caseSensitive?: boolean, _multiValue?: boolean, _sanitizeKeys?: boolean) {
// unsupported as _postman_propertyIndexKey is not supported
throw unsupportedError('toObject');
// it just dump all properties of each element without arguments
// then user is able to handle them by themself
return this.list.map(elem => elem.toJSON());
}
toString() {

View File

@ -67,6 +67,8 @@ export class ProxyConfig extends Property {
this.bypass = def.bypass || [];
}
static _index: string = 'key';
static isProxyConfig(obj: object) {
return '_kind' in obj && obj._kind === 'ProxyConfig';
}

View File

@ -9,31 +9,37 @@ import type { CookieOptions } from './cookies';
import { Request, type RequestOptions } from './request';
import { Response } from './response';
export function sendRequest(
export async function sendRequest(
request: string | Request | RequestOptions,
cb: (error?: string, response?: Response) => void,
settings: Settings, // TODO: modify this after introducing settings
) {
// TODO(george): enable cascading cancellation later as current solution just adds complexity
const requestOptions = requestToCurlOptions(request, settings);
settings: Settings,
): Promise<Response | undefined> {
// returning Promise here makes migration easier by just adding `await` before calling
return new Promise<Response | undefined>(resolve => {
// TODO(george): enable cascading cancellation later as current solution just adds complexity
const requestOptions = requestToCurlOptions(request, settings);
try {
window.bridge.curlRequest(requestOptions)
.then(result => {
const output = result as CurlRequestOutput;
return curlOutputToResponse(output, request);
}).then(transformedOutput => {
cb(undefined, transformedOutput);
}).catch(e => {
cb(e, undefined);
});
} catch (err) {
if (err.name === 'AbortError') {
cb(`Request was cancelled: ${err.message}`, undefined);
return;
try {
window.bridge.curlRequest(requestOptions)
.then(result => {
const output = result as CurlRequestOutput;
return curlOutputToResponse(output, request);
}).then(transformedOutput => {
cb(undefined, transformedOutput);
resolve(transformedOutput);
}).catch(e => {
cb(e, undefined);
resolve(undefined);
});
} catch (err) {
if (err.name === 'AbortError') {
cb(`Request was cancelled: ${err.message}`, undefined);
} else {
cb(`Something went wrong: ${err.message}`, undefined);
}
resolve(undefined);
}
cb(`Something went wrong: ${err.message}`, undefined);
}
});
};
function requestToCurlOptions(req: string | Request | RequestOptions, settings: Settings) {

View File

@ -108,6 +108,7 @@ export class QueryParam extends Property {
}
export interface UrlOptions {
id?: string;
auth?: {
username: string;
password: string;
@ -124,6 +125,7 @@ export interface UrlOptions {
export class Url extends PropertyBase {
_kind: string = 'Url';
id?: string;
// TODO: should be related to RequestAuth
// but the implementation seems only supports username + password
auth?: { username: string; password: string };
@ -184,6 +186,8 @@ export class Url extends PropertyBase {
}
}
static _index: string = 'id';
static isUrl(obj: object) {
return '_kind' in obj && obj._kind === 'Url';
}
@ -279,7 +283,7 @@ export class Url extends PropertyBase {
this.query = new PropertyList(
QueryParam,
undefined,
this.query.filter(queryParam => queryParam.key === params, {})
this.query.filter(queryParam => queryParam.key !== params, {})
);
} else if (params.length > 0) {
let toBeRemoved: Set<string>;
@ -301,7 +305,7 @@ export class Url extends PropertyBase {
this.query.filter(queryParam => !toBeRemoved.has(queryParam.key), {})
);
} else {
console.error('failed to remove query params: unknown params type, only supports QueryParam[], string[] or string');
throw Error('failed to remove query params: unknown params type, only supports QueryParam[], string[] or string');
}
}
@ -352,6 +356,7 @@ export class UrlMatchPattern extends Property {
// "http://localhost/*"
// It doesn't support match patterns for top Level domains (TLD).
id: string = '';
private pattern: string;
constructor(pattern: string) {
@ -360,6 +365,7 @@ export class UrlMatchPattern extends Property {
this.pattern = pattern;
}
static _index = 'id';
static readonly MATCH_ALL_URLS: string = '<all_urls>';
static pattern: string | undefined = undefined; // TODO: its usage is unknown
static readonly PROTOCOL_DELIMITER: string = '+';

View File

@ -27,6 +27,8 @@ export class Variable extends Property {
this.disabled = def ? def.disabled : false;
}
static _index = 'key';
// unknown usage and unsupported
static types() {
throw unsupportedError('types');