Add evidence type link

close #44

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-03-20 17:25:29 +01:00
parent 7117c6a210
commit b8bbeaba11
13 changed files with 798 additions and 167 deletions

View File

@@ -28,15 +28,18 @@ import (
type (
Evidence struct {
ID gid.GID `db:"id"`
TaskID gid.GID `db:"task_id"`
State EvidenceState `db:"state"`
ObjectKey string `db:"object_key"`
MimeType string `db:"mime_type"`
Size uint64 `db:"size"`
Filename string `db:"filename"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
TaskID gid.GID `db:"task_id"`
State EvidenceState `db:"state"`
Type EvidenceType `db:"type"`
ObjectKey string `db:"object_key"`
MimeType string `db:"mime_type"`
Size uint64 `db:"size"`
Filename string `db:"filename"`
URL string `db:"url"`
Description string `db:"description"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Evidences []*Evidence
@@ -66,7 +69,10 @@ INSERT INTO
mime_type,
size,
state,
type,
filename,
url,
description,
created_at,
updated_at
)
@@ -78,7 +84,10 @@ VALUES (
@mime_type,
@size,
@state,
@type,
@filename,
@url,
@description,
@created_at,
@updated_at
)
@@ -95,6 +104,9 @@ VALUES (
"created_at": e.CreatedAt,
"updated_at": e.UpdatedAt,
"state": e.State,
"type": e.Type,
"url": e.URL,
"description": e.Description,
}
_, err := conn.Exec(ctx, q, args)
return err
@@ -111,10 +123,13 @@ SELECT
id,
task_id,
state,
type,
object_key,
mime_type,
size,
filename,
url,
description,
created_at,
updated_at
FROM
@@ -157,10 +172,13 @@ SELECT
id,
task_id,
state,
type,
object_key,
mime_type,
size,
filename,
url,
description,
created_at,
updated_at
FROM

View File

@@ -0,0 +1,74 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata
import (
"database/sql/driver"
"fmt"
)
type (
EvidenceType uint8
)
const (
EvidenceTypeFile EvidenceType = iota
EvidenceTypeLink
)
func (et EvidenceType) MarshalText() ([]byte, error) {
return []byte(et.String()), nil
}
func (et *EvidenceType) UnmarshalText(data []byte) error {
val := string(data)
switch val {
case EvidenceTypeFile.String():
*et = EvidenceTypeFile
case EvidenceTypeLink.String():
*et = EvidenceTypeLink
default:
return fmt.Errorf("invalid EvidenceType value: %q", val)
}
return nil
}
func (et EvidenceType) String() string {
var val string
switch et {
case EvidenceTypeFile:
val = "FILE"
case EvidenceTypeLink:
val = "LINK"
}
return val
}
func (et *EvidenceType) Scan(value any) error {
val, ok := value.(string)
if !ok {
return fmt.Errorf("invalid scan source for EvidenceType, expected string got %T", value)
}
return et.UnmarshalText([]byte(val))
}
func (et EvidenceType) Value() (driver.Value, error) {
return et.String(), nil
}

View File

@@ -0,0 +1,15 @@
-- Add evidence type enum
CREATE TYPE evidence_type AS ENUM (
'FILE',
'LINK'
);
ALTER TABLE evidences
ADD COLUMN type evidence_type NOT NULL DEFAULT 'FILE',
ADD COLUMN url TEXT NOT NULL DEFAULT '',
ADD COLUMN description TEXT NOT NULL DEFAULT '';
ALTER TABLE evidences
ALTER COLUMN type DROP DEFAULT,
ALTER COLUMN url DROP DEFAULT,
ALTER COLUMN description DROP DEFAULT;

View File

@@ -37,9 +37,12 @@ type (
}
CreateEvidenceRequest struct {
TaskID gid.GID
Name string
File io.Reader
TaskID gid.GID
Name string
Type coredata.EvidenceType
File io.Reader
URL string
Description string
}
)
@@ -73,50 +76,61 @@ func (s EvidenceService) Create(
return nil, fmt.Errorf("cannot create evidence global id: %w", err)
}
contentType := "application/octet-stream"
if req.Name != "" {
if detectedType := mime.TypeByExtension(filepath.Ext(req.Name)); detectedType != "" {
contentType = detectedType
evidence := &coredata.Evidence{
ID: evidenceID,
TaskID: req.TaskID,
State: coredata.EvidenceStateValid,
Type: req.Type,
Filename: req.Name,
URL: req.URL,
Description: req.Description,
CreatedAt: now,
UpdatedAt: now,
}
if req.Type == coredata.EvidenceTypeFile {
contentType := "application/octet-stream"
if req.Name != "" {
if detectedType := mime.TypeByExtension(filepath.Ext(req.Name)); detectedType != "" {
contentType = detectedType
}
}
}
objectKey, err := uuid.NewV7()
if err != nil {
return nil, fmt.Errorf("cannot generate object key: %w", err)
}
objectKey, err := uuid.NewV7()
if err != nil {
return nil, fmt.Errorf("cannot generate object key: %w", err)
}
putObjectOutput, err := s.svc.s3.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(objectKey.String()),
Body: req.File,
ContentType: aws.String(contentType),
})
if err != nil {
return nil, fmt.Errorf("cannot upload file to S3: %w", err)
}
putObjectOutput, err := s.svc.s3.PutObject(ctx, &s3.PutObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(objectKey.String()),
Body: req.File,
ContentType: aws.String(contentType),
})
if err != nil {
return nil, fmt.Errorf("cannot upload file to S3: %w", err)
}
headOutput, err := s.svc.s3.HeadObject(ctx, &s3.HeadObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(objectKey.String()),
})
if err != nil {
return nil, fmt.Errorf("cannot get object metadata: %w", err)
}
headOutput, err := s.svc.s3.HeadObject(ctx, &s3.HeadObjectInput{
Bucket: aws.String(s.svc.bucket),
Key: aws.String(objectKey.String()),
})
if err != nil {
return nil, fmt.Errorf("cannot get object metadata: %w", err)
}
fmt.Println("putObjectOutput", putObjectOutput)
fmt.Println("putObjectOutput", putObjectOutput)
evidence.ObjectKey = objectKey.String()
evidence.MimeType = contentType
evidence.Size = uint64(*headOutput.ContentLength)
} else if req.Type == coredata.EvidenceTypeLink {
evidence.MimeType = "text/uri-list"
evidence.Size = uint64(len(req.URL))
evidence.ObjectKey = ""
}
task := &coredata.Task{}
evidence := &coredata.Evidence{
ID: evidenceID,
TaskID: req.TaskID,
State: coredata.EvidenceStateValid,
ObjectKey: objectKey.String(),
MimeType: contentType,
Size: uint64(*headOutput.ContentLength),
Filename: req.Name,
CreatedAt: now,
UpdatedAt: now,
}
err = s.svc.pg.WithTx(
ctx,
@@ -134,7 +148,7 @@ func (s EvidenceService) Create(
)
if err != nil {
// TODO try do delete file from s3
// TODO try do delete file from s3 if it's a file type
return nil, err
}
@@ -151,6 +165,10 @@ func (s EvidenceService) GenerateFileURL(
return nil, fmt.Errorf("cannot get evidence: %w", err)
}
if evidence.Type == coredata.EvidenceTypeLink {
return nil, fmt.Errorf("cannot generate file URL for link type evidence")
}
presignClient := s3.NewPresignClient(s.svc.s3)
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{

View File

@@ -411,13 +411,22 @@ type EvidenceEdge {
node: Evidence!
}
enum EvidenceType
@goModel(model: "github.com/getprobo/probo/pkg/coredata.EvidenceType") {
FILE @goEnum(value: "github.com/getprobo/probo/pkg/coredata.EvidenceTypeFile")
LINK @goEnum(value: "github.com/getprobo/probo/pkg/coredata.EvidenceTypeLink")
}
type Evidence implements Node {
id: ID!
fileUrl: String! @goField(forceResolver: true)
fileUrl: String @goField(forceResolver: true)
mimeType: String!
size: Int!
state: EvidenceState!
type: EvidenceType!
filename: String!
url: String
description: String!
createdAt: Datetime!
updatedAt: Datetime!
@@ -707,7 +716,10 @@ type UpdateControlPayload {
input UploadEvidenceInput {
taskId: ID!
name: String!
file: Upload!
file: Upload
type: EvidenceType!
url: String
description: String!
}
type UploadEvidencePayload {

View File

@@ -141,14 +141,17 @@ type ComplexityRoot struct {
}
Evidence struct {
CreatedAt func(childComplexity int) int
FileURL func(childComplexity int) int
Filename func(childComplexity int) int
ID func(childComplexity int) int
MimeType func(childComplexity int) int
Size func(childComplexity int) int
State func(childComplexity int) int
UpdatedAt func(childComplexity int) int
CreatedAt func(childComplexity int) int
Description func(childComplexity int) int
FileURL func(childComplexity int) int
Filename func(childComplexity int) int
ID func(childComplexity int) int
MimeType func(childComplexity int) int
Size func(childComplexity int) int
State func(childComplexity int) int
Type func(childComplexity int) int
URL func(childComplexity int) int
UpdatedAt func(childComplexity int) int
}
EvidenceConnection struct {
@@ -420,7 +423,7 @@ type ControlResolver interface {
Tasks(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) (*types.TaskConnection, error)
}
type EvidenceResolver interface {
FileURL(ctx context.Context, obj *types.Evidence) (string, error)
FileURL(ctx context.Context, obj *types.Evidence) (*string, error)
}
type FrameworkResolver interface {
Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error)
@@ -711,6 +714,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Evidence.CreatedAt(childComplexity), true
case "Evidence.description":
if e.complexity.Evidence.Description == nil {
break
}
return e.complexity.Evidence.Description(childComplexity), true
case "Evidence.fileUrl":
if e.complexity.Evidence.FileURL == nil {
break
@@ -753,6 +763,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Evidence.State(childComplexity), true
case "Evidence.type":
if e.complexity.Evidence.Type == nil {
break
}
return e.complexity.Evidence.Type(childComplexity), true
case "Evidence.url":
if e.complexity.Evidence.URL == nil {
break
}
return e.complexity.Evidence.URL(childComplexity), true
case "Evidence.updatedAt":
if e.complexity.Evidence.UpdatedAt == nil {
break
@@ -2502,13 +2526,22 @@ type EvidenceEdge {
node: Evidence!
}
enum EvidenceType
@goModel(model: "github.com/getprobo/probo/pkg/coredata.EvidenceType") {
FILE @goEnum(value: "github.com/getprobo/probo/pkg/coredata.EvidenceTypeFile")
LINK @goEnum(value: "github.com/getprobo/probo/pkg/coredata.EvidenceTypeLink")
}
type Evidence implements Node {
id: ID!
fileUrl: String! @goField(forceResolver: true)
fileUrl: String @goField(forceResolver: true)
mimeType: String!
size: Int!
state: EvidenceState!
type: EvidenceType!
filename: String!
url: String
description: String!
createdAt: Datetime!
updatedAt: Datetime!
@@ -2798,7 +2831,10 @@ type UpdateControlPayload {
input UploadEvidenceInput {
taskId: ID!
name: String!
file: Upload!
file: Upload
type: EvidenceType!
url: String
description: String!
}
type UploadEvidencePayload {
@@ -5868,14 +5904,11 @@ func (ec *executionContext) _Evidence_fileUrl(ctx context.Context, field graphql
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
res := resTmp.(*string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Evidence_fileUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@@ -6005,6 +6038,44 @@ func (ec *executionContext) fieldContext_Evidence_state(_ context.Context, field
return fc, nil
}
func (ec *executionContext) _Evidence_type(ctx context.Context, field graphql.CollectedField, obj *types.Evidence) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Evidence_type(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.Type, 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.(coredata.EvidenceType)
fc.Result = res
return ec.marshalNEvidenceType2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐEvidenceType(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Evidence_type(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Evidence",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type EvidenceType does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Evidence_filename(ctx context.Context, field graphql.CollectedField, obj *types.Evidence) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Evidence_filename(ctx, field)
if err != nil {
@@ -6043,6 +6114,79 @@ func (ec *executionContext) fieldContext_Evidence_filename(_ context.Context, fi
return fc, nil
}
func (ec *executionContext) _Evidence_url(ctx context.Context, field graphql.CollectedField, obj *types.Evidence) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Evidence_url(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.URL, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*string)
fc.Result = res
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Evidence_url(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Evidence",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Evidence_description(ctx context.Context, field graphql.CollectedField, obj *types.Evidence) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Evidence_description(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.Description, 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.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Evidence_description(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Evidence",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Evidence_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Evidence) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Evidence_createdAt(ctx, field)
if err != nil {
@@ -6292,8 +6436,14 @@ func (ec *executionContext) fieldContext_EvidenceEdge_node(_ context.Context, fi
return ec.fieldContext_Evidence_size(ctx, field)
case "state":
return ec.fieldContext_Evidence_state(ctx, field)
case "type":
return ec.fieldContext_Evidence_type(ctx, field)
case "filename":
return ec.fieldContext_Evidence_filename(ctx, field)
case "url":
return ec.fieldContext_Evidence_url(ctx, field)
case "description":
return ec.fieldContext_Evidence_description(ctx, field)
case "createdAt":
return ec.fieldContext_Evidence_createdAt(ctx, field)
case "updatedAt":
@@ -15668,7 +15818,7 @@ func (ec *executionContext) unmarshalInputUploadEvidenceInput(ctx context.Contex
asMap[k] = v
}
fieldsInOrder := [...]string{"taskId", "name", "file"}
fieldsInOrder := [...]string{"taskId", "name", "file", "type", "url", "description"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -15691,11 +15841,32 @@ func (ec *executionContext) unmarshalInputUploadEvidenceInput(ctx context.Contex
it.Name = data
case "file":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("file"))
data, err := ec.unmarshalNUpload2githubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, v)
data, err := ec.unmarshalOUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, v)
if err != nil {
return it, err
}
it.File = data
case "type":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type"))
data, err := ec.unmarshalNEvidenceType2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐEvidenceType(ctx, v)
if err != nil {
return it, err
}
it.Type = data
case "url":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("url"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.URL = data
case "description":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("description"))
data, err := ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
it.Description = data
}
}
@@ -16652,11 +16823,8 @@ func (ec *executionContext) _Evidence(ctx context.Context, sel ast.SelectionSet,
case "fileUrl":
field := field
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) {
res = ec._Evidence_fileUrl(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&fs.Invalids, 1)
}
return res
}
@@ -16695,11 +16863,23 @@ func (ec *executionContext) _Evidence(ctx context.Context, sel ast.SelectionSet,
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "type":
out.Values[i] = ec._Evidence_type(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "filename":
out.Values[i] = ec._Evidence_filename(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "url":
out.Values[i] = ec._Evidence_url(ctx, field, obj)
case "description":
out.Values[i] = ec._Evidence_description(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "createdAt":
out.Values[i] = ec._Evidence_createdAt(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -20101,6 +20281,33 @@ var (
}
)
func (ec *executionContext) unmarshalNEvidenceType2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐEvidenceType(ctx context.Context, v any) (coredata.EvidenceType, error) {
tmp, err := graphql.UnmarshalString(v)
res := unmarshalNEvidenceType2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐEvidenceType[tmp]
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNEvidenceType2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐEvidenceType(ctx context.Context, sel ast.SelectionSet, v coredata.EvidenceType) graphql.Marshaler {
res := graphql.MarshalString(marshalNEvidenceType2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐEvidenceType[v])
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
}
return res
}
var (
unmarshalNEvidenceType2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐEvidenceType = map[string]coredata.EvidenceType{
"FILE": coredata.EvidenceTypeFile,
"LINK": coredata.EvidenceTypeLink,
}
marshalNEvidenceType2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐEvidenceType = map[coredata.EvidenceType]string{
coredata.EvidenceTypeFile: "FILE",
coredata.EvidenceTypeLink: "LINK",
}
)
func (ec *executionContext) marshalNFramework2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐFramework(ctx context.Context, sel ast.SelectionSet, v *types.Framework) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {

View File

@@ -44,14 +44,25 @@ func NewEvidenceEdge(e *coredata.Evidence, orderBy coredata.EvidenceOrderField)
}
func NewEvidence(e *coredata.Evidence) *Evidence {
var fileURL *string = nil
var urlPtr *string = nil
if e.URL != "" {
urlCopy := e.URL
urlPtr = &urlCopy
}
return &Evidence{
ID: e.ID,
State: e.State,
FileURL: "",
Filename: e.Filename,
MimeType: e.MimeType,
Size: int(e.Size),
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
ID: e.ID,
State: e.State,
Type: e.Type,
FileURL: fileURL,
Filename: e.Filename,
MimeType: e.MimeType,
Size: int(e.Size),
URL: urlPtr,
Description: e.Description,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
}
}

View File

@@ -195,14 +195,17 @@ type DeleteVendorPayload struct {
}
type Evidence struct {
ID gid.GID `json:"id"`
FileURL string `json:"fileUrl"`
MimeType string `json:"mimeType"`
Size int `json:"size"`
State coredata.EvidenceState `json:"state"`
Filename string `json:"filename"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID gid.GID `json:"id"`
FileURL *string `json:"fileUrl,omitempty"`
MimeType string `json:"mimeType"`
Size int `json:"size"`
State coredata.EvidenceState `json:"state"`
Type coredata.EvidenceType `json:"type"`
Filename string `json:"filename"`
URL *string `json:"url,omitempty"`
Description string `json:"description"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (Evidence) IsNode() {}
@@ -495,9 +498,12 @@ type UpdateVendorPayload struct {
}
type UploadEvidenceInput struct {
TaskID gid.GID `json:"taskId"`
Name string `json:"name"`
File graphql.Upload `json:"file"`
TaskID gid.GID `json:"taskId"`
Name string `json:"name"`
File *graphql.Upload `json:"file,omitempty"`
Type coredata.EvidenceType `json:"type"`
URL *string `json:"url,omitempty"`
Description string `json:"description"`
}
type UploadEvidencePayload struct {

View File

@@ -45,15 +45,20 @@ func (r *controlResolver) Tasks(ctx context.Context, obj *types.Control, first *
}
// FileURL is the resolver for the fileUrl field.
func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (string, error) {
func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (*string, error) {
svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())
if obj.Type == coredata.EvidenceTypeLink {
return obj.URL, nil
}
fileURL, err := svc.Evidences.GenerateFileURL(ctx, obj.ID, 15*time.Minute)
if err != nil {
return "", fmt.Errorf("cannot generate file URL: %w", err)
return nil, fmt.Errorf("cannot generate file URL: %w", err)
}
return *fileURL, nil
result := *fileURL
return &result, nil
}
// Controls is the resolver for the controls field.
@@ -436,10 +441,28 @@ func (r *mutationResolver) UpdateControl(ctx context.Context, input types.Update
func (r *mutationResolver) UploadEvidence(ctx context.Context, input types.UploadEvidenceInput) (*types.UploadEvidencePayload, error) {
svc := r.GetTenantServiceIfAuthorized(ctx, input.TaskID.TenantID())
var url string
if input.URL != nil {
url = *input.URL
}
req := probo.CreateEvidenceRequest{
TaskID: input.TaskID,
Name: input.Name,
File: input.File.File,
TaskID: input.TaskID,
Name: input.Name,
Type: input.Type,
URL: url,
Description: input.Description,
}
if input.Type == coredata.EvidenceTypeFile {
if input.File == nil {
return nil, fmt.Errorf("file is required for FILE type evidence")
}
req.File = input.File.File
} else if input.Type == coredata.EvidenceTypeLink {
if input.URL == nil || *input.URL == "" {
return nil, fmt.Errorf("URL is required for LINK type evidence")
}
}
evidence, err := svc.Evidences.Create(ctx, req)