Add delete evidence

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-03-01 21:02:28 +01:00
parent 3fb04c99fe
commit 10b9ded41c
11 changed files with 584 additions and 4 deletions

View File

@@ -53,6 +53,7 @@ import type { ControlOverviewPageUpdateTaskStateMutation as ControlOverviewPageU
import type { ControlOverviewPageCreateTaskMutation as ControlOverviewPageCreateTaskMutationType } from "./__generated__/ControlOverviewPageCreateTaskMutation.graphql";
import type { ControlOverviewPageDeleteTaskMutation as ControlOverviewPageDeleteTaskMutationType } from "./__generated__/ControlOverviewPageDeleteTaskMutation.graphql";
import type { ControlOverviewPageUploadEvidenceMutation as ControlOverviewPageUploadEvidenceMutationType } from "./__generated__/ControlOverviewPageUploadEvidenceMutation.graphql";
import type { ControlOverviewPageDeleteEvidenceMutation as ControlOverviewPageDeleteEvidenceMutationType } from "./__generated__/ControlOverviewPageDeleteEvidenceMutation.graphql";
const controlOverviewPageQuery = graphql`
query ControlOverviewPageQuery($controlId: ID!) {
@@ -155,6 +156,17 @@ const uploadEvidenceMutation = graphql`
}
`;
const deleteEvidenceMutation = graphql`
mutation ControlOverviewPageDeleteEvidenceMutation(
$input: DeleteEvidenceInput!
$connections: [ID!]!
) {
deleteEvidence(input: $input) {
deletedEvidenceId @deleteEdge(connections: $connections)
}
}
`;
// Add a GraphQL query to fetch the fileUrl for an evidence item
const getEvidenceFileUrlQuery = graphql`
query ControlOverviewPageGetEvidenceFileUrlQuery($evidenceId: ID!) {
@@ -192,6 +204,10 @@ function ControlOverviewPageContent({
useMutation<ControlOverviewPageUploadEvidenceMutationType>(
uploadEvidenceMutation
);
const [deleteEvidence] =
useMutation<ControlOverviewPageDeleteEvidenceMutationType>(
deleteEvidenceMutation
);
const [isCreateTaskOpen, setIsCreateTaskOpen] = useState(false);
const [newTaskName, setNewTaskName] = useState("");
@@ -232,6 +248,13 @@ function ControlOverviewPageContent({
} | null>(null);
const [isLoadingFileUrl, setIsLoadingFileUrl] = useState(false);
const [isDeleteEvidenceOpen, setIsDeleteEvidenceOpen] = useState(false);
const [evidenceToDelete, setEvidenceToDelete] = useState<{
id: string;
filename: string;
taskId: string;
} | null>(null);
const tasks = data.control.tasks?.edges.map((edge) => edge.node) || [];
const getEvidenceConnectionId = useCallback(
@@ -604,6 +627,47 @@ function ControlOverviewPageContent({
});
};
const handleDeleteEvidence = (
evidenceId: string,
filename: string,
taskId: string
) => {
setEvidenceToDelete({ id: evidenceId, filename, taskId });
setIsDeleteEvidenceOpen(true);
};
const confirmDeleteEvidence = () => {
if (!evidenceToDelete) return;
const evidenceConnectionId = getEvidenceConnectionId(
evidenceToDelete.taskId
);
deleteEvidence({
variables: {
input: {
evidenceId: evidenceToDelete.id,
},
connections: evidenceConnectionId ? [evidenceConnectionId] : [],
},
onCompleted: () => {
toast({
title: "Evidence deleted",
description: "Evidence has been deleted successfully.",
});
setIsDeleteEvidenceOpen(false);
setEvidenceToDelete(null);
},
onError: (error) => {
toast({
title: "Error deleting evidence",
description: error.message,
variant: "destructive",
});
},
});
};
return (
<>
<Helmet>
@@ -928,6 +992,20 @@ function ControlOverviewPageContent({
>
<Download className="w-4 h-4 text-gray-600" />
</button>
<button
onClick={(e) => {
e.preventDefault();
handleDeleteEvidence(
evidence.id,
evidence.filename,
task.id
);
}}
className="p-1 rounded-full hover:bg-gray-100 hover:bg-red-50 hover:text-red-600"
title="Delete Evidence"
>
<Trash2 className="w-4 h-4 text-gray-600 hover:text-red-600" />
</button>
</div>
</div>
);
@@ -1103,6 +1181,34 @@ function ControlOverviewPageContent({
</DialogFooter>
</DialogContent>
</Dialog>
{/* Add Delete Evidence Confirmation Dialog */}
<Dialog
open={isDeleteEvidenceOpen}
onOpenChange={setIsDeleteEvidenceOpen}
>
<DialogContent>
<DialogHeader>
<DialogTitle>Delete Evidence</DialogTitle>
<DialogDescription>
Are you sure you want to delete the evidence &quot;
{evidenceToDelete?.filename}&quot;? This action cannot be
undone.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button
variant="outline"
onClick={() => setIsDeleteEvidenceOpen(false)}
>
Cancel
</Button>
<Button variant="destructive" onClick={confirmDeleteEvidence}>
Delete
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</>
);

View File

@@ -0,0 +1,132 @@
/**
* @generated SignedSource<<c09b83c20157fc20bf74a2c9c031b716>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DeleteEvidenceInput = {
evidenceId: string;
};
export type ControlOverviewPageDeleteEvidenceMutation$variables = {
connections: ReadonlyArray<string>;
input: DeleteEvidenceInput;
};
export type ControlOverviewPageDeleteEvidenceMutation$data = {
readonly deleteEvidence: {
readonly deletedEvidenceId: string;
};
};
export type ControlOverviewPageDeleteEvidenceMutation = {
response: ControlOverviewPageDeleteEvidenceMutation$data;
variables: ControlOverviewPageDeleteEvidenceMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "connections"
},
v1 = {
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
},
v2 = [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "deletedEvidenceId",
"storageKey": null
};
return {
"fragment": {
"argumentDefinitions": [
(v0/*: any*/),
(v1/*: any*/)
],
"kind": "Fragment",
"metadata": null,
"name": "ControlOverviewPageDeleteEvidenceMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteEvidencePayload",
"kind": "LinkedField",
"name": "deleteEvidence",
"plural": false,
"selections": [
(v3/*: any*/)
],
"storageKey": null
}
],
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [
(v1/*: any*/),
(v0/*: any*/)
],
"kind": "Operation",
"name": "ControlOverviewPageDeleteEvidenceMutation",
"selections": [
{
"alias": null,
"args": (v2/*: any*/),
"concreteType": "DeleteEvidencePayload",
"kind": "LinkedField",
"name": "deleteEvidence",
"plural": false,
"selections": [
(v3/*: any*/),
{
"alias": null,
"args": null,
"filters": null,
"handle": "deleteEdge",
"key": "",
"kind": "ScalarHandle",
"name": "deletedEvidenceId",
"handleArgs": [
{
"kind": "Variable",
"name": "connections",
"variableName": "connections"
}
]
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "51d62fb682189fa0aaa5cc5b7a5b7862",
"id": null,
"metadata": {},
"name": "ControlOverviewPageDeleteEvidenceMutation",
"operationKind": "mutation",
"text": "mutation ControlOverviewPageDeleteEvidenceMutation(\n $input: DeleteEvidenceInput!\n) {\n deleteEvidence(input: $input) {\n deletedEvidenceId\n }\n}\n"
}
};
})();
(node as any).hash = "f55cf1c279c95dcabc11e1b5aafcdc46";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<3c761f476fc4dcbc2f175eefa625bb28>>
* @generated SignedSource<<e1f21afb2bf00563c8343f3700c08c6e>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -31,7 +31,7 @@ export type ControlOverviewPageQuery$data = {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly createdAt: any;
readonly createdAt: string;
readonly filename: string;
readonly id: string;
readonly mimeType: string;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<09af19b5a7657e04606a42962db9db2c>>
* @generated SignedSource<<f1cdd20d14ca9fefe868019e5aa43741>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -23,7 +23,7 @@ export type ControlOverviewPageUploadEvidenceMutation$data = {
readonly uploadEvidence: {
readonly evidenceEdge: {
readonly node: {
readonly createdAt: any;
readonly createdAt: string;
readonly fileUrl: string;
readonly id: string;
readonly mimeType: string;

View File

@@ -403,6 +403,7 @@ type Mutation {
updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload!
updateControl(input: UpdateControlInput!): UpdateControlPayload!
uploadEvidence(input: UploadEvidenceInput!): UploadEvidencePayload!
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
}
input CreateVendorInput {
@@ -612,3 +613,11 @@ input UploadEvidenceInput {
type UploadEvidencePayload {
evidenceEdge: EvidenceEdge!
}
input DeleteEvidenceInput {
evidenceId: ID!
}
type DeleteEvidencePayload {
deletedEvidenceId: ID!
}

View File

@@ -122,6 +122,10 @@ type ComplexityRoot struct {
VendorEdge func(childComplexity int) int
}
DeleteEvidencePayload struct {
DeletedEvidenceID func(childComplexity int) int
}
DeleteOrganizationPayload struct {
DeletedOrganizationID func(childComplexity int) int
}
@@ -206,6 +210,7 @@ type ComplexityRoot struct {
CreatePeople func(childComplexity int, input types.CreatePeopleInput) int
CreateTask func(childComplexity int, input types.CreateTaskInput) int
CreateVendor func(childComplexity int, input types.CreateVendorInput) int
DeleteEvidence func(childComplexity int, input types.DeleteEvidenceInput) int
DeleteOrganization func(childComplexity int, input types.DeleteOrganizationInput) int
DeletePeople func(childComplexity int, input types.DeletePeopleInput) int
DeleteTask func(childComplexity int, input types.DeleteTaskInput) int
@@ -406,6 +411,7 @@ type MutationResolver interface {
UpdateFramework(ctx context.Context, input types.UpdateFrameworkInput) (*types.UpdateFrameworkPayload, error)
UpdateControl(ctx context.Context, input types.UpdateControlInput) (*types.UpdateControlPayload, error)
UploadEvidence(ctx context.Context, input types.UploadEvidenceInput) (*types.UploadEvidencePayload, error)
DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error)
}
type OrganizationResolver interface {
Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.FrameworkConnection, error)
@@ -663,6 +669,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.CreateVendorPayload.VendorEdge(childComplexity), true
case "DeleteEvidencePayload.deletedEvidenceId":
if e.complexity.DeleteEvidencePayload.DeletedEvidenceID == nil {
break
}
return e.complexity.DeleteEvidencePayload.DeletedEvidenceID(childComplexity), true
case "DeleteOrganizationPayload.deletedOrganizationId":
if e.complexity.DeleteOrganizationPayload.DeletedOrganizationID == nil {
break
@@ -1011,6 +1024,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Mutation.CreateVendor(childComplexity, args["input"].(types.CreateVendorInput)), true
case "Mutation.deleteEvidence":
if e.complexity.Mutation.DeleteEvidence == nil {
break
}
args, err := ec.field_Mutation_deleteEvidence_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.DeleteEvidence(childComplexity, args["input"].(types.DeleteEvidenceInput)), true
case "Mutation.deleteOrganization":
if e.complexity.Mutation.DeleteOrganization == nil {
break
@@ -1761,6 +1786,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
ec.unmarshalInputCreatePeopleInput,
ec.unmarshalInputCreateTaskInput,
ec.unmarshalInputCreateVendorInput,
ec.unmarshalInputDeleteEvidenceInput,
ec.unmarshalInputDeleteOrganizationInput,
ec.unmarshalInputDeletePeopleInput,
ec.unmarshalInputDeleteTaskInput,
@@ -2273,6 +2299,7 @@ type Mutation {
updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload!
updateControl(input: UpdateControlInput!): UpdateControlPayload!
uploadEvidence(input: UploadEvidenceInput!): UploadEvidencePayload!
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
}
input CreateVendorInput {
@@ -2482,6 +2509,14 @@ input UploadEvidenceInput {
type UploadEvidencePayload {
evidenceEdge: EvidenceEdge!
}
input DeleteEvidenceInput {
evidenceId: ID!
}
type DeleteEvidencePayload {
deletedEvidenceId: ID!
}
`, BuiltIn: false},
}
var parsedSchema = gqlparser.MustLoadSchema(sources...)
@@ -2936,6 +2971,29 @@ func (ec *executionContext) field_Mutation_createVendor_argsInput(
return zeroVal, nil
}
func (ec *executionContext) field_Mutation_deleteEvidence_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
arg0, err := ec.field_Mutation_deleteEvidence_argsInput(ctx, rawArgs)
if err != nil {
return nil, err
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_deleteEvidence_argsInput(
ctx context.Context,
rawArgs map[string]any,
) (types.DeleteEvidenceInput, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("input"))
if tmp, ok := rawArgs["input"]; ok {
return ec.unmarshalNDeleteEvidenceInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteEvidenceInput(ctx, tmp)
}
var zeroVal types.DeleteEvidenceInput
return zeroVal, nil
}
func (ec *executionContext) field_Mutation_deleteOrganization_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -4988,6 +5046,44 @@ func (ec *executionContext) fieldContext_CreateVendorPayload_vendorEdge(_ contex
return fc, nil
}
func (ec *executionContext) _DeleteEvidencePayload_deletedEvidenceId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteEvidencePayload) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_DeleteEvidencePayload_deletedEvidenceId(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return obj.DeletedEvidenceID, 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.(gid.GID)
fc.Result = res
return ec.marshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_DeleteEvidencePayload_deletedEvidenceId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "DeleteEvidencePayload",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type ID does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _DeleteOrganizationPayload_deletedOrganizationId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteOrganizationPayload) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_DeleteOrganizationPayload_deletedOrganizationId(ctx, field)
if err != nil {
@@ -7298,6 +7394,53 @@ func (ec *executionContext) fieldContext_Mutation_uploadEvidence(ctx context.Con
return fc, nil
}
func (ec *executionContext) _Mutation_deleteEvidence(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Mutation_deleteEvidence(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Mutation().DeleteEvidence(rctx, fc.Args["input"].(types.DeleteEvidenceInput))
})
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.(*types.DeleteEvidencePayload)
fc.Result = res
return ec.marshalNDeleteEvidencePayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteEvidencePayload(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Mutation_deleteEvidence(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Mutation",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "deletedEvidenceId":
return ec.fieldContext_DeleteEvidencePayload_deletedEvidenceId(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type DeleteEvidencePayload", field.Name)
},
}
ctx = graphql.WithFieldContext(ctx, fc)
if fc.Args, err = ec.field_Mutation_deleteEvidence_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return fc, err
}
return fc, nil
}
func (ec *executionContext) _Organization_id(ctx context.Context, field graphql.CollectedField, obj *types.Organization) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Organization_id(ctx, field)
if err != nil {
@@ -12761,6 +12904,33 @@ func (ec *executionContext) unmarshalInputCreateVendorInput(ctx context.Context,
return it, nil
}
func (ec *executionContext) unmarshalInputDeleteEvidenceInput(ctx context.Context, obj any) (types.DeleteEvidenceInput, error) {
var it types.DeleteEvidenceInput
asMap := map[string]any{}
for k, v := range obj.(map[string]any) {
asMap[k] = v
}
fieldsInOrder := [...]string{"evidenceId"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
continue
}
switch k {
case "evidenceId":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("evidenceId"))
data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v)
if err != nil {
return it, err
}
it.EvidenceID = data
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputDeleteOrganizationInput(ctx context.Context, obj any) (types.DeleteOrganizationInput, error) {
var it types.DeleteOrganizationInput
asMap := map[string]any{}
@@ -13890,6 +14060,45 @@ func (ec *executionContext) _CreateVendorPayload(ctx context.Context, sel ast.Se
return out
}
var deleteEvidencePayloadImplementors = []string{"DeleteEvidencePayload"}
func (ec *executionContext) _DeleteEvidencePayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteEvidencePayload) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, deleteEvidencePayloadImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("DeleteEvidencePayload")
case "deletedEvidenceId":
out.Values[i] = ec._DeleteEvidencePayload_deletedEvidenceId(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.processDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var deleteOrganizationPayloadImplementors = []string{"DeleteOrganizationPayload"}
func (ec *executionContext) _DeleteOrganizationPayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteOrganizationPayload) graphql.Marshaler {
@@ -14725,6 +14934,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "deleteEvidence":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
return ec._Mutation_deleteEvidence(ctx, field)
})
if out.Values[i] == graphql.Null {
out.Invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
@@ -16845,6 +17061,25 @@ func (ec *executionContext) marshalNDatetime2timeᚐTime(ctx context.Context, se
return res
}
func (ec *executionContext) unmarshalNDeleteEvidenceInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteEvidenceInput(ctx context.Context, v any) (types.DeleteEvidenceInput, error) {
res, err := ec.unmarshalInputDeleteEvidenceInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNDeleteEvidencePayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteEvidencePayload(ctx context.Context, sel ast.SelectionSet, v types.DeleteEvidencePayload) graphql.Marshaler {
return ec._DeleteEvidencePayload(ctx, sel, &v)
}
func (ec *executionContext) marshalNDeleteEvidencePayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteEvidencePayload(ctx context.Context, sel ast.SelectionSet, v *types.DeleteEvidencePayload) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._DeleteEvidencePayload(ctx, sel, v)
}
func (ec *executionContext) unmarshalNDeleteOrganizationInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteOrganizationInput(ctx context.Context, v any) (types.DeleteOrganizationInput, error) {
res, err := ec.unmarshalInputDeleteOrganizationInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)

View File

@@ -129,6 +129,14 @@ type CreateVendorPayload struct {
VendorEdge *VendorEdge `json:"vendorEdge"`
}
type DeleteEvidenceInput struct {
EvidenceID gid.GID `json:"evidenceId"`
}
type DeleteEvidencePayload struct {
DeletedEvidenceID gid.GID `json:"deletedEvidenceId"`
}
type DeleteOrganizationInput struct {
OrganizationID gid.GID `json:"organizationId"`
}

View File

@@ -363,6 +363,18 @@ func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.Uploa
}, nil
}
// DeleteEvidence is the resolver for the deleteEvidence field.
func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error) {
err := r.proboSvc.DeleteEvidence(ctx, input.EvidenceID)
if err != nil {
return nil, fmt.Errorf("failed to delete evidence: %w", err)
}
return &types.DeleteEvidencePayload{
DeletedEvidenceID: input.EvidenceID,
}, nil
}
// Frameworks is the resolver for the frameworks field.
func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.FrameworkConnection, error) {
cursor := types.NewCursor(first, after, last, before)

View File

@@ -232,3 +232,25 @@ WHERE
return nil
}
func (e Evidence) Delete(
ctx context.Context,
conn pg.Conn,
scope *Scope,
) error {
q := `
DELETE FROM
evidences
WHERE
%s
AND id = @evidence_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.NamedArgs{"evidence_id": e.ID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
}

View File

@@ -143,3 +143,26 @@ WHERE
return nil
}
func (cst *EvidenceStateTransitions) DeleteForEvidenceID(
ctx context.Context,
conn pg.Conn,
scope *Scope,
evidenceID gid.GID,
) error {
q := `
DELETE FROM
evidence_state_transitions
WHERE
%s
AND evidence_id = @evidence_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.NamedArgs{"evidence_id": evidenceID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
}

View File

@@ -0,0 +1,33 @@
package probo
import (
"context"
"fmt"
"github.com/getprobo/probo/pkg/gid"
"github.com/getprobo/probo/pkg/probo/coredata"
"go.gearno.de/kit/pg"
)
func (s *Service) DeleteEvidence(
ctx context.Context,
evidenceID gid.GID,
) error {
evidenceStateTransitions := &coredata.EvidenceStateTransitions{}
evidence := &coredata.Evidence{ID: evidenceID}
return s.pg.WithTx(
ctx,
func(conn pg.Conn) error {
if err := evidenceStateTransitions.DeleteForEvidenceID(ctx, conn, s.scope, evidenceID); err != nil {
return fmt.Errorf("cannot delete evidence state transitions: %w", err)
}
if err := evidence.Delete(ctx, conn, s.scope); err != nil {
return fmt.Errorf("cannot delete evidence: %w", err)
}
return nil
},
)
}