Clear all HTTP request logs (#49)

* Create mutation to clear request logs

* Add UI for clearing all HTTP request logs

* Use consistent naming

* Explicitly delete only from http_requests

* Check if datebase is open

* Add confirmation dialog
This commit is contained in:
Michał Załęcki
2020-11-28 15:48:19 +01:00
committed by GitHub
parent efc115e961
commit e59b9d6663
12 changed files with 385 additions and 105 deletions

View File

@ -0,0 +1,53 @@
import React, { useState } from "react";
import Button from "@material-ui/core/Button";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
export function useConfirmationDialog() {
const [isOpen, setIsOpen] = useState(false);
const close = () => setIsOpen(false);
const open = () => setIsOpen(true);
return { open, close, isOpen };
}
interface ConfirmationDialog {
isOpen: boolean;
onClose: () => void;
onConfirm: () => void;
children: React.ReactNode;
}
export function ConfirmationDialog(props: ConfirmationDialog) {
const { onClose, onConfirm, isOpen, children } = props;
function confirm() {
onConfirm();
onClose();
}
return (
<Dialog
open={isOpen}
onClose={onClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Are you sure?</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
{children}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>Abort</Button>
<Button onClick={confirm} autoFocus>
Confirm
</Button>
</DialogActions>
</Dialog>
);
}

View File

@ -1,41 +1,24 @@
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { gql, useQuery } from "@apollo/client";
import Link from "next/link"; import Link from "next/link";
import { import {
Box, Box,
Typography,
CircularProgress, CircularProgress,
Link as MaterialLink, Link as MaterialLink,
Typography,
} from "@material-ui/core"; } from "@material-ui/core";
import Alert from "@material-ui/lab/Alert"; import Alert from "@material-ui/lab/Alert";
import RequestList from "./RequestList"; import RequestList from "./RequestList";
import LogDetail from "./LogDetail"; import LogDetail from "./LogDetail";
import CenteredPaper from "../CenteredPaper"; import CenteredPaper from "../CenteredPaper";
import { useHttpRequestLogs } from "./hooks/useHttpRequestLogs";
const HTTP_REQUEST_LOGS = gql`
query HttpRequestLogs {
httpRequestLogs {
id
method
url
timestamp
response {
statusCode
statusReason
}
}
}
`;
function LogsOverview(): JSX.Element { function LogsOverview(): JSX.Element {
const router = useRouter(); const router = useRouter();
const detailReqLogId = const detailReqLogId =
router.query.id && parseInt(router.query.id as string, 10); router.query.id && parseInt(router.query.id as string, 10);
const { loading, error, data } = useQuery(HTTP_REQUEST_LOGS, { const { loading, error, data } = useHttpRequestLogs();
pollInterval: 1000,
});
const handleLogClick = (reqId: number) => { const handleLogClick = (reqId: number) => {
router.push("/proxy/logs?id=" + reqId, undefined, { router.push("/proxy/logs?id=" + reqId, undefined, {

View File

@ -16,10 +16,16 @@ import {
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton";
import SearchIcon from "@material-ui/icons/Search"; import SearchIcon from "@material-ui/icons/Search";
import FilterListIcon from "@material-ui/icons/FilterList"; import FilterListIcon from "@material-ui/icons/FilterList";
import DeleteIcon from "@material-ui/icons/Delete";
import React, { useRef, useState } from "react"; import React, { useRef, useState } from "react";
import { gql, useApolloClient, useMutation, useQuery } from "@apollo/client"; import { gql, useMutation, useQuery } from "@apollo/client";
import { withoutTypename } from "../../lib/omitTypename"; import { withoutTypename } from "../../lib/omitTypename";
import { Alert } from "@material-ui/lab"; import { Alert } from "@material-ui/lab";
import { useClearHTTPRequestLog } from "./hooks/useClearHTTPRequestLog";
import {
ConfirmationDialog,
useConfirmationDialog,
} from "./ConfirmationDialog";
const FILTER = gql` const FILTER = gql`
query HttpRequestLogFilter { query HttpRequestLogFilter {
@ -79,15 +85,14 @@ function Search(): JSX.Element {
FILTER FILTER
); );
const client = useApolloClient();
const [ const [
setFilterMutate, setFilterMutate,
{ error: setFilterErr, loading: setFilterLoading }, { error: setFilterErr, loading: setFilterLoading },
] = useMutation<{ ] = useMutation<{
setHttpRequestLogFilter: SearchFilter | null; setHttpRequestLogFilter: SearchFilter | null;
}>(SET_FILTER, { }>(SET_FILTER, {
update(_, { data: { setHttpRequestLogFilter } }) { update(cache, { data: { setHttpRequestLogFilter } }) {
client.writeQuery({ cache.writeQuery({
query: FILTER, query: FILTER,
data: { data: {
httpRequestLogFilter: setHttpRequestLogFilter, httpRequestLogFilter: setHttpRequestLogFilter,
@ -96,6 +101,12 @@ function Search(): JSX.Element {
}, },
}); });
const [
clearHTTPRequestLog,
clearHTTPRequestLogResult,
] = useClearHTTPRequestLog();
const clearHTTPConfirmationDialog = useConfirmationDialog();
const filterRef = useRef<HTMLElement | null>(); const filterRef = useRef<HTMLElement | null>();
const [filterOpen, setFilterOpen] = useState(false); const [filterOpen, setFilterOpen] = useState(false);
@ -111,90 +122,112 @@ function Search(): JSX.Element {
}; };
return ( return (
<ClickAwayListener onClickAway={handleClickAway}> <Box>
<Box style={{ display: "inline-block" }}> <Error prefix="Error fetching filter" error={filterErr} />
{filterErr && ( <Error prefix="Error setting filter" error={setFilterErr} />
<Box mb={4}> <Error
<Alert severity="error"> prefix="Error clearing all HTTP logs"
Error fetching filter: {filterErr.message} error={clearHTTPRequestLogResult.error}
</Alert> />
</Box> <Box style={{ display: "flex", flex: 1 }}>
)} <ClickAwayListener onClickAway={handleClickAway}>
{setFilterErr && ( <Paper
<Box mb={4}> component="form"
<Alert severity="error"> onSubmit={handleSubmit}
Error setting filter: {setFilterErr.message} ref={filterRef}
</Alert> className={classes.root}
</Box> >
)} <Tooltip title="Toggle filter options">
<Paper <IconButton
component="form" className={classes.iconButton}
onSubmit={handleSubmit} onClick={() => setFilterOpen(!filterOpen)}
ref={filterRef} style={{
className={classes.root} color:
> filter?.httpRequestLogFilter !== null
<Tooltip title="Toggle filter options"> ? theme.palette.secondary.main
<IconButton : "inherit",
className={classes.iconButton} }}
onClick={() => setFilterOpen(!filterOpen)} >
style={{ {filterLoading || setFilterLoading ? (
color: <CircularProgress
filter?.httpRequestLogFilter !== null className={classes.filterLoading}
? theme.palette.secondary.main size={23}
: "inherit", />
}} ) : (
> <FilterListIcon />
{filterLoading || setFilterLoading ? ( )}
<CircularProgress className={classes.filterLoading} size={23} /> </IconButton>
) : ( </Tooltip>
<FilterListIcon /> <InputBase
)} className={classes.input}
</IconButton> placeholder="Search proxy logs…"
</Tooltip> onFocus={() => setFilterOpen(true)}
<InputBase
className={classes.input}
placeholder="Search proxy logs…"
onFocus={() => setFilterOpen(true)}
/>
<Tooltip title="Search">
<IconButton type="submit" className={classes.iconButton}>
<SearchIcon />
</IconButton>
</Tooltip>
</Paper>
<Popper
className={classes.filterPopper}
open={filterOpen}
anchorEl={filterRef.current}
placement="bottom-start"
>
<Paper className={classes.filterOptions}>
<FormControlLabel
control={
<Checkbox
checked={
filter?.httpRequestLogFilter?.onlyInScope ? true : false
}
disabled={filterLoading || setFilterLoading}
onChange={(e) =>
setFilterMutate({
variables: {
filter: {
...withoutTypename(filter?.httpRequestLogFilter),
onlyInScope: e.target.checked,
},
},
})
}
/>
}
label="Only show in-scope requests"
/> />
<Tooltip title="Search">
<IconButton type="submit" className={classes.iconButton}>
<SearchIcon />
</IconButton>
</Tooltip>
<Popper
className={classes.filterPopper}
open={filterOpen}
anchorEl={filterRef.current}
placement="bottom-start"
>
<Paper className={classes.filterOptions}>
<FormControlLabel
control={
<Checkbox
checked={
filter?.httpRequestLogFilter?.onlyInScope ? true : false
}
disabled={filterLoading || setFilterLoading}
onChange={(e) =>
setFilterMutate({
variables: {
filter: {
...withoutTypename(filter?.httpRequestLogFilter),
onlyInScope: e.target.checked,
},
},
})
}
/>
}
label="Only show in-scope requests"
/>
</Paper>
</Popper>
</Paper> </Paper>
</Popper> </ClickAwayListener>
<Box style={{ marginLeft: "auto" }}>
<Tooltip title="Clear all">
<IconButton onClick={clearHTTPConfirmationDialog.open}>
<DeleteIcon />
</IconButton>
</Tooltip>
</Box>
</Box> </Box>
</ClickAwayListener> <ConfirmationDialog
isOpen={clearHTTPConfirmationDialog.isOpen}
onClose={clearHTTPConfirmationDialog.close}
onConfirm={clearHTTPRequestLog}
>
All proxy logs are going to be removed. This action cannot be undone.
</ConfirmationDialog>
</Box>
);
}
function Error(props: { prefix: string; error?: Error }) {
if (!props.error) return null;
return (
<Box mb={4}>
<Alert severity="error">
{props.prefix}: {props.error.message}
</Alert>
</Box>
); );
} }

View File

@ -0,0 +1,16 @@
import { gql, useMutation } from "@apollo/client";
import { HTTP_REQUEST_LOGS } from "./useHttpRequestLogs";
const CLEAR_HTTP_REQUEST_LOG = gql`
mutation ClearHTTPRequestLog {
clearHTTPRequestLog {
success
}
}
`;
export function useClearHTTPRequestLog() {
return useMutation(CLEAR_HTTP_REQUEST_LOG, {
refetchQueries: [{ query: HTTP_REQUEST_LOGS }],
});
}

View File

@ -0,0 +1,22 @@
import { gql, useQuery } from "@apollo/client";
export const HTTP_REQUEST_LOGS = gql`
query HttpRequestLogs {
httpRequestLogs {
id
method
url
timestamp
response {
statusCode
statusReason
}
}
}
`;
export function useHttpRequestLogs() {
return useQuery(HTTP_REQUEST_LOGS, {
pollInterval: 1000,
});
}

View File

@ -43,6 +43,10 @@ type DirectiveRoot struct {
} }
type ComplexityRoot struct { type ComplexityRoot struct {
ClearHTTPRequestLogResult struct {
Success func(childComplexity int) int
}
CloseProjectResult struct { CloseProjectResult struct {
Success func(childComplexity int) int Success func(childComplexity int) int
} }
@ -81,6 +85,7 @@ type ComplexityRoot struct {
} }
Mutation struct { Mutation struct {
ClearHTTPRequestLog func(childComplexity int) int
CloseProject func(childComplexity int) int CloseProject func(childComplexity int) int
DeleteProject func(childComplexity int, name string) int DeleteProject func(childComplexity int, name string) int
OpenProject func(childComplexity int, name string) int OpenProject func(childComplexity int, name string) int
@ -118,6 +123,7 @@ type MutationResolver interface {
OpenProject(ctx context.Context, name string) (*Project, error) OpenProject(ctx context.Context, name string) (*Project, error)
CloseProject(ctx context.Context) (*CloseProjectResult, error) CloseProject(ctx context.Context) (*CloseProjectResult, error)
DeleteProject(ctx context.Context, name string) (*DeleteProjectResult, error) DeleteProject(ctx context.Context, name string) (*DeleteProjectResult, error)
ClearHTTPRequestLog(ctx context.Context) (*ClearHTTPRequestLogResult, error)
SetScope(ctx context.Context, scope []ScopeRuleInput) ([]ScopeRule, error) SetScope(ctx context.Context, scope []ScopeRuleInput) ([]ScopeRule, error)
SetHTTPRequestLogFilter(ctx context.Context, filter *HTTPRequestLogFilterInput) (*HTTPRequestLogFilter, error) SetHTTPRequestLogFilter(ctx context.Context, filter *HTTPRequestLogFilterInput) (*HTTPRequestLogFilter, error)
} }
@ -145,6 +151,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
_ = ec _ = ec
switch typeName + "." + field { switch typeName + "." + field {
case "ClearHTTPRequestLogResult.success":
if e.complexity.ClearHTTPRequestLogResult.Success == nil {
break
}
return e.complexity.ClearHTTPRequestLogResult.Success(childComplexity), true
case "CloseProjectResult.success": case "CloseProjectResult.success":
if e.complexity.CloseProjectResult.Success == nil { if e.complexity.CloseProjectResult.Success == nil {
break break
@ -278,6 +291,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.HTTPResponseLog.StatusReason(childComplexity), true return e.complexity.HTTPResponseLog.StatusReason(childComplexity), true
case "Mutation.clearHTTPRequestLog":
if e.complexity.Mutation.ClearHTTPRequestLog == nil {
break
}
return e.complexity.Mutation.ClearHTTPRequestLog(childComplexity), true
case "Mutation.closeProject": case "Mutation.closeProject":
if e.complexity.Mutation.CloseProject == nil { if e.complexity.Mutation.CloseProject == nil {
break break
@ -553,6 +573,10 @@ type DeleteProjectResult {
success: Boolean! success: Boolean!
} }
type ClearHTTPRequestLogResult {
success: Boolean!
}
input HttpRequestLogFilterInput { input HttpRequestLogFilterInput {
onlyInScope: Boolean onlyInScope: Boolean
} }
@ -574,6 +598,7 @@ type Mutation {
openProject(name: String!): Project openProject(name: String!): Project
closeProject: CloseProjectResult! closeProject: CloseProjectResult!
deleteProject(name: String!): DeleteProjectResult! deleteProject(name: String!): DeleteProjectResult!
clearHTTPRequestLog: ClearHTTPRequestLogResult!
setScope(scope: [ScopeRuleInput!]!): [ScopeRule!]! setScope(scope: [ScopeRuleInput!]!): [ScopeRule!]!
setHttpRequestLogFilter( setHttpRequestLogFilter(
filter: HttpRequestLogFilterInput filter: HttpRequestLogFilterInput
@ -730,6 +755,41 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
// region **************************** field.gotpl ***************************** // region **************************** field.gotpl *****************************
func (ec *executionContext) _ClearHTTPRequestLogResult_success(ctx context.Context, field graphql.CollectedField, obj *ClearHTTPRequestLogResult) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "ClearHTTPRequestLogResult",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Success, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _CloseProjectResult_success(ctx context.Context, field graphql.CollectedField, obj *CloseProjectResult) (ret graphql.Marshaler) { func (ec *executionContext) _CloseProjectResult_success(ctx context.Context, field graphql.CollectedField, obj *CloseProjectResult) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -1502,6 +1562,41 @@ func (ec *executionContext) _Mutation_deleteProject(ctx context.Context, field g
return ec.marshalNDeleteProjectResult2ᚖgithubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐDeleteProjectResult(ctx, field.Selections, res) return ec.marshalNDeleteProjectResult2ᚖgithubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐDeleteProjectResult(ctx, field.Selections, res)
} }
func (ec *executionContext) _Mutation_clearHTTPRequestLog(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Mutation",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().ClearHTTPRequestLog(rctx)
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*ClearHTTPRequestLogResult)
fc.Result = res
return ec.marshalNClearHTTPRequestLogResult2ᚖgithubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐClearHTTPRequestLogResult(ctx, field.Selections, res)
}
func (ec *executionContext) _Mutation_setScope(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Mutation_setScope(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -3271,6 +3366,33 @@ func (ec *executionContext) unmarshalInputScopeRuleInput(ctx context.Context, ob
// region **************************** object.gotpl **************************** // region **************************** object.gotpl ****************************
var clearHTTPRequestLogResultImplementors = []string{"ClearHTTPRequestLogResult"}
func (ec *executionContext) _ClearHTTPRequestLogResult(ctx context.Context, sel ast.SelectionSet, obj *ClearHTTPRequestLogResult) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, clearHTTPRequestLogResultImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("ClearHTTPRequestLogResult")
case "success":
out.Values[i] = ec._ClearHTTPRequestLogResult_success(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var closeProjectResultImplementors = []string{"CloseProjectResult"} var closeProjectResultImplementors = []string{"CloseProjectResult"}
func (ec *executionContext) _CloseProjectResult(ctx context.Context, sel ast.SelectionSet, obj *CloseProjectResult) graphql.Marshaler { func (ec *executionContext) _CloseProjectResult(ctx context.Context, sel ast.SelectionSet, obj *CloseProjectResult) graphql.Marshaler {
@ -3516,6 +3638,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
invalids++ invalids++
} }
case "clearHTTPRequestLog":
out.Values[i] = ec._Mutation_clearHTTPRequestLog(ctx, field)
if out.Values[i] == graphql.Null {
invalids++
}
case "setScope": case "setScope":
out.Values[i] = ec._Mutation_setScope(ctx, field) out.Values[i] = ec._Mutation_setScope(ctx, field)
if out.Values[i] == graphql.Null { if out.Values[i] == graphql.Null {
@ -3985,6 +4112,20 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
return res return res
} }
func (ec *executionContext) marshalNClearHTTPRequestLogResult2githubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐClearHTTPRequestLogResult(ctx context.Context, sel ast.SelectionSet, v ClearHTTPRequestLogResult) graphql.Marshaler {
return ec._ClearHTTPRequestLogResult(ctx, sel, &v)
}
func (ec *executionContext) marshalNClearHTTPRequestLogResult2ᚖgithubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐClearHTTPRequestLogResult(ctx context.Context, sel ast.SelectionSet, v *ClearHTTPRequestLogResult) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._ClearHTTPRequestLogResult(ctx, sel, v)
}
func (ec *executionContext) marshalNCloseProjectResult2githubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐCloseProjectResult(ctx context.Context, sel ast.SelectionSet, v CloseProjectResult) graphql.Marshaler { func (ec *executionContext) marshalNCloseProjectResult2githubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐCloseProjectResult(ctx context.Context, sel ast.SelectionSet, v CloseProjectResult) graphql.Marshaler {
return ec._CloseProjectResult(ctx, sel, &v) return ec._CloseProjectResult(ctx, sel, &v)
} }

View File

@ -9,6 +9,10 @@ import (
"time" "time"
) )
type ClearHTTPRequestLogResult struct {
Success bool `json:"success"`
}
type CloseProjectResult struct { type CloseProjectResult struct {
Success bool `json:"success"` Success bool `json:"success"`
} }

View File

@ -209,6 +209,13 @@ func (r *mutationResolver) DeleteProject(ctx context.Context, name string) (*Del
}, nil }, nil
} }
func (r *mutationResolver) ClearHTTPRequestLog(ctx context.Context) (*ClearHTTPRequestLogResult, error) {
if err := r.RequestLogService.ClearRequests(ctx); err != nil {
return nil, fmt.Errorf("could not clear request log: %v", err)
}
return &ClearHTTPRequestLogResult{true}, nil
}
func (r *mutationResolver) SetScope(ctx context.Context, input []ScopeRuleInput) ([]ScopeRule, error) { func (r *mutationResolver) SetScope(ctx context.Context, input []ScopeRuleInput) ([]ScopeRule, error) {
rules := make([]scope.Rule, len(input)) rules := make([]scope.Rule, len(input))
for i, rule := range input { for i, rule := range input {

View File

@ -58,6 +58,10 @@ type DeleteProjectResult {
success: Boolean! success: Boolean!
} }
type ClearHTTPRequestLogResult {
success: Boolean!
}
input HttpRequestLogFilterInput { input HttpRequestLogFilterInput {
onlyInScope: Boolean onlyInScope: Boolean
} }
@ -79,6 +83,7 @@ type Mutation {
openProject(name: String!): Project openProject(name: String!): Project
closeProject: CloseProjectResult! closeProject: CloseProjectResult!
deleteProject(name: String!): DeleteProjectResult! deleteProject(name: String!): DeleteProjectResult!
clearHTTPRequestLog: ClearHTTPRequestLogResult!
setScope(scope: [ScopeRuleInput!]!): [ScopeRule!]! setScope(scope: [ScopeRuleInput!]!): [ScopeRule!]!
setHttpRequestLogFilter( setHttpRequestLogFilter(
filter: HttpRequestLogFilterInput filter: HttpRequestLogFilterInput

View File

@ -215,6 +215,17 @@ var headerFieldToColumnMap = map[string]string{
"value": "value", "value": "value",
} }
func (c *Client) ClearRequestLogs(ctx context.Context) error {
if c.db == nil {
return proj.ErrNoProject
}
_, err := c.db.Exec("DELETE FROM http_requests")
if err != nil {
return fmt.Errorf("sqlite: could not delete requests: %v", err)
}
return nil
}
func (c *Client) FindRequestLogs( func (c *Client) FindRequestLogs(
ctx context.Context, ctx context.Context,
filter reqlog.FindRequestsFilter, filter reqlog.FindRequestsFilter,

View File

@ -17,6 +17,7 @@ type Repository interface {
FindRequestLogByID(ctx context.Context, id int64) (Request, error) FindRequestLogByID(ctx context.Context, id int64) (Request, error)
AddRequestLog(ctx context.Context, req http.Request, body []byte, timestamp time.Time) (*Request, error) AddRequestLog(ctx context.Context, req http.Request, body []byte, timestamp time.Time) (*Request, error)
AddResponseLog(ctx context.Context, reqID int64, res http.Response, body []byte, timestamp time.Time) (*Response, error) AddResponseLog(ctx context.Context, reqID int64, res http.Response, body []byte, timestamp time.Time) (*Response, error)
ClearRequestLogs(ctx context.Context) error
UpsertSettings(ctx context.Context, module string, settings interface{}) error UpsertSettings(ctx context.Context, module string, settings interface{}) error
FindSettingsByModule(ctx context.Context, module string, settings interface{}) error FindSettingsByModule(ctx context.Context, module string, settings interface{}) error
} }

View File

@ -99,6 +99,10 @@ func (svc *Service) SetRequestLogFilter(ctx context.Context, filter FindRequests
return svc.repo.UpsertSettings(ctx, "reqlog", svc) return svc.repo.UpsertSettings(ctx, "reqlog", svc)
} }
func (svc *Service) ClearRequests(ctx context.Context) error {
return svc.repo.ClearRequestLogs(ctx)
}
func (svc *Service) addRequest( func (svc *Service) addRequest(
ctx context.Context, ctx context.Context,
req http.Request, req http.Request,