File refs in one table

Added a file service
Refactored evidence and vendor compliance associated services and resolvers
Created SQL migration scripts
Updated GraphQL schema accordingly and the UI components
Removed unused methods and resolvers for evidence service

Signed-off-by: Yannis Varni <yannis@edinomis.fr>
This commit is contained in:
Yannis Varni
2025-10-03 15:38:00 +02:00
committed by Sacha Al Himdani
parent 3822b1916c
commit ff7ba08fc1
35 changed files with 1586 additions and 1658 deletions

View File

@@ -28,20 +28,17 @@ import (
type (
Evidence struct {
ID gid.GID `db:"id"`
MeasureID gid.GID `db:"measure_id"`
TaskID *gid.GID `db:"task_id"`
State EvidenceState `db:"state"`
ReferenceID string `db:"reference_id"`
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"`
ID gid.GID `db:"id"`
MeasureID gid.GID `db:"measure_id"`
TaskID *gid.GID `db:"task_id"`
State EvidenceState `db:"state"`
ReferenceID string `db:"reference_id"`
Type EvidenceType `db:"type"`
URL string `db:"url"`
EvidenceFileId *gid.GID `db:"evidence_file_id"`
Description string `db:"description"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Evidences []*Evidence
@@ -69,13 +66,10 @@ INSERT INTO
measure_id,
task_id,
reference_id,
object_key,
mime_type,
size,
state,
type,
filename,
url,
evidence_file_id,
description,
created_at,
updated_at
@@ -86,13 +80,10 @@ VALUES (
@measure_id,
@task_id,
@reference_id,
@object_key,
@mime_type,
@size,
@state,
@type,
@filename,
@url,
@evidence_file_id,
@description,
@created_at,
@updated_at
@@ -105,21 +96,18 @@ WHERE evidences.state = 'REQUESTED';
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"evidence_id": e.ID,
"measure_id": e.MeasureID,
"task_id": e.TaskID,
"reference_id": e.ReferenceID,
"object_key": e.ObjectKey,
"mime_type": e.MimeType,
"size": e.Size,
"filename": e.Filename,
"created_at": e.CreatedAt,
"updated_at": e.UpdatedAt,
"state": e.State,
"type": e.Type,
"url": e.URL,
"description": e.Description,
"tenant_id": scope.GetTenantID(),
"evidence_id": e.ID,
"measure_id": e.MeasureID,
"task_id": e.TaskID,
"reference_id": e.ReferenceID,
"evidence_file_id": e.EvidenceFileId,
"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
@@ -138,13 +126,10 @@ INSERT INTO
measure_id,
task_id,
reference_id,
object_key,
mime_type,
size,
state,
type,
filename,
url,
evidence_file_id,
description,
created_at,
updated_at
@@ -155,13 +140,10 @@ VALUES (
@measure_id,
@task_id,
@reference_id,
@object_key,
@mime_type,
@size,
@state,
@type,
@filename,
@url,
@evidence_file_id,
@description,
@created_at,
@updated_at
@@ -169,21 +151,18 @@ VALUES (
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"evidence_id": e.ID,
"measure_id": e.MeasureID,
"task_id": e.TaskID,
"reference_id": e.ReferenceID,
"object_key": e.ObjectKey,
"mime_type": e.MimeType,
"size": e.Size,
"filename": e.Filename,
"created_at": e.CreatedAt,
"updated_at": e.UpdatedAt,
"state": e.State,
"type": e.Type,
"url": e.URL,
"description": e.Description,
"tenant_id": scope.GetTenantID(),
"evidence_id": e.ID,
"measure_id": e.MeasureID,
"task_id": e.TaskID,
"reference_id": e.ReferenceID,
"evidence_file_id": e.EvidenceFileId,
"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
@@ -203,11 +182,8 @@ SELECT
reference_id,
state,
type,
object_key,
mime_type,
size,
filename,
url,
evidence_file_id,
description,
created_at,
updated_at
@@ -286,11 +262,8 @@ SELECT
reference_id,
state,
type,
object_key,
mime_type,
size,
filename,
url,
evidence_file_id,
description,
created_at,
updated_at
@@ -370,11 +343,8 @@ SELECT
reference_id,
state,
type,
object_key,
mime_type,
size,
filename,
url,
evidence_file_id,
description,
created_at,
updated_at
@@ -418,10 +388,7 @@ UPDATE
SET
type = @type,
state = @state,
object_key = @object_key,
mime_type = @mime_type,
size = @size,
filename = @filename,
evidence_file_id = @evidence_file_id,
url = @url,
description = @description,
updated_at = @updated_at
@@ -433,16 +400,13 @@ WHERE
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"evidence_id": e.ID,
"type": e.Type,
"state": e.State,
"object_key": e.ObjectKey,
"mime_type": e.MimeType,
"size": e.Size,
"filename": e.Filename,
"url": e.URL,
"description": e.Description,
"updated_at": e.UpdatedAt,
"evidence_id": e.ID,
"type": e.Type,
"state": e.State,
"evidence_file_id": e.EvidenceFileId,
"url": e.URL,
"description": e.Description,
"updated_at": e.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
@@ -454,13 +418,14 @@ func (e Evidence) Delete(
ctx context.Context,
conn pg.Conn,
scope Scoper,
) error {
) (*string, error) {
q := `
DELETE FROM
evidences
WHERE
%s
%s
AND id = @evidence_id
RETURNING evidence_file_id;
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -468,6 +433,23 @@ WHERE
args := pgx.StrictNamedArgs{"evidence_id": e.ID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
var evidenceFileId *gid.GID
err := conn.QueryRow(ctx, q, args).Scan(&evidenceFileId)
if err != nil {
return nil, fmt.Errorf("failed to delete evidence: %w", err)
}
if evidenceFileId != nil {
var err error
var fileKey *string
file := &File{ID: *evidenceFileId}
if fileKey, err = file.HardDelete(ctx, conn, scope); err != nil {
return nil, fmt.Errorf("failed to hard delete evidence file: %w", err)
}
return fileKey, nil
}
return nil, err
}

View File

@@ -135,3 +135,39 @@ VALUES (
_, err := conn.Exec(ctx, q, args)
return err
}
func (f File) SoftDelete(ctx context.Context, conn pg.Conn, scope Scoper) error {
q := `
UPDATE files
SET deleted_at = NOW()
WHERE %s
AND id = @file_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"file_id": f.ID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
}
func (f File) HardDelete(ctx context.Context, conn pg.Conn, scope Scoper) (*string, error) {
q := `
DELETE FROM files
WHERE %s
AND id = @file_id
RETURNING file_key
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"file_id": f.ID}
maps.Copy(args, scope.SQLArguments())
var vcrFileKey *string
err := conn.QueryRow(ctx, q, args).Scan(&vcrFileKey)
return vcrFileKey, err
}

View File

@@ -0,0 +1,41 @@
ALTER TABLE evidences
ADD COLUMN evidence_file_id text;
/* 10 is for FileEntityType */
WITH
evidence_files AS (
SELECT
e.id as evidence_id,
generate_gid(decode_base64_unpadded(e.tenant_id), 25) as file_id,
e.tenant_id,
'probod' as bucket_name,
e.mime_type,
e.filename,
e.object_key,
e.size,
e.created_at,
e.updated_at
FROM evidences e
),
inserted_files AS (
INSERT INTO files (id, tenant_id, bucket_name, mime_type, file_name, file_key, file_size, created_at, updated_at)
SELECT file_id, tenant_id, bucket_name, mime_type, filename, object_key::uuid, size, created_at, updated_at
FROM evidence_files
RETURNING id, tenant_id
)
SELECT ef.evidence_id, ef.file_id
INTO TEMP TABLE file_evidence_mapping
FROM evidence_files ef;
UPDATE evidences
SET evidence_file_id = fm.file_id
FROM file_evidence_mapping fm
WHERE evidences.id = fm.evidence_id;
ALTER TABLE evidences
ALTER COLUMN filename DROP NOT NULL,
ALTER COLUMN mime_type DROP NOT NULL,
ALTER COLUMN size DROP NOT NULL,
ALTER COLUMN oject_key DROP NOT NULL;

View File

@@ -0,0 +1,41 @@
ALTER TABLE
vendor_compliance_reports
ADD COLUMN report_file_id text
REFERENCES files(id)
ON UPDATE CASCADE
ON DELETE RESTRICT;
WITH
/* 25 is for FileEntityType */
vcr_files AS (
SELECT
vcr.id as report_id,
generate_gid(decode_base64_unpadded(vcr.tenant_id), 25) as file_id,
vcr.tenant_id,
'probod' as bucket_name,
'application/pdf' as mime_type,
vcr.report_name,
vcr.file_key,
vcr.file_size,
vcr.created_at,
vcr.updated_at
FROM vendor_compliance_reports vcr
),
inserted_files AS (
INSERT INTO files (id, tenant_id, bucket_name, mime_type, file_name, file_key, file_size, created_at, updated_at)
SELECT file_id, tenant_id, bucket_name, mime_type, report_name, file_key::uuid, file_size, created_at, updated_at
FROM vcr_files
RETURNING id, tenant_id
)
SELECT vf.report_id, vf.file_id
INTO TEMP TABLE file_vcr_mapping
FROM vcr_files vf;
UPDATE vendor_compliance_reports
SET report_file_id = fv.file_id
FROM file_vcr_mapping fv
WHERE vendor_compliance_reports.id = fv.report_id;
ALTER TABLE vendor_compliance_reports
ALTER COLUMN file_key DROP NOT NULL,
ALTER COLUMN file_size DROP NOT NULL;

View File

@@ -0,0 +1,7 @@
ALTER TABLE
evidences
ADD CONSTRAINT fk_evidence_file
FOREIGN KEY (evidence_file_id)
REFERENCES files(id)
ON UPDATE CASCADE
ON DELETE RESTRICT;

View File

@@ -28,17 +28,16 @@ import (
type (
VendorComplianceReport struct {
ID gid.GID
VendorID gid.GID
ReportDate time.Time
ValidUntil *time.Time
ReportName string
FileKey string
FileSize int64
SnapshotID *gid.GID
SourceID *gid.GID
CreatedAt time.Time
UpdatedAt time.Time
ID gid.GID
VendorID gid.GID
ReportDate time.Time
ValidUntil *time.Time
ReportName string
ReportFileId *gid.GID
SnapshotID *gid.GID
SourceID *gid.GID
CreatedAt time.Time
UpdatedAt time.Time
}
VendorComplianceReports []*VendorComplianceReport
@@ -69,8 +68,7 @@ SELECT
report_date,
valid_until,
report_name,
file_key,
file_size,
report_file_id,
snapshot_id,
source_id,
created_at,
@@ -117,8 +115,7 @@ SELECT
report_date,
valid_until,
report_name,
file_key,
file_size,
report_file_id,
snapshot_id,
source_id,
created_at,
@@ -165,8 +162,7 @@ INSERT INTO
report_date,
valid_until,
report_name,
file_key,
file_size,
report_file_id,
created_at,
updated_at
)
@@ -177,23 +173,21 @@ VALUES (
@report_date,
@valid_until,
@report_name,
@file_key,
@file_size,
@report_file_id,
@created_at,
@updated_at
)
`
args := pgx.NamedArgs{
"id": vcr.ID,
"tenant_id": scope.GetTenantID(),
"vendor_id": vcr.VendorID,
"report_date": vcr.ReportDate,
"valid_until": vcr.ValidUntil,
"report_name": vcr.ReportName,
"file_key": vcr.FileKey,
"file_size": vcr.FileSize,
"created_at": vcr.CreatedAt,
"updated_at": vcr.UpdatedAt,
"id": vcr.ID,
"tenant_id": scope.GetTenantID(),
"vendor_id": vcr.VendorID,
"report_date": vcr.ReportDate,
"valid_until": vcr.ValidUntil,
"report_name": vcr.ReportName,
"report_file_id": vcr.ReportFileId,
"created_at": vcr.CreatedAt,
"updated_at": vcr.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)
@@ -213,6 +207,7 @@ WHERE
%s
AND id = @id
AND snapshot_id IS NULL
RETURNING report_file_id
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -220,8 +215,20 @@ WHERE
args := pgx.StrictNamedArgs{"id": vcr.ID}
maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, q, args)
return err
var vcrFileId *gid.GID
err := conn.QueryRow(ctx, q, args).Scan(&vcrFileId)
if err != nil {
return fmt.Errorf("failed to delete vendor compliance report: %w", err)
}
if vcrFileId != nil {
file := &File{ID: *vcrFileId}
if err = file.SoftDelete(ctx, conn, scope); err != nil {
return fmt.Errorf("failed to soft delete vendor compliance file: %w", err)
}
}
return nil
}
func (vcrs VendorComplianceReports) InsertVendorSnapshots(
@@ -247,8 +254,7 @@ INSERT INTO vendor_compliance_reports (
report_date,
valid_until,
report_name,
file_key,
file_size,
report_file_id,
created_at,
updated_at
)
@@ -261,8 +267,7 @@ SELECT
vcr.report_date,
vcr.valid_until,
vcr.report_name,
vcr.file_key,
vcr.file_size,
vcr.report_file_id,
vcr.created_at,
vcr.updated_at
FROM vendor_compliance_reports vcr