desktop/plugins/public/network/request-mocking/MockResponseDetails.tsx (202 lines of code) (raw):
/**
* Copyright (c) Meta Platforms, Inc. and 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} from 'react';
import {
NetworkRouteContext,
NetworkRouteManager,
Route,
} from './NetworkRouteManager';
import {RequestId} from '../types';
import {Button, Input, Select} from 'antd';
import {Layout, produce, Tabs, Tab, theme} from 'flipper-plugin';
import {CloseCircleOutlined, WarningOutlined} from '@ant-design/icons';
type Props = {
id: RequestId;
route: Route;
isDuplicated: boolean;
};
function HeaderInput(props: {
initialValue: string;
onUpdate: (newValue: string) => void;
style?: React.CSSProperties;
}) {
const [value, setValue] = useState(props.initialValue);
return (
<Input
type="text"
placeholder="Name"
value={value}
onChange={(event) => {
setValue(event.target.value);
props.onUpdate(event.target.value);
}}
style={props.style}
/>
);
}
function ResponseHeaders({
routeId,
route,
networkRouteManager,
}: {
routeId: string;
route: Route;
networkRouteManager: NetworkRouteManager;
}) {
return (
<Layout.Container gap style={{paddingRight: theme.space.small}}>
{Object.entries(route.responseHeaders).map(([id, header]) => (
<Layout.Horizontal center gap key={id}>
<HeaderInput
initialValue={header.key}
onUpdate={(newValue: string) => {
const newHeaders = produce(
route.responseHeaders,
(draftHeaders) => {
draftHeaders[id].key = newValue;
},
);
networkRouteManager.modifyRoute(routeId, {
responseHeaders: newHeaders,
});
}}
style={{width: 300}}
/>
<HeaderInput
initialValue={header.value}
onUpdate={(newValue: string) => {
const newHeaders = produce(
route.responseHeaders,
(draftHeaders) => {
draftHeaders[id].value = newValue;
},
);
networkRouteManager.modifyRoute(routeId, {
responseHeaders: newHeaders,
});
}}
/>
<Layout.Container
onClick={() => {
const newHeaders = produce(
route.responseHeaders,
(draftHeaders) => {
delete draftHeaders[id];
},
);
networkRouteManager.modifyRoute(routeId, {
responseHeaders: newHeaders,
});
}}>
<CloseCircleOutlined />
</Layout.Container>
</Layout.Horizontal>
))}
</Layout.Container>
);
}
const httpMethods = [
'GET',
'POST',
'PATCH',
'HEAD',
'PUT',
'DELETE',
'TRACE',
'OPTIONS',
'CONNECT',
].map((v) => ({value: v, label: v}));
export function MockResponseDetails({id, route, isDuplicated}: Props) {
const networkRouteManager = useContext(NetworkRouteContext);
const [nextHeaderId, setNextHeaderId] = useState(0);
const {requestUrl, requestMethod, responseData, responseStatus} = route;
let formattedResponse = '';
try {
formattedResponse = JSON.stringify(JSON.parse(responseData), null, 2);
} catch (e) {
formattedResponse = responseData;
}
return (
<Layout.Container gap>
<Layout.Horizontal gap>
<Select
value={requestMethod}
options={httpMethods}
onChange={(text) =>
networkRouteManager.modifyRoute(id, {requestMethod: text})
}
/>
<Input
type="text"
placeholder="URL"
value={requestUrl}
onChange={(event) =>
networkRouteManager.modifyRoute(id, {
requestUrl: event.target.value,
})
}
style={{flex: 1}}
/>
<Input
type="text"
placeholder="STATUS"
value={responseStatus}
onChange={(event) =>
networkRouteManager.modifyRoute(id, {
responseStatus: event.target.value,
})
}
style={{width: 100}}
/>
</Layout.Horizontal>
{isDuplicated && (
<Layout.Horizontal gap>
<WarningOutlined />
Route is duplicated (Same URL and Method)
</Layout.Horizontal>
)}
<Layout.Container height={500}>
<Tabs grow>
<Tab tab={'Data'}>
<Input.TextArea
wrap="soft"
autoComplete="off"
spellCheck={false}
value={formattedResponse}
onChange={(event) =>
networkRouteManager.modifyRoute(id, {
responseData: event.target.value,
})
}
style={{flex: 1}}
/>
</Tab>
<Tab tab={'Headers'}>
<Layout.Top gap>
<Layout.Horizontal>
<Button
onClick={() => {
const newHeaders = {
...route.responseHeaders,
[nextHeaderId.toString()]: {key: '', value: ''},
};
setNextHeaderId(nextHeaderId + 1);
networkRouteManager.modifyRoute(id, {
responseHeaders: newHeaders,
});
}}>
Add Header
</Button>
</Layout.Horizontal>
<Layout.ScrollContainer>
<ResponseHeaders
routeId={id}
route={route}
networkRouteManager={networkRouteManager}
/>
</Layout.ScrollContainer>
</Layout.Top>
</Tab>
</Tabs>
</Layout.Container>
</Layout.Container>
);
}