@stoplight/elements-core 7.7.21 → 7.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Docs/HttpService/ServerInfo.d.ts +4 -0
- package/components/TryIt/Servers/ServerVariables.d.ts +9 -0
- package/components/TryIt/Servers/VariableEditor.d.ts +11 -0
- package/components/TryIt/Servers/useServerVariables.d.ts +4 -0
- package/components/TryIt/TryIt.stories.d.ts +1 -0
- package/components/TryIt/TryItWithRequestSamples.stories.d.ts +1 -0
- package/components/TryIt/build-request.d.ts +3 -2
- package/index.esm.js +165 -45
- package/index.js +168 -45
- package/index.mjs +165 -45
- package/package.json +1 -1
- package/utils/http-spec/IServer.d.ts +9 -3
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ServerVariable } from '../../../utils/http-spec/IServer';
|
|
3
|
+
interface ServerVariablesProps<P extends keyof any = string> {
|
|
4
|
+
variables: readonly ServerVariable[];
|
|
5
|
+
values: Record<P, string>;
|
|
6
|
+
onChangeValue: (variableName: P, newValue: string) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const ServerVariables: React.FC<ServerVariablesProps>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SelectProps } from '@stoplight/mosaic';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { ServerVariable } from '../../../utils/http-spec/IServer';
|
|
4
|
+
interface VariableProps {
|
|
5
|
+
variable: ServerVariable;
|
|
6
|
+
value?: string;
|
|
7
|
+
onChange: SelectProps['onChange'];
|
|
8
|
+
validate?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare const VariableEditor: React.FC<VariableProps>;
|
|
11
|
+
export {};
|
|
@@ -4,6 +4,7 @@ declare const _default: Meta<TryItProps>;
|
|
|
4
4
|
export default _default;
|
|
5
5
|
export declare const SimpleGET: Story<TryItProps>;
|
|
6
6
|
export declare const WithParameters: Story<TryItProps>;
|
|
7
|
+
export declare const WithVariables: Story<TryItProps>;
|
|
7
8
|
export declare const UrlEncoded: Story<TryItProps>;
|
|
8
9
|
export declare const Multipart: Story<TryItProps>;
|
|
9
10
|
export declare const RequestBodySchema: Story<TryItProps>;
|
|
@@ -3,3 +3,4 @@ import { TryItWithRequestSamplesProps } from './TryItWithRequestSamples';
|
|
|
3
3
|
declare const _default: Meta<TryItWithRequestSamplesProps>;
|
|
4
4
|
export default _default;
|
|
5
5
|
export declare const WithParameters: Story<TryItWithRequestSamplesProps>;
|
|
6
|
+
export declare const WithVariables: Story<TryItWithRequestSamplesProps>;
|
|
@@ -7,6 +7,7 @@ interface BuildRequestInput {
|
|
|
7
7
|
httpOperation: IHttpOperation;
|
|
8
8
|
mediaTypeContent: IMediaTypeContent | undefined;
|
|
9
9
|
parameterValues: Dictionary<string, string>;
|
|
10
|
+
serverVariableValues: Dictionary<string, string>;
|
|
10
11
|
bodyInput?: BodyParameterValues | string;
|
|
11
12
|
mockData?: MockData;
|
|
12
13
|
auth?: HttpSecuritySchemeWithValues;
|
|
@@ -18,7 +19,7 @@ export declare const getQueryParams: ({ httpOperation, parameterValues, }: Pick<
|
|
|
18
19
|
name: string;
|
|
19
20
|
value: string;
|
|
20
21
|
}[];
|
|
21
|
-
export declare function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, mockData, auth, chosenServer, credentials, corsProxy, }: BuildRequestInput): Promise<Parameters<typeof fetch>>;
|
|
22
|
-
export declare function buildHarRequest({ httpOperation, bodyInput, parameterValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }: BuildRequestInput): Promise<HarRequest>;
|
|
22
|
+
export declare function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, serverVariableValues, mockData, auth, chosenServer, credentials, corsProxy, }: BuildRequestInput): Promise<Parameters<typeof fetch>>;
|
|
23
|
+
export declare function buildHarRequest({ httpOperation, bodyInput, parameterValues, serverVariableValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }: BuildRequestInput): Promise<HarRequest>;
|
|
23
24
|
export declare function getAcceptedMimeTypes(httpOperation: IHttpOperation): string[];
|
|
24
25
|
export {};
|
package/index.esm.js
CHANGED
|
@@ -41,6 +41,9 @@ import entries from 'lodash/entries.js';
|
|
|
41
41
|
import keys from 'lodash/keys.js';
|
|
42
42
|
import { JsonSchemaViewer } from '@stoplight/json-schema-viewer';
|
|
43
43
|
import sortBy from 'lodash/sortBy.js';
|
|
44
|
+
import isEmpty from 'lodash/isEmpty.js';
|
|
45
|
+
import isNil from 'lodash/isNil.js';
|
|
46
|
+
import omitBy from 'lodash/omitBy.js';
|
|
44
47
|
import { HashLink } from 'react-router-hash-link';
|
|
45
48
|
import { QueryClient, useQueryClient, QueryClientProvider } from 'react-query';
|
|
46
49
|
import $RefParser from '@stoplight/json-schema-ref-parser';
|
|
@@ -486,15 +489,17 @@ function useChosenServerUrl(chosenServerUrl) {
|
|
|
486
489
|
const chosenServerAtom = atom(undefined);
|
|
487
490
|
|
|
488
491
|
function isValidServer(server) {
|
|
489
|
-
return server.url !== null
|
|
492
|
+
return server.url !== null;
|
|
490
493
|
}
|
|
491
|
-
const getServersToDisplay = (originalServers, mockUrl) => {
|
|
492
|
-
const servers = originalServers
|
|
493
|
-
.map((server, i) => {
|
|
494
|
+
const getServersToDisplay = (originalServers, mockUrl, inlineDefaults) => {
|
|
495
|
+
const servers = originalServers.map((server, i) => {
|
|
494
496
|
const fallbackDescription = originalServers.length === 1 ? 'Live Server' : `Server ${i + 1}`;
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
497
|
+
let url = server.url;
|
|
498
|
+
if (inlineDefaults) {
|
|
499
|
+
url = getServerUrlWithVariableValues(server, {});
|
|
500
|
+
}
|
|
501
|
+
return Object.assign(Object.assign({}, server), { url, description: server.description || fallbackDescription });
|
|
502
|
+
});
|
|
498
503
|
if (mockUrl) {
|
|
499
504
|
servers.push({
|
|
500
505
|
id: 'mock',
|
|
@@ -502,27 +507,48 @@ const getServersToDisplay = (originalServers, mockUrl) => {
|
|
|
502
507
|
url: mockUrl,
|
|
503
508
|
});
|
|
504
509
|
}
|
|
505
|
-
return servers;
|
|
510
|
+
return servers.filter(isValidServer);
|
|
506
511
|
};
|
|
507
|
-
const
|
|
512
|
+
const getServerVariables = (server) => {
|
|
508
513
|
var _a;
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
+
return Object.entries((_a = server === null || server === void 0 ? void 0 : server.variables) !== null && _a !== void 0 ? _a : {}).map(([key, value]) => ({
|
|
515
|
+
name: key,
|
|
516
|
+
default: value.default,
|
|
517
|
+
description: value.description,
|
|
518
|
+
enum: value.enum,
|
|
519
|
+
}));
|
|
520
|
+
};
|
|
521
|
+
function resolveUrl(urlString) {
|
|
522
|
+
if (urlString === null)
|
|
523
|
+
return null;
|
|
514
524
|
let url;
|
|
515
525
|
try {
|
|
516
526
|
url = URI(urlString);
|
|
517
527
|
}
|
|
518
|
-
catch (
|
|
528
|
+
catch (_a) {
|
|
519
529
|
return null;
|
|
520
530
|
}
|
|
531
|
+
let stringifiedUrl;
|
|
521
532
|
if (url.is('relative') && typeof window !== 'undefined') {
|
|
522
|
-
|
|
533
|
+
stringifiedUrl = url.absoluteTo(window.location.origin).toString();
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
stringifiedUrl = url.toString();
|
|
523
537
|
}
|
|
524
|
-
|
|
525
|
-
|
|
538
|
+
if (isProperUrl(stringifiedUrl)) {
|
|
539
|
+
return stringifiedUrl.endsWith('/') ? stringifiedUrl.slice(0, -1) : stringifiedUrl;
|
|
540
|
+
}
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
const getServerUrlWithVariableValues = (server, values) => {
|
|
544
|
+
var _a;
|
|
545
|
+
let urlString = server.url;
|
|
546
|
+
const variables = Object.entries((_a = server.variables) !== null && _a !== void 0 ? _a : {});
|
|
547
|
+
variables.forEach(([variableName, variableInfo]) => {
|
|
548
|
+
var _a;
|
|
549
|
+
urlString = urlString.replaceAll(`{${variableName}}`, (_a = values[variableName]) !== null && _a !== void 0 ? _a : variableInfo.default);
|
|
550
|
+
});
|
|
551
|
+
return urlString;
|
|
526
552
|
};
|
|
527
553
|
|
|
528
554
|
const persistAtom = (key, atomInstance) => {
|
|
@@ -1401,11 +1427,11 @@ const useTextRequestBodyState = (mediaTypeContent) => {
|
|
|
1401
1427
|
};
|
|
1402
1428
|
|
|
1403
1429
|
const nameAndValueObjectToPair = ({ name, value }) => [name, value];
|
|
1404
|
-
const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, }) => {
|
|
1430
|
+
const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, serverVariableValues, }) => {
|
|
1405
1431
|
var _a;
|
|
1406
1432
|
const server = chosenServer || ((_a = httpOperation.servers) === null || _a === void 0 ? void 0 : _a[0]);
|
|
1407
|
-
const chosenServerUrl = server &&
|
|
1408
|
-
const serverUrl = (mockData === null || mockData === void 0 ? void 0 : mockData.url) || chosenServerUrl || window.location.origin;
|
|
1433
|
+
const chosenServerUrl = server && getServerUrlWithVariableValues(server, serverVariableValues);
|
|
1434
|
+
const serverUrl = resolveUrl((mockData === null || mockData === void 0 ? void 0 : mockData.url) || chosenServerUrl || window.location.origin);
|
|
1409
1435
|
if (corsProxy && !mockData) {
|
|
1410
1436
|
return `${corsProxy}${serverUrl}`;
|
|
1411
1437
|
}
|
|
@@ -1482,10 +1508,10 @@ const getQueryParams = ({ httpOperation, parameterValues, }) => {
|
|
|
1482
1508
|
return acc;
|
|
1483
1509
|
}, []);
|
|
1484
1510
|
};
|
|
1485
|
-
function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
|
|
1511
|
+
function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, serverVariableValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
|
|
1486
1512
|
var _a, _b, _c;
|
|
1487
1513
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1488
|
-
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
|
|
1514
|
+
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy, serverVariableValues });
|
|
1489
1515
|
const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase()) && bodyInput !== undefined;
|
|
1490
1516
|
const queryParams = getQueryParams({ httpOperation, parameterValues });
|
|
1491
1517
|
const rawHeaders = filterOutAuthorizationParams((_b = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : [], httpOperation.security)
|
|
@@ -1558,10 +1584,10 @@ const runAuthRequestEhancements = (auth, queryParams, headers) => {
|
|
|
1558
1584
|
}
|
|
1559
1585
|
return [newQueryParams, newHeaders];
|
|
1560
1586
|
};
|
|
1561
|
-
function buildHarRequest({ httpOperation, bodyInput, parameterValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
|
|
1587
|
+
function buildHarRequest({ httpOperation, bodyInput, parameterValues, serverVariableValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
|
|
1562
1588
|
var _a, _b, _c, _d;
|
|
1563
1589
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1564
|
-
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
|
|
1590
|
+
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy, serverVariableValues });
|
|
1565
1591
|
const mimeType = (_a = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _a !== void 0 ? _a : 'application/json';
|
|
1566
1592
|
const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase()) && bodyInput !== undefined;
|
|
1567
1593
|
const queryParams = getQueryParams({ httpOperation, parameterValues });
|
|
@@ -1883,8 +1909,18 @@ class NetworkError extends Error {
|
|
|
1883
1909
|
}
|
|
1884
1910
|
const isNetworkError = (error) => error instanceof NetworkError;
|
|
1885
1911
|
|
|
1912
|
+
const persistedServerVariableValuesAtom = atom({});
|
|
1913
|
+
const useServerVariables = () => {
|
|
1914
|
+
const [serverVariables, setPersistedServerVariableValues] = useAtom(persistedServerVariableValuesAtom);
|
|
1915
|
+
const updateServerVariableValue = (name, value) => {
|
|
1916
|
+
setPersistedServerVariableValues(Object.assign({}, serverVariables, { [name]: value }));
|
|
1917
|
+
};
|
|
1918
|
+
return { serverVariables, updateServerVariableValue };
|
|
1919
|
+
};
|
|
1920
|
+
|
|
1886
1921
|
const ServersDropdown = ({ servers }) => {
|
|
1887
1922
|
const [chosenServer, setChosenServer] = useAtom(chosenServerAtom);
|
|
1923
|
+
const { serverVariables } = useServerVariables();
|
|
1888
1924
|
const serverItems = [
|
|
1889
1925
|
{
|
|
1890
1926
|
type: 'option_group',
|
|
@@ -1898,7 +1934,7 @@ const ServersDropdown = ({ servers }) => {
|
|
|
1898
1934
|
...servers.map((server, i) => ({
|
|
1899
1935
|
id: server.url,
|
|
1900
1936
|
title: server.description,
|
|
1901
|
-
description: server
|
|
1937
|
+
description: getServerUrlWithVariableValues(server, serverVariables),
|
|
1902
1938
|
value: server.url,
|
|
1903
1939
|
})),
|
|
1904
1940
|
],
|
|
@@ -1908,6 +1944,21 @@ const ServersDropdown = ({ servers }) => {
|
|
|
1908
1944
|
};
|
|
1909
1945
|
ServersDropdown.displayName = 'ServersDropdown';
|
|
1910
1946
|
|
|
1947
|
+
const VariableEditor = ({ variable, value, onChange }) => {
|
|
1948
|
+
const inputId = useUniqueId(`id_${variable.name}_`);
|
|
1949
|
+
return (React.createElement(React.Fragment, null,
|
|
1950
|
+
React.createElement(Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, variable.name),
|
|
1951
|
+
React.createElement(Text, { mx: 3 }, ":"),
|
|
1952
|
+
React.createElement("div", null, variable.enum ? (React.createElement(Select, { flex: 1, "aria-label": variable.name, options: variable.enum.map(s => ({ value: s })), value: value || variable.default, onChange: onChange })) : (React.createElement(Flex, { flex: 1 },
|
|
1953
|
+
React.createElement(Input, { id: inputId, "aria-label": variable.name, appearance: 'minimal', flex: 1, placeholder: variable.default, type: "text", required: true, intent: 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }))))));
|
|
1954
|
+
};
|
|
1955
|
+
|
|
1956
|
+
const ServerVariables = ({ variables, values, onChangeValue }) => {
|
|
1957
|
+
return (React.createElement(Panel, { defaultIsOpen: true },
|
|
1958
|
+
React.createElement(Panel.Titlebar, null, "Server Variables"),
|
|
1959
|
+
React.createElement(Panel.Content, { className: "sl-overflow-y-auto ParameterGrid ServerVariablesContent" }, variables.map(variable => (React.createElement(VariableEditor, { key: variable.name, variable: variable, value: values[variable.name], onChange: (value) => onChangeValue(variable.name, String(value)) }))))));
|
|
1960
|
+
};
|
|
1961
|
+
|
|
1911
1962
|
const defaultServers = [];
|
|
1912
1963
|
const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embeddedInMd = false, tryItCredentialsPolicy, corsProxy, }) => {
|
|
1913
1964
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
@@ -1924,11 +1975,12 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1924
1975
|
const [textRequestBody, setTextRequestBody] = useTextRequestBodyState(mediaTypeContent);
|
|
1925
1976
|
const [operationAuthValue, setOperationAuthValue] = usePersistedSecuritySchemeWithValues();
|
|
1926
1977
|
const servers = React.useMemo(() => {
|
|
1927
|
-
|
|
1928
|
-
return toDisplay;
|
|
1978
|
+
return getServersToDisplay(httpOperation.servers || defaultServers, mockUrl, false);
|
|
1929
1979
|
}, [httpOperation.servers, mockUrl]);
|
|
1930
1980
|
const firstServer = servers[0] || null;
|
|
1931
1981
|
const [chosenServer, setChosenServer] = useAtom(chosenServerAtom);
|
|
1982
|
+
const serverVariables = getServerVariables(chosenServer);
|
|
1983
|
+
const { serverVariables: serverVariableValues, updateServerVariableValue } = useServerVariables();
|
|
1932
1984
|
const isMockingEnabled = mockUrl && (chosenServer === null || chosenServer === void 0 ? void 0 : chosenServer.url) === mockUrl;
|
|
1933
1985
|
const hasRequiredButEmptyParameters = allParameters.some(parameter => parameter.required && !parameterValuesWithDefaults[parameter.name]);
|
|
1934
1986
|
const getValues = () => Object.keys(bodyParameterValues)
|
|
@@ -1950,7 +2002,8 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1950
2002
|
React.useEffect(() => {
|
|
1951
2003
|
let isMounted = true;
|
|
1952
2004
|
if (onRequestChange || embeddedInMd) {
|
|
1953
|
-
buildHarRequest(Object.assign(Object.assign({ mediaTypeContent, parameterValues: parameterValuesWithDefaults,
|
|
2005
|
+
buildHarRequest(Object.assign(Object.assign({ mediaTypeContent, parameterValues: parameterValuesWithDefaults, serverVariableValues,
|
|
2006
|
+
httpOperation, bodyInput: formDataState.isFormDataBody ? getValues() : textRequestBody, auth: operationAuthValue }, (isMockingEnabled && { mockData: getMockData(mockUrl, httpOperation, mockingOptions) })), { chosenServer,
|
|
1954
2007
|
corsProxy })).then(request => {
|
|
1955
2008
|
if (isMounted) {
|
|
1956
2009
|
if (onRequestChange) {
|
|
@@ -1970,6 +2023,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1970
2023
|
parameterValuesWithDefaults,
|
|
1971
2024
|
formDataState.isFormDataBody,
|
|
1972
2025
|
bodyParameterValues,
|
|
2026
|
+
serverVariableValues,
|
|
1973
2027
|
isAllowedEmptyValues,
|
|
1974
2028
|
textRequestBody,
|
|
1975
2029
|
operationAuthValue,
|
|
@@ -1987,6 +2041,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1987
2041
|
const mockData = isMockingEnabled ? getMockData(mockUrl, httpOperation, mockingOptions) : undefined;
|
|
1988
2042
|
const request = yield buildFetchRequest({
|
|
1989
2043
|
parameterValues: parameterValuesWithDefaults,
|
|
2044
|
+
serverVariableValues,
|
|
1990
2045
|
httpOperation,
|
|
1991
2046
|
mediaTypeContent,
|
|
1992
2047
|
bodyInput: formDataState.isFormDataBody ? getValues() : textRequestBody,
|
|
@@ -2027,6 +2082,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
2027
2082
|
const isOnlySendButton = !((_d = httpOperation.security) === null || _d === void 0 ? void 0 : _d.length) && !allParameters.length && !formDataState.isFormDataBody && !mediaTypeContent;
|
|
2028
2083
|
const tryItPanelContents = (React.createElement(React.Fragment, null,
|
|
2029
2084
|
((_e = httpOperation.security) === null || _e === void 0 ? void 0 : _e.length) ? (React.createElement(TryItAuth, { onChange: setOperationAuthValue, operationSecurityScheme: (_f = httpOperation.security) !== null && _f !== void 0 ? _f : [], value: operationAuthValue })) : null,
|
|
2085
|
+
serverVariables.length > 0 && (React.createElement(ServerVariables, { variables: serverVariables, values: serverVariableValues, onChangeValue: updateServerVariableValue })),
|
|
2030
2086
|
allParameters.length > 0 && (React.createElement(OperationParameters, { parameters: allParameters, values: parameterValuesWithDefaults, onChangeValue: updateParameterValue, validate: validateParameters })),
|
|
2031
2087
|
formDataState.isFormDataBody ? (React.createElement(FormDataBody, { specification: formDataState.bodySpecification, values: bodyParameterValues, onChangeValues: setBodyParameterValues, onChangeParameterAllow: setAllowedEmptyValues, isAllowedEmptyValues: isAllowedEmptyValues })) : mediaTypeContent ? (React.createElement(RequestBody, { examples: (_g = mediaTypeContent.examples) !== null && _g !== void 0 ? _g : [], requestBody: textRequestBody, onChange: setTextRequestBody })) : null,
|
|
2032
2088
|
React.createElement(Panel.Content, { className: "SendButtonHolder", mt: 4, pt: !isOnlySendButton && !embeddedInMd ? 0 : undefined },
|
|
@@ -2538,7 +2594,8 @@ const ServerInfo = ({ servers, mockUrl }) => {
|
|
|
2538
2594
|
const mocking = React.useContext(MockingContext);
|
|
2539
2595
|
const showMocking = !mocking.hideMocking && mockUrl && isProperUrl(mockUrl);
|
|
2540
2596
|
const $mockUrl = showMocking ? mockUrl || mocking.mockUrl : undefined;
|
|
2541
|
-
const serversToDisplay = getServersToDisplay(servers, $mockUrl);
|
|
2597
|
+
const serversToDisplay = React.useMemo(() => getServersToDisplay(servers, $mockUrl, false), [servers, $mockUrl]);
|
|
2598
|
+
const firstServerVariableIndex = React.useMemo(() => serversToDisplay.findIndex(server => !isEmpty(server.variables)), [serversToDisplay]);
|
|
2542
2599
|
if (!showMocking && serversToDisplay.length === 0) {
|
|
2543
2600
|
return null;
|
|
2544
2601
|
}
|
|
@@ -2546,25 +2603,88 @@ const ServerInfo = ({ servers, mockUrl }) => {
|
|
|
2546
2603
|
React.createElement(Panel, { rounded: true, isCollapsible: false, className: "BaseURLContent", w: "full" },
|
|
2547
2604
|
React.createElement(Panel.Titlebar, { whitespace: "nowrap" }, "API Base URL"),
|
|
2548
2605
|
React.createElement(Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
|
|
2549
|
-
React.createElement(VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React.createElement(ServerUrl, Object.assign({}, server, {
|
|
2606
|
+
React.createElement(VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React.createElement(ServerUrl, Object.assign({}, server, { defaultIsOpen: index === firstServerVariableIndex, hasAnyServerVariables: firstServerVariableIndex !== -1, key: server.id })))))))));
|
|
2550
2607
|
};
|
|
2551
|
-
const ServerUrl = ({ id, description, url }) => {
|
|
2608
|
+
const ServerUrl = ({ id, description, url, variables, hasAnyServerVariables, defaultIsOpen, }) => {
|
|
2552
2609
|
const { nodeHasChanged } = useOptionsCtx();
|
|
2553
2610
|
const { onCopy, hasCopied } = useClipboard(url);
|
|
2611
|
+
const urlFragments = useSplitUrl(url);
|
|
2554
2612
|
const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id });
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
}
|
|
2613
|
+
const variablesSchema = useVariablesJSONSchema(variables);
|
|
2614
|
+
const titlePaddingLeft = hasAnyServerVariables && !variablesSchema ? 4 : 0;
|
|
2615
|
+
const handleCopyClick = React.useCallback(e => {
|
|
2616
|
+
e.stopPropagation();
|
|
2617
|
+
onCopy();
|
|
2618
|
+
}, [onCopy]);
|
|
2619
|
+
return (React.createElement(Panel, { isCollapsible: !!variablesSchema, defaultIsOpen: defaultIsOpen, w: "full" },
|
|
2620
|
+
React.createElement(Panel.Titlebar, { whitespace: "nowrap" },
|
|
2621
|
+
React.createElement(Text, { pl: titlePaddingLeft, pr: 2, fontWeight: "bold" },
|
|
2622
|
+
description,
|
|
2623
|
+
":"),
|
|
2624
|
+
React.createElement(Tooltip, { placement: "right", renderTrigger: () => (React.createElement(Text, { "aria-label": description }, urlFragments.map(({ kind, value }, i) => (React.createElement(Text, { key: i, fontWeight: kind === 'variable' ? 'semibold' : 'normal' }, value))))) },
|
|
2625
|
+
!hasCopied && (React.createElement(Box, { p: 1, onClick: handleCopyClick, cursor: "pointer" },
|
|
2626
|
+
"Copy Server URL ",
|
|
2627
|
+
React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'copy'] }))),
|
|
2628
|
+
hasCopied && (React.createElement(Box, { p: 1 },
|
|
2629
|
+
"Copied Server URL ",
|
|
2630
|
+
React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'check'] })))),
|
|
2631
|
+
React.createElement(NodeAnnotation, { change: hasChanged, additionalLeftOffset: 16 })),
|
|
2632
|
+
variablesSchema && (React.createElement(Panel.Content, { w: "full" },
|
|
2633
|
+
React.createElement(Box, { pl: 4 },
|
|
2634
|
+
React.createElement(JsonSchemaViewer, { schema: variablesSchema }))))));
|
|
2635
|
+
};
|
|
2636
|
+
function useVariablesJSONSchema(variables) {
|
|
2637
|
+
return React.useMemo(() => {
|
|
2638
|
+
if (isEmpty(variables))
|
|
2639
|
+
return;
|
|
2640
|
+
const propertiesPairs = Object.entries(variables).map(([name, variable]) => [
|
|
2641
|
+
name,
|
|
2642
|
+
Object.assign({ type: 'string' }, omitBy({
|
|
2643
|
+
description: variable.description,
|
|
2644
|
+
enum: variable.enum,
|
|
2645
|
+
default: variable.default,
|
|
2646
|
+
}, isNil)),
|
|
2647
|
+
]);
|
|
2648
|
+
return {
|
|
2649
|
+
type: 'object',
|
|
2650
|
+
properties: Object.fromEntries(propertiesPairs),
|
|
2651
|
+
};
|
|
2652
|
+
}, [variables]);
|
|
2653
|
+
}
|
|
2654
|
+
function useSplitUrl(url) {
|
|
2655
|
+
return React.useMemo(() => {
|
|
2656
|
+
const curly = /[{}]/g;
|
|
2657
|
+
const fragments = [];
|
|
2658
|
+
let startOffset = 0;
|
|
2659
|
+
let curPos = 0;
|
|
2660
|
+
let match;
|
|
2661
|
+
while ((match = curly.exec(url))) {
|
|
2662
|
+
if (match[0] === '{' || startOffset + 1 === match.index) {
|
|
2663
|
+
startOffset = match.index;
|
|
2664
|
+
continue;
|
|
2665
|
+
}
|
|
2666
|
+
if (startOffset !== curPos) {
|
|
2667
|
+
fragments.push({
|
|
2668
|
+
kind: 'static',
|
|
2669
|
+
value: url.slice(curPos, startOffset),
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
const variable = url.slice(startOffset, match.index + 1);
|
|
2673
|
+
fragments.push({
|
|
2674
|
+
kind: 'variable',
|
|
2675
|
+
value: variable,
|
|
2676
|
+
});
|
|
2677
|
+
curPos = startOffset + variable.length;
|
|
2678
|
+
}
|
|
2679
|
+
if (curPos < url.length) {
|
|
2680
|
+
fragments.push({
|
|
2681
|
+
kind: 'static',
|
|
2682
|
+
value: url.slice(curPos),
|
|
2683
|
+
});
|
|
2684
|
+
}
|
|
2685
|
+
return fragments;
|
|
2686
|
+
}, [url]);
|
|
2687
|
+
}
|
|
2568
2688
|
|
|
2569
2689
|
const HttpServiceComponent = React.memo(({ data: unresolvedData, location = {}, layoutOptions, exportProps }) => {
|
|
2570
2690
|
var _a, _b, _c, _d;
|
package/index.js
CHANGED
|
@@ -43,6 +43,9 @@ var entries = require('lodash/entries.js');
|
|
|
43
43
|
var keys = require('lodash/keys.js');
|
|
44
44
|
var jsonSchemaViewer = require('@stoplight/json-schema-viewer');
|
|
45
45
|
var sortBy = require('lodash/sortBy.js');
|
|
46
|
+
var isEmpty = require('lodash/isEmpty.js');
|
|
47
|
+
var isNil = require('lodash/isNil.js');
|
|
48
|
+
var omitBy = require('lodash/omitBy.js');
|
|
46
49
|
var reactRouterHashLink = require('react-router-hash-link');
|
|
47
50
|
var reactQuery = require('react-query');
|
|
48
51
|
var $RefParser = require('@stoplight/json-schema-ref-parser');
|
|
@@ -96,6 +99,9 @@ var formatXml__default = /*#__PURE__*/_interopDefaultLegacy(formatXml);
|
|
|
96
99
|
var entries__default = /*#__PURE__*/_interopDefaultLegacy(entries);
|
|
97
100
|
var keys__default = /*#__PURE__*/_interopDefaultLegacy(keys);
|
|
98
101
|
var sortBy__default = /*#__PURE__*/_interopDefaultLegacy(sortBy);
|
|
102
|
+
var isEmpty__default = /*#__PURE__*/_interopDefaultLegacy(isEmpty);
|
|
103
|
+
var isNil__default = /*#__PURE__*/_interopDefaultLegacy(isNil);
|
|
104
|
+
var omitBy__default = /*#__PURE__*/_interopDefaultLegacy(omitBy);
|
|
99
105
|
var $RefParser__default = /*#__PURE__*/_interopDefaultLegacy($RefParser);
|
|
100
106
|
var PropTypes__namespace = /*#__PURE__*/_interopNamespace(PropTypes);
|
|
101
107
|
var isEqual__default = /*#__PURE__*/_interopDefaultLegacy(isEqual);
|
|
@@ -539,15 +545,17 @@ function useChosenServerUrl(chosenServerUrl) {
|
|
|
539
545
|
const chosenServerAtom = jotai.atom(undefined);
|
|
540
546
|
|
|
541
547
|
function isValidServer(server) {
|
|
542
|
-
return server.url !== null
|
|
548
|
+
return server.url !== null;
|
|
543
549
|
}
|
|
544
|
-
const getServersToDisplay = (originalServers, mockUrl) => {
|
|
545
|
-
const servers = originalServers
|
|
546
|
-
.map((server, i) => {
|
|
550
|
+
const getServersToDisplay = (originalServers, mockUrl, inlineDefaults) => {
|
|
551
|
+
const servers = originalServers.map((server, i) => {
|
|
547
552
|
const fallbackDescription = originalServers.length === 1 ? 'Live Server' : `Server ${i + 1}`;
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
553
|
+
let url = server.url;
|
|
554
|
+
if (inlineDefaults) {
|
|
555
|
+
url = getServerUrlWithVariableValues(server, {});
|
|
556
|
+
}
|
|
557
|
+
return Object.assign(Object.assign({}, server), { url, description: server.description || fallbackDescription });
|
|
558
|
+
});
|
|
551
559
|
if (mockUrl) {
|
|
552
560
|
servers.push({
|
|
553
561
|
id: 'mock',
|
|
@@ -555,27 +563,48 @@ const getServersToDisplay = (originalServers, mockUrl) => {
|
|
|
555
563
|
url: mockUrl,
|
|
556
564
|
});
|
|
557
565
|
}
|
|
558
|
-
return servers;
|
|
566
|
+
return servers.filter(isValidServer);
|
|
559
567
|
};
|
|
560
|
-
const
|
|
568
|
+
const getServerVariables = (server) => {
|
|
561
569
|
var _a;
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
570
|
+
return Object.entries((_a = server === null || server === void 0 ? void 0 : server.variables) !== null && _a !== void 0 ? _a : {}).map(([key, value]) => ({
|
|
571
|
+
name: key,
|
|
572
|
+
default: value.default,
|
|
573
|
+
description: value.description,
|
|
574
|
+
enum: value.enum,
|
|
575
|
+
}));
|
|
576
|
+
};
|
|
577
|
+
function resolveUrl(urlString) {
|
|
578
|
+
if (urlString === null)
|
|
579
|
+
return null;
|
|
567
580
|
let url;
|
|
568
581
|
try {
|
|
569
582
|
url = URI__default["default"](urlString);
|
|
570
583
|
}
|
|
571
|
-
catch (
|
|
584
|
+
catch (_a) {
|
|
572
585
|
return null;
|
|
573
586
|
}
|
|
587
|
+
let stringifiedUrl;
|
|
574
588
|
if (url.is('relative') && typeof window !== 'undefined') {
|
|
575
|
-
|
|
589
|
+
stringifiedUrl = url.absoluteTo(window.location.origin).toString();
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
stringifiedUrl = url.toString();
|
|
576
593
|
}
|
|
577
|
-
|
|
578
|
-
|
|
594
|
+
if (isProperUrl(stringifiedUrl)) {
|
|
595
|
+
return stringifiedUrl.endsWith('/') ? stringifiedUrl.slice(0, -1) : stringifiedUrl;
|
|
596
|
+
}
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
const getServerUrlWithVariableValues = (server, values) => {
|
|
600
|
+
var _a;
|
|
601
|
+
let urlString = server.url;
|
|
602
|
+
const variables = Object.entries((_a = server.variables) !== null && _a !== void 0 ? _a : {});
|
|
603
|
+
variables.forEach(([variableName, variableInfo]) => {
|
|
604
|
+
var _a;
|
|
605
|
+
urlString = urlString.replaceAll(`{${variableName}}`, (_a = values[variableName]) !== null && _a !== void 0 ? _a : variableInfo.default);
|
|
606
|
+
});
|
|
607
|
+
return urlString;
|
|
579
608
|
};
|
|
580
609
|
|
|
581
610
|
const persistAtom = (key, atomInstance) => {
|
|
@@ -1454,11 +1483,11 @@ const useTextRequestBodyState = (mediaTypeContent) => {
|
|
|
1454
1483
|
};
|
|
1455
1484
|
|
|
1456
1485
|
const nameAndValueObjectToPair = ({ name, value }) => [name, value];
|
|
1457
|
-
const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, }) => {
|
|
1486
|
+
const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, serverVariableValues, }) => {
|
|
1458
1487
|
var _a;
|
|
1459
1488
|
const server = chosenServer || ((_a = httpOperation.servers) === null || _a === void 0 ? void 0 : _a[0]);
|
|
1460
|
-
const chosenServerUrl = server &&
|
|
1461
|
-
const serverUrl = (mockData === null || mockData === void 0 ? void 0 : mockData.url) || chosenServerUrl || window.location.origin;
|
|
1489
|
+
const chosenServerUrl = server && getServerUrlWithVariableValues(server, serverVariableValues);
|
|
1490
|
+
const serverUrl = resolveUrl((mockData === null || mockData === void 0 ? void 0 : mockData.url) || chosenServerUrl || window.location.origin);
|
|
1462
1491
|
if (corsProxy && !mockData) {
|
|
1463
1492
|
return `${corsProxy}${serverUrl}`;
|
|
1464
1493
|
}
|
|
@@ -1535,10 +1564,10 @@ const getQueryParams = ({ httpOperation, parameterValues, }) => {
|
|
|
1535
1564
|
return acc;
|
|
1536
1565
|
}, []);
|
|
1537
1566
|
};
|
|
1538
|
-
function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
|
|
1567
|
+
function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, serverVariableValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
|
|
1539
1568
|
var _a, _b, _c;
|
|
1540
1569
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1541
|
-
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
|
|
1570
|
+
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy, serverVariableValues });
|
|
1542
1571
|
const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase()) && bodyInput !== undefined;
|
|
1543
1572
|
const queryParams = getQueryParams({ httpOperation, parameterValues });
|
|
1544
1573
|
const rawHeaders = filterOutAuthorizationParams((_b = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : [], httpOperation.security)
|
|
@@ -1611,10 +1640,10 @@ const runAuthRequestEhancements = (auth, queryParams, headers) => {
|
|
|
1611
1640
|
}
|
|
1612
1641
|
return [newQueryParams, newHeaders];
|
|
1613
1642
|
};
|
|
1614
|
-
function buildHarRequest({ httpOperation, bodyInput, parameterValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
|
|
1643
|
+
function buildHarRequest({ httpOperation, bodyInput, parameterValues, serverVariableValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
|
|
1615
1644
|
var _a, _b, _c, _d;
|
|
1616
1645
|
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
1617
|
-
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
|
|
1646
|
+
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy, serverVariableValues });
|
|
1618
1647
|
const mimeType = (_a = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _a !== void 0 ? _a : 'application/json';
|
|
1619
1648
|
const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase()) && bodyInput !== undefined;
|
|
1620
1649
|
const queryParams = getQueryParams({ httpOperation, parameterValues });
|
|
@@ -1936,8 +1965,18 @@ class NetworkError extends Error {
|
|
|
1936
1965
|
}
|
|
1937
1966
|
const isNetworkError = (error) => error instanceof NetworkError;
|
|
1938
1967
|
|
|
1968
|
+
const persistedServerVariableValuesAtom = jotai.atom({});
|
|
1969
|
+
const useServerVariables = () => {
|
|
1970
|
+
const [serverVariables, setPersistedServerVariableValues] = jotai.useAtom(persistedServerVariableValuesAtom);
|
|
1971
|
+
const updateServerVariableValue = (name, value) => {
|
|
1972
|
+
setPersistedServerVariableValues(Object.assign({}, serverVariables, { [name]: value }));
|
|
1973
|
+
};
|
|
1974
|
+
return { serverVariables, updateServerVariableValue };
|
|
1975
|
+
};
|
|
1976
|
+
|
|
1939
1977
|
const ServersDropdown = ({ servers }) => {
|
|
1940
1978
|
const [chosenServer, setChosenServer] = jotai.useAtom(chosenServerAtom);
|
|
1979
|
+
const { serverVariables } = useServerVariables();
|
|
1941
1980
|
const serverItems = [
|
|
1942
1981
|
{
|
|
1943
1982
|
type: 'option_group',
|
|
@@ -1951,7 +1990,7 @@ const ServersDropdown = ({ servers }) => {
|
|
|
1951
1990
|
...servers.map((server, i) => ({
|
|
1952
1991
|
id: server.url,
|
|
1953
1992
|
title: server.description,
|
|
1954
|
-
description: server
|
|
1993
|
+
description: getServerUrlWithVariableValues(server, serverVariables),
|
|
1955
1994
|
value: server.url,
|
|
1956
1995
|
})),
|
|
1957
1996
|
],
|
|
@@ -1961,6 +2000,21 @@ const ServersDropdown = ({ servers }) => {
|
|
|
1961
2000
|
};
|
|
1962
2001
|
ServersDropdown.displayName = 'ServersDropdown';
|
|
1963
2002
|
|
|
2003
|
+
const VariableEditor = ({ variable, value, onChange }) => {
|
|
2004
|
+
const inputId = useUniqueId(`id_${variable.name}_`);
|
|
2005
|
+
return (React__namespace.createElement(React__namespace.Fragment, null,
|
|
2006
|
+
React__namespace.createElement(mosaic.Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, variable.name),
|
|
2007
|
+
React__namespace.createElement(mosaic.Text, { mx: 3 }, ":"),
|
|
2008
|
+
React__namespace.createElement("div", null, variable.enum ? (React__namespace.createElement(mosaic.Select, { flex: 1, "aria-label": variable.name, options: variable.enum.map(s => ({ value: s })), value: value || variable.default, onChange: onChange })) : (React__namespace.createElement(mosaic.Flex, { flex: 1 },
|
|
2009
|
+
React__namespace.createElement(mosaic.Input, { id: inputId, "aria-label": variable.name, appearance: 'minimal', flex: 1, placeholder: variable.default, type: "text", required: true, intent: 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }))))));
|
|
2010
|
+
};
|
|
2011
|
+
|
|
2012
|
+
const ServerVariables = ({ variables, values, onChangeValue }) => {
|
|
2013
|
+
return (React__namespace.createElement(mosaic.Panel, { defaultIsOpen: true },
|
|
2014
|
+
React__namespace.createElement(mosaic.Panel.Titlebar, null, "Server Variables"),
|
|
2015
|
+
React__namespace.createElement(mosaic.Panel.Content, { className: "sl-overflow-y-auto ParameterGrid ServerVariablesContent" }, variables.map(variable => (React__namespace.createElement(VariableEditor, { key: variable.name, variable: variable, value: values[variable.name], onChange: (value) => onChangeValue(variable.name, String(value)) }))))));
|
|
2016
|
+
};
|
|
2017
|
+
|
|
1964
2018
|
const defaultServers = [];
|
|
1965
2019
|
const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embeddedInMd = false, tryItCredentialsPolicy, corsProxy, }) => {
|
|
1966
2020
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
@@ -1977,11 +2031,12 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1977
2031
|
const [textRequestBody, setTextRequestBody] = useTextRequestBodyState(mediaTypeContent);
|
|
1978
2032
|
const [operationAuthValue, setOperationAuthValue] = usePersistedSecuritySchemeWithValues();
|
|
1979
2033
|
const servers = React__namespace.useMemo(() => {
|
|
1980
|
-
|
|
1981
|
-
return toDisplay;
|
|
2034
|
+
return getServersToDisplay(httpOperation.servers || defaultServers, mockUrl, false);
|
|
1982
2035
|
}, [httpOperation.servers, mockUrl]);
|
|
1983
2036
|
const firstServer = servers[0] || null;
|
|
1984
2037
|
const [chosenServer, setChosenServer] = jotai.useAtom(chosenServerAtom);
|
|
2038
|
+
const serverVariables = getServerVariables(chosenServer);
|
|
2039
|
+
const { serverVariables: serverVariableValues, updateServerVariableValue } = useServerVariables();
|
|
1985
2040
|
const isMockingEnabled = mockUrl && (chosenServer === null || chosenServer === void 0 ? void 0 : chosenServer.url) === mockUrl;
|
|
1986
2041
|
const hasRequiredButEmptyParameters = allParameters.some(parameter => parameter.required && !parameterValuesWithDefaults[parameter.name]);
|
|
1987
2042
|
const getValues = () => Object.keys(bodyParameterValues)
|
|
@@ -2003,7 +2058,8 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
2003
2058
|
React__namespace.useEffect(() => {
|
|
2004
2059
|
let isMounted = true;
|
|
2005
2060
|
if (onRequestChange || embeddedInMd) {
|
|
2006
|
-
buildHarRequest(Object.assign(Object.assign({ mediaTypeContent, parameterValues: parameterValuesWithDefaults,
|
|
2061
|
+
buildHarRequest(Object.assign(Object.assign({ mediaTypeContent, parameterValues: parameterValuesWithDefaults, serverVariableValues,
|
|
2062
|
+
httpOperation, bodyInput: formDataState.isFormDataBody ? getValues() : textRequestBody, auth: operationAuthValue }, (isMockingEnabled && { mockData: getMockData(mockUrl, httpOperation, mockingOptions) })), { chosenServer,
|
|
2007
2063
|
corsProxy })).then(request => {
|
|
2008
2064
|
if (isMounted) {
|
|
2009
2065
|
if (onRequestChange) {
|
|
@@ -2023,6 +2079,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
2023
2079
|
parameterValuesWithDefaults,
|
|
2024
2080
|
formDataState.isFormDataBody,
|
|
2025
2081
|
bodyParameterValues,
|
|
2082
|
+
serverVariableValues,
|
|
2026
2083
|
isAllowedEmptyValues,
|
|
2027
2084
|
textRequestBody,
|
|
2028
2085
|
operationAuthValue,
|
|
@@ -2040,6 +2097,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
2040
2097
|
const mockData = isMockingEnabled ? getMockData(mockUrl, httpOperation, mockingOptions) : undefined;
|
|
2041
2098
|
const request = yield buildFetchRequest({
|
|
2042
2099
|
parameterValues: parameterValuesWithDefaults,
|
|
2100
|
+
serverVariableValues,
|
|
2043
2101
|
httpOperation,
|
|
2044
2102
|
mediaTypeContent,
|
|
2045
2103
|
bodyInput: formDataState.isFormDataBody ? getValues() : textRequestBody,
|
|
@@ -2080,6 +2138,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
2080
2138
|
const isOnlySendButton = !((_d = httpOperation.security) === null || _d === void 0 ? void 0 : _d.length) && !allParameters.length && !formDataState.isFormDataBody && !mediaTypeContent;
|
|
2081
2139
|
const tryItPanelContents = (React__namespace.createElement(React__namespace.Fragment, null,
|
|
2082
2140
|
((_e = httpOperation.security) === null || _e === void 0 ? void 0 : _e.length) ? (React__namespace.createElement(TryItAuth, { onChange: setOperationAuthValue, operationSecurityScheme: (_f = httpOperation.security) !== null && _f !== void 0 ? _f : [], value: operationAuthValue })) : null,
|
|
2141
|
+
serverVariables.length > 0 && (React__namespace.createElement(ServerVariables, { variables: serverVariables, values: serverVariableValues, onChangeValue: updateServerVariableValue })),
|
|
2083
2142
|
allParameters.length > 0 && (React__namespace.createElement(OperationParameters, { parameters: allParameters, values: parameterValuesWithDefaults, onChangeValue: updateParameterValue, validate: validateParameters })),
|
|
2084
2143
|
formDataState.isFormDataBody ? (React__namespace.createElement(FormDataBody, { specification: formDataState.bodySpecification, values: bodyParameterValues, onChangeValues: setBodyParameterValues, onChangeParameterAllow: setAllowedEmptyValues, isAllowedEmptyValues: isAllowedEmptyValues })) : mediaTypeContent ? (React__namespace.createElement(RequestBody, { examples: (_g = mediaTypeContent.examples) !== null && _g !== void 0 ? _g : [], requestBody: textRequestBody, onChange: setTextRequestBody })) : null,
|
|
2085
2144
|
React__namespace.createElement(mosaic.Panel.Content, { className: "SendButtonHolder", mt: 4, pt: !isOnlySendButton && !embeddedInMd ? 0 : undefined },
|
|
@@ -2591,7 +2650,8 @@ const ServerInfo = ({ servers, mockUrl }) => {
|
|
|
2591
2650
|
const mocking = React__namespace.useContext(MockingContext);
|
|
2592
2651
|
const showMocking = !mocking.hideMocking && mockUrl && isProperUrl(mockUrl);
|
|
2593
2652
|
const $mockUrl = showMocking ? mockUrl || mocking.mockUrl : undefined;
|
|
2594
|
-
const serversToDisplay = getServersToDisplay(servers, $mockUrl);
|
|
2653
|
+
const serversToDisplay = React__namespace.useMemo(() => getServersToDisplay(servers, $mockUrl, false), [servers, $mockUrl]);
|
|
2654
|
+
const firstServerVariableIndex = React__namespace.useMemo(() => serversToDisplay.findIndex(server => !isEmpty__default["default"](server.variables)), [serversToDisplay]);
|
|
2595
2655
|
if (!showMocking && serversToDisplay.length === 0) {
|
|
2596
2656
|
return null;
|
|
2597
2657
|
}
|
|
@@ -2599,25 +2659,88 @@ const ServerInfo = ({ servers, mockUrl }) => {
|
|
|
2599
2659
|
React__namespace.createElement(mosaic.Panel, { rounded: true, isCollapsible: false, className: "BaseURLContent", w: "full" },
|
|
2600
2660
|
React__namespace.createElement(mosaic.Panel.Titlebar, { whitespace: "nowrap" }, "API Base URL"),
|
|
2601
2661
|
React__namespace.createElement(mosaic.Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
|
|
2602
|
-
React__namespace.createElement(mosaic.VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React__namespace.createElement(ServerUrl, Object.assign({}, server, {
|
|
2662
|
+
React__namespace.createElement(mosaic.VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React__namespace.createElement(ServerUrl, Object.assign({}, server, { defaultIsOpen: index === firstServerVariableIndex, hasAnyServerVariables: firstServerVariableIndex !== -1, key: server.id })))))))));
|
|
2603
2663
|
};
|
|
2604
|
-
const ServerUrl = ({ id, description, url }) => {
|
|
2664
|
+
const ServerUrl = ({ id, description, url, variables, hasAnyServerVariables, defaultIsOpen, }) => {
|
|
2605
2665
|
const { nodeHasChanged } = useOptionsCtx();
|
|
2606
2666
|
const { onCopy, hasCopied } = mosaic.useClipboard(url);
|
|
2667
|
+
const urlFragments = useSplitUrl(url);
|
|
2607
2668
|
const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id });
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
}
|
|
2669
|
+
const variablesSchema = useVariablesJSONSchema(variables);
|
|
2670
|
+
const titlePaddingLeft = hasAnyServerVariables && !variablesSchema ? 4 : 0;
|
|
2671
|
+
const handleCopyClick = React__namespace.useCallback(e => {
|
|
2672
|
+
e.stopPropagation();
|
|
2673
|
+
onCopy();
|
|
2674
|
+
}, [onCopy]);
|
|
2675
|
+
return (React__namespace.createElement(mosaic.Panel, { isCollapsible: !!variablesSchema, defaultIsOpen: defaultIsOpen, w: "full" },
|
|
2676
|
+
React__namespace.createElement(mosaic.Panel.Titlebar, { whitespace: "nowrap" },
|
|
2677
|
+
React__namespace.createElement(mosaic.Text, { pl: titlePaddingLeft, pr: 2, fontWeight: "bold" },
|
|
2678
|
+
description,
|
|
2679
|
+
":"),
|
|
2680
|
+
React__namespace.createElement(mosaic.Tooltip, { placement: "right", renderTrigger: () => (React__namespace.createElement(mosaic.Text, { "aria-label": description }, urlFragments.map(({ kind, value }, i) => (React__namespace.createElement(mosaic.Text, { key: i, fontWeight: kind === 'variable' ? 'semibold' : 'normal' }, value))))) },
|
|
2681
|
+
!hasCopied && (React__namespace.createElement(mosaic.Box, { p: 1, onClick: handleCopyClick, cursor: "pointer" },
|
|
2682
|
+
"Copy Server URL ",
|
|
2683
|
+
React__namespace.createElement(mosaic.Icon, { className: "sl-ml-1", icon: ['fas', 'copy'] }))),
|
|
2684
|
+
hasCopied && (React__namespace.createElement(mosaic.Box, { p: 1 },
|
|
2685
|
+
"Copied Server URL ",
|
|
2686
|
+
React__namespace.createElement(mosaic.Icon, { className: "sl-ml-1", icon: ['fas', 'check'] })))),
|
|
2687
|
+
React__namespace.createElement(mosaic.NodeAnnotation, { change: hasChanged, additionalLeftOffset: 16 })),
|
|
2688
|
+
variablesSchema && (React__namespace.createElement(mosaic.Panel.Content, { w: "full" },
|
|
2689
|
+
React__namespace.createElement(mosaic.Box, { pl: 4 },
|
|
2690
|
+
React__namespace.createElement(jsonSchemaViewer.JsonSchemaViewer, { schema: variablesSchema }))))));
|
|
2691
|
+
};
|
|
2692
|
+
function useVariablesJSONSchema(variables) {
|
|
2693
|
+
return React__namespace.useMemo(() => {
|
|
2694
|
+
if (isEmpty__default["default"](variables))
|
|
2695
|
+
return;
|
|
2696
|
+
const propertiesPairs = Object.entries(variables).map(([name, variable]) => [
|
|
2697
|
+
name,
|
|
2698
|
+
Object.assign({ type: 'string' }, omitBy__default["default"]({
|
|
2699
|
+
description: variable.description,
|
|
2700
|
+
enum: variable.enum,
|
|
2701
|
+
default: variable.default,
|
|
2702
|
+
}, isNil__default["default"])),
|
|
2703
|
+
]);
|
|
2704
|
+
return {
|
|
2705
|
+
type: 'object',
|
|
2706
|
+
properties: Object.fromEntries(propertiesPairs),
|
|
2707
|
+
};
|
|
2708
|
+
}, [variables]);
|
|
2709
|
+
}
|
|
2710
|
+
function useSplitUrl(url) {
|
|
2711
|
+
return React__namespace.useMemo(() => {
|
|
2712
|
+
const curly = /[{}]/g;
|
|
2713
|
+
const fragments = [];
|
|
2714
|
+
let startOffset = 0;
|
|
2715
|
+
let curPos = 0;
|
|
2716
|
+
let match;
|
|
2717
|
+
while ((match = curly.exec(url))) {
|
|
2718
|
+
if (match[0] === '{' || startOffset + 1 === match.index) {
|
|
2719
|
+
startOffset = match.index;
|
|
2720
|
+
continue;
|
|
2721
|
+
}
|
|
2722
|
+
if (startOffset !== curPos) {
|
|
2723
|
+
fragments.push({
|
|
2724
|
+
kind: 'static',
|
|
2725
|
+
value: url.slice(curPos, startOffset),
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2728
|
+
const variable = url.slice(startOffset, match.index + 1);
|
|
2729
|
+
fragments.push({
|
|
2730
|
+
kind: 'variable',
|
|
2731
|
+
value: variable,
|
|
2732
|
+
});
|
|
2733
|
+
curPos = startOffset + variable.length;
|
|
2734
|
+
}
|
|
2735
|
+
if (curPos < url.length) {
|
|
2736
|
+
fragments.push({
|
|
2737
|
+
kind: 'static',
|
|
2738
|
+
value: url.slice(curPos),
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
return fragments;
|
|
2742
|
+
}, [url]);
|
|
2743
|
+
}
|
|
2621
2744
|
|
|
2622
2745
|
const HttpServiceComponent = React__namespace.memo(({ data: unresolvedData, location = {}, layoutOptions, exportProps }) => {
|
|
2623
2746
|
var _a, _b, _c, _d;
|
package/index.mjs
CHANGED
|
@@ -41,6 +41,9 @@ import entries from 'lodash/entries.js';
|
|
|
41
41
|
import keys from 'lodash/keys.js';
|
|
42
42
|
import { JsonSchemaViewer } from '@stoplight/json-schema-viewer';
|
|
43
43
|
import sortBy from 'lodash/sortBy.js';
|
|
44
|
+
import isEmpty from 'lodash/isEmpty.js';
|
|
45
|
+
import isNil from 'lodash/isNil.js';
|
|
46
|
+
import omitBy from 'lodash/omitBy.js';
|
|
44
47
|
import { HashLink } from 'react-router-hash-link';
|
|
45
48
|
import { QueryClient, useQueryClient, QueryClientProvider } from 'react-query';
|
|
46
49
|
import $RefParser from '@stoplight/json-schema-ref-parser';
|
|
@@ -486,15 +489,17 @@ function useChosenServerUrl(chosenServerUrl) {
|
|
|
486
489
|
const chosenServerAtom = atom(undefined);
|
|
487
490
|
|
|
488
491
|
function isValidServer(server) {
|
|
489
|
-
return server.url !== null
|
|
492
|
+
return server.url !== null;
|
|
490
493
|
}
|
|
491
|
-
const getServersToDisplay = (originalServers, mockUrl) => {
|
|
492
|
-
const servers = originalServers
|
|
493
|
-
.map((server, i) => {
|
|
494
|
+
const getServersToDisplay = (originalServers, mockUrl, inlineDefaults) => {
|
|
495
|
+
const servers = originalServers.map((server, i) => {
|
|
494
496
|
const fallbackDescription = originalServers.length === 1 ? 'Live Server' : `Server ${i + 1}`;
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
497
|
+
let url = server.url;
|
|
498
|
+
if (inlineDefaults) {
|
|
499
|
+
url = getServerUrlWithVariableValues(server, {});
|
|
500
|
+
}
|
|
501
|
+
return Object.assign(Object.assign({}, server), { url, description: server.description || fallbackDescription });
|
|
502
|
+
});
|
|
498
503
|
if (mockUrl) {
|
|
499
504
|
servers.push({
|
|
500
505
|
id: 'mock',
|
|
@@ -502,27 +507,48 @@ const getServersToDisplay = (originalServers, mockUrl) => {
|
|
|
502
507
|
url: mockUrl,
|
|
503
508
|
});
|
|
504
509
|
}
|
|
505
|
-
return servers;
|
|
510
|
+
return servers.filter(isValidServer);
|
|
506
511
|
};
|
|
507
|
-
const
|
|
512
|
+
const getServerVariables = (server) => {
|
|
508
513
|
var _a;
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
+
return Object.entries((_a = server === null || server === void 0 ? void 0 : server.variables) !== null && _a !== void 0 ? _a : {}).map(([key, value]) => ({
|
|
515
|
+
name: key,
|
|
516
|
+
default: value.default,
|
|
517
|
+
description: value.description,
|
|
518
|
+
enum: value.enum,
|
|
519
|
+
}));
|
|
520
|
+
};
|
|
521
|
+
function resolveUrl(urlString) {
|
|
522
|
+
if (urlString === null)
|
|
523
|
+
return null;
|
|
514
524
|
let url;
|
|
515
525
|
try {
|
|
516
526
|
url = URI(urlString);
|
|
517
527
|
}
|
|
518
|
-
catch (
|
|
528
|
+
catch (_a) {
|
|
519
529
|
return null;
|
|
520
530
|
}
|
|
531
|
+
let stringifiedUrl;
|
|
521
532
|
if (url.is('relative') && typeof window !== 'undefined') {
|
|
522
|
-
|
|
533
|
+
stringifiedUrl = url.absoluteTo(window.location.origin).toString();
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
stringifiedUrl = url.toString();
|
|
523
537
|
}
|
|
524
|
-
|
|
525
|
-
|
|
538
|
+
if (isProperUrl(stringifiedUrl)) {
|
|
539
|
+
return stringifiedUrl.endsWith('/') ? stringifiedUrl.slice(0, -1) : stringifiedUrl;
|
|
540
|
+
}
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
const getServerUrlWithVariableValues = (server, values) => {
|
|
544
|
+
var _a;
|
|
545
|
+
let urlString = server.url;
|
|
546
|
+
const variables = Object.entries((_a = server.variables) !== null && _a !== void 0 ? _a : {});
|
|
547
|
+
variables.forEach(([variableName, variableInfo]) => {
|
|
548
|
+
var _a;
|
|
549
|
+
urlString = urlString.replaceAll(`{${variableName}}`, (_a = values[variableName]) !== null && _a !== void 0 ? _a : variableInfo.default);
|
|
550
|
+
});
|
|
551
|
+
return urlString;
|
|
526
552
|
};
|
|
527
553
|
|
|
528
554
|
const persistAtom = (key, atomInstance) => {
|
|
@@ -1401,11 +1427,11 @@ const useTextRequestBodyState = (mediaTypeContent) => {
|
|
|
1401
1427
|
};
|
|
1402
1428
|
|
|
1403
1429
|
const nameAndValueObjectToPair = ({ name, value }) => [name, value];
|
|
1404
|
-
const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, }) => {
|
|
1430
|
+
const getServerUrl = ({ chosenServer, httpOperation, mockData, corsProxy, serverVariableValues, }) => {
|
|
1405
1431
|
var _a;
|
|
1406
1432
|
const server = chosenServer || ((_a = httpOperation.servers) === null || _a === void 0 ? void 0 : _a[0]);
|
|
1407
|
-
const chosenServerUrl = server &&
|
|
1408
|
-
const serverUrl = (mockData === null || mockData === void 0 ? void 0 : mockData.url) || chosenServerUrl || window.location.origin;
|
|
1433
|
+
const chosenServerUrl = server && getServerUrlWithVariableValues(server, serverVariableValues);
|
|
1434
|
+
const serverUrl = resolveUrl((mockData === null || mockData === void 0 ? void 0 : mockData.url) || chosenServerUrl || window.location.origin);
|
|
1409
1435
|
if (corsProxy && !mockData) {
|
|
1410
1436
|
return `${corsProxy}${serverUrl}`;
|
|
1411
1437
|
}
|
|
@@ -1482,10 +1508,10 @@ const getQueryParams = ({ httpOperation, parameterValues, }) => {
|
|
|
1482
1508
|
return acc;
|
|
1483
1509
|
}, []);
|
|
1484
1510
|
};
|
|
1485
|
-
function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
|
|
1511
|
+
function buildFetchRequest({ httpOperation, mediaTypeContent, bodyInput, parameterValues, serverVariableValues, mockData, auth, chosenServer, credentials = 'omit', corsProxy, }) {
|
|
1486
1512
|
var _a, _b, _c;
|
|
1487
1513
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1488
|
-
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
|
|
1514
|
+
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy, serverVariableValues });
|
|
1489
1515
|
const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase()) && bodyInput !== undefined;
|
|
1490
1516
|
const queryParams = getQueryParams({ httpOperation, parameterValues });
|
|
1491
1517
|
const rawHeaders = filterOutAuthorizationParams((_b = (_a = httpOperation.request) === null || _a === void 0 ? void 0 : _a.headers) !== null && _b !== void 0 ? _b : [], httpOperation.security)
|
|
@@ -1558,10 +1584,10 @@ const runAuthRequestEhancements = (auth, queryParams, headers) => {
|
|
|
1558
1584
|
}
|
|
1559
1585
|
return [newQueryParams, newHeaders];
|
|
1560
1586
|
};
|
|
1561
|
-
function buildHarRequest({ httpOperation, bodyInput, parameterValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
|
|
1587
|
+
function buildHarRequest({ httpOperation, bodyInput, parameterValues, serverVariableValues, mediaTypeContent, auth, mockData, chosenServer, corsProxy, }) {
|
|
1562
1588
|
var _a, _b, _c, _d;
|
|
1563
1589
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1564
|
-
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy });
|
|
1590
|
+
const serverUrl = getServerUrl({ httpOperation, mockData, chosenServer, corsProxy, serverVariableValues });
|
|
1565
1591
|
const mimeType = (_a = mediaTypeContent === null || mediaTypeContent === void 0 ? void 0 : mediaTypeContent.mediaType) !== null && _a !== void 0 ? _a : 'application/json';
|
|
1566
1592
|
const shouldIncludeBody = ['PUT', 'POST', 'PATCH'].includes(httpOperation.method.toUpperCase()) && bodyInput !== undefined;
|
|
1567
1593
|
const queryParams = getQueryParams({ httpOperation, parameterValues });
|
|
@@ -1883,8 +1909,18 @@ class NetworkError extends Error {
|
|
|
1883
1909
|
}
|
|
1884
1910
|
const isNetworkError = (error) => error instanceof NetworkError;
|
|
1885
1911
|
|
|
1912
|
+
const persistedServerVariableValuesAtom = atom({});
|
|
1913
|
+
const useServerVariables = () => {
|
|
1914
|
+
const [serverVariables, setPersistedServerVariableValues] = useAtom(persistedServerVariableValuesAtom);
|
|
1915
|
+
const updateServerVariableValue = (name, value) => {
|
|
1916
|
+
setPersistedServerVariableValues(Object.assign({}, serverVariables, { [name]: value }));
|
|
1917
|
+
};
|
|
1918
|
+
return { serverVariables, updateServerVariableValue };
|
|
1919
|
+
};
|
|
1920
|
+
|
|
1886
1921
|
const ServersDropdown = ({ servers }) => {
|
|
1887
1922
|
const [chosenServer, setChosenServer] = useAtom(chosenServerAtom);
|
|
1923
|
+
const { serverVariables } = useServerVariables();
|
|
1888
1924
|
const serverItems = [
|
|
1889
1925
|
{
|
|
1890
1926
|
type: 'option_group',
|
|
@@ -1898,7 +1934,7 @@ const ServersDropdown = ({ servers }) => {
|
|
|
1898
1934
|
...servers.map((server, i) => ({
|
|
1899
1935
|
id: server.url,
|
|
1900
1936
|
title: server.description,
|
|
1901
|
-
description: server
|
|
1937
|
+
description: getServerUrlWithVariableValues(server, serverVariables),
|
|
1902
1938
|
value: server.url,
|
|
1903
1939
|
})),
|
|
1904
1940
|
],
|
|
@@ -1908,6 +1944,21 @@ const ServersDropdown = ({ servers }) => {
|
|
|
1908
1944
|
};
|
|
1909
1945
|
ServersDropdown.displayName = 'ServersDropdown';
|
|
1910
1946
|
|
|
1947
|
+
const VariableEditor = ({ variable, value, onChange }) => {
|
|
1948
|
+
const inputId = useUniqueId(`id_${variable.name}_`);
|
|
1949
|
+
return (React.createElement(React.Fragment, null,
|
|
1950
|
+
React.createElement(Text, { as: "label", "aria-hidden": "true", "data-testid": "param-label", htmlFor: inputId, fontSize: "base" }, variable.name),
|
|
1951
|
+
React.createElement(Text, { mx: 3 }, ":"),
|
|
1952
|
+
React.createElement("div", null, variable.enum ? (React.createElement(Select, { flex: 1, "aria-label": variable.name, options: variable.enum.map(s => ({ value: s })), value: value || variable.default, onChange: onChange })) : (React.createElement(Flex, { flex: 1 },
|
|
1953
|
+
React.createElement(Input, { id: inputId, "aria-label": variable.name, appearance: 'minimal', flex: 1, placeholder: variable.default, type: "text", required: true, intent: 'default', value: value || '', onChange: e => onChange && onChange(e.currentTarget.value) }))))));
|
|
1954
|
+
};
|
|
1955
|
+
|
|
1956
|
+
const ServerVariables = ({ variables, values, onChangeValue }) => {
|
|
1957
|
+
return (React.createElement(Panel, { defaultIsOpen: true },
|
|
1958
|
+
React.createElement(Panel.Titlebar, null, "Server Variables"),
|
|
1959
|
+
React.createElement(Panel.Content, { className: "sl-overflow-y-auto ParameterGrid ServerVariablesContent" }, variables.map(variable => (React.createElement(VariableEditor, { key: variable.name, variable: variable, value: values[variable.name], onChange: (value) => onChangeValue(variable.name, String(value)) }))))));
|
|
1960
|
+
};
|
|
1961
|
+
|
|
1911
1962
|
const defaultServers = [];
|
|
1912
1963
|
const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embeddedInMd = false, tryItCredentialsPolicy, corsProxy, }) => {
|
|
1913
1964
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
@@ -1924,11 +1975,12 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1924
1975
|
const [textRequestBody, setTextRequestBody] = useTextRequestBodyState(mediaTypeContent);
|
|
1925
1976
|
const [operationAuthValue, setOperationAuthValue] = usePersistedSecuritySchemeWithValues();
|
|
1926
1977
|
const servers = React.useMemo(() => {
|
|
1927
|
-
|
|
1928
|
-
return toDisplay;
|
|
1978
|
+
return getServersToDisplay(httpOperation.servers || defaultServers, mockUrl, false);
|
|
1929
1979
|
}, [httpOperation.servers, mockUrl]);
|
|
1930
1980
|
const firstServer = servers[0] || null;
|
|
1931
1981
|
const [chosenServer, setChosenServer] = useAtom(chosenServerAtom);
|
|
1982
|
+
const serverVariables = getServerVariables(chosenServer);
|
|
1983
|
+
const { serverVariables: serverVariableValues, updateServerVariableValue } = useServerVariables();
|
|
1932
1984
|
const isMockingEnabled = mockUrl && (chosenServer === null || chosenServer === void 0 ? void 0 : chosenServer.url) === mockUrl;
|
|
1933
1985
|
const hasRequiredButEmptyParameters = allParameters.some(parameter => parameter.required && !parameterValuesWithDefaults[parameter.name]);
|
|
1934
1986
|
const getValues = () => Object.keys(bodyParameterValues)
|
|
@@ -1950,7 +2002,8 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1950
2002
|
React.useEffect(() => {
|
|
1951
2003
|
let isMounted = true;
|
|
1952
2004
|
if (onRequestChange || embeddedInMd) {
|
|
1953
|
-
buildHarRequest(Object.assign(Object.assign({ mediaTypeContent, parameterValues: parameterValuesWithDefaults,
|
|
2005
|
+
buildHarRequest(Object.assign(Object.assign({ mediaTypeContent, parameterValues: parameterValuesWithDefaults, serverVariableValues,
|
|
2006
|
+
httpOperation, bodyInput: formDataState.isFormDataBody ? getValues() : textRequestBody, auth: operationAuthValue }, (isMockingEnabled && { mockData: getMockData(mockUrl, httpOperation, mockingOptions) })), { chosenServer,
|
|
1954
2007
|
corsProxy })).then(request => {
|
|
1955
2008
|
if (isMounted) {
|
|
1956
2009
|
if (onRequestChange) {
|
|
@@ -1970,6 +2023,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1970
2023
|
parameterValuesWithDefaults,
|
|
1971
2024
|
formDataState.isFormDataBody,
|
|
1972
2025
|
bodyParameterValues,
|
|
2026
|
+
serverVariableValues,
|
|
1973
2027
|
isAllowedEmptyValues,
|
|
1974
2028
|
textRequestBody,
|
|
1975
2029
|
operationAuthValue,
|
|
@@ -1987,6 +2041,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
1987
2041
|
const mockData = isMockingEnabled ? getMockData(mockUrl, httpOperation, mockingOptions) : undefined;
|
|
1988
2042
|
const request = yield buildFetchRequest({
|
|
1989
2043
|
parameterValues: parameterValuesWithDefaults,
|
|
2044
|
+
serverVariableValues,
|
|
1990
2045
|
httpOperation,
|
|
1991
2046
|
mediaTypeContent,
|
|
1992
2047
|
bodyInput: formDataState.isFormDataBody ? getValues() : textRequestBody,
|
|
@@ -2027,6 +2082,7 @@ const TryIt = ({ httpOperation, mockUrl, onRequestChange, requestBodyIndex, embe
|
|
|
2027
2082
|
const isOnlySendButton = !((_d = httpOperation.security) === null || _d === void 0 ? void 0 : _d.length) && !allParameters.length && !formDataState.isFormDataBody && !mediaTypeContent;
|
|
2028
2083
|
const tryItPanelContents = (React.createElement(React.Fragment, null,
|
|
2029
2084
|
((_e = httpOperation.security) === null || _e === void 0 ? void 0 : _e.length) ? (React.createElement(TryItAuth, { onChange: setOperationAuthValue, operationSecurityScheme: (_f = httpOperation.security) !== null && _f !== void 0 ? _f : [], value: operationAuthValue })) : null,
|
|
2085
|
+
serverVariables.length > 0 && (React.createElement(ServerVariables, { variables: serverVariables, values: serverVariableValues, onChangeValue: updateServerVariableValue })),
|
|
2030
2086
|
allParameters.length > 0 && (React.createElement(OperationParameters, { parameters: allParameters, values: parameterValuesWithDefaults, onChangeValue: updateParameterValue, validate: validateParameters })),
|
|
2031
2087
|
formDataState.isFormDataBody ? (React.createElement(FormDataBody, { specification: formDataState.bodySpecification, values: bodyParameterValues, onChangeValues: setBodyParameterValues, onChangeParameterAllow: setAllowedEmptyValues, isAllowedEmptyValues: isAllowedEmptyValues })) : mediaTypeContent ? (React.createElement(RequestBody, { examples: (_g = mediaTypeContent.examples) !== null && _g !== void 0 ? _g : [], requestBody: textRequestBody, onChange: setTextRequestBody })) : null,
|
|
2032
2088
|
React.createElement(Panel.Content, { className: "SendButtonHolder", mt: 4, pt: !isOnlySendButton && !embeddedInMd ? 0 : undefined },
|
|
@@ -2538,7 +2594,8 @@ const ServerInfo = ({ servers, mockUrl }) => {
|
|
|
2538
2594
|
const mocking = React.useContext(MockingContext);
|
|
2539
2595
|
const showMocking = !mocking.hideMocking && mockUrl && isProperUrl(mockUrl);
|
|
2540
2596
|
const $mockUrl = showMocking ? mockUrl || mocking.mockUrl : undefined;
|
|
2541
|
-
const serversToDisplay = getServersToDisplay(servers, $mockUrl);
|
|
2597
|
+
const serversToDisplay = React.useMemo(() => getServersToDisplay(servers, $mockUrl, false), [servers, $mockUrl]);
|
|
2598
|
+
const firstServerVariableIndex = React.useMemo(() => serversToDisplay.findIndex(server => !isEmpty(server.variables)), [serversToDisplay]);
|
|
2542
2599
|
if (!showMocking && serversToDisplay.length === 0) {
|
|
2543
2600
|
return null;
|
|
2544
2601
|
}
|
|
@@ -2546,25 +2603,88 @@ const ServerInfo = ({ servers, mockUrl }) => {
|
|
|
2546
2603
|
React.createElement(Panel, { rounded: true, isCollapsible: false, className: "BaseURLContent", w: "full" },
|
|
2547
2604
|
React.createElement(Panel.Titlebar, { whitespace: "nowrap" }, "API Base URL"),
|
|
2548
2605
|
React.createElement(Panel.Content, { w: "full", className: "sl-flex sl-flex-col" },
|
|
2549
|
-
React.createElement(VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React.createElement(ServerUrl, Object.assign({}, server, {
|
|
2606
|
+
React.createElement(VStack, { spacing: 1, divider: true }, serversToDisplay.map((server, index) => (React.createElement(ServerUrl, Object.assign({}, server, { defaultIsOpen: index === firstServerVariableIndex, hasAnyServerVariables: firstServerVariableIndex !== -1, key: server.id })))))))));
|
|
2550
2607
|
};
|
|
2551
|
-
const ServerUrl = ({ id, description, url }) => {
|
|
2608
|
+
const ServerUrl = ({ id, description, url, variables, hasAnyServerVariables, defaultIsOpen, }) => {
|
|
2552
2609
|
const { nodeHasChanged } = useOptionsCtx();
|
|
2553
2610
|
const { onCopy, hasCopied } = useClipboard(url);
|
|
2611
|
+
const urlFragments = useSplitUrl(url);
|
|
2554
2612
|
const hasChanged = nodeHasChanged === null || nodeHasChanged === void 0 ? void 0 : nodeHasChanged({ nodeId: id });
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
}
|
|
2613
|
+
const variablesSchema = useVariablesJSONSchema(variables);
|
|
2614
|
+
const titlePaddingLeft = hasAnyServerVariables && !variablesSchema ? 4 : 0;
|
|
2615
|
+
const handleCopyClick = React.useCallback(e => {
|
|
2616
|
+
e.stopPropagation();
|
|
2617
|
+
onCopy();
|
|
2618
|
+
}, [onCopy]);
|
|
2619
|
+
return (React.createElement(Panel, { isCollapsible: !!variablesSchema, defaultIsOpen: defaultIsOpen, w: "full" },
|
|
2620
|
+
React.createElement(Panel.Titlebar, { whitespace: "nowrap" },
|
|
2621
|
+
React.createElement(Text, { pl: titlePaddingLeft, pr: 2, fontWeight: "bold" },
|
|
2622
|
+
description,
|
|
2623
|
+
":"),
|
|
2624
|
+
React.createElement(Tooltip, { placement: "right", renderTrigger: () => (React.createElement(Text, { "aria-label": description }, urlFragments.map(({ kind, value }, i) => (React.createElement(Text, { key: i, fontWeight: kind === 'variable' ? 'semibold' : 'normal' }, value))))) },
|
|
2625
|
+
!hasCopied && (React.createElement(Box, { p: 1, onClick: handleCopyClick, cursor: "pointer" },
|
|
2626
|
+
"Copy Server URL ",
|
|
2627
|
+
React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'copy'] }))),
|
|
2628
|
+
hasCopied && (React.createElement(Box, { p: 1 },
|
|
2629
|
+
"Copied Server URL ",
|
|
2630
|
+
React.createElement(Icon, { className: "sl-ml-1", icon: ['fas', 'check'] })))),
|
|
2631
|
+
React.createElement(NodeAnnotation, { change: hasChanged, additionalLeftOffset: 16 })),
|
|
2632
|
+
variablesSchema && (React.createElement(Panel.Content, { w: "full" },
|
|
2633
|
+
React.createElement(Box, { pl: 4 },
|
|
2634
|
+
React.createElement(JsonSchemaViewer, { schema: variablesSchema }))))));
|
|
2635
|
+
};
|
|
2636
|
+
function useVariablesJSONSchema(variables) {
|
|
2637
|
+
return React.useMemo(() => {
|
|
2638
|
+
if (isEmpty(variables))
|
|
2639
|
+
return;
|
|
2640
|
+
const propertiesPairs = Object.entries(variables).map(([name, variable]) => [
|
|
2641
|
+
name,
|
|
2642
|
+
Object.assign({ type: 'string' }, omitBy({
|
|
2643
|
+
description: variable.description,
|
|
2644
|
+
enum: variable.enum,
|
|
2645
|
+
default: variable.default,
|
|
2646
|
+
}, isNil)),
|
|
2647
|
+
]);
|
|
2648
|
+
return {
|
|
2649
|
+
type: 'object',
|
|
2650
|
+
properties: Object.fromEntries(propertiesPairs),
|
|
2651
|
+
};
|
|
2652
|
+
}, [variables]);
|
|
2653
|
+
}
|
|
2654
|
+
function useSplitUrl(url) {
|
|
2655
|
+
return React.useMemo(() => {
|
|
2656
|
+
const curly = /[{}]/g;
|
|
2657
|
+
const fragments = [];
|
|
2658
|
+
let startOffset = 0;
|
|
2659
|
+
let curPos = 0;
|
|
2660
|
+
let match;
|
|
2661
|
+
while ((match = curly.exec(url))) {
|
|
2662
|
+
if (match[0] === '{' || startOffset + 1 === match.index) {
|
|
2663
|
+
startOffset = match.index;
|
|
2664
|
+
continue;
|
|
2665
|
+
}
|
|
2666
|
+
if (startOffset !== curPos) {
|
|
2667
|
+
fragments.push({
|
|
2668
|
+
kind: 'static',
|
|
2669
|
+
value: url.slice(curPos, startOffset),
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
const variable = url.slice(startOffset, match.index + 1);
|
|
2673
|
+
fragments.push({
|
|
2674
|
+
kind: 'variable',
|
|
2675
|
+
value: variable,
|
|
2676
|
+
});
|
|
2677
|
+
curPos = startOffset + variable.length;
|
|
2678
|
+
}
|
|
2679
|
+
if (curPos < url.length) {
|
|
2680
|
+
fragments.push({
|
|
2681
|
+
kind: 'static',
|
|
2682
|
+
value: url.slice(curPos),
|
|
2683
|
+
});
|
|
2684
|
+
}
|
|
2685
|
+
return fragments;
|
|
2686
|
+
}, [url]);
|
|
2687
|
+
}
|
|
2568
2688
|
|
|
2569
2689
|
const HttpServiceComponent = React.memo(({ data: unresolvedData, location = {}, layoutOptions, exportProps }) => {
|
|
2570
2690
|
var _a, _b, _c, _d;
|
package/package.json
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
import type { IServer } from '@stoplight/types';
|
|
2
|
-
export declare
|
|
3
|
-
|
|
1
|
+
import type { Dictionary, INodeVariable, IServer } from '@stoplight/types';
|
|
2
|
+
export declare type ServerVariable = INodeVariable & {
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const getServersToDisplay: (originalServers: IServer[], mockUrl: string | undefined, inlineDefaults: boolean) => IServer[];
|
|
6
|
+
export declare const getServerVariables: (server?: IServer | null) => ServerVariable[];
|
|
7
|
+
export declare const getServerVariableDefaults: (server: IServer) => Record<string, string>;
|
|
8
|
+
export declare function resolveUrl(urlString: string | null): string | null;
|
|
9
|
+
export declare const getServerUrlWithVariableValues: (server: IServer, values: Dictionary<string, string>) => string;
|