Skip to content

Commit a641e34

Browse files
authored
Merge pull request #5 from hyunw9/add-Response-Header
Update Review
2 parents 64a9e31 + add4c40 commit a641e34

File tree

8 files changed

+191
-39
lines changed

8 files changed

+191
-39
lines changed

docs-client/src/containers/MethodPage/DebugPage.tsx

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ import { TRANSPORTS } from '../../lib/transports';
5959
import { SelectOption } from '../../lib/types';
6060
import DebugInputs from './DebugInputs';
6161

62+
const stringifyHeaders = (headers: [string, string[]][]): string =>
63+
JSON.stringify(
64+
Object.fromEntries(headers.map(([key, value]) => [key, value.join(', ')])),
65+
null,
66+
2,
67+
);
6268
const useStyles = makeStyles((theme: Theme) =>
6369
createStyles({
6470
actionDialog: {
@@ -149,6 +155,9 @@ const DebugPage: React.FunctionComponent<Props> = ({
149155
const [requestBody, setRequestBody] = useState('');
150156
const [debugResponse, setDebugResponse] = useState('');
151157
const [additionalQueries, setAdditionalQueries] = useState('');
158+
const [debugResponseHeaders, setDebugResponseHeaders] = useState<
159+
[string, string[]][]
160+
>([]);
152161
const [additionalPath, setAdditionalPath] = useState('');
153162
const [additionalHeaders, setAdditionalHeaders] = useState('');
154163
const [stickyHeaders, toggleStickyHeaders] = useReducer(toggle, false);
@@ -159,13 +168,34 @@ const DebugPage: React.FunctionComponent<Props> = ({
159168
false,
160169
);
161170

171+
const [currentApiId, setCurrentApiId] = useState<string>(method.id);
172+
const [responseCache, setResponseCache] = useState<
173+
Record<string, { body: string; headers: Map<string, string[]> }>
174+
>({});
175+
162176
const classes = useStyles();
163177

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

183+
useEffect(() => {
184+
const apiId = method.id;
185+
if (apiId !== currentApiId) {
186+
setCurrentApiId(apiId);
187+
if (responseCache[apiId]) {
188+
setDebugResponse(responseCache[apiId].body);
189+
setDebugResponseHeaders(
190+
Array.from(responseCache[apiId].headers.entries()),
191+
);
192+
} else {
193+
setDebugResponse('');
194+
setDebugResponseHeaders([]);
195+
}
196+
}
197+
}, [method, currentApiId, responseCache]);
198+
169199
useEffect(() => {
170200
const urlParams = new URLSearchParams(location.search);
171201

@@ -202,6 +232,7 @@ const DebugPage: React.FunctionComponent<Props> = ({
202232

203233
if (!keepDebugResponse) {
204234
setDebugResponse('');
235+
setDebugResponseHeaders([]);
205236
toggleKeepDebugResponse(false);
206237
}
207238
setSnackbarOpen(false);
@@ -364,6 +395,7 @@ const DebugPage: React.FunctionComponent<Props> = ({
364395

365396
const onClear = useCallback(() => {
366397
setDebugResponse('');
398+
setDebugResponseHeaders([]);
367399
}, []);
368400

369401
const executeRequest = useCallback(
@@ -390,24 +422,29 @@ const DebugPage: React.FunctionComponent<Props> = ({
390422
const headersText = params.get('headers');
391423
const headers = headersText ? JSON.parse(headersText) : {};
392424

393-
let executedDebugResponse;
394425
try {
395-
executedDebugResponse = await transport.send(
426+
const { body, headers: responseHeaders } = await transport.send(
396427
method,
397428
headers,
398429
parseServerRootPath(docServiceRoute),
399430
executedRequestBody,
400431
executedEndpointPath,
401432
queries,
402433
);
434+
setDebugResponse(body);
435+
setDebugResponseHeaders(Array.from(responseHeaders.entries()));
436+
setResponseCache((prev) => ({
437+
...prev,
438+
[currentApiId]: {
439+
body,
440+
headers: responseHeaders,
441+
},
442+
}));
403443
} catch (e) {
404-
if (e instanceof Object) {
405-
executedDebugResponse = e.toString();
406-
} else {
407-
executedDebugResponse = '<unknown>';
408-
}
444+
const message = e instanceof Object ? e.toString() : '<unknown>';
445+
setDebugResponse(message);
446+
setDebugResponseHeaders([]);
409447
}
410-
setDebugResponse(executedDebugResponse);
411448
},
412449
[
413450
useRequestBody,
@@ -416,6 +453,7 @@ const DebugPage: React.FunctionComponent<Props> = ({
416453
method,
417454
transport,
418455
docServiceRoute,
456+
currentApiId,
419457
],
420458
);
421459

@@ -572,7 +610,7 @@ const DebugPage: React.FunctionComponent<Props> = ({
572610
<Grid item xs={12} sm={6}>
573611
<Grid container spacing={1}>
574612
<Grid item xs="auto">
575-
<Tooltip title="Copy response">
613+
<Tooltip title="Copy response body">
576614
<div>
577615
<IconButton
578616
onClick={onCopy}
@@ -596,13 +634,37 @@ const DebugPage: React.FunctionComponent<Props> = ({
596634
</Tooltip>
597635
</Grid>
598636
</Grid>
599-
<SyntaxHighlighter
600-
language="json"
601-
style={githubGist}
602-
wrapLines={false}
603-
>
604-
{debugResponse}
605-
</SyntaxHighlighter>
637+
{debugResponse && (
638+
<>
639+
{Object.keys(debugResponseHeaders).length > 0 && (
640+
<>
641+
<Typography
642+
variant="subtitle1"
643+
style={{ marginTop: '1rem' }}
644+
>
645+
Response Headers:
646+
</Typography>
647+
<SyntaxHighlighter
648+
language="json"
649+
style={githubGist}
650+
wrapLines={false}
651+
>
652+
{stringifyHeaders(debugResponseHeaders)}
653+
</SyntaxHighlighter>
654+
</>
655+
)}
656+
<Typography variant="subtitle1" style={{ marginTop: '1rem' }}>
657+
Response Body:
658+
</Typography>
659+
<SyntaxHighlighter
660+
language="json"
661+
style={githubGist}
662+
wrapLines={false}
663+
>
664+
{debugResponse}
665+
</SyntaxHighlighter>
666+
</>
667+
)}
606668
</Grid>
607669
</Grid>
608670
<Snackbar
@@ -684,6 +746,26 @@ const DebugPage: React.FunctionComponent<Props> = ({
684746
</Tooltip>
685747
</Grid>
686748
</Grid>
749+
{Object.keys(debugResponseHeaders).length > 0 && (
750+
<>
751+
<Typography
752+
variant="subtitle1"
753+
style={{ marginTop: '1rem' }}
754+
>
755+
Response Headers:
756+
</Typography>
757+
<SyntaxHighlighter
758+
language="json"
759+
style={githubGist}
760+
wrapLines={false}
761+
>
762+
{stringifyHeaders(debugResponseHeaders)}
763+
</SyntaxHighlighter>
764+
</>
765+
)}
766+
<Typography variant="subtitle1" style={{ marginTop: '1rem' }}>
767+
Response Body:
768+
</Typography>
687769
<SyntaxHighlighter
688770
language="json"
689771
style={githubGist}

docs-client/src/lib/json-util.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,17 @@ export function isValidJsonMimeType(applicationType: string | null) {
134134
}
135135
return applicationType.indexOf('json') >= 0;
136136
}
137+
138+
export function extractResponseHeaders(
139+
headers: Headers,
140+
): Map<string, string[]> {
141+
const responseHeaders = new Map<string, string[]>();
142+
headers.forEach((value, key) => {
143+
const lowerKey = key.toLowerCase();
144+
if (!responseHeaders.has(lowerKey)) {
145+
responseHeaders.set(lowerKey, []);
146+
}
147+
responseHeaders.get(lowerKey)!.push(value);
148+
});
149+
return responseHeaders;
150+
}

docs-client/src/lib/transports/annotated-http.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
import { Endpoint, Method } from '../specification';
1717

1818
import Transport from './transport';
19-
import { isValidJsonMimeType, validateJsonObject } from '../json-util';
19+
import {
20+
extractResponseHeaders,
21+
isValidJsonMimeType,
22+
validateJsonObject,
23+
} from '../json-util';
24+
import { ResponseData } from '../types';
2025

2126
export const ANNOTATED_HTTP_MIME_TYPE = 'application/json; charset=utf-8';
2227

@@ -88,7 +93,7 @@ export default class AnnotatedHttpTransport extends Transport {
8893
bodyJson?: string,
8994
endpointPath?: string,
9095
queries?: string,
91-
): Promise<Response> {
96+
): Promise<ResponseData> {
9297
const endpoint = this.getDebugMimeTypeEndpoint(method);
9398

9499
const hdrs = new Headers();
@@ -116,10 +121,18 @@ export default class AnnotatedHttpTransport extends Transport {
116121
}
117122
newPath = pathPrefix + newPath;
118123

119-
return fetch(encodeURI(newPath), {
124+
const response = await fetch(encodeURI(newPath), {
120125
headers: hdrs,
121126
method: method.httpMethod,
122127
body: bodyJson,
123128
});
129+
130+
const responseHeaders = extractResponseHeaders(response.headers);
131+
const responseText = await response.text();
132+
133+
return {
134+
body: responseText,
135+
headers: responseHeaders,
136+
};
124137
}
125138
}

docs-client/src/lib/transports/grahpql-http.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616

1717
import Transport from './transport';
1818
import { Method } from '../specification';
19-
import { validateJsonObject } from '../json-util';
19+
import { extractResponseHeaders, validateJsonObject } from '../json-util';
20+
import { ResponseData } from '../types';
2021

2122
export const GRAPHQL_HTTP_MIME_TYPE = 'application/graphql+json';
2223

@@ -36,7 +37,7 @@ export default class GraphqlHttpTransport extends Transport {
3637
bodyJson?: string,
3738
endpointPath?: string,
3839
queries?: string,
39-
): Promise<Response> {
40+
): Promise<ResponseData> {
4041
const endpoint = this.getDebugMimeTypeEndpoint(method);
4142

4243
const hdrs = new Headers();
@@ -59,10 +60,18 @@ export default class GraphqlHttpTransport extends Transport {
5960
}
6061
newPath = pathPrefix + newPath;
6162

62-
return fetch(encodeURI(newPath), {
63+
const response = await fetch(encodeURI(newPath), {
6364
headers: hdrs,
6465
method: method.httpMethod,
6566
body: bodyJson,
6667
});
68+
69+
const responseHeaders = extractResponseHeaders(response.headers);
70+
const responseText = await response.text();
71+
72+
return {
73+
body: responseText,
74+
headers: responseHeaders,
75+
};
6776
}
6877
}

docs-client/src/lib/transports/grpc-unframed.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
import { Method } from '../specification';
1717

1818
import Transport from './transport';
19-
import { validateJsonObject } from '../json-util';
19+
import { extractResponseHeaders, validateJsonObject } from '../json-util';
20+
import { ResponseData } from '../types';
2021

2122
export const GRPC_UNFRAMED_MIME_TYPE =
2223
'application/json; charset=utf-8; protocol=gRPC';
@@ -36,7 +37,7 @@ export default class GrpcUnframedTransport extends Transport {
3637
pathPrefix: string,
3738
bodyJson?: string,
3839
endpointPath?: string,
39-
): Promise<Response> {
40+
): Promise<ResponseData> {
4041
if (!bodyJson) {
4142
throw new Error('A gRPC request must have body.');
4243
}
@@ -56,11 +57,18 @@ export default class GrpcUnframedTransport extends Transport {
5657
}
5758

5859
const newPath = pathPrefix + (endpointPath ?? endpoint.pathMapping);
59-
60-
return fetch(newPath, {
60+
const response = await fetch(newPath, {
6161
headers: hdrs,
6262
method: 'POST',
6363
body: bodyJson,
6464
});
65+
66+
const responseHeaders = extractResponseHeaders(response.headers);
67+
const responseText = await response.text();
68+
69+
return {
70+
body: responseText,
71+
headers: responseHeaders,
72+
};
6573
}
6674
}

docs-client/src/lib/transports/thrift.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
import { Endpoint, Method } from '../specification';
1818

1919
import Transport from './transport';
20-
import { validateJsonObject } from '../json-util';
20+
import { extractResponseHeaders, validateJsonObject } from '../json-util';
21+
import { ResponseData } from '../types';
2122

2223
export const TTEXT_MIME_TYPE = 'application/x-thrift; protocol=TTEXT';
2324

@@ -47,7 +48,7 @@ export default class ThriftTransport extends Transport {
4748
pathPrefix: string,
4849
bodyJson?: string,
4950
endpointPath?: string,
50-
): Promise<Response> {
51+
): Promise<ResponseData> {
5152
if (!bodyJson) {
5253
throw new Error('A Thrift request must have body.');
5354
}
@@ -66,10 +67,18 @@ export default class ThriftTransport extends Transport {
6667

6768
const newPath = pathPrefix + (endpointPath ?? endpoint.pathMapping);
6869

69-
return fetch(newPath, {
70+
const response = await fetch(newPath, {
7071
headers: hdrs,
7172
method: 'POST',
7273
body: `{"method": "${thriftMethod}", "type": "CALL", "args": ${bodyJson}}`,
7374
});
75+
76+
const responseHeaders = extractResponseHeaders(response.headers);
77+
const responseText = await response.text();
78+
79+
return {
80+
body: responseText,
81+
headers: responseHeaders,
82+
};
7483
}
7584
}

0 commit comments

Comments
 (0)