Convert Flipper plugin "Network" to TypeScript
Summary: _typescript_ Reviewed By: danielbuechele Differential Revision: D17155509 fbshipit-source-id: 45ae3e2de8cd7b3cdf7271905ef7c318d4289391
This commit is contained in:
committed by
Facebook Github Bot
parent
0a53cccb40
commit
705ba8eaa8
@@ -5,13 +5,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {
|
import {Request, Response, Header, Insights, RetryInsights} from './types';
|
||||||
Request,
|
|
||||||
Response,
|
|
||||||
Header,
|
|
||||||
Insights,
|
|
||||||
RetryInsights,
|
|
||||||
} from './types.tsx';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component,
|
Component,
|
||||||
@@ -24,11 +18,11 @@ import {
|
|||||||
styled,
|
styled,
|
||||||
colors,
|
colors,
|
||||||
} from 'flipper';
|
} from 'flipper';
|
||||||
import {decodeBody, getHeaderValue} from './utils.tsx';
|
import {decodeBody, getHeaderValue} from './utils';
|
||||||
import {formatBytes} from './index.js';
|
import {formatBytes} from './index';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
import querystring from 'querystring';
|
import querystring from 'querystring';
|
||||||
// $FlowFixMe
|
|
||||||
import xmlBeautifier from 'xml-beautifier';
|
import xmlBeautifier from 'xml-beautifier';
|
||||||
|
|
||||||
const WrappingText = styled(Text)({
|
const WrappingText = styled(Text)({
|
||||||
@@ -55,17 +49,17 @@ const KeyValueColumns = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type RequestDetailsProps = {
|
type RequestDetailsProps = {
|
||||||
request: Request,
|
request: Request;
|
||||||
response: ?Response,
|
response: Response | null | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RequestDetailsState = {
|
type RequestDetailsState = {
|
||||||
bodyFormat: string,
|
bodyFormat: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class RequestDetails extends Component<
|
export default class RequestDetails extends Component<
|
||||||
RequestDetailsProps,
|
RequestDetailsProps,
|
||||||
RequestDetailsState,
|
RequestDetailsState
|
||||||
> {
|
> {
|
||||||
static Container = styled(FlexColumn)({
|
static Container = styled(FlexColumn)({
|
||||||
height: '100%',
|
height: '100%',
|
||||||
@@ -219,21 +213,21 @@ class QueryInspector extends Component<{queryParams: URLSearchParams}> {
|
|||||||
render() {
|
render() {
|
||||||
const {queryParams} = this.props;
|
const {queryParams} = this.props;
|
||||||
|
|
||||||
const rows = [];
|
const rows: any = [];
|
||||||
for (const kv of queryParams.entries()) {
|
queryParams.forEach((value: string, key: string) => {
|
||||||
rows.push({
|
rows.push({
|
||||||
columns: {
|
columns: {
|
||||||
key: {
|
key: {
|
||||||
value: <WrappingText>{kv[0]}</WrappingText>,
|
value: <WrappingText>{key}</WrappingText>,
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
value: <WrappingText>{kv[1]}</WrappingText>,
|
value: <WrappingText>{value}</WrappingText>,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
copyText: kv[1],
|
copyText: value,
|
||||||
key: kv[0],
|
key: key,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return rows.length > 0 ? (
|
return rows.length > 0 ? (
|
||||||
<ManagedTable
|
<ManagedTable
|
||||||
@@ -250,37 +244,40 @@ class QueryInspector extends Component<{queryParams: URLSearchParams}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HeaderInspectorProps = {
|
type HeaderInspectorProps = {
|
||||||
headers: Array<Header>,
|
headers: Array<Header>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type HeaderInspectorState = {
|
type HeaderInspectorState = {
|
||||||
computedHeaders: Object,
|
computedHeaders: Object;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HeaderInspector extends Component<
|
class HeaderInspector extends Component<
|
||||||
HeaderInspectorProps,
|
HeaderInspectorProps,
|
||||||
HeaderInspectorState,
|
HeaderInspectorState
|
||||||
> {
|
> {
|
||||||
render() {
|
render() {
|
||||||
const computedHeaders = this.props.headers.reduce((sum, header) => {
|
const computedHeaders: Map<string, string> = this.props.headers.reduce(
|
||||||
return {...sum, [header.key]: header.value};
|
(sum, header) => {
|
||||||
}, {});
|
return sum.set(header.key, header.value);
|
||||||
|
},
|
||||||
|
new Map(),
|
||||||
|
);
|
||||||
|
|
||||||
const rows = [];
|
const rows: any = [];
|
||||||
for (const key in computedHeaders) {
|
computedHeaders.forEach((value: string, key: string) => {
|
||||||
rows.push({
|
rows.push({
|
||||||
columns: {
|
columns: {
|
||||||
key: {
|
key: {
|
||||||
value: <WrappingText>{key}</WrappingText>,
|
value: <WrappingText>{key}</WrappingText>,
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
value: <WrappingText>{computedHeaders[key]}</WrappingText>,
|
value: <WrappingText>{value}</WrappingText>,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
copyText: computedHeaders[key],
|
copyText: value,
|
||||||
key,
|
key,
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
return rows.length > 0 ? (
|
return rows.length > 0 ? (
|
||||||
<ManagedTable
|
<ManagedTable
|
||||||
@@ -302,13 +299,13 @@ const BodyContainer = styled('div')({
|
|||||||
});
|
});
|
||||||
|
|
||||||
type BodyFormatter = {
|
type BodyFormatter = {
|
||||||
formatRequest?: (request: Request) => any,
|
formatRequest?: (request: Request) => any;
|
||||||
formatResponse?: (request: Request, response: Response) => any,
|
formatResponse?: (request: Request, response: Response) => any;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RequestBodyInspector extends Component<{
|
class RequestBodyInspector extends Component<{
|
||||||
request: Request,
|
request: Request;
|
||||||
formattedText: boolean,
|
formattedText: boolean;
|
||||||
}> {
|
}> {
|
||||||
render() {
|
render() {
|
||||||
const {request, formattedText} = this.props;
|
const {request, formattedText} = this.props;
|
||||||
@@ -337,9 +334,9 @@ class RequestBodyInspector extends Component<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ResponseBodyInspector extends Component<{
|
class ResponseBodyInspector extends Component<{
|
||||||
response: Response,
|
response: Response;
|
||||||
request: Request,
|
request: Request;
|
||||||
formattedText: boolean,
|
formattedText: boolean;
|
||||||
}> {
|
}> {
|
||||||
render() {
|
render() {
|
||||||
const {request, response, formattedText} = this.props;
|
const {request, response, formattedText} = this.props;
|
||||||
@@ -374,12 +371,12 @@ const MediaContainer = styled(FlexColumn)({
|
|||||||
});
|
});
|
||||||
|
|
||||||
type ImageWithSizeProps = {
|
type ImageWithSizeProps = {
|
||||||
src: string,
|
src: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ImageWithSizeState = {
|
type ImageWithSizeState = {
|
||||||
width: number,
|
width: number;
|
||||||
height: number,
|
height: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ImageWithSize extends Component<ImageWithSizeProps, ImageWithSizeState> {
|
class ImageWithSize extends Component<ImageWithSizeProps, ImageWithSizeState> {
|
||||||
@@ -394,7 +391,7 @@ class ImageWithSize extends Component<ImageWithSizeProps, ImageWithSizeState> {
|
|||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props: ImageWithSizeProps, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = {
|
this.state = {
|
||||||
width: 0,
|
width: 0,
|
||||||
@@ -595,7 +592,7 @@ class LogEventFormatter {
|
|||||||
formatRequest = (request: Request) => {
|
formatRequest = (request: Request) => {
|
||||||
if (request.url.indexOf('logging_client_event') > 0) {
|
if (request.url.indexOf('logging_client_event') > 0) {
|
||||||
const data = querystring.parse(decodeBody(request));
|
const data = querystring.parse(decodeBody(request));
|
||||||
if (data.message) {
|
if (typeof data.message === 'string') {
|
||||||
data.message = JSON.parse(data.message);
|
data.message = JSON.parse(data.message);
|
||||||
}
|
}
|
||||||
return <ManagedDataInspector expandRoot={true} data={data} />;
|
return <ManagedDataInspector expandRoot={true} data={data} />;
|
||||||
@@ -607,7 +604,7 @@ class GraphQLBatchFormatter {
|
|||||||
formatRequest = (request: Request) => {
|
formatRequest = (request: Request) => {
|
||||||
if (request.url.indexOf('graphqlbatch') > 0) {
|
if (request.url.indexOf('graphqlbatch') > 0) {
|
||||||
const data = querystring.parse(decodeBody(request));
|
const data = querystring.parse(decodeBody(request));
|
||||||
if (data.queries) {
|
if (typeof data.queries === 'string') {
|
||||||
data.queries = JSON.parse(data.queries);
|
data.queries = JSON.parse(data.queries);
|
||||||
}
|
}
|
||||||
return <ManagedDataInspector expandRoot={true} data={data} />;
|
return <ManagedDataInspector expandRoot={true} data={data} />;
|
||||||
@@ -643,10 +640,10 @@ class GraphQLFormatter {
|
|||||||
formatRequest = (request: Request) => {
|
formatRequest = (request: Request) => {
|
||||||
if (request.url.indexOf('graphql') > 0) {
|
if (request.url.indexOf('graphql') > 0) {
|
||||||
const data = querystring.parse(decodeBody(request));
|
const data = querystring.parse(decodeBody(request));
|
||||||
if (data.variables) {
|
if (typeof data.variables === 'string') {
|
||||||
data.variables = JSON.parse(data.variables);
|
data.variables = JSON.parse(data.variables);
|
||||||
}
|
}
|
||||||
if (data.query_params) {
|
if (typeof data.query_params === 'string') {
|
||||||
data.query_params = JSON.parse(data.query_params);
|
data.query_params = JSON.parse(data.query_params);
|
||||||
}
|
}
|
||||||
return <ManagedDataInspector expandRoot={true} data={data} />;
|
return <ManagedDataInspector expandRoot={true} data={data} />;
|
||||||
@@ -745,7 +742,11 @@ class InsightsInspector extends Component<{insights: Insights}> {
|
|||||||
} ${timesWord} out of ${retry.limit})`;
|
} ${timesWord} out of ${retry.limit})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildRow<T>(name: string, value: ?T, formatter: T => string): any {
|
buildRow<T>(
|
||||||
|
name: string,
|
||||||
|
value: T | null | undefined,
|
||||||
|
formatter: (value: T) => string,
|
||||||
|
): any {
|
||||||
return value
|
return value
|
||||||
? {
|
? {
|
||||||
columns: {
|
columns: {
|
||||||
@@ -5,13 +5,10 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {
|
import {TableHighlightedRows, TableRows, TableBodyRow} from 'flipper';
|
||||||
TableHighlightedRows,
|
|
||||||
TableRows,
|
|
||||||
TableBodyRow,
|
|
||||||
MetricType,
|
|
||||||
} from 'flipper';
|
|
||||||
import {padStart} from 'lodash';
|
import {padStart} from 'lodash';
|
||||||
|
import React from 'react';
|
||||||
|
import {MenuItemConstructorOptions} from 'electron';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
@@ -26,25 +23,21 @@ import {
|
|||||||
SearchableTable,
|
SearchableTable,
|
||||||
FlipperPlugin,
|
FlipperPlugin,
|
||||||
} from 'flipper';
|
} from 'flipper';
|
||||||
import type {Request, RequestId, Response} from './types.tsx';
|
import {Request, RequestId, Response} from './types';
|
||||||
import {
|
import {convertRequestToCurlCommand, getHeaderValue, decodeBody} from './utils';
|
||||||
convertRequestToCurlCommand,
|
import RequestDetails from './RequestDetails';
|
||||||
getHeaderValue,
|
|
||||||
decodeBody,
|
|
||||||
} from './utils.tsx';
|
|
||||||
import RequestDetails from './RequestDetails.js';
|
|
||||||
import {clipboard} from 'electron';
|
import {clipboard} from 'electron';
|
||||||
import {URL} from 'url';
|
import {URL} from 'url';
|
||||||
import type {Notification} from '../../plugin.tsx';
|
import {DefaultKeyboardAction} from 'src/MenuBar';
|
||||||
|
|
||||||
type PersistedState = {|
|
type PersistedState = {
|
||||||
requests: {[id: RequestId]: Request},
|
requests: Map<RequestId, Request>;
|
||||||
responses: {[id: RequestId]: Response},
|
responses: Map<RequestId, Response>;
|
||||||
|};
|
};
|
||||||
|
|
||||||
type State = {|
|
type State = {
|
||||||
selectedIds: Array<RequestId>,
|
selectedIds: Array<RequestId>;
|
||||||
|};
|
};
|
||||||
|
|
||||||
const COLUMN_SIZE = {
|
const COLUMN_SIZE = {
|
||||||
requestTimestamp: 100,
|
requestTimestamp: 100,
|
||||||
@@ -67,27 +60,13 @@ const COLUMN_ORDER = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const COLUMNS = {
|
const COLUMNS = {
|
||||||
requestTimestamp: {
|
requestTimestamp: {value: 'Request Time'},
|
||||||
value: 'Request Time',
|
responseTimestamp: {value: 'Response Time'},
|
||||||
},
|
domain: {value: 'Domain'},
|
||||||
responseTimestamp: {
|
method: {value: 'Method'},
|
||||||
value: 'Response Time',
|
status: {value: 'Status'},
|
||||||
},
|
size: {value: 'Size'},
|
||||||
domain: {
|
duration: {value: 'Duration'},
|
||||||
value: 'Domain',
|
|
||||||
},
|
|
||||||
method: {
|
|
||||||
value: 'Method',
|
|
||||||
},
|
|
||||||
status: {
|
|
||||||
value: 'Status',
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
value: 'Size',
|
|
||||||
},
|
|
||||||
duration: {
|
|
||||||
value: 'Duration',
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function formatBytes(count: number): string {
|
export function formatBytes(count: number): string {
|
||||||
@@ -108,66 +87,75 @@ const TextEllipsis = styled(Text)({
|
|||||||
paddingTop: 4,
|
paddingTop: 4,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default class extends FlipperPlugin<State, *, PersistedState> {
|
export default class extends FlipperPlugin<State, any, PersistedState> {
|
||||||
static keyboardActions = ['clear'];
|
static keyboardActions: Array<DefaultKeyboardAction> = ['clear'];
|
||||||
static subscribed = [];
|
static subscribed = [];
|
||||||
static defaultPersistedState = {
|
static defaultPersistedState = {
|
||||||
requests: {},
|
requests: new Map(),
|
||||||
responses: {},
|
responses: new Map(),
|
||||||
};
|
};
|
||||||
|
|
||||||
static metricsReducer = (
|
static metricsReducer(persistedState: PersistedState) {
|
||||||
persistedState: PersistedState,
|
const failures = Object.values(persistedState.responses).reduce(function(
|
||||||
): Promise<MetricType> => {
|
|
||||||
const failures = Object.keys(persistedState.responses).reduce(function(
|
|
||||||
previous,
|
previous,
|
||||||
key,
|
values,
|
||||||
) {
|
) {
|
||||||
return previous + (persistedState.responses[key].status >= 400);
|
return previous + (values.status >= 400 ? 1 : 0);
|
||||||
},
|
},
|
||||||
0);
|
0);
|
||||||
return Promise.resolve({NUMBER_NETWORK_FAILURES: failures});
|
return Promise.resolve({NUMBER_NETWORK_FAILURES: failures});
|
||||||
};
|
}
|
||||||
|
|
||||||
static persistedStateReducer = (
|
static persistedStateReducer(
|
||||||
persistedState: PersistedState,
|
persistedState: PersistedState,
|
||||||
method: string,
|
method: string,
|
||||||
data: Request | Response,
|
data: Request | Response,
|
||||||
): PersistedState => {
|
) {
|
||||||
const dataType: 'requests' | 'responses' = data.url
|
switch (method) {
|
||||||
? 'requests'
|
case 'newRequest':
|
||||||
: 'responses';
|
return Object.assign({}, persistedState, {
|
||||||
return {
|
requests: new Map(persistedState.requests).set(
|
||||||
...persistedState,
|
data.id,
|
||||||
[dataType]: {
|
data as Request,
|
||||||
...persistedState[dataType],
|
),
|
||||||
[data.id]: data,
|
});
|
||||||
},
|
case 'newResponse':
|
||||||
};
|
return Object.assign({}, persistedState, {
|
||||||
};
|
responses: new Map(persistedState.responses).set(
|
||||||
|
data.id,
|
||||||
|
data as Response,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
default:
|
||||||
|
return persistedState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static getActiveNotifications = (
|
static getActiveNotifications(persistedState: PersistedState) {
|
||||||
persistedState: PersistedState,
|
const responses = persistedState
|
||||||
): Array<Notification> => {
|
? persistedState.responses || new Map()
|
||||||
const responses = persistedState ? persistedState.responses || [] : [];
|
: new Map();
|
||||||
const r: Array<Response> = Object.values(responses);
|
const r: Array<Response> = Array.from(responses.values());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
r
|
r
|
||||||
// Show error messages for all status codes indicating a client or server error
|
// Show error messages for all status codes indicating a client or server error
|
||||||
.filter((response: Response) => response.status >= 400)
|
.filter((response: Response) => response.status >= 400)
|
||||||
.map((response: Response) => ({
|
.map((response: Response) => {
|
||||||
|
const request = persistedState.requests.get(response.id);
|
||||||
|
const url: string = (request && request.url) || '(URL missing)';
|
||||||
|
return {
|
||||||
id: response.id,
|
id: response.id,
|
||||||
title: `HTTP ${response.status}: Network request failed`,
|
title: `HTTP ${response.status}: Network request failed`,
|
||||||
message: `Request to "${persistedState.requests[response.id]?.url ||
|
message: `Request to ${url} failed. ${response.reason}`,
|
||||||
'(URL missing)'}" failed. ${response.reason}`,
|
severity: 'error' as 'error',
|
||||||
severity: 'error',
|
|
||||||
timestamp: response.timestamp,
|
timestamp: response.timestamp,
|
||||||
category: `HTTP${response.status}`,
|
category: `HTTP${response.status}`,
|
||||||
action: response.id,
|
action: response.id,
|
||||||
}))
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
onKeyboardAction = (action: string) => {
|
onKeyboardAction = (action: string) => {
|
||||||
if (action === 'clear') {
|
if (action === 'clear') {
|
||||||
@@ -190,14 +178,17 @@ export default class extends FlipperPlugin<State, *, PersistedState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const request = requests[selectedIds[0]];
|
const request = requests.get(selectedIds[0]);
|
||||||
|
if (!request) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const command = convertRequestToCurlCommand(request);
|
const command = convertRequestToCurlCommand(request);
|
||||||
clipboard.writeText(command);
|
clipboard.writeText(command);
|
||||||
};
|
};
|
||||||
|
|
||||||
clearLogs = () => {
|
clearLogs = () => {
|
||||||
this.setState({selectedIds: []});
|
this.setState({selectedIds: []});
|
||||||
this.props.setPersistedState({responses: {}, requests: {}});
|
this.props.setPersistedState({responses: new Map(), requests: new Map()});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderSidebar = () => {
|
renderSidebar = () => {
|
||||||
@@ -205,13 +196,20 @@ export default class extends FlipperPlugin<State, *, PersistedState> {
|
|||||||
const {selectedIds} = this.state;
|
const {selectedIds} = this.state;
|
||||||
const selectedId = selectedIds.length === 1 ? selectedIds[0] : null;
|
const selectedId = selectedIds.length === 1 ? selectedIds[0] : null;
|
||||||
|
|
||||||
return selectedId != null ? (
|
if (!selectedId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const requestWithId = requests.get(selectedId);
|
||||||
|
if (!requestWithId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
<RequestDetails
|
<RequestDetails
|
||||||
key={selectedId}
|
key={selectedId}
|
||||||
request={requests[selectedId]}
|
request={requestWithId}
|
||||||
response={responses[selectedId]}
|
response={responses.get(selectedId)}
|
||||||
/>
|
/>
|
||||||
) : null;
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -220,8 +218,8 @@ export default class extends FlipperPlugin<State, *, PersistedState> {
|
|||||||
return (
|
return (
|
||||||
<FlexColumn grow={true}>
|
<FlexColumn grow={true}>
|
||||||
<NetworkTable
|
<NetworkTable
|
||||||
requests={requests || {}}
|
requests={requests || new Map()}
|
||||||
responses={responses || {}}
|
responses={responses || new Map()}
|
||||||
clear={this.clearLogs}
|
clear={this.clearLogs}
|
||||||
copyRequestCurlCommand={this.copyRequestCurlCommand}
|
copyRequestCurlCommand={this.copyRequestCurlCommand}
|
||||||
onRowHighlighted={this.onRowHighlighted}
|
onRowHighlighted={this.onRowHighlighted}
|
||||||
@@ -236,17 +234,17 @@ export default class extends FlipperPlugin<State, *, PersistedState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type NetworkTableProps = {
|
type NetworkTableProps = {
|
||||||
requests: {[id: RequestId]: Request},
|
requests: Map<RequestId, Request>;
|
||||||
responses: {[id: RequestId]: Response},
|
responses: Map<RequestId, Response>;
|
||||||
clear: () => void,
|
clear: () => void;
|
||||||
copyRequestCurlCommand: () => void,
|
copyRequestCurlCommand: () => void;
|
||||||
onRowHighlighted: (keys: TableHighlightedRows) => void,
|
onRowHighlighted: (keys: TableHighlightedRows) => void;
|
||||||
highlightedRows: ?Set<string>,
|
highlightedRows: Set<string> | null | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
type NetworkTableState = {|
|
type NetworkTableState = {
|
||||||
sortedRows: TableRows,
|
sortedRows: TableRows;
|
||||||
|};
|
};
|
||||||
|
|
||||||
function formatTimestamp(timestamp: number): string {
|
function formatTimestamp(timestamp: number): string {
|
||||||
const date = new Date(timestamp);
|
const date = new Date(timestamp);
|
||||||
@@ -261,9 +259,12 @@ function formatTimestamp(timestamp: number): string {
|
|||||||
)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildRow(request: Request, response: ?Response): ?TableBodyRow {
|
function buildRow(
|
||||||
|
request: Request,
|
||||||
|
response: Response | null | undefined,
|
||||||
|
): TableBodyRow | null | undefined {
|
||||||
if (request == null) {
|
if (request == null) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const domain = url.host + url.pathname;
|
const domain = url.host + url.pathname;
|
||||||
@@ -273,7 +274,10 @@ function buildRow(request: Request, response: ?Response): ?TableBodyRow {
|
|||||||
## Request
|
## Request
|
||||||
HTTP ${request.method} ${request.url}
|
HTTP ${request.method} ${request.url}
|
||||||
${request.headers
|
${request.headers
|
||||||
.map(({key, value}) => `${key}: ${String(value)}`)
|
.map(
|
||||||
|
({key, value}: {key: string; value: string}): string =>
|
||||||
|
`${key}: ${String(value)}`,
|
||||||
|
)
|
||||||
.join('\n')}`;
|
.join('\n')}`;
|
||||||
|
|
||||||
if (request.data) {
|
if (request.data) {
|
||||||
@@ -286,7 +290,10 @@ ${request.headers
|
|||||||
## Response
|
## Response
|
||||||
HTTP ${response.status} ${response.reason}
|
HTTP ${response.status} ${response.reason}
|
||||||
${response.headers
|
${response.headers
|
||||||
.map(({key, value}) => `${key}: ${String(value)}`)
|
.map(
|
||||||
|
({key, value}: {key: string; value: string}): string =>
|
||||||
|
`${key}: ${String(value)}`,
|
||||||
|
)
|
||||||
.join('\n')}`;
|
.join('\n')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,50 +348,49 @@ ${response.headers
|
|||||||
|
|
||||||
function calculateState(
|
function calculateState(
|
||||||
props: {
|
props: {
|
||||||
requests: {[id: RequestId]: Request},
|
requests: Map<RequestId, Request>;
|
||||||
responses: {[id: RequestId]: Response},
|
responses: Map<RequestId, Response>;
|
||||||
},
|
},
|
||||||
nextProps: NetworkTableProps,
|
nextProps: NetworkTableProps,
|
||||||
rows: TableRows = [],
|
rows: TableRows = [],
|
||||||
): NetworkTableState {
|
): NetworkTableState {
|
||||||
rows = [...rows];
|
rows = [...rows];
|
||||||
|
|
||||||
if (Object.keys(nextProps.requests).length === 0) {
|
// if (nextProps.requests.size === undefined || nextProps.requests.size === 0) {
|
||||||
|
if (nextProps.requests.size === 0) {
|
||||||
// cleared
|
// cleared
|
||||||
rows = [];
|
rows = [];
|
||||||
} else if (props.requests !== nextProps.requests) {
|
} else if (props.requests !== nextProps.requests) {
|
||||||
// new request
|
// new request
|
||||||
for (const requestId in nextProps.requests) {
|
nextProps.requests.forEach((request: Request, requestId: RequestId) => {
|
||||||
if (props.requests[requestId] == null) {
|
if (props.requests.get(requestId) == null) {
|
||||||
const newRow = buildRow(
|
const newRow = buildRow(request, nextProps.responses.get(requestId));
|
||||||
nextProps.requests[requestId],
|
|
||||||
nextProps.responses[requestId],
|
|
||||||
);
|
|
||||||
if (newRow) {
|
if (newRow) {
|
||||||
rows.push(newRow);
|
rows.push(newRow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
} else if (props.responses !== nextProps.responses) {
|
} else if (props.responses !== nextProps.responses) {
|
||||||
// new response
|
// new response
|
||||||
for (const responseId in nextProps.responses) {
|
const resId = Array.from(nextProps.responses.keys()).find(
|
||||||
if (props.responses[responseId] == null) {
|
(responseId: RequestId) => !props.responses.get(responseId),
|
||||||
const newRow = buildRow(
|
|
||||||
nextProps.requests[responseId],
|
|
||||||
nextProps.responses[responseId],
|
|
||||||
);
|
|
||||||
const index = rows.findIndex(
|
|
||||||
r => r.key === nextProps.requests[responseId]?.id,
|
|
||||||
);
|
);
|
||||||
|
if (resId) {
|
||||||
|
const request = nextProps.requests.get(resId);
|
||||||
|
// sanity check; to pass null check
|
||||||
|
if (request) {
|
||||||
|
const newRow = buildRow(request, nextProps.responses.get(resId));
|
||||||
|
const index = rows.findIndex((r: TableBodyRow) => r.key === request.id);
|
||||||
if (index > -1 && newRow) {
|
if (index > -1 && newRow) {
|
||||||
rows[index] = newRow;
|
rows[index] = newRow;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rows.sort((a, b) => Number(a.sortKey) - Number(b.sortKey));
|
rows.sort(
|
||||||
|
(a: TableBodyRow, b: TableBodyRow) => Number(a.sortKey) - Number(b.sortKey),
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sortedRows: rows,
|
sortedRows: rows,
|
||||||
@@ -400,8 +406,8 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = calculateState(
|
this.state = calculateState(
|
||||||
{
|
{
|
||||||
requests: {},
|
requests: new Map(),
|
||||||
responses: {},
|
responses: new Map(),
|
||||||
},
|
},
|
||||||
props,
|
props,
|
||||||
);
|
);
|
||||||
@@ -411,13 +417,20 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
|||||||
this.setState(calculateState(this.props, nextProps, this.state.sortedRows));
|
this.setState(calculateState(this.props, nextProps, this.state.sortedRows));
|
||||||
}
|
}
|
||||||
|
|
||||||
contextMenuItems() {
|
contextMenuItems(): Array<MenuItemConstructorOptions> {
|
||||||
|
type ContextMenuType =
|
||||||
|
| 'normal'
|
||||||
|
| 'separator'
|
||||||
|
| 'submenu'
|
||||||
|
| 'checkbox'
|
||||||
|
| 'radio';
|
||||||
|
const separator: ContextMenuType = 'separator';
|
||||||
const {clear, copyRequestCurlCommand, highlightedRows} = this.props;
|
const {clear, copyRequestCurlCommand, highlightedRows} = this.props;
|
||||||
const highlightedMenuItems =
|
const highlightedMenuItems =
|
||||||
highlightedRows && highlightedRows.size === 1
|
highlightedRows && highlightedRows.size === 1
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
type: 'separator',
|
type: separator,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Copy as cURL',
|
label: 'Copy as cURL',
|
||||||
@@ -428,7 +441,7 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
|||||||
|
|
||||||
return highlightedMenuItems.concat([
|
return highlightedMenuItems.concat([
|
||||||
{
|
{
|
||||||
type: 'separator',
|
type: separator,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Clear all',
|
label: 'Clear all',
|
||||||
@@ -439,7 +452,9 @@ class NetworkTable extends PureComponent<NetworkTableProps, NetworkTableState> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<NetworkTable.ContextMenu items={this.contextMenuItems()}>
|
<NetworkTable.ContextMenu
|
||||||
|
items={this.contextMenuItems()}
|
||||||
|
component={FlexColumn}>
|
||||||
<SearchableTable
|
<SearchableTable
|
||||||
virtual={true}
|
virtual={true}
|
||||||
multiline={false}
|
multiline={false}
|
||||||
@@ -468,7 +483,7 @@ const Icon = styled(Glyph)({
|
|||||||
});
|
});
|
||||||
|
|
||||||
class StatusColumn extends PureComponent<{
|
class StatusColumn extends PureComponent<{
|
||||||
children?: number,
|
children?: number;
|
||||||
}> {
|
}> {
|
||||||
render() {
|
render() {
|
||||||
const {children} = this.props;
|
const {children} = this.props;
|
||||||
@@ -488,8 +503,8 @@ class StatusColumn extends PureComponent<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DurationColumn extends PureComponent<{
|
class DurationColumn extends PureComponent<{
|
||||||
request: Request,
|
request: Request;
|
||||||
response: ?Response,
|
response: Response | null | undefined;
|
||||||
}> {
|
}> {
|
||||||
static Text = styled(Text)({
|
static Text = styled(Text)({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -511,7 +526,7 @@ class DurationColumn extends PureComponent<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SizeColumn extends PureComponent<{
|
class SizeColumn extends PureComponent<{
|
||||||
response: ?Response,
|
response: Response | null | undefined;
|
||||||
}> {
|
}> {
|
||||||
static Text = styled(Text)({
|
static Text = styled(Text)({
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@@ -529,7 +544,11 @@ class SizeColumn extends PureComponent<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getResponseLength(response) {
|
getResponseLength(response: Response | null | undefined) {
|
||||||
|
if (!response) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
let length = 0;
|
let length = 0;
|
||||||
const lengthString = response.headers
|
const lengthString = response.headers
|
||||||
? getHeaderValue(response.headers, 'content-length')
|
? getHeaderValue(response.headers, 'content-length')
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Network",
|
"name": "Network",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "index.js",
|
"main": "index.tsx",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pako": "^1.0.6",
|
"pako": "^1.0.6",
|
||||||
|
|||||||
10
types/XmlBeautifier.d.tsx
Normal file
10
types/XmlBeautifier.d.tsx
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018-present Facebook.
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare module 'xml-beautifier' {
|
||||||
|
export default function(xml: string, indent?: string): string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user