mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
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:
53
admin/src/components/reqlog/ConfirmationDialog.tsx
Normal file
53
admin/src/components/reqlog/ConfirmationDialog.tsx
Normal 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>
|
||||
);
|
||||
}
|
@ -1,41 +1,24 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { gql, useQuery } from "@apollo/client";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
CircularProgress,
|
||||
Link as MaterialLink,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import Alert from "@material-ui/lab/Alert";
|
||||
|
||||
import RequestList from "./RequestList";
|
||||
import LogDetail from "./LogDetail";
|
||||
import CenteredPaper from "../CenteredPaper";
|
||||
|
||||
const HTTP_REQUEST_LOGS = gql`
|
||||
query HttpRequestLogs {
|
||||
httpRequestLogs {
|
||||
id
|
||||
method
|
||||
url
|
||||
timestamp
|
||||
response {
|
||||
statusCode
|
||||
statusReason
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
import { useHttpRequestLogs } from "./hooks/useHttpRequestLogs";
|
||||
|
||||
function LogsOverview(): JSX.Element {
|
||||
const router = useRouter();
|
||||
const detailReqLogId =
|
||||
router.query.id && parseInt(router.query.id as string, 10);
|
||||
|
||||
const { loading, error, data } = useQuery(HTTP_REQUEST_LOGS, {
|
||||
pollInterval: 1000,
|
||||
});
|
||||
const { loading, error, data } = useHttpRequestLogs();
|
||||
|
||||
const handleLogClick = (reqId: number) => {
|
||||
router.push("/proxy/logs?id=" + reqId, undefined, {
|
||||
|
@ -16,10 +16,16 @@ import {
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import FilterListIcon from "@material-ui/icons/FilterList";
|
||||
import DeleteIcon from "@material-ui/icons/Delete";
|
||||
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 { Alert } from "@material-ui/lab";
|
||||
import { useClearHTTPRequestLog } from "./hooks/useClearHTTPRequestLog";
|
||||
import {
|
||||
ConfirmationDialog,
|
||||
useConfirmationDialog,
|
||||
} from "./ConfirmationDialog";
|
||||
|
||||
const FILTER = gql`
|
||||
query HttpRequestLogFilter {
|
||||
@ -79,15 +85,14 @@ function Search(): JSX.Element {
|
||||
FILTER
|
||||
);
|
||||
|
||||
const client = useApolloClient();
|
||||
const [
|
||||
setFilterMutate,
|
||||
{ error: setFilterErr, loading: setFilterLoading },
|
||||
] = useMutation<{
|
||||
setHttpRequestLogFilter: SearchFilter | null;
|
||||
}>(SET_FILTER, {
|
||||
update(_, { data: { setHttpRequestLogFilter } }) {
|
||||
client.writeQuery({
|
||||
update(cache, { data: { setHttpRequestLogFilter } }) {
|
||||
cache.writeQuery({
|
||||
query: FILTER,
|
||||
data: {
|
||||
httpRequestLogFilter: setHttpRequestLogFilter,
|
||||
@ -96,6 +101,12 @@ function Search(): JSX.Element {
|
||||
},
|
||||
});
|
||||
|
||||
const [
|
||||
clearHTTPRequestLog,
|
||||
clearHTTPRequestLogResult,
|
||||
] = useClearHTTPRequestLog();
|
||||
const clearHTTPConfirmationDialog = useConfirmationDialog();
|
||||
|
||||
const filterRef = useRef<HTMLElement | null>();
|
||||
const [filterOpen, setFilterOpen] = useState(false);
|
||||
|
||||
@ -111,90 +122,112 @@ function Search(): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<ClickAwayListener onClickAway={handleClickAway}>
|
||||
<Box style={{ display: "inline-block" }}>
|
||||
{filterErr && (
|
||||
<Box mb={4}>
|
||||
<Alert severity="error">
|
||||
Error fetching filter: {filterErr.message}
|
||||
</Alert>
|
||||
</Box>
|
||||
)}
|
||||
{setFilterErr && (
|
||||
<Box mb={4}>
|
||||
<Alert severity="error">
|
||||
Error setting filter: {setFilterErr.message}
|
||||
</Alert>
|
||||
</Box>
|
||||
)}
|
||||
<Paper
|
||||
component="form"
|
||||
onSubmit={handleSubmit}
|
||||
ref={filterRef}
|
||||
className={classes.root}
|
||||
>
|
||||
<Tooltip title="Toggle filter options">
|
||||
<IconButton
|
||||
className={classes.iconButton}
|
||||
onClick={() => setFilterOpen(!filterOpen)}
|
||||
style={{
|
||||
color:
|
||||
filter?.httpRequestLogFilter !== null
|
||||
? theme.palette.secondary.main
|
||||
: "inherit",
|
||||
}}
|
||||
>
|
||||
{filterLoading || setFilterLoading ? (
|
||||
<CircularProgress className={classes.filterLoading} size={23} />
|
||||
) : (
|
||||
<FilterListIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<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"
|
||||
<Box>
|
||||
<Error prefix="Error fetching filter" error={filterErr} />
|
||||
<Error prefix="Error setting filter" error={setFilterErr} />
|
||||
<Error
|
||||
prefix="Error clearing all HTTP logs"
|
||||
error={clearHTTPRequestLogResult.error}
|
||||
/>
|
||||
<Box style={{ display: "flex", flex: 1 }}>
|
||||
<ClickAwayListener onClickAway={handleClickAway}>
|
||||
<Paper
|
||||
component="form"
|
||||
onSubmit={handleSubmit}
|
||||
ref={filterRef}
|
||||
className={classes.root}
|
||||
>
|
||||
<Tooltip title="Toggle filter options">
|
||||
<IconButton
|
||||
className={classes.iconButton}
|
||||
onClick={() => setFilterOpen(!filterOpen)}
|
||||
style={{
|
||||
color:
|
||||
filter?.httpRequestLogFilter !== null
|
||||
? theme.palette.secondary.main
|
||||
: "inherit",
|
||||
}}
|
||||
>
|
||||
{filterLoading || setFilterLoading ? (
|
||||
<CircularProgress
|
||||
className={classes.filterLoading}
|
||||
size={23}
|
||||
/>
|
||||
) : (
|
||||
<FilterListIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<InputBase
|
||||
className={classes.input}
|
||||
placeholder="Search proxy logs…"
|
||||
onFocus={() => setFilterOpen(true)}
|
||||
/>
|
||||
<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>
|
||||
</Popper>
|
||||
</ClickAwayListener>
|
||||
<Box style={{ marginLeft: "auto" }}>
|
||||
<Tooltip title="Clear all">
|
||||
<IconButton onClick={clearHTTPConfirmationDialog.open}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
|
16
admin/src/components/reqlog/hooks/useClearHTTPRequestLog.ts
Normal file
16
admin/src/components/reqlog/hooks/useClearHTTPRequestLog.ts
Normal 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 }],
|
||||
});
|
||||
}
|
22
admin/src/components/reqlog/hooks/useHttpRequestLogs.ts
Normal file
22
admin/src/components/reqlog/hooks/useHttpRequestLogs.ts
Normal 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,
|
||||
});
|
||||
}
|
@ -43,6 +43,10 @@ type DirectiveRoot struct {
|
||||
}
|
||||
|
||||
type ComplexityRoot struct {
|
||||
ClearHTTPRequestLogResult struct {
|
||||
Success func(childComplexity int) int
|
||||
}
|
||||
|
||||
CloseProjectResult struct {
|
||||
Success func(childComplexity int) int
|
||||
}
|
||||
@ -81,6 +85,7 @@ type ComplexityRoot struct {
|
||||
}
|
||||
|
||||
Mutation struct {
|
||||
ClearHTTPRequestLog func(childComplexity int) int
|
||||
CloseProject func(childComplexity int) int
|
||||
DeleteProject 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)
|
||||
CloseProject(ctx context.Context) (*CloseProjectResult, error)
|
||||
DeleteProject(ctx context.Context, name string) (*DeleteProjectResult, error)
|
||||
ClearHTTPRequestLog(ctx context.Context) (*ClearHTTPRequestLogResult, error)
|
||||
SetScope(ctx context.Context, scope []ScopeRuleInput) ([]ScopeRule, error)
|
||||
SetHTTPRequestLogFilter(ctx context.Context, filter *HTTPRequestLogFilterInput) (*HTTPRequestLogFilter, error)
|
||||
}
|
||||
@ -145,6 +151,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
_ = ec
|
||||
switch typeName + "." + field {
|
||||
|
||||
case "ClearHTTPRequestLogResult.success":
|
||||
if e.complexity.ClearHTTPRequestLogResult.Success == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.ClearHTTPRequestLogResult.Success(childComplexity), true
|
||||
|
||||
case "CloseProjectResult.success":
|
||||
if e.complexity.CloseProjectResult.Success == nil {
|
||||
break
|
||||
@ -278,6 +291,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
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":
|
||||
if e.complexity.Mutation.CloseProject == nil {
|
||||
break
|
||||
@ -553,6 +573,10 @@ type DeleteProjectResult {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type ClearHTTPRequestLogResult {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
input HttpRequestLogFilterInput {
|
||||
onlyInScope: Boolean
|
||||
}
|
||||
@ -574,6 +598,7 @@ type Mutation {
|
||||
openProject(name: String!): Project
|
||||
closeProject: CloseProjectResult!
|
||||
deleteProject(name: String!): DeleteProjectResult!
|
||||
clearHTTPRequestLog: ClearHTTPRequestLogResult!
|
||||
setScope(scope: [ScopeRuleInput!]!): [ScopeRule!]!
|
||||
setHttpRequestLogFilter(
|
||||
filter: HttpRequestLogFilterInput
|
||||
@ -730,6 +755,41 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
|
||||
|
||||
// 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) {
|
||||
defer func() {
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -3271,6 +3366,33 @@ func (ec *executionContext) unmarshalInputScopeRuleInput(ctx context.Context, ob
|
||||
|
||||
// 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"}
|
||||
|
||||
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 {
|
||||
invalids++
|
||||
}
|
||||
case "clearHTTPRequestLog":
|
||||
out.Values[i] = ec._Mutation_clearHTTPRequestLog(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "setScope":
|
||||
out.Values[i] = ec._Mutation_setScope(ctx, field)
|
||||
if out.Values[i] == graphql.Null {
|
||||
@ -3985,6 +4112,20 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
|
||||
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 {
|
||||
return ec._CloseProjectResult(ctx, sel, &v)
|
||||
}
|
||||
|
@ -9,6 +9,10 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type ClearHTTPRequestLogResult struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type CloseProjectResult struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
@ -209,6 +209,13 @@ func (r *mutationResolver) DeleteProject(ctx context.Context, name string) (*Del
|
||||
}, 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) {
|
||||
rules := make([]scope.Rule, len(input))
|
||||
for i, rule := range input {
|
||||
|
@ -58,6 +58,10 @@ type DeleteProjectResult {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type ClearHTTPRequestLogResult {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
input HttpRequestLogFilterInput {
|
||||
onlyInScope: Boolean
|
||||
}
|
||||
@ -79,6 +83,7 @@ type Mutation {
|
||||
openProject(name: String!): Project
|
||||
closeProject: CloseProjectResult!
|
||||
deleteProject(name: String!): DeleteProjectResult!
|
||||
clearHTTPRequestLog: ClearHTTPRequestLogResult!
|
||||
setScope(scope: [ScopeRuleInput!]!): [ScopeRule!]!
|
||||
setHttpRequestLogFilter(
|
||||
filter: HttpRequestLogFilterInput
|
||||
|
@ -215,6 +215,17 @@ var headerFieldToColumnMap = map[string]string{
|
||||
"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(
|
||||
ctx context.Context,
|
||||
filter reqlog.FindRequestsFilter,
|
||||
|
@ -17,6 +17,7 @@ type Repository interface {
|
||||
FindRequestLogByID(ctx context.Context, id int64) (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)
|
||||
ClearRequestLogs(ctx context.Context) error
|
||||
UpsertSettings(ctx context.Context, module string, settings interface{}) error
|
||||
FindSettingsByModule(ctx context.Context, module string, settings interface{}) error
|
||||
}
|
||||
|
@ -99,6 +99,10 @@ func (svc *Service) SetRequestLogFilter(ctx context.Context, filter FindRequests
|
||||
return svc.repo.UpsertSettings(ctx, "reqlog", svc)
|
||||
}
|
||||
|
||||
func (svc *Service) ClearRequests(ctx context.Context) error {
|
||||
return svc.repo.ClearRequestLogs(ctx)
|
||||
}
|
||||
|
||||
func (svc *Service) addRequest(
|
||||
ctx context.Context,
|
||||
req http.Request,
|
||||
|
Reference in New Issue
Block a user