fix: remove use of node headers to fix variable template usage (#8514)

This commit is contained in:
Ryan Willis
2025-03-25 09:08:53 -07:00
committed by Jay Wu
parent dbd9ea5e35
commit 0b3c443e4e
5 changed files with 111 additions and 9 deletions

View File

@@ -0,0 +1,51 @@
type: collection.insomnia.rest/5.0
name: headerTemplates
meta:
id: wrk_bbe65db250e94e31a7d88d8f779c9dd5
created: 1741133210975
modified: 1742840304001
collection:
- url: "{{ _.baseUrl }}/pets/2"
name: pet2
meta:
id: req_eaa7211a4c2e4fd492a38db5c7c926fd
created: 1741133214878
modified: 1742840346014
isPrivate: false
sortKey: -1741133214878
method: GET
headers:
- name: User-Agent
value: insomnia/11.0.0-beta.3
id: pair_b4cf2c942ed84dd28857d2ccfff55907
disabled: true
- id: pair_a16a834b625d453183898c8896abfde7
name: "{{ _.xHeaderExample }}"
value: baz
disabled: false
authentication:
type: none
settings:
renderRequestBody: true
encodeUrl: true
followRedirects: global
cookies:
send: true
store: true
rebuildPath: true
cookieJar:
name: Default Jar
meta:
id: jar_0e0f846c6042e206736fb4edf8d2b90ed218362d
created: 1741133210977
modified: 1742840291561
environments:
name: example
meta:
id: env_0e0f846c6042e206736fb4edf8d2b90ed218362d
created: 1741133210976
modified: 1742840291562
isPrivate: false
data:
xHeaderExample: X-Foo-Bar
baseUrl: http://localhost:4010

View File

@@ -0,0 +1,33 @@
import { expect } from '@playwright/test';
import { loadFixture } from '../../playwright/paths';
import { test } from '../../playwright/test';
test('can requests that contain templated header keys and values', async ({ app, page }) => {
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
const statusTag = page.locator('[data-testid="response-status-tag"]:visible');
const responsePaneContents = page.locator('[data-testid="response-pane"] >> [data-testid="CodeEditor"]:visible', {
has: page.locator('.CodeMirror-activeline'),
});
const collectionText = await loadFixture('header-templates.yaml');
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), collectionText);
await page.getByLabel('Import').click();
await page.locator('[data-test-id="import-from-clipboard"]').click();
await page.getByRole('button', { name: 'Scan' }).click();
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
await page.getByLabel('headerTemplates').click();
await page.getByLabel('Request Collection').getByTestId('pet2').press('Enter');
await page.getByRole('button', { name: 'Send' }).click();
await expect(statusTag).toContainText('200 OK');
await page.getByRole('button', { name: 'Preview' }).click();
await page.getByRole('menuitem', { name: 'Raw Data' }).click();
await expect(responsePaneContents).toContainText('{"id":"2"}');
await page.getByRole('tab', { name: 'Console' }).click();
await expect(responsePaneContents).toContainText('X-Foo-Bar: baz');
});

View File

@@ -18,6 +18,7 @@ import {
SORT_TYPE_DESC,
} from '../constants';
import {
ascendingFirstIndexStringSort,
ascendingNumberSort,
descendingNumberSort,
metaSortKeySort,
@@ -1033,4 +1034,14 @@ describe('Sorting methods', () => {
expect(descendingNumberSort(2, 1)).toBe(-1);
expect(descendingNumberSort(1, -2)).toBe(-1);
});
it('sorts the first string value in an array', () => {
expect(ascendingFirstIndexStringSort(['a'], ['b'])).toBe(-1);
expect(ascendingFirstIndexStringSort(['b'], ['a'])).toBe(1);
expect(ascendingFirstIndexStringSort(['ab'], ['abb'])).toBe(-1);
expect(ascendingFirstIndexStringSort(['abb'], ['ab'])).toBe(1);
expect(ascendingFirstIndexStringSort(['Abb'], ['bbb'])).toBe(-1);
expect(ascendingFirstIndexStringSort(['bbb'], ['Abb'])).toBe(1);
expect(ascendingFirstIndexStringSort(['x'], ['x'])).toBe(0);
});
});

View File

@@ -18,15 +18,15 @@ import {
type SortableModel = Request | RequestGroup | GrpcRequest;
type SortFunction<SortableType> = (a: SortableType, b: SortableType) => number;
export const ascendingNameSort: SortFunction<{name: string}> = (a, b) => {
export const ascendingNameSort: SortFunction<{ name: string }> = (a, b) => {
return a.name.localeCompare(b.name);
};
export const descendingNameSort: SortFunction<{name: string}> = (a, b) => {
export const descendingNameSort: SortFunction<{ name: string }> = (a, b) => {
return b.name.localeCompare(a.name);
};
export const createdFirstSort: SortFunction<{created: number}> = (a, b) => {
export const createdFirstSort: SortFunction<{ created: number }> = (a, b) => {
if (a.created === b.created) {
return 0;
}
@@ -34,7 +34,7 @@ export const createdFirstSort: SortFunction<{created: number}> = (a, b) => {
return a.created < b.created ? -1 : 1;
};
export const createdLastSort: SortFunction<{created: number}> = (a, b) => {
export const createdLastSort: SortFunction<{ created: number }> = (a, b) => {
if (a.created === b.created) {
return 0;
}
@@ -42,7 +42,7 @@ export const createdLastSort: SortFunction<{created: number}> = (a, b) => {
return a.created > b.created ? -1 : 1;
};
export const ascendingModifiedSort: SortFunction<{lastModifiedTimestamp: number}> = (a, b) => {
export const ascendingModifiedSort: SortFunction<{ lastModifiedTimestamp: number }> = (a, b) => {
if (a.lastModifiedTimestamp === b.lastModifiedTimestamp) {
return 0;
}
@@ -50,7 +50,7 @@ export const ascendingModifiedSort: SortFunction<{lastModifiedTimestamp: number}
return a.lastModifiedTimestamp < b.lastModifiedTimestamp ? -1 : 1;
};
export const descendingModifiedSort: SortFunction<{lastModifiedTimestamp: number}> = (a, b) => {
export const descendingModifiedSort: SortFunction<{ lastModifiedTimestamp: number }> = (a, b) => {
if (a.lastModifiedTimestamp === b.lastModifiedTimestamp) {
return 0;
}
@@ -125,6 +125,8 @@ export const descendingNumberSort: SortFunction<number> = (a, b) => {
return ascendingNumberSort(b, a);
};
export const ascendingFirstIndexStringSort: SortFunction<string[]> = (a, b) => a[0].localeCompare(b[0]);
export const sortMethodMap = {
[SORT_NAME_ASC]: ascendingNameSort,
[SORT_NAME_DESC]: descendingNameSort,

View File

@@ -16,6 +16,7 @@ import {
import {
getRenderedRequestAndContext,
} from '../common/render';
import { ascendingFirstIndexStringSort } from '../common/sorting';
import type { HeaderResult, ResponsePatch, ResponseTimelineEntry } from '../main/network/libcurl-promise';
import * as models from '../models';
import type { CaCertificate } from '../models/ca-certificate';
@@ -66,7 +67,7 @@ export const getOrInheritAuthentication = ({ request, requestGroups }: { request
return { type: 'none' };
};
export function getOrInheritHeaders({ request, requestGroups }: { request: Pick<BaseRequest, 'headers'>; requestGroups: Pick<RequestGroup, 'headers'>[] }): RequestHeader[] {
const httpHeaders = new Headers();
const httpHeaders = new Map<string, string>();
const originalCaseMap = new Map<string, string>();
// parent folders, then child folders, then request
const headerContexts = [...requestGroups.reverse(), request];
@@ -84,9 +85,13 @@ export function getOrInheritHeaders({ request, requestGroups }: { request: Pick<
return;
}
// appending will join matching header values with a comma
httpHeaders.append(normalizedCase, value);
if (httpHeaders.has(normalizedCase)) {
httpHeaders.set(normalizedCase, `${httpHeaders.get(normalizedCase)}, ${value}`);
return;
}
httpHeaders.set(normalizedCase, value);
});
return Array.from(httpHeaders.entries()).map(([name, value]) => ({ name: originalCaseMap.get(name)!, value }));
return Array.from(httpHeaders.entries()).sort(ascendingFirstIndexStringSort).map(([name, value]) => ({ name: originalCaseMap.get(name)!, value }));
}
// (only used for getOAuth2 token) Intended to gather all required database objects and initialize ids
export const fetchRequestGroupData = async (requestGroupId: string) => {