Skip to content

Commit

Permalink
Request path parameters (#6994)
Browse files Browse the repository at this point in the history
* add path parameters to the request/ws-request model

* render path params

* show path params on the ui

* fix rendering of params

* cleanup params ui in ws

* cleanup request model

* add empty state info

* update styles

* certificate-styles

* fix tests

* cleanup code

* fix e2e tests
  • Loading branch information
gatzjames authored Jan 18, 2024
1 parent b24008c commit a8c1da7
Show file tree
Hide file tree
Showing 16 changed files with 380 additions and 218 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ test.describe('Environment Editor', async () => {
await page.getByLabel('Request Collection').getByTestId('New Request').press('Enter');

// Add number variable to request body
await page.getByRole('tab', { name: 'Plain' }).click();
await page.locator('pre').filter({ hasText: '_.exampleObject.anotherNumber' }).click();
await page.getByTestId('CodeEditor').getByRole('textbox').press('Enter');
await page.getByTestId('CodeEditor').getByRole('textbox').press('Control+ ');
Expand Down
4 changes: 4 additions & 0 deletions packages/insomnia-smoke-test/tests/smoke/graphql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ test('can render schema and send GraphQL requests', async ({ app, page }) => {

// Open the graphql request
await page.getByLabel('Request Collection').getByTestId('GraphQL request').press('Enter');
await page.getByRole('tab', { name: 'GraphQL' }).click();
// Assert the schema is fetched after switching to GraphQL request
await expect(page.locator('.graphql-editor__meta')).toContainText('schema fetched just now');

Expand Down Expand Up @@ -62,6 +63,7 @@ test('can render schema and send GraphQL requests with object variables', async

// Open the graphql request
await page.getByLabel('Request Collection').getByTestId('GraphQL request with variables').press('Enter');
await page.getByRole('tab', { name: 'GraphQL' }).click();
// Assert the schema is fetched after switching to GraphQL request
await expect(page.locator('.graphql-editor__meta')).toContainText('schema fetched just now');

Expand Down Expand Up @@ -103,6 +105,7 @@ test('can render numeric environment', async ({ app, page }) => {

// Open the graphql request
await page.getByLabel('Request Collection').getByTestId('GraphQL request with number').press('Enter');
await page.getByRole('tab', { name: 'GraphQL' }).click();
// Assert the schema is fetched after switching to GraphQL request
await expect(page.locator('.graphql-editor__meta')).toContainText('schema fetched just now');

Expand Down Expand Up @@ -141,6 +144,7 @@ test('can send GraphQL requests after editing and prettifying query', async ({ a
await page.getByLabel('Request Collection').getByTestId('GraphQL request').press('Enter');

// Edit and prettify query
await page.getByRole('tab', { name: 'GraphQL' }).click();
await page.locator('pre[role="presentation"]:has-text("bearer")').click();
await page.locator('.app').press('Enter');
await page.locator('text=Prettify GraphQL').click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ test('Request tabs', async ({ page }) => {

await page.getByLabel('Create in collection').click();
await page.getByRole('menuitemradio', { name: 'HTTP Request' }).press('Enter');
await page.getByRole('tab', { name: 'Body' }).click();
await page.getByRole('button', { name: 'Body' }).click();
await page.getByRole('menuitem', { name: 'JSON' }).click();
await page.getByRole('tab', { name: 'Auth' }).click();
await page.getByRole('button', { name: 'Auth' }).click();
await page.getByRole('menuitem', { name: 'OAuth 1.0' }).click();
await page.getByRole('tab', { name: 'Query' }).click();
await page.getByRole('tab', { name: 'Parameters' }).click();
await page.getByRole('tab', { name: 'Headers' }).click();
await page.getByRole('tab', { name: 'Docs' }).click();
await page.locator('text=Add Description').click();
Expand All @@ -26,9 +27,10 @@ test('WS tabs', async ({ page }) => {
await page.getByLabel('Create in collection').click();
await page.getByRole('menuitemradio', { name: 'WebSocket Request' }).click();
await page.getByRole('tab', { name: 'JSON' }).click();
await page.getByLabel('Websocket request pane tabs').getByRole('button', { name: 'JSON' }).click();
await page.getByRole('menuitem', { name: 'JSON' }).click();
await page.getByRole('tab', { name: 'Auth' }).click();
await page.getByRole('tab', { name: 'Query' }).click();
await page.getByRole('tab', { name: 'Parameters' }).click();
await page.getByRole('tab', { name: 'Headers' }).click();
await page.getByRole('tab', { name: 'Docs' }).click();
await page.getByRole('button', { name: 'Add Description' }).click();
Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/common/__tests__/database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ describe('requestCreate()', () => {
parentId: 'wrk_123',
};
const r = await models.request.create(patch);
expect(Object.keys(r).length).toBe(21);
expect(Object.keys(r).length).toBe(22);
expect(r._id).toMatch(/^req_[a-zA-Z0-9]{32}$/);
expect(r.created).toBeGreaterThanOrEqual(now);
expect(r.modified).toBeGreaterThanOrEqual(now);
Expand Down
22 changes: 21 additions & 1 deletion packages/insomnia/src/common/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { CookieJar } from '../models/cookie-jar';
import type { Environment } from '../models/environment';
import type { GrpcRequest, GrpcRequestBody } from '../models/grpc-request';
import { isProject, Project } from '../models/project';
import type { Request } from '../models/request';
import { PATH_PARAMETER_REGEX, type Request } from '../models/request';
import { isRequestGroup, RequestGroup } from '../models/request-group';
import { WebSocketRequest } from '../models/websocket-request';
import { isWorkspace, Workspace } from '../models/workspace';
Expand Down Expand Up @@ -493,6 +493,7 @@ export async function getRenderedRequestAndContext(
renderContext,
request.settingDisableRenderRequestBody ? /^body.*/ : null,
);

const renderedRequest = renderResult._request;
const renderedCookieJar = renderResult._cookieJar;
renderedRequest.description = await render(description, renderContext, null, KEEP_ON_ERROR);
Expand All @@ -514,6 +515,24 @@ export async function getRenderedRequestAndContext(

// Default the proto if it doesn't exist
renderedRequest.url = setDefaultProtocol(renderedRequest.url);

// Render path parameters
if (renderedRequest.pathParameters) {
// Replace path parameters in URL with their rendered values
// Path parameters are path segments that start with a colon, e.g. :id
renderedRequest.url = renderedRequest.url.replace(PATH_PARAMETER_REGEX, match => {
const paramName = match.replace('\/:', '');
const param = renderedRequest.pathParameters?.find(p => p.name === paramName);

if (param && param.value) {
// The parameter value needs to be URL encoded
return `/${encodeURIComponent(param.value)}`;
}

return match;
});
}

return {
context: renderContext,
request: {
Expand All @@ -523,6 +542,7 @@ export async function getRenderedRequestAndContext(
isPrivate: false,
_id: renderedRequest._id,
authentication: renderedRequest.authentication,
pathParameters: renderedRequest.pathParameters,
body: renderedRequest.body,
created: renderedRequest.created,
modified: renderedRequest.modified,
Expand Down
3 changes: 3 additions & 0 deletions packages/insomnia/src/models/__tests__/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe('init()', () => {
name: 'New Request',
description: '',
parameters: [],
pathParameters: [],
url: '',
settingStoreCookies: true,
settingSendCookies: true,
Expand Down Expand Up @@ -56,6 +57,7 @@ describe('create()', () => {
method: 'GET',
name: 'Test Request',
parameters: [],
pathParameters: [],
url: '',
settingStoreCookies: true,
settingSendCookies: true,
Expand Down Expand Up @@ -388,6 +390,7 @@ describe('migrate()', () => {
headers: [],
authentication: {},
parameters: [],
pathParameters: [],
parentId: null,
body: {
mimeType: '',
Expand Down
46 changes: 46 additions & 0 deletions packages/insomnia/src/models/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,50 @@ export interface RequestBodyParameter {
type?: string;
}

export interface RequestPathParameter {
name: string;
value: string;
}

export const PATH_PARAMETER_REGEX = /\/:[^/?#]+/g;

export const getPathParametersFromUrl = (url: string): string[] => {
// Find all path parameters in the URL. Path parameters are defined as segments of the URL that start with a colon.
const urlPathParameters = url.match(PATH_PARAMETER_REGEX)?.map(String).map(match => match.replace('\/:', '')) || [];
const uniqueUrlPathParameters = [...new Set(urlPathParameters)];

return uniqueUrlPathParameters;
};

export const getCombinedPathParametersFromUrl = (url: string, pathParameters: RequestPathParameter[]): RequestPathParameter[] => {
// Extract path parameters from the URL
const urlPathParameters = getPathParametersFromUrl(url);

// Initialize an empty array for saved path parameters
let savedPathParameters: RequestPathParameter[] = [];

// Check if there are any path parameters in the active request
if (pathParameters) {
// Filter out the saved path parameters
savedPathParameters = pathParameters.filter(p => urlPathParameters.includes(p.name));
}

// Initialize an empty set for unsaved URL path parameters
let unsavedUrlPathParameters = new Set<RequestPathParameter>();

// Check if there are any path parameters in the URL
if (urlPathParameters) {
// Filter out the unsaved URL path parameters
unsavedUrlPathParameters = new Set(
urlPathParameters.filter(p => !savedPathParameters.map(p => p.name).includes(p))
.map(p => ({ name: p, value: '' }))
);
}

// Combine the saved and unsaved path parameters
return [...savedPathParameters, ...unsavedUrlPathParameters];
};

export interface RequestBody {
mimeType?: string | null;
text?: string;
Expand All @@ -96,6 +140,7 @@ export interface BaseRequest {
method: string;
body: RequestBody;
parameters: RequestParameter[];
pathParameters: RequestPathParameter[];
headers: RequestHeader[];
authentication: RequestAuthentication;
metaSortKey: number;
Expand Down Expand Up @@ -135,6 +180,7 @@ export function init(): BaseRequest {
authentication: {},
metaSortKey: -1 * Date.now(),
isPrivate: false,
pathParameters: [],
// Settings
settingStoreCookies: true,
settingSendCookies: true,
Expand Down
4 changes: 3 additions & 1 deletion packages/insomnia/src/models/websocket-request.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { database } from '../common/database';
import type { BaseModel } from '.';
import { RequestAuthentication, RequestHeader, RequestParameter } from './request';
import { RequestAuthentication, RequestHeader, RequestParameter, RequestPathParameter } from './request';

export const name = 'WebSocket Request';

Expand All @@ -20,6 +20,7 @@ export interface BaseWebSocketRequest {
headers: RequestHeader[];
authentication: RequestAuthentication;
parameters: RequestParameter[];
pathParameters: RequestPathParameter[];
settingEncodeUrl: boolean;
settingStoreCookies: boolean;
settingSendCookies: boolean;
Expand All @@ -43,6 +44,7 @@ export const init = (): BaseWebSocketRequest => ({
headers: [],
authentication: {},
parameters: [],
pathParameters: [],
settingEncodeUrl: true,
settingStoreCookies: true,
settingSendCookies: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ describe('app.export.*', () => {
modified: 222,
name: 'New Request',
parameters: [],
pathParameters: [],
parentId: 'wrk_1',
settingDisableRenderRequestBody: false,
settingEncodeUrl: true,
Expand Down
31 changes: 0 additions & 31 deletions packages/insomnia/src/ui/components/editors/query-editor.tsx

This file was deleted.

Loading

0 comments on commit a8c1da7

Please sign in to comment.