From 0a06d6c54676cfe46814f4ae16c5e52b0172c8f7 Mon Sep 17 00:00:00 2001 From: James Harmon Date: Fri, 18 Sep 2020 07:24:34 -0700 Subject: [PATCH] Add button to copy highlighted requests to routes (mock requests) (#1447) Summary: Add feature to Network mocks in the Network Plugin which would allow a user to highlight network requests and copy into new routes (mocks). For many production apps, network requests can contain many headers (easily 20 or more) and a large amount of data returned in the response (1000's of characters). Creating mocks for these manually is time consuming and error prone. It would be better to make mocks automatically by allowing the user to highlight desired requests and have them automatically copied into new routes, also copying the headers and the response data. Changelog: [network] Allow user to create new mock routes by highlighting existing network requests in the Network plugin Pull Request resolved: https://github.com/facebook/flipper/pull/1447 Test Plan: Tested this change manually by running through the following scenario using the sample Android app: 1). Run a GET request from the Sample app. Verify that the request/response is correct. Highlight the request to be copied. ![request-screen](https://user-images.githubusercontent.com/337874/89750945-baf6b700-da93-11ea-86f6-3ec600e1727d.png) 2). Go to the Mock dialog by clicking on the "Mock" button ![mock-screen](https://user-images.githubusercontent.com/337874/89750979-e8436500-da93-11ea-9dde-8717436a03bb.png) 3). Click on "Copy Highlighted Call" to create a mock Route from the selected request. Verify that the "data" and "headers" tab panels are correct. ![mock-screen-2](https://user-images.githubusercontent.com/337874/89751029-132db900-da94-11ea-9419-6294a304f232.png) ![mock-screen-3](https://user-images.githubusercontent.com/337874/89751053-29d41000-da94-11ea-85db-a034a20e5c18.png) Close the Dialog 4). Run the request again from the sample app and verify that a mock request is returned with the correct data. ![response-mock-1](https://user-images.githubusercontent.com/337874/89751083-4cfebf80-da94-11ea-8523-192ebdc869f6.png) ![response-mock-2](https://user-images.githubusercontent.com/337874/89751092-58ea8180-da94-11ea-85fe-82b7a660789f.png) Reviewed By: cekkaewnumchai Differential Revision: D23027793 Pulled By: mweststrate fbshipit-source-id: 197fd5c3d120a20b6bc5d9121ae781923d69b748 --- .../network/ManageMockResponsePanel.tsx | 82 ++++++++++++------- .../plugins/network/MockResponseDialog.tsx | 12 ++- desktop/plugins/network/index.tsx | 61 ++++++++++++++ 3 files changed, 124 insertions(+), 31 deletions(-) diff --git a/desktop/plugins/network/ManageMockResponsePanel.tsx b/desktop/plugins/network/ManageMockResponsePanel.tsx index 8f7427ce9..0174d9116 100644 --- a/desktop/plugins/network/ManageMockResponsePanel.tsx +++ b/desktop/plugins/network/ManageMockResponsePanel.tsx @@ -20,13 +20,18 @@ import { } from 'flipper'; import React, {useContext, useState, useMemo, useEffect} from 'react'; -import {Route} from './types'; +import {Route, Request, Response} from './types'; import {MockResponseDetails} from './MockResponseDetails'; import {NetworkRouteContext} from './index'; import {RequestId} from './types'; -type Props = {routes: {[id: string]: Route}}; +type Props = { + routes: {[id: string]: Route}; + highlightedRows: Set | null | undefined; + requests: {[id: string]: Request}; + responses: {[id: string]: Response}; +}; const ColumnSizes = {route: 'flex'}; @@ -35,7 +40,17 @@ const Columns = {route: {value: 'Route', resizable: false}}; const AddRouteButton = styled(FlexBox)({ color: colors.blackAlpha50, alignItems: 'center', - padding: 10, + padding: 5, + flexShrink: 0, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', +}); + +const CopyHighlightedCallsButton = styled(FlexBox)({ + color: colors.blueDark, + alignItems: 'center', + padding: 5, flexShrink: 0, whiteSpace: 'nowrap', overflow: 'hidden', @@ -119,12 +134,11 @@ function RouteRow(props: { showWarning: boolean; handleRemoveId: () => void; }) { - const [showCloseButton, setShowCloseButton] = useState(false); return ( - setShowCloseButton(true)} - onMouseLeave={() => setShowCloseButton(false)}> + + + + {props.showWarning && ( @@ -137,11 +151,6 @@ function RouteRow(props: { {props.text} )} - {showCloseButton && ( - - - - )} ); } @@ -171,21 +180,20 @@ function ManagedMockResponseRightPanel(props: { export function ManageMockResponsePanel(props: Props) { const networkRouteManager = useContext(NetworkRouteContext); const [selectedId, setSelectedId] = useState(null); - const [currentRouteSize, setCurrentRouteSize] = useState(0); - const {routes} = props; useEffect(() => { - const keys = Object.keys(routes); - const routeSize = keys.length; - if (currentRouteSize === routeSize) { - return; - } - if (routeSize > 0 && routeSize > currentRouteSize) { - setSelectedId(keys[routeSize - 1]); - } - setCurrentRouteSize(routeSize); - }, [routes]); - const duplicatedIds = useMemo(() => _duplicateIds(routes), [routes]); + setSelectedId((selectedId) => { + const keys = Object.keys(props.routes); + return keys.length === 0 + ? null + : selectedId === null || !keys.includes(selectedId) + ? keys[keys.length - 1] + : selectedId; + }); + }, [props.routes]); + const duplicatedIds = useMemo(() => _duplicateIds(props.routes), [ + props.routes, + ]); return ( @@ -201,12 +209,28 @@ export function ManageMockResponsePanel(props: Props) { />  Add Route + { + networkRouteManager.copyHighlightedCalls( + props.highlightedRows as Set, + props.requests, + props.responses, + ); + }}> + +  Copy Highlighted Calls + { + rows={_buildRows(props.routes, duplicatedIds, (id) => { networkRouteManager.removeRoute(id); setSelectedId(null); })} @@ -223,10 +247,10 @@ export function ManageMockResponsePanel(props: Props) { /> - {selectedId && routes.hasOwnProperty(selectedId) && ( + {selectedId && props.routes.hasOwnProperty(selectedId) && ( )} diff --git a/desktop/plugins/network/MockResponseDialog.tsx b/desktop/plugins/network/MockResponseDialog.tsx index 243ad1013..a484c8b0d 100644 --- a/desktop/plugins/network/MockResponseDialog.tsx +++ b/desktop/plugins/network/MockResponseDialog.tsx @@ -10,12 +10,15 @@ import {FlexColumn, Button, styled} from 'flipper'; import {ManageMockResponsePanel} from './ManageMockResponsePanel'; -import {Route} from './types'; +import {Route, Request, Response} from './types'; import React from 'react'; type Props = { routes: {[id: string]: Route}; onHide: () => void; + highlightedRows: Set | null | undefined; + requests: {[id: string]: Request}; + responses: {[id: string]: Response}; }; const Title = styled('div')({ @@ -39,7 +42,12 @@ export function MockResponseDialog(props: Props) { return ( Mock Network Responses - +