Skip to content

Commit

Permalink
Separate endpoint page, only dropdown for rpc.
Browse files Browse the repository at this point in the history
Disable query for rpc.
  • Loading branch information
kojilin committed Sep 7, 2020
1 parent 4d45fe2 commit 5b760ca
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 142 deletions.
152 changes: 59 additions & 93 deletions docs-client/src/containers/MethodPage/DebugPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ const DebugPage: React.FunctionComponent<Props> = ({
const [stickyHeaders, toggleStickyHeaders] = useReducer(toggle, false);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState('');
const [keepDebugResponse, toggleKeepDebugResponse] = useReducer(
toggle,
false,
);

const transport = TRANSPORTS.getDebugTransport(method);
if (!transport) {
throw new Error("This method doesn't have a debug transport.");
}

useEffect(() => {
const urlParams = new URLSearchParams(location.search);
Expand All @@ -146,13 +155,27 @@ const DebugPage: React.FunctionComponent<Props> = ({
}
}

const urlPath =
isAnnotatedService && exactPathMapping
? method.endpoints[0].pathMapping.substring('exact:'.length)
: urlParams.get('endpoint_path') || '';
let urlPath;
if (isAnnotatedService) {
if (exactPathMapping) {
urlPath = method.endpoints[0].pathMapping.substring('exact:'.length);
} else {
urlPath = urlParams.get('endpoint_path') || '';
}
} else {
urlPath =
transport.findDebugMimeTypeEndpoint(
method,
urlParams.get('endpoint_path'),
)?.pathMapping || '';
}

const urlQueries = isAnnotatedService ? urlParams.get('queries') : '';

setDebugResponse('');
if (!keepDebugResponse) {
setDebugResponse('');
toggleKeepDebugResponse(false);
}
setSnackbarOpen(false);
setRequestBody(urlRequestBody || method.exampleRequests[0] || '');
setAdditionalPath(urlPath || '');
Expand All @@ -163,9 +186,10 @@ const DebugPage: React.FunctionComponent<Props> = ({
isAnnotatedService,
location.search,
match.params,
method.endpoints,
method.exampleRequests,
method,
transport,
useRequestBody,
keepDebugResponse,
]);

/* eslint-disable react-hooks/exhaustive-deps */
Expand Down Expand Up @@ -196,55 +220,6 @@ const DebugPage: React.FunctionComponent<Props> = ({
setSnackbarOpen(false);
}, []);

const validateEndpointPath = useCallback(
(newEndpointPath: string) => {
if (!newEndpointPath) {
throw new Error('You must specify the endpoint path.');
}
const endpoint = method.endpoints[0];
const regexPathPrefix = endpoint.regexPathPrefix;
const originalPath = endpoint.pathMapping;

if (originalPath.startsWith('prefix:')) {
// Prefix path mapping.
const prefix = originalPath.substring('prefix:'.length);
if (!newEndpointPath.startsWith(prefix)) {
throw new Error(
`The path: '${newEndpointPath}' should start with the prefix: ${prefix}`,
);
}
}

if (originalPath.startsWith('regex:')) {
let regexPart;
if (regexPathPrefix) {
// Prefix adding path mapping.
const prefix = regexPathPrefix.substring('prefix:'.length);
if (!newEndpointPath.startsWith(prefix)) {
throw new Error(
`The path: '${newEndpointPath}' should start with the prefix: ${prefix}`,
);
}

// Remove the prefix from the endpointPath so that we can test the regex.
regexPart = newEndpointPath.substring(prefix.length - 1);
} else {
regexPart = newEndpointPath;
}
const regExp = new RegExp(originalPath.substring('regex:'.length));
if (!regExp.test(regexPart)) {
const expectedPath = regexPathPrefix
? `${regexPathPrefix} ${originalPath}`
: originalPath;
throw new Error(
`Endpoint path: ${newEndpointPath} (expected: ${expectedPath})`,
);
}
}
},
[method],
);

const onSelectedQueriesChange = useCallback((selectedQueries: Option) => {
setAdditionalQueries(selectedQueries.value);
}, []);
Expand Down Expand Up @@ -298,35 +273,30 @@ const DebugPage: React.FunctionComponent<Props> = ({
`${window.location.protocol}//${window.location.hostname}` +
`${window.location.port ? `:${window.location.port}` : ''}`;

const transport = TRANSPORTS.getDebugTransport(method);
if (!transport) {
throw new Error("This method doesn't have a debug transport.");
}

const httpMethod = method.httpMethod;
let uri;
let endpoint;

if (isAnnotatedService) {
endpoint = transport.findDebugMimeTypeEndpoint(method);
const queries = additionalQueries;
if (exactPathMapping) {
endpoint = transport.getDebugMimeTypeEndpoint(method);
uri =
`'${host}${escapeSingleQuote(
endpoint.pathMapping.substring('exact:'.length),
)}` +
`${queries.length > 0 ? `?${escapeSingleQuote(queries)}` : ''}'`;
} else {
validateEndpointPath(additionalPath);
endpoint = transport.getDebugMimeTypeEndpoint(method, additionalPath);
uri =
`'${host}${escapeSingleQuote(additionalPath)}'` +
`${queries.length > 0 ? `?${escapeSingleQuote(queries)}` : ''}'`;
}
} else if (additionalPath.length > 0) {
endpoint = transport.findDebugMimeTypeEndpoint(method, additionalPath);
endpoint = transport.getDebugMimeTypeEndpoint(method, additionalPath);
uri = `'${host}${escapeSingleQuote(additionalPath)}'`;
} else {
endpoint = transport.findDebugMimeTypeEndpoint(method);
endpoint = transport.getDebugMimeTypeEndpoint(method);
uri = `'${host}${escapeSingleQuote(endpoint.pathMapping)}'`;
}

Expand Down Expand Up @@ -360,12 +330,12 @@ const DebugPage: React.FunctionComponent<Props> = ({
useRequestBody,
additionalHeaders,
method,
transport,
requestBody,
isAnnotatedService,
showSnackbar,
additionalQueries,
exactPathMapping,
validateEndpointPath,
additionalPath,
]);

Expand Down Expand Up @@ -405,7 +375,6 @@ const DebugPage: React.FunctionComponent<Props> = ({
const headersText = params.get('headers');
const headers = headersText ? JSON.parse(headersText) : {};

const transport = TRANSPORTS.getDebugTransport(method)!;
let executedDebugResponse;
try {
executedDebugResponse = await transport.send(
Expand All @@ -420,10 +389,10 @@ const DebugPage: React.FunctionComponent<Props> = ({
}
setDebugResponse(executedDebugResponse);
},
[useRequestBody, isAnnotatedService, exactPathMapping, method],
[useRequestBody, isAnnotatedService, exactPathMapping, method, transport],
);

const onSubmit = useCallback(() => {
const onSubmit = useCallback(async () => {
setDebugResponse('');

const queries = additionalQueries;
Expand All @@ -449,7 +418,7 @@ const DebugPage: React.FunctionComponent<Props> = ({
params.set('queries', queries);
}
if (!exactPathMapping) {
validateEndpointPath(additionalPath);
transport.getDebugMimeTypeEndpoint(method, additionalPath);
params.set('endpoint_path', additionalPath);
}
} else if (additionalPath.length > 0) {
Expand Down Expand Up @@ -482,9 +451,11 @@ const DebugPage: React.FunctionComponent<Props> = ({

const serializedParams = `?${params.toString()}`;
if (serializedParams !== location.search) {
// executeRequest may throw error before useEffect, we need to avoid useEffect cleanup the debug response.
toggleKeepDebugResponse(true);
history.push(`${location.pathname}${serializedParams}`);
}
executeRequest(params);
await executeRequest(params);
}, [
additionalQueries,
additionalHeaders,
Expand All @@ -496,30 +467,20 @@ const DebugPage: React.FunctionComponent<Props> = ({
isAnnotatedService,
requestBody,
exactPathMapping,
validateEndpointPath,
additionalPath,
history,
method,
transport,
]);

const supportedExamplePaths = useMemo(() => {
if (isAnnotatedService) {
return examplePaths;
}
const transport = TRANSPORTS.getDebugTransport(method);
if (!transport) {
throw new Error("This method doesn't have a debug transport.");
}
return examplePaths.filter((path) =>
method.endpoints.some((endpoint) => {
return (
endpoint.pathMapping === path.value &&
endpoint.availableMimeTypes.some((mimeType) => {
return transport.supportsMimeType(mimeType);
})
);
}),
transport.findDebugMimeTypeEndpoint(method, path.value),
);
}, [examplePaths, method, isAnnotatedService]);
}, [examplePaths, method, isAnnotatedService, transport]);

return (
<Section>
Expand All @@ -544,20 +505,25 @@ const DebugPage: React.FunctionComponent<Props> = ({
<EndpointPath
examplePaths={supportedExamplePaths}
editable={!exactPathMapping}
isAnnotatedService={isAnnotatedService}
endpointPathOpen={endpointPathOpen}
additionalPath={additionalPath}
onEditEndpointPathClick={toggleEndpointPathOpen}
onPathFormChange={onPathFormChange}
onSelectedPathChange={onSelectedPathChange}
/>
<HttpQueryString
exampleQueries={exampleQueries}
additionalQueriesOpen={additionalQueriesOpen}
additionalQueries={additionalQueries}
onEditHttpQueriesClick={toggleAdditionalQueriesOpen}
onQueriesFormChange={onQueriesFormChange}
onSelectedQueriesChange={onSelectedQueriesChange}
/>
{isAnnotatedService && (
<>
<HttpQueryString
exampleQueries={exampleQueries}
additionalQueriesOpen={additionalQueriesOpen}
additionalQueries={additionalQueries}
onEditHttpQueriesClick={toggleAdditionalQueriesOpen}
onQueriesFormChange={onQueriesFormChange}
onSelectedQueriesChange={onSelectedQueriesChange}
/>
</>
)}
<HttpHeaders
exampleHeaders={exampleHeaders}
additionalHeadersOpen={additionalHeadersOpen}
Expand Down
84 changes: 50 additions & 34 deletions docs-client/src/containers/MethodPage/EndpointPath.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const endpointPathPlaceHolder = '/foo/bar';

interface Props {
editable: boolean;
isAnnotatedService: boolean;
endpointPathOpen: boolean;
examplePaths: Option[];
additionalPath: string;
Expand All @@ -32,39 +33,54 @@ interface Props {
onPathFormChange: (e: ChangeEvent<HTMLInputElement>) => void;
}

const EndpointPath: React.FunctionComponent<Props> = (props) => (
<>
<Typography variant="body2" paragraph />
<Button color="secondary" onClick={props.onEditEndpointPathClick}>
Endpoint path
</Button>
<Typography variant="body2" paragraph />
{props.endpointPathOpen && (
<>
{props.examplePaths.length > 0 && (
<>
<Typography variant="body2" paragraph />
<Dropdown
placeholder="Select an example path..."
options={props.examplePaths}
onChange={props.onSelectedPathChange}
/>
</>
)}
<Typography variant="body2" paragraph />
<TextField
fullWidth
value={props.additionalPath}
placeholder={endpointPathPlaceHolder}
onChange={props.onPathFormChange}
inputProps={{
readOnly: !props.editable,
className: 'code',
}}
/>
</>
)}
</>
);
const EndpointPath: React.FunctionComponent<Props> = (props) => {
return (
<>
<Typography variant="body2" paragraph />
<Button color="secondary" onClick={props.onEditEndpointPathClick}>
Endpoint path
</Button>
<Typography variant="body2" paragraph />
{props.endpointPathOpen && (
<>
{props.isAnnotatedService ? (
<>
{props.examplePaths.length > 0 && (
<>
<Typography variant="body2" paragraph />
<Dropdown
placeholder="Select an example path..."
options={props.examplePaths}
onChange={props.onSelectedPathChange}
/>
</>
)}
<Typography variant="body2" paragraph />
<TextField
fullWidth
value={props.additionalPath}
placeholder={endpointPathPlaceHolder}
onChange={props.onPathFormChange}
inputProps={{
readOnly: !props.editable,
className: 'code',
}}
/>
</>
) : (
<>
<Typography variant="body2" paragraph />
<Dropdown
options={props.examplePaths}
onChange={props.onSelectedPathChange}
value={props.additionalPath}
/>
</>
)}
</>
)}
</>
);
};

export default React.memo(EndpointPath);

0 comments on commit 5b760ca

Please sign in to comment.