Files
flipper/desktop/plugins/public/network/request-mocking/ManageMockResponsePanel.tsx
bizzguy b378d8b946 fix problem with mock request data (#2340)
Summary:
Network Plugin - When creating a mock request from a selected request, the request data is not in the proper format.  It is decoded instead of just being copied from the call (which has already been decoded properly).  This PR fixes that problem.

Below is a screenshot showing the problem (which occurs for all text response data):

![image](https://user-images.githubusercontent.com/337874/118744068-423e3b80-b819-11eb-9076-216459517fdb.png)

## Changelog

Network Plugin - Fix problem with decoding request data for mocks copied from selection

Pull Request resolved: https://github.com/facebook/flipper/pull/2340

Test Plan:
Using the sample Android app, issue a network request

In Flipper, create a mock for the network request by selecting it and using the "Copy Selected Calls" function in the mock

Verify that the request data is readable:

![image](https://user-images.githubusercontent.com/337874/118744220-8af5f480-b819-11eb-9206-0fa40e7d7e46.png)

Note:

Testing was done using the sample app which uses responses with JSON data.  I was not able to provide testing for other types of calls, specifically calls that would return binary data.

Reviewed By: passy

Differential Revision: D28533224

Pulled By: mweststrate

fbshipit-source-id: ce11d23ade60843c110286f7a5bbeba91febbcf0
2021-05-19 03:15:30 -07:00

213 lines
5.9 KiB
TypeScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import React, {
useContext,
useState,
useMemo,
useEffect,
useCallback,
} from 'react';
import {MockResponseDetails} from './MockResponseDetails';
import {NetworkRouteContext, Route} from './NetworkRouteManager';
import {RequestId} from '../types';
import {Checkbox, Modal, Tooltip, Button, Typography} from 'antd';
import {
NUX,
Layout,
DataList,
Toolbar,
createState,
useValue,
} from 'flipper-plugin';
import {CloseCircleOutlined, WarningOutlined} from '@ant-design/icons';
const {Text} = Typography;
type Props = {
routes: {[id: string]: Route};
};
type RouteItem = {
id: string;
title: string;
route: Route;
isDuplicate: boolean;
};
// return ids that have the same pair of requestUrl and method; this will return only the duplicate
function _duplicateIds(routes: {[id: string]: Route}): Array<RequestId> {
const idSet: {[id: string]: {[method: string]: boolean}} = {};
return Object.entries(routes).reduce((acc: Array<RequestId>, [id, route]) => {
if (idSet.hasOwnProperty(route.requestUrl)) {
if (idSet[route.requestUrl].hasOwnProperty(route.requestMethod)) {
return acc.concat(id);
}
idSet[route.requestUrl] = {
...idSet[route.requestUrl],
[route.requestMethod]: true,
};
return acc;
} else {
idSet[route.requestUrl] = {[route.requestMethod]: true};
return acc;
}
}, []);
}
export function ManageMockResponsePanel(props: Props) {
const networkRouteManager = useContext(NetworkRouteContext);
const [selectedIdAtom] = useState(() => createState<RequestId | undefined>());
const selectedId = useValue(selectedIdAtom);
useEffect(() => {
selectedIdAtom.update((selectedId) => {
const keys = Object.keys(props.routes);
let returnValue: string | undefined = undefined;
// selectId is undefined when there are no rows or it is the first time rows are shown
if (selectedId === undefined) {
if (keys.length === 0) {
// there are no rows
returnValue = undefined;
} else {
// first time rows are shown
returnValue = keys[0];
}
} else {
if (keys.includes(selectedId)) {
returnValue = selectedId;
} else {
// selectedId row value not in routes so default to first line
returnValue = keys[0];
}
}
return returnValue;
});
}, [props.routes, selectedIdAtom]);
const duplicatedIds = useMemo(
() => _duplicateIds(props.routes),
[props.routes],
);
const items: RouteItem[] = Object.entries(props.routes).map(
([id, route]) => ({
id,
route,
title: route.requestUrl,
isDuplicate: duplicatedIds.includes(id),
}),
);
const handleDelete = useCallback(
(id: string) => {
Modal.confirm({
title: 'Are you sure you want to delete this item?',
icon: '',
onOk() {
networkRouteManager.removeRoute(id);
selectedIdAtom.set(undefined);
},
onCancel() {},
});
},
[networkRouteManager, selectedIdAtom],
);
const handleToggle = useCallback(
(id: string) => {
networkRouteManager.enableRoute(id);
},
[networkRouteManager],
);
const handleRender = useCallback(
(item: RouteItem) => (
<RouteEntry item={item} onDelete={handleDelete} onToggle={handleToggle} />
),
[handleDelete, handleToggle],
);
return (
<Layout.Left resizable style={{minHeight: 400}}>
<Layout.Top>
<Toolbar>
<Button
onClick={() => {
const newId = networkRouteManager.addRoute();
selectedIdAtom.set(newId);
}}>
Add Route
</Button>
<NUX
title="It is now possible to select calls from the network call list and convert them into mock routes."
placement="bottom">
<Button
onClick={() => {
networkRouteManager.copySelectedCalls();
}}>
Copy Selected Calls
</Button>
</NUX>
<Button onClick={networkRouteManager.importRoutes}>Import</Button>
<Button onClick={networkRouteManager.exportRoutes}>Export</Button>
<Button onClick={networkRouteManager.clearRoutes}>Clear</Button>
</Toolbar>
<DataList
items={items}
selection={selectedIdAtom}
onRenderItem={handleRender}
scrollable
/>
</Layout.Top>
<Layout.Container gap pad>
{selectedId && props.routes.hasOwnProperty(selectedId) && (
<MockResponseDetails
id={selectedId}
route={props.routes[selectedId]}
isDuplicated={duplicatedIds.includes(selectedId)}
/>
)}
</Layout.Container>
</Layout.Left>
);
}
const RouteEntry = ({
item,
onToggle,
onDelete,
}: {
item: RouteItem;
onToggle(id: string): void;
onDelete(id: string): void;
}) => {
const tip = item.route.enabled
? 'Un-check to disable mock route'
: 'Check to enable mock route';
return (
<Layout.Horizontal gap center>
<Tooltip title={tip} mouseEnterDelay={1.1}>
<Checkbox
onClick={() => onToggle(item.id)}
checked={item.route.enabled}></Checkbox>
</Tooltip>
{item.route.requestUrl.length === 0 ? (
<Text ellipsis>untitled</Text>
) : (
<Text ellipsis>{item.route.requestUrl}</Text>
)}
<Tooltip title="Click to delete mock route" mouseEnterDelay={1.1}>
<Layout.Horizontal onClick={() => onDelete(item.id)}>
<CloseCircleOutlined />
</Layout.Horizontal>
</Tooltip>
{item.isDuplicate && <WarningOutlined />}
</Layout.Horizontal>
);
};