Add intercept response filter

This commit is contained in:
David Stotijn
2022-03-21 10:33:11 +01:00
parent cf55456c42
commit 89141afd3b
15 changed files with 556 additions and 113 deletions

View File

@ -5,8 +5,10 @@ query ActiveProject {
isActive
settings {
intercept {
enabled
requestsEnabled
responsesEnabled
requestFilter
responseFilter
}
}
}

View File

@ -29,7 +29,7 @@ function Actions(): JSX.Element {
{clearLogsResult.error && <Alert severity="error">Failed to clear HTTP logs: {clearLogsResult.error}</Alert>}
{activeProject?.settings.intercept.enabled && (
{(activeProject?.settings.intercept.requestsEnabled || activeProject?.settings.intercept.responsesEnabled) && (
<Link href="/proxy/intercept/?id=" passHref>
<Button
variant="contained"

View File

@ -11,6 +11,7 @@ import {
Switch,
Tab,
TextField,
TextFieldProps,
Typography,
} from "@mui/material";
import { SwitchBaseProps } from "@mui/material/internal/SwitchBase";
@ -25,6 +26,26 @@ enum TabValue {
Intercept = "intercept",
}
function FilterTextField(props: TextFieldProps): JSX.Element {
return (
<TextField
color="primary"
variant="outlined"
InputProps={{
sx: { fontFamily: "'JetBrains Mono', monospace" },
autoCorrect: "false",
spellCheck: "false",
}}
InputLabelProps={{
shrink: true,
}}
margin="normal"
sx={{ mr: 1 }}
{...props}
/>
);
}
export default function Settings(): JSX.Element {
const client = useApolloClient();
const activeProject = useActiveProject();
@ -41,16 +62,22 @@ export default function Settings(): JSX.Element {
}));
setInterceptReqFilter(data.updateInterceptSettings.requestFilter || "");
setInterceptResFilter(data.updateInterceptSettings.responseFilter || "");
},
});
const [interceptReqFilter, setInterceptReqFilter] = useState("");
const [interceptResFilter, setInterceptResFilter] = useState("");
useEffect(() => {
setInterceptReqFilter(activeProject?.settings.intercept.requestFilter || "");
}, [activeProject?.settings.intercept.requestFilter]);
const handleInterceptEnabled: SwitchBaseProps["onChange"] = (e, checked) => {
useEffect(() => {
setInterceptResFilter(activeProject?.settings.intercept.responseFilter || "");
}, [activeProject?.settings.intercept.responseFilter]);
const handleReqInterceptEnabled: SwitchBaseProps["onChange"] = (e, checked) => {
if (!activeProject) {
e.preventDefault();
return;
@ -60,7 +87,23 @@ export default function Settings(): JSX.Element {
variables: {
input: {
...withoutTypename(activeProject.settings.intercept),
enabled: checked,
requestsEnabled: checked,
},
},
});
};
const handleResInterceptEnabled: SwitchBaseProps["onChange"] = (e, checked) => {
if (!activeProject) {
e.preventDefault();
return;
}
updateInterceptSettings({
variables: {
input: {
...withoutTypename(activeProject.settings.intercept),
responsesEnabled: checked,
},
},
});
@ -81,6 +124,21 @@ export default function Settings(): JSX.Element {
});
};
const handleInterceptResFilter = () => {
if (!activeProject) {
return;
}
updateInterceptSettings({
variables: {
input: {
...withoutTypename(activeProject.settings.intercept),
responseFilter: interceptResFilter,
},
},
});
};
const [tabValue, setTabValue] = useState(TabValue.Intercept);
const tabSx = {
@ -111,16 +169,19 @@ export default function Settings(): JSX.Element {
</TabList>
<TabPanel value={TabValue.Intercept} sx={{ px: 0 }}>
<FormControl>
<Typography variant="h6" sx={{ mt: 3, mb: 1 }}>
Requests
</Typography>
<FormControl sx={{ mb: 2 }}>
<FormControlLabel
control={
<Switch
disabled={updateIntercepSettingsResult.loading}
onChange={handleInterceptEnabled}
checked={activeProject.settings.intercept.enabled}
onChange={handleReqInterceptEnabled}
checked={activeProject.settings.intercept.requestsEnabled}
/>
}
label="Enable proxy interception"
label="Enable request interception"
labelPlacement="start"
sx={{ display: "inline-block", m: 0 }}
/>
@ -129,28 +190,13 @@ export default function Settings(): JSX.Element {
<Link href="/proxy/intercept">manual review</Link>.
</FormHelperText>
</FormControl>
<Typography variant="h6" sx={{ mt: 3 }}>
Rules
</Typography>
<form>
<FormControl sx={{ width: "50%" }}>
<TextField
<FilterTextField
label="Request filter"
placeholder={`method = "GET" OR url =~ "/foobar"`}
color="primary"
variant="outlined"
placeholder={`Example: method = "GET" OR url =~ "/foobar"`}
value={interceptReqFilter}
onChange={(e) => setInterceptReqFilter(e.target.value)}
InputProps={{
sx: { fontFamily: "'JetBrains Mono', monospace" },
autoCorrect: "false",
spellCheck: "false",
}}
InputLabelProps={{
shrink: true,
}}
margin="normal"
sx={{ mr: 1 }}
/>
<FormHelperText>
Filter expression to match incoming requests on. When set, only matching requests are intercepted.
@ -172,6 +218,55 @@ export default function Settings(): JSX.Element {
Update
</Button>
</form>
<Typography variant="h6" sx={{ mt: 3 }}>
Responses
</Typography>
<FormControl sx={{ mb: 2 }}>
<FormControlLabel
control={
<Switch
disabled={updateIntercepSettingsResult.loading}
onChange={handleResInterceptEnabled}
checked={activeProject.settings.intercept.responsesEnabled}
/>
}
label="Enable response interception"
labelPlacement="start"
sx={{ display: "inline-block", m: 0 }}
/>
<FormHelperText>
When enabled, HTTP responses received by the proxy are stalled for{" "}
<Link href="/proxy/intercept">manual review</Link>.
</FormHelperText>
</FormControl>
<form>
<FormControl sx={{ width: "50%" }}>
<FilterTextField
label="Response filter"
placeholder={`Example: statusCode =~ "^2" OR body =~ "foobar"`}
value={interceptResFilter}
onChange={(e) => setInterceptResFilter(e.target.value)}
/>
<FormHelperText>
Filter expression to match received responses on. When set, only matching responses are intercepted.
</FormHelperText>
</FormControl>
<Button
type="submit"
variant="text"
color="primary"
size="large"
sx={{
mt: 2,
py: 1.8,
}}
onClick={handleInterceptResFilter}
disabled={updateIntercepSettingsResult.loading}
startIcon={updateIntercepSettingsResult.loading ? <CircularProgress size={22} /> : undefined}
>
Update
</Button>
</form>
</TabPanel>
</TabContext>
</>

View File

@ -1,6 +1,8 @@
mutation UpdateInterceptSettings($input: UpdateInterceptSettingsInput!) {
updateInterceptSettings(input: $input) {
enabled
requestsEnabled
responsesEnabled
requestFilter
responseFilter
}
}

View File

@ -135,8 +135,10 @@ export type HttpResponseLog = {
export type InterceptSettings = {
__typename?: 'InterceptSettings';
enabled: Scalars['Boolean'];
requestFilter?: Maybe<Scalars['String']>;
requestsEnabled: Scalars['Boolean'];
responseFilter?: Maybe<Scalars['String']>;
responsesEnabled: Scalars['Boolean'];
};
export type ModifyRequestInput = {
@ -359,8 +361,10 @@ export type SenderRequestInput = {
};
export type UpdateInterceptSettingsInput = {
enabled: Scalars['Boolean'];
requestFilter?: InputMaybe<Scalars['String']>;
requestsEnabled: Scalars['Boolean'];
responseFilter?: InputMaybe<Scalars['String']>;
responsesEnabled: Scalars['Boolean'];
};
export type CancelRequestMutationVariables = Exact<{
@ -401,7 +405,7 @@ export type ModifyResponseMutation = { __typename?: 'Mutation', modifyResponse:
export type ActiveProjectQueryVariables = Exact<{ [key: string]: never; }>;
export type ActiveProjectQuery = { __typename?: 'Query', activeProject?: { __typename?: 'Project', id: string, name: string, isActive: boolean, settings: { __typename?: 'ProjectSettings', intercept: { __typename?: 'InterceptSettings', enabled: boolean, requestFilter?: string | null } } } | null };
export type ActiveProjectQuery = { __typename?: 'Query', activeProject?: { __typename?: 'Project', id: string, name: string, isActive: boolean, settings: { __typename?: 'ProjectSettings', intercept: { __typename?: 'InterceptSettings', requestsEnabled: boolean, responsesEnabled: boolean, requestFilter?: string | null, responseFilter?: string | null } } } | null };
export type CloseProjectMutationVariables = Exact<{ [key: string]: never; }>;
@ -513,7 +517,7 @@ export type UpdateInterceptSettingsMutationVariables = Exact<{
}>;
export type UpdateInterceptSettingsMutation = { __typename?: 'Mutation', updateInterceptSettings: { __typename?: 'InterceptSettings', enabled: boolean, requestFilter?: string | null } };
export type UpdateInterceptSettingsMutation = { __typename?: 'Mutation', updateInterceptSettings: { __typename?: 'InterceptSettings', requestsEnabled: boolean, responsesEnabled: boolean, requestFilter?: string | null, responseFilter?: string | null } };
export type GetInterceptedRequestsQueryVariables = Exact<{ [key: string]: never; }>;
@ -715,8 +719,10 @@ export const ActiveProjectDocument = gql`
isActive
settings {
intercept {
enabled
requestsEnabled
responsesEnabled
requestFilter
responseFilter
}
}
}
@ -1381,8 +1387,10 @@ export type GetSenderRequestsQueryResult = Apollo.QueryResult<GetSenderRequestsQ
export const UpdateInterceptSettingsDocument = gql`
mutation UpdateInterceptSettings($input: UpdateInterceptSettingsInput!) {
updateInterceptSettings(input: $input) {
enabled
requestsEnabled
responsesEnabled
requestFilter
responseFilter
}
}
`;