Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/insomnia/src/network/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { MockServer } from '../models/mock-server';
import type { Request, RequestAuthentication, RequestHeader, RequestParameter } from '../models/request';
import { isRequestGroup, RequestGroup } from '../models/request-group';
import type { Settings } from '../models/settings';
import { WebSocketRequest } from '../models/websocket-request';
import { isWorkspace, Workspace } from '../models/workspace';
import * as pluginContexts from '../plugins/context/index';
import * as plugins from '../plugins/index';
Expand All @@ -41,7 +42,7 @@ import { cancellableCurlRequest, cancellableRunScript } from './cancellation';
import { filterClientCertificates } from './certificate';
import { addSetCookiesToToughCookieJar } from './set-cookie-util';

export const getOrInheritAuthentication = ({ request, requestGroups }: { request: Request; requestGroups: RequestGroup[] }): RequestAuthentication | {} => {
export const getOrInheritAuthentication = ({ request, requestGroups }: { request: Request | WebSocketRequest; requestGroups: RequestGroup[] }): RequestAuthentication | {} => {
const hasValidAuth = getAuthObjectOrNull(request.authentication) && isAuthEnabled(request.authentication);
if (hasValidAuth) {
return request.authentication;
Expand Down
115 changes: 76 additions & 39 deletions packages/insomnia/src/ui/components/rendered-query-string.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import classNames from 'classnames';
import React, { FC, useState } from 'react';
import { useAsync } from 'react-use';
import React, { FC, useEffect, useState } from 'react';
import styled from 'styled-components';

import { PATH_PARAMETER_REGEX, Request } from '../../models/request';
import { database as db } from '../../common/database';
import * as models from '../../models';
import { PATH_PARAMETER_REGEX, Request, RequestAuthentication, RequestParameter } from '../../models/request';
import { isRequestGroup, RequestGroup } from '../../models/request-group';
import { WebSocketRequest } from '../../models/websocket-request';
import { getAuthObjectOrNull, isAuthEnabled } from '../../network/authentication';
import { getOrInheritAuthentication } from '../../network/network';
import { buildQueryStringFromParams, joinUrlAndQueryString, smartEncodeUrl } from '../../utils/url/querystring';
import { useNunjucks } from '../context/nunjucks/use-nunjucks';
import { CopyButton as _CopyButton } from './base/copy-button';
Expand Down Expand Up @@ -33,50 +37,83 @@ interface Props {

const defaultPreview = '...';

const addApiKeyToParams = (requestAuth: RequestAuthentication) => {
const shouldAddAuthParamsToQuery = requestAuth.type === 'apikey' && requestAuth.addTo === 'queryParams';
return shouldAddAuthParamsToQuery && requestAuth.key && requestAuth.value ?
[{ name: requestAuth.key, value: requestAuth.value }] : [];
};

async function getQueryParamsFromAuth(request: Request | WebSocketRequest): Promise<RequestParameter[]> {
const requestAuth = getAuthObjectOrNull(request.authentication);
const hasAuthSetOnRequest = requestAuth !== null && isAuthEnabled(request.authentication);
if (hasAuthSetOnRequest) {
return addApiKeyToParams(requestAuth);
}

const ancestors = await db.withAncestors<Request | WebSocketRequest | RequestGroup>(request, [
models.requestGroup.type,
]);
const requestGroups = ancestors.filter(isRequestGroup);
const auth = getOrInheritAuthentication({ request, requestGroups });
const closestAuth = getAuthObjectOrNull(auth);
if (!closestAuth) {
return [];
}
return addApiKeyToParams(closestAuth);
}

export const RenderedQueryString: FC<Props> = ({ request }) => {
const [previewString, setPreviewString] = useState(defaultPreview);
const { handleRender } = useNunjucks();

useAsync(async () => {
const enabledParameters = request.parameters.filter(({ disabled }) => !disabled);
try {
const result = await handleRender({
url: request.url,
parameters: enabledParameters,
pathParameters: request.pathParameters,
});

if (!result) {
return;
}
useEffect(() => {
const fn = async () => {
const enabledParameters = request.parameters.filter(({ disabled }) => !disabled);
const authQueryParams = await getQueryParamsFromAuth(request);

const { parameters, pathParameters } = result;
let { url } = result;

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

if (param && param.value) {
return `/${encodeURIComponent(param.value)}`;
}
// The parameter should also be URL encoded
return match;
try {
const result = await handleRender({
url: request.url,
parameters: enabledParameters,
pathParameters: request.pathParameters,
authQueryParams,
});

if (!result) {
return;
}

const { parameters, pathParameters, authQueryParams: renderedAuthQueryParams } = result;
let { url } = result;

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

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

const mergedParams = [...parameters, ...renderedAuthQueryParams];
const qs = buildQueryStringFromParams(mergedParams);
const fullUrl = joinUrlAndQueryString(url, qs);
const encoded = smartEncodeUrl(fullUrl, request.settingEncodeUrl);
setPreviewString(encoded === '' ? defaultPreview : encoded);
} catch (error: unknown) {
console.error(error);
setPreviewString(defaultPreview);
}
};
fn();

const qs = buildQueryStringFromParams(parameters);
const fullUrl = joinUrlAndQueryString(url, qs);
const encoded = smartEncodeUrl(fullUrl, request.settingEncodeUrl);
setPreviewString(encoded === '' ? defaultPreview : encoded);
} catch (error: unknown) {
console.error(error);
setPreviewString(defaultPreview);
}
}, [request.parameters, request.url, request.pathParameters, request.settingEncodeUrl, handleRender]);
}, [request.parameters, request.url, request.pathParameters, request.settingEncodeUrl, handleRender, request.authentication, request]);

const className = previewString === defaultPreview ? 'super-duper-faint' : 'selectable force-wrap';

Expand Down