From faaf8c4f32642d26ad66c96e5963865a61098f44 Mon Sep 17 00:00:00 2001 From: Anton Nikolaev Date: Fri, 1 Nov 2019 06:53:42 -0700 Subject: [PATCH] Search full request details Summary: Simple implementation of searching through request/response body on "network" tab Reviewed By: passy Differential Revision: D18268026 fbshipit-source-id: 39386d2d6ec50b47c3ca3dec976821282b51636f --- src/plugins/network/index.tsx | 14 +++++++--- src/ui/components/ToggleSwitch.tsx | 3 ++- src/ui/components/searchable/Searchable.tsx | 27 +++++++++++++++++++ .../components/searchable/SearchableTable.tsx | 15 ++++++++++- src/ui/components/table/types.tsx | 2 ++ 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/plugins/network/index.tsx b/src/plugins/network/index.tsx index 789840590..a46f58a55 100644 --- a/src/plugins/network/index.tsx +++ b/src/plugins/network/index.tsx @@ -283,8 +283,11 @@ ${request.headers ) .join('\n')}`; - if (request.data) { - copyText += `\n\n${decodeBody(request)}`; + const requestData = request.data ? decodeBody(request) : null; + const responseData = response && response.data ? decodeBody(response) : null; + + if (requestData) { + copyText += `\n\n${requestData}`; } if (response) { @@ -300,8 +303,8 @@ ${response.headers .join('\n')}`; } - if (response) { - copyText += `\n\n${decodeBody(response)}`; + if (responseData) { + copyText += `\n\n${responseData}`; } return { @@ -346,6 +349,8 @@ ${response.headers sortKey: request.timestamp, copyText, highlightOnHover: true, + requestBody: requestData, + responseBody: responseData, }; } @@ -471,6 +476,7 @@ class NetworkTable extends PureComponent { highlightedRows={this.props.highlightedRows} rowLineHeight={26} allowRegexSearch={true} + allowBodySearch={true} zebra={false} actions={} /> diff --git a/src/ui/components/ToggleSwitch.tsx b/src/ui/components/ToggleSwitch.tsx index cce1c0283..465b458d3 100644 --- a/src/ui/components/ToggleSwitch.tsx +++ b/src/ui/components/ToggleSwitch.tsx @@ -56,6 +56,7 @@ type Props = { toggled?: boolean; className?: string; label?: string; + tooltip?: string; }; /** @@ -71,7 +72,7 @@ type Props = { export default class ToggleButton extends React.Component { render() { return ( - + ; allowRegexSearch?: boolean; + allowBodySearch?: boolean; regexEnabled?: boolean; + bodySearchEnabled?: boolean; }; type Props = { @@ -104,6 +106,7 @@ type Props = { onFilterChange: (filters: Array) => void; defaultFilters: Array; allowRegexSearch: boolean; + allowBodySearch: boolean; }; type State = { @@ -112,6 +115,7 @@ type State = { searchTerm: string; hasFocus: boolean; regexEnabled: boolean; + bodySearchEnabled: boolean; compiledRegex: RegExp | null | undefined; }; @@ -137,6 +141,7 @@ const Searchable = ( searchTerm: '', hasFocus: false, regexEnabled: false, + bodySearchEnabled: false, compiledRegex: null, }; @@ -149,6 +154,7 @@ const Searchable = ( | { filters: Array; regexEnabled?: boolean; + bodySearchEnabled?: boolean; searchTerm?: string; } | undefined; @@ -192,6 +198,8 @@ const Searchable = ( searchTerm: searchTerm, filters: savedState.filters || this.state.filters, regexEnabled: savedState.regexEnabled || this.state.regexEnabled, + bodySearchEnabled: + savedState.bodySearchEnabled || this.state.bodySearchEnabled, compiledRegex: compileRegex(searchTerm), }); } @@ -202,6 +210,7 @@ const Searchable = ( this.getTableKey() && (prevState.searchTerm !== this.state.searchTerm || prevState.regexEnabled != this.state.regexEnabled || + prevState.bodySearchEnabled != this.state.bodySearchEnabled || prevState.filters !== this.state.filters) ) { window.localStorage.setItem( @@ -210,6 +219,7 @@ const Searchable = ( searchTerm: this.state.searchTerm, filters: this.state.filters, regexEnabled: this.state.regexEnabled, + bodySearchEnabled: this.state.bodySearchEnabled, }), ); if (this.props.onFilterChange != null) { @@ -406,6 +416,12 @@ const Searchable = ( }); }; + onBodySearchToggled = () => { + this.setState({ + bodySearchEnabled: !this.state.bodySearchEnabled, + }); + }; + hasFocus = (): boolean => { return this.state.focusedToken !== -1 || this.state.hasFocus; }; @@ -464,6 +480,16 @@ const Searchable = ( label={'Regex'} /> ) : null} + {this.props.allowBodySearch ? ( + + ) : null} {(this.state.searchTerm || this.state.filters.length > 0) && ( × )} @@ -475,6 +501,7 @@ const Searchable = ( addFilter={this.addFilter} searchTerm={this.state.searchTerm} regexEnabled={this.state.regexEnabled} + bodySearchEnabled={this.state.bodySearchEnabled} filters={this.state.filters} />, ]; diff --git a/src/ui/components/searchable/SearchableTable.tsx b/src/ui/components/searchable/SearchableTable.tsx index 87bf9dacd..c91142e28 100644 --- a/src/ui/components/searchable/SearchableTable.tsx +++ b/src/ui/components/searchable/SearchableTable.tsx @@ -60,6 +60,7 @@ function rowMatchesRegex(values: Array, regex: string): boolean { function rowMatchesSearchTerm( searchTerm: string, isRegex: boolean, + isBodySearchEnabled: boolean, row: TableBodyRow, ): boolean { if (searchTerm == null || searchTerm.length === 0) { @@ -68,6 +69,14 @@ function rowMatchesSearchTerm( const rowValues = Object.keys(row.columns).map(key => textContent(row.columns[key].value), ); + if (isBodySearchEnabled) { + if (row.requestBody) { + rowValues.push(row.requestBody); + } + if (row.responseBody) { + rowValues.push(row.responseBody); + } + } if (isRegex) { return rowMatchesRegex(rowValues, searchTerm); } @@ -80,9 +89,10 @@ const filterRowsFactory = ( filters: Array, searchTerm: string, regexSearch: boolean, + bodySearch: boolean, ) => (row: TableBodyRow): boolean => rowMatchesFilters(filters, row) && - rowMatchesSearchTerm(searchTerm, regexSearch, row); + rowMatchesSearchTerm(searchTerm, regexSearch, bodySearch, row); class SearchableManagedTable extends PureComponent { static defaultProps = { @@ -94,6 +104,7 @@ class SearchableManagedTable extends PureComponent { this.props.filters, this.props.searchTerm, this.props.regexEnabled || false, + this.props.bodySearchEnabled || false, ), }; @@ -105,6 +116,7 @@ class SearchableManagedTable extends PureComponent { if ( nextProps.searchTerm !== this.props.searchTerm || nextProps.regexEnabled != this.props.regexEnabled || + nextProps.bodySearchEnabled != this.props.bodySearchEnabled || !deepEqual(this.props.filters, nextProps.filters) ) { this.setState({ @@ -112,6 +124,7 @@ class SearchableManagedTable extends PureComponent { nextProps.filters, nextProps.searchTerm, nextProps.regexEnabled || false, + nextProps.bodySearchEnabled || false, ), }); } diff --git a/src/ui/components/table/types.tsx b/src/ui/components/table/types.tsx index 81d91f0e0..904cdc5fd 100644 --- a/src/ui/components/table/types.tsx +++ b/src/ui/components/table/types.tsx @@ -55,6 +55,8 @@ export type TableBodyRow = { highlightedBackgroundColor?: BackgroundColorProperty | undefined; onDoubleClick?: (e: React.MouseEvent) => void; copyText?: string; + requestBody?: string | null | undefined; + responseBody?: string | null | undefined; highlightOnHover?: boolean; columns: { [key: string]: TableBodyColumn;