diff --git a/admin/src/features/intercept/components/EditRequest.tsx b/admin/src/features/intercept/components/EditRequest.tsx
index b9e3140..a15086e 100644
--- a/admin/src/features/intercept/components/EditRequest.tsx
+++ b/admin/src/features/intercept/components/EditRequest.tsx
@@ -1,3 +1,4 @@
+import CancelIcon from "@mui/icons-material/Cancel";
import SendIcon from "@mui/icons-material/Send";
import { Alert, Box, Button, CircularProgress, Typography } from "@mui/material";
import { useRouter } from "next/router";
@@ -12,6 +13,7 @@ import UrlBar, { HttpMethod, HttpProto, httpProtoMap } from "lib/components/UrlB
import {
HttpProtocol,
HttpRequest,
+ useCancelRequestMutation,
useGetInterceptedRequestQuery,
useModifyRequestMutation,
} from "lib/graphql/generated";
@@ -27,7 +29,6 @@ function EditRequest(): JSX.Element {
// If there's no request selected and there are pending reqs, navigate to
// the first one in the list. This helps you quickly review/handle reqs
// without having to manually select the next one in the requests table.
- console.log(router.isReady, router.query.id, interceptedRequests?.length);
if (router.isReady && !router.query.id && interceptedRequests?.length) {
const req = interceptedRequests[0];
router.replace(`/proxy/intercept?id=${req.id}`);
@@ -104,6 +105,16 @@ function EditRequest(): JSX.Element {
const interceptedReq = reqId ? getReqResult?.data?.interceptedRequest : undefined;
const [modifyRequest, modifyResult] = useModifyRequestMutation();
+ const [cancelRequest, cancelResult] = useCancelRequestMutation();
+
+ const onActionCompleted = () => {
+ setURL("");
+ setMethod(HttpMethod.Get);
+ setBody("");
+ setQueryParams([]);
+ setHeaders([]);
+ router.replace(`/proxy/intercept`);
+ };
const handleFormSubmit: React.FormEventHandler = (e) => {
e.preventDefault();
@@ -132,15 +143,29 @@ function EditRequest(): JSX.Element {
},
});
},
- onCompleted: () => {
- setURL("");
- setMethod(HttpMethod.Get);
- setBody("");
- setQueryParams([]);
- setHeaders([]);
- console.log("done!");
- router.replace(`/proxy/intercept`);
+ onCompleted: onActionCompleted,
+ });
+ };
+
+ const handleCancelClick = () => {
+ if (!interceptedReq) {
+ return;
+ }
+
+ cancelRequest({
+ variables: {
+ id: interceptedReq.id,
},
+ update(cache) {
+ cache.modify({
+ fields: {
+ interceptedRequests(existing: HttpRequest[], { readField }) {
+ return existing.filter((ref) => interceptedReq.id !== readField("id", ref));
+ },
+ },
+ });
+ },
+ onCompleted: onActionCompleted,
});
};
@@ -161,17 +186,32 @@ function EditRequest(): JSX.Element {
variant="contained"
disableElevation
type="submit"
- disabled={!interceptedReq || modifyResult.loading}
+ disabled={!interceptedReq || modifyResult.loading || cancelResult.loading}
startIcon={modifyResult.loading ? : }
>
Send
+ : }
+ >
+ Cancel
+
{modifyResult.error && (
{modifyResult.error.message}
)}
+ {cancelResult.error && (
+
+ {cancelResult.error.message}
+
+ )}
diff --git a/admin/src/features/intercept/graphql/cancelRequest.graphql b/admin/src/features/intercept/graphql/cancelRequest.graphql
new file mode 100644
index 0000000..3658c52
--- /dev/null
+++ b/admin/src/features/intercept/graphql/cancelRequest.graphql
@@ -0,0 +1,5 @@
+mutation CancelRequest($id: ID!) {
+ cancelRequest(id: $id) {
+ success
+ }
+}
diff --git a/admin/src/lib/graphql/generated.tsx b/admin/src/lib/graphql/generated.tsx
index 14ff524..79d9461 100644
--- a/admin/src/lib/graphql/generated.tsx
+++ b/admin/src/lib/graphql/generated.tsx
@@ -18,6 +18,11 @@ export type Scalars = {
URL: any;
};
+export type CancelRequestResult = {
+ __typename?: 'CancelRequestResult';
+ success: Scalars['Boolean'];
+};
+
export type ClearHttpRequestLogResult = {
__typename?: 'ClearHTTPRequestLogResult';
success: Scalars['Boolean'];
@@ -127,6 +132,7 @@ export type ModifyRequestResult = {
export type Mutation = {
__typename?: 'Mutation';
+ cancelRequest: CancelRequestResult;
clearHTTPRequestLog: ClearHttpRequestLogResult;
closeProject: CloseProjectResult;
createOrUpdateSenderRequest: SenderRequest;
@@ -143,6 +149,11 @@ export type Mutation = {
};
+export type MutationCancelRequestArgs = {
+ id: Scalars['ID'];
+};
+
+
export type MutationCreateOrUpdateSenderRequestArgs = {
request: SenderRequestInput;
};
@@ -285,6 +296,13 @@ export type SenderRequestInput = {
url: Scalars['URL'];
};
+export type CancelRequestMutationVariables = Exact<{
+ id: Scalars['ID'];
+}>;
+
+
+export type CancelRequestMutation = { __typename?: 'Mutation', cancelRequest: { __typename?: 'CancelRequestResult', success: boolean } };
+
export type GetInterceptedRequestQueryVariables = Exact<{
id: Scalars['ID'];
}>;
@@ -410,6 +428,39 @@ export type GetInterceptedRequestsQueryVariables = Exact<{ [key: string]: never;
export type GetInterceptedRequestsQuery = { __typename?: 'Query', interceptedRequests: Array<{ __typename?: 'HttpRequest', id: string, url: any, method: HttpMethod }> };
+export const CancelRequestDocument = gql`
+ mutation CancelRequest($id: ID!) {
+ cancelRequest(id: $id) {
+ success
+ }
+}
+ `;
+export type CancelRequestMutationFn = Apollo.MutationFunction;
+
+/**
+ * __useCancelRequestMutation__
+ *
+ * To run a mutation, you first call `useCancelRequestMutation` within a React component and pass it any options that fit your needs.
+ * When your component renders, `useCancelRequestMutation` returns a tuple that includes:
+ * - A mutate function that you can call at any time to execute the mutation
+ * - An object with fields that represent the current status of the mutation's execution
+ *
+ * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
+ *
+ * @example
+ * const [cancelRequestMutation, { data, loading, error }] = useCancelRequestMutation({
+ * variables: {
+ * id: // value for 'id'
+ * },
+ * });
+ */
+export function useCancelRequestMutation(baseOptions?: Apollo.MutationHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useMutation(CancelRequestDocument, options);
+ }
+export type CancelRequestMutationHookResult = ReturnType;
+export type CancelRequestMutationResult = Apollo.MutationResult;
+export type CancelRequestMutationOptions = Apollo.BaseMutationOptions;
export const GetInterceptedRequestDocument = gql`
query GetInterceptedRequest($id: ID!) {
interceptedRequest(id: $id) {
diff --git a/pkg/api/generated.go b/pkg/api/generated.go
index b671d3d..5ad3515 100644
--- a/pkg/api/generated.go
+++ b/pkg/api/generated.go
@@ -45,6 +45,10 @@ type DirectiveRoot struct {
}
type ComplexityRoot struct {
+ CancelRequestResult struct {
+ Success func(childComplexity int) int
+ }
+
ClearHTTPRequestLogResult struct {
Success func(childComplexity int) int
}
@@ -105,6 +109,7 @@ type ComplexityRoot struct {
}
Mutation struct {
+ CancelRequest func(childComplexity int, id ulid.ULID) int
ClearHTTPRequestLog func(childComplexity int) int
CloseProject func(childComplexity int) int
CreateOrUpdateSenderRequest func(childComplexity int, request SenderRequestInput) int
@@ -182,6 +187,7 @@ type MutationResolver interface {
SendRequest(ctx context.Context, id ulid.ULID) (*SenderRequest, error)
DeleteSenderRequests(ctx context.Context) (*DeleteSenderRequestsResult, error)
ModifyRequest(ctx context.Context, request ModifyRequestInput) (*ModifyRequestResult, error)
+ CancelRequest(ctx context.Context, id ulid.ULID) (*CancelRequestResult, error)
}
type QueryResolver interface {
HTTPRequestLog(ctx context.Context, id ulid.ULID) (*HTTPRequestLog, error)
@@ -211,6 +217,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
_ = ec
switch typeName + "." + field {
+ case "CancelRequestResult.success":
+ if e.complexity.CancelRequestResult.Success == nil {
+ break
+ }
+
+ return e.complexity.CancelRequestResult.Success(childComplexity), true
+
case "ClearHTTPRequestLogResult.success":
if e.complexity.ClearHTTPRequestLogResult.Success == nil {
break
@@ -414,6 +427,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.ModifyRequestResult.Success(childComplexity), true
+ case "Mutation.cancelRequest":
+ if e.complexity.Mutation.CancelRequest == nil {
+ break
+ }
+
+ args, err := ec.field_Mutation_cancelRequest_args(context.TODO(), rawArgs)
+ if err != nil {
+ return 0, false
+ }
+
+ return e.complexity.Mutation.CancelRequest(childComplexity, args["id"].(ulid.ULID)), true
+
case "Mutation.clearHTTPRequestLog":
if e.complexity.Mutation.ClearHTTPRequestLog == nil {
break
@@ -977,6 +1002,10 @@ type ModifyRequestResult {
success: Boolean!
}
+type CancelRequestResult {
+ success: Boolean!
+}
+
type Query {
httpRequestLog(id: ID!): HttpRequestLog
httpRequestLogs: [HttpRequestLog!]!
@@ -1006,6 +1035,7 @@ type Mutation {
sendRequest(id: ID!): SenderRequest!
deleteSenderRequests: DeleteSenderRequestsResult!
modifyRequest(request: ModifyRequestInput!): ModifyRequestResult!
+ cancelRequest(id: ID!): CancelRequestResult!
}
enum HttpMethod {
@@ -1037,6 +1067,21 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...)
// region ***************************** args.gotpl *****************************
+func (ec *executionContext) field_Mutation_cancelRequest_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
+ var err error
+ args := map[string]interface{}{}
+ var arg0 ulid.ULID
+ if tmp, ok := rawArgs["id"]; ok {
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id"))
+ arg0, err = ec.unmarshalNID2githubᚗcomᚋoklogᚋulidᚐULID(ctx, tmp)
+ if err != nil {
+ return nil, err
+ }
+ }
+ args["id"] = arg0
+ return args, nil
+}
+
func (ec *executionContext) field_Mutation_createOrUpdateSenderRequest_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
@@ -1285,6 +1330,41 @@ func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArg
// region **************************** field.gotpl *****************************
+func (ec *executionContext) _CancelRequestResult_success(ctx context.Context, field graphql.CollectedField, obj *CancelRequestResult) (ret graphql.Marshaler) {
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ fc := &graphql.FieldContext{
+ Object: "CancelRequestResult",
+ 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) _ClearHTTPRequestLogResult_success(ctx context.Context, field graphql.CollectedField, obj *ClearHTTPRequestLogResult) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@@ -2798,6 +2878,48 @@ func (ec *executionContext) _Mutation_modifyRequest(ctx context.Context, field g
return ec.marshalNModifyRequestResult2ᚖgithubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐModifyRequestResult(ctx, field.Selections, res)
}
+func (ec *executionContext) _Mutation_cancelRequest(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)
+ rawArgs := field.ArgumentMap(ec.Variables)
+ args, err := ec.field_Mutation_cancelRequest_args(ctx, rawArgs)
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ fc.Args = args
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
+ ctx = rctx // use context from middleware stack in children
+ return ec.resolvers.Mutation().CancelRequest(rctx, args["id"].(ulid.ULID))
+ })
+ 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.(*CancelRequestResult)
+ fc.Result = res
+ return ec.marshalNCancelRequestResult2ᚖgithubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐCancelRequestResult(ctx, field.Selections, res)
+}
+
func (ec *executionContext) _Project_id(ctx context.Context, field graphql.CollectedField, obj *Project) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@@ -5279,6 +5401,33 @@ func (ec *executionContext) unmarshalInputSenderRequestInput(ctx context.Context
// region **************************** object.gotpl ****************************
+var cancelRequestResultImplementors = []string{"CancelRequestResult"}
+
+func (ec *executionContext) _CancelRequestResult(ctx context.Context, sel ast.SelectionSet, obj *CancelRequestResult) graphql.Marshaler {
+ fields := graphql.CollectFields(ec.OperationContext, sel, cancelRequestResultImplementors)
+
+ out := graphql.NewFieldSet(fields)
+ var invalids uint32
+ for i, field := range fields {
+ switch field.Name {
+ case "__typename":
+ out.Values[i] = graphql.MarshalString("CancelRequestResult")
+ case "success":
+ out.Values[i] = ec._CancelRequestResult_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 clearHTTPRequestLogResultImplementors = []string{"ClearHTTPRequestLogResult"}
func (ec *executionContext) _ClearHTTPRequestLogResult(ctx context.Context, sel ast.SelectionSet, obj *ClearHTTPRequestLogResult) graphql.Marshaler {
@@ -5697,6 +5846,11 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null {
invalids++
}
+ case "cancelRequest":
+ out.Values[i] = ec._Mutation_cancelRequest(ctx, field)
+ if out.Values[i] == graphql.Null {
+ invalids++
+ }
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@@ -6303,6 +6457,20 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se
return res
}
+func (ec *executionContext) marshalNCancelRequestResult2githubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐCancelRequestResult(ctx context.Context, sel ast.SelectionSet, v CancelRequestResult) graphql.Marshaler {
+ return ec._CancelRequestResult(ctx, sel, &v)
+}
+
+func (ec *executionContext) marshalNCancelRequestResult2ᚖgithubᚗcomᚋdstotijnᚋhettyᚋpkgᚋapiᚐCancelRequestResult(ctx context.Context, sel ast.SelectionSet, v *CancelRequestResult) graphql.Marshaler {
+ if v == nil {
+ if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
+ ec.Errorf(ctx, "must not be null")
+ }
+ return graphql.Null
+ }
+ return ec._CancelRequestResult(ctx, sel, v)
+}
+
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)
}
diff --git a/pkg/api/models_gen.go b/pkg/api/models_gen.go
index 6cc66b5..09b8749 100644
--- a/pkg/api/models_gen.go
+++ b/pkg/api/models_gen.go
@@ -12,6 +12,10 @@ import (
"github.com/oklog/ulid"
)
+type CancelRequestResult struct {
+ Success bool `json:"success"`
+}
+
type ClearHTTPRequestLogResult struct {
Success bool `json:"success"`
}
diff --git a/pkg/api/resolvers.go b/pkg/api/resolvers.go
index 81cb17f..588c188 100644
--- a/pkg/api/resolvers.go
+++ b/pkg/api/resolvers.go
@@ -581,6 +581,15 @@ func (r *mutationResolver) ModifyRequest(ctx context.Context, input ModifyReques
return &ModifyRequestResult{Success: true}, nil
}
+func (r *mutationResolver) CancelRequest(ctx context.Context, id ulid.ULID) (*CancelRequestResult, error) {
+ err := r.InterceptService.CancelRequest(id)
+ if err != nil {
+ return nil, fmt.Errorf("could not cancel http request: %w", err)
+ }
+
+ return &CancelRequestResult{Success: true}, nil
+}
+
func parseSenderRequest(req sender.Request) (SenderRequest, error) {
method := HTTPMethod(req.Method)
if method != "" && !method.IsValid() {
diff --git a/pkg/api/schema.graphql b/pkg/api/schema.graphql
index 0f001ca..9c4aa48 100644
--- a/pkg/api/schema.graphql
+++ b/pkg/api/schema.graphql
@@ -138,6 +138,10 @@ type ModifyRequestResult {
success: Boolean!
}
+type CancelRequestResult {
+ success: Boolean!
+}
+
type Query {
httpRequestLog(id: ID!): HttpRequestLog
httpRequestLogs: [HttpRequestLog!]!
@@ -167,6 +171,7 @@ type Mutation {
sendRequest(id: ID!): SenderRequest!
deleteSenderRequests: DeleteSenderRequestsResult!
modifyRequest(request: ModifyRequestInput!): ModifyRequestResult!
+ cancelRequest(id: ID!): CancelRequestResult!
}
enum HttpMethod {
diff --git a/pkg/proxy/intercept/intercept.go b/pkg/proxy/intercept/intercept.go
index 7fd50fa..201b3f5 100644
--- a/pkg/proxy/intercept/intercept.go
+++ b/pkg/proxy/intercept/intercept.go
@@ -144,6 +144,11 @@ func (svc *Service) ModifyRequest(reqID ulid.ULID, modReq *http.Request) error {
}
}
+// CancelRequest ensures an intercepted request is dropped.
+func (svc *Service) CancelRequest(reqID ulid.ULID) error {
+ return svc.ModifyRequest(reqID, nil)
+}
+
func (svc *Service) ClearRequests() {
svc.mu.Lock()
defer svc.mu.Unlock()