diff --git a/db/dbinfo/fileupload.file.bob.go b/db/dbinfo/fileupload.file.bob.go index a26e4e13..c6a7aa3e 100644 --- a/db/dbinfo/fileupload.file.bob.go +++ b/db/dbinfo/fileupload.file.bob.go @@ -114,6 +114,15 @@ var FileuploadFiles = Table[ Generated: false, AutoIncr: false, }, + Error: column{ + Name: "error", + DBType: "text", + Default: "", + Comment: "", + Nullable: false, + Generated: false, + AutoIncr: false, + }, }, Indexes: fileuploadFileIndexes{ FilePkey: index{ @@ -184,11 +193,12 @@ type fileuploadFileColumns struct { SizeBytes column FileUUID column Committer column + Error column } func (c fileuploadFileColumns) AsSlice() []column { return []column{ - c.ID, c.ContentType, c.Created, c.CreatorID, c.Deleted, c.Name, c.OrganizationID, c.Status, c.SizeBytes, c.FileUUID, c.Committer, + c.ID, c.ContentType, c.Created, c.CreatorID, c.Deleted, c.Name, c.OrganizationID, c.Status, c.SizeBytes, c.FileUUID, c.Committer, c.Error, } } diff --git a/db/migrations/00135_fileupload_file_error.sql b/db/migrations/00135_fileupload_file_error.sql new file mode 100644 index 00000000..0f757061 --- /dev/null +++ b/db/migrations/00135_fileupload_file_error.sql @@ -0,0 +1,6 @@ +-- +goose Up +ALTER TABLE fileupload.file ADD COLUMN error TEXT; +UPDATE fileupload.file SET error = ''; +ALTER TABLE fileupload.file ALTER COLUMN error SET NOT NULL; +-- +goose Down +ALTER TABLE fileupload.file DROP COLUMN error; diff --git a/db/models/fileupload.file.bob.go b/db/models/fileupload.file.bob.go index 2e0de097..d8be2128 100644 --- a/db/models/fileupload.file.bob.go +++ b/db/models/fileupload.file.bob.go @@ -38,6 +38,7 @@ type FileuploadFile struct { SizeBytes int32 `db:"size_bytes" ` FileUUID uuid.UUID `db:"file_uuid" ` Committer null.Val[int32] `db:"committer" ` + Error string `db:"error" ` R fileuploadFileR `db:"-" ` } @@ -65,7 +66,7 @@ type fileuploadFileR struct { func buildFileuploadFileColumns(alias string) fileuploadFileColumns { return fileuploadFileColumns{ ColumnsExpr: expr.NewColumnsExpr( - "id", "content_type", "created", "creator_id", "deleted", "name", "organization_id", "status", "size_bytes", "file_uuid", "committer", + "id", "content_type", "created", "creator_id", "deleted", "name", "organization_id", "status", "size_bytes", "file_uuid", "committer", "error", ).WithParent("fileupload.file"), tableAlias: alias, ID: psql.Quote(alias, "id"), @@ -79,6 +80,7 @@ func buildFileuploadFileColumns(alias string) fileuploadFileColumns { SizeBytes: psql.Quote(alias, "size_bytes"), FileUUID: psql.Quote(alias, "file_uuid"), Committer: psql.Quote(alias, "committer"), + Error: psql.Quote(alias, "error"), } } @@ -96,6 +98,7 @@ type fileuploadFileColumns struct { SizeBytes psql.Expression FileUUID psql.Expression Committer psql.Expression + Error psql.Expression } func (c fileuploadFileColumns) Alias() string { @@ -121,10 +124,11 @@ type FileuploadFileSetter struct { SizeBytes omit.Val[int32] `db:"size_bytes" ` FileUUID omit.Val[uuid.UUID] `db:"file_uuid" ` Committer omitnull.Val[int32] `db:"committer" ` + Error omit.Val[string] `db:"error" ` } func (s FileuploadFileSetter) SetColumns() []string { - vals := make([]string, 0, 11) + vals := make([]string, 0, 12) if s.ID.IsValue() { vals = append(vals, "id") } @@ -158,6 +162,9 @@ func (s FileuploadFileSetter) SetColumns() []string { if !s.Committer.IsUnset() { vals = append(vals, "committer") } + if s.Error.IsValue() { + vals = append(vals, "error") + } return vals } @@ -195,6 +202,9 @@ func (s FileuploadFileSetter) Overwrite(t *FileuploadFile) { if !s.Committer.IsUnset() { t.Committer = s.Committer.MustGetNull() } + if s.Error.IsValue() { + t.Error = s.Error.MustGet() + } } func (s *FileuploadFileSetter) Apply(q *dialect.InsertQuery) { @@ -203,7 +213,7 @@ func (s *FileuploadFileSetter) Apply(q *dialect.InsertQuery) { }) q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) { - vals := make([]bob.Expression, 11) + vals := make([]bob.Expression, 12) if s.ID.IsValue() { vals[0] = psql.Arg(s.ID.MustGet()) } else { @@ -270,6 +280,12 @@ func (s *FileuploadFileSetter) Apply(q *dialect.InsertQuery) { vals[10] = psql.Raw("DEFAULT") } + if s.Error.IsValue() { + vals[11] = psql.Arg(s.Error.MustGet()) + } else { + vals[11] = psql.Raw("DEFAULT") + } + return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "") })) } @@ -279,7 +295,7 @@ func (s FileuploadFileSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] { } func (s FileuploadFileSetter) Expressions(prefix ...string) []bob.Expression { - exprs := make([]bob.Expression, 0, 11) + exprs := make([]bob.Expression, 0, 12) if s.ID.IsValue() { exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ @@ -358,6 +374,13 @@ func (s FileuploadFileSetter) Expressions(prefix ...string) []bob.Expression { }}) } + if s.Error.IsValue() { + exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{ + psql.Quote(append(prefix, "error")...), + psql.Arg(s.Error), + }}) + } + return exprs } @@ -1074,6 +1097,7 @@ type fileuploadFileWhere[Q psql.Filterable] struct { SizeBytes psql.WhereMod[Q, int32] FileUUID psql.WhereMod[Q, uuid.UUID] Committer psql.WhereNullMod[Q, int32] + Error psql.WhereMod[Q, string] } func (fileuploadFileWhere[Q]) AliasedAs(alias string) fileuploadFileWhere[Q] { @@ -1093,6 +1117,7 @@ func buildFileuploadFileWhere[Q psql.Filterable](cols fileuploadFileColumns) fil SizeBytes: psql.Where[Q, int32](cols.SizeBytes), FileUUID: psql.Where[Q, uuid.UUID](cols.FileUUID), Committer: psql.WhereNull[Q, int32](cols.Committer), + Error: psql.Where[Q, string](cols.Error), } } diff --git a/platform/csv/csv.go b/platform/csv/csv.go index 40f97a60..7d5c0aa6 100644 --- a/platform/csv/csv.go +++ b/platform/csv/csv.go @@ -192,9 +192,11 @@ func JobImport(ctx context.Context, txn bob.Executor, file_id int32) error { err = importCSV(ctx, file_id, parseCSVFlyover, processCSVFlyover) } if err != nil { + log.Debug().Err(err).Msg("failed to import CSV") _, err := psql.Update( um.Table("fileupload.file"), um.SetCol("status").ToArg("error"), + um.SetCol("error").ToArg(err.Error()), um.Where(psql.Quote("id").EQ(psql.Arg(file_id))), ).Exec(ctx, db.PGInstance.BobDB) if err != nil { diff --git a/platform/upload.go b/platform/upload.go index 69629b37..c7560ce1 100644 --- a/platform/upload.go +++ b/platform/upload.go @@ -36,6 +36,7 @@ const ( type Upload struct { Created time.Time `db:"created" json:"created"` + Error string `db:"error" json:"error"` Filename string `db:"filename" json:"filename"` ID int32 `db:"id" json:"id"` RecordCount int `db:"recordcount" json:"recordcount"` @@ -92,6 +93,7 @@ func NewUpload(ctx context.Context, u User, upload file.Upload, t enums.Fileuplo Created: omit.From(time.Now()), CreatorID: omit.From(int32(u.ID)), Deleted: omitnull.FromPtr[time.Time](nil), + Error: omit.From(""), Name: omit.From(upload.Name), OrganizationID: omit.From(u.Organization.ID), Status: omit.From(enums.FileuploadFilestatustypeUploaded), @@ -167,6 +169,7 @@ func UploadList(ctx context.Context, org Organization) ([]Upload, error) { "file.created AS created", //"file.creator_id", //"file.deleted", + "file.error AS error", "file.id AS id", "file.name AS filename", //"file.organization_id", @@ -235,9 +238,9 @@ func getUploadDetailPool(ctx context.Context, file *models.FileuploadFile) (*Upl Tags: tags, }) } - log.Debug().Str("status", file.Status.String()).Int32("id", file.ID).Msg("returning") return &Upload{ Created: file.Created, + Error: file.Error, Filename: file.Name, ID: file.ID, RecordCount: len(pool_rows), diff --git a/ts/type/api.ts b/ts/type/api.ts index 7d0706d8..365e4a92 100644 --- a/ts/type/api.ts +++ b/ts/type/api.ts @@ -559,6 +559,17 @@ export interface ReviewTaskListResponse { } export interface UploadDTO { created: string; + error: string; + filename: string; + id: number; + recordcount: number; + status: string; + type: string; + csv_pool?: CSVPoolDetail; +} +export interface UploadOptions { + created: Date; + error: string; filename: string; id: number; recordcount: number; @@ -567,25 +578,29 @@ export interface UploadDTO { csv_pool?: CSVPoolDetail; } export class Upload { - constructor( - public created: Date, - public filename: string, - public id: number, - public recordcount: number, - public status: string, - public type: string, - public csv_pool?: CSVPoolDetail, - ) {} + created: Date; + error: string; + filename: string; + id: number; + recordcount: number; + status: string; + type: string; + csv_pool?: CSVPoolDetail; + constructor(options: UploadOptions) { + this.created = options.created; + this.error = options.error; + this.filename = options.filename; + this.id = options.id; + this.recordcount = options.recordcount; + this.status = options.status; + this.type = options.type; + this.csv_pool = options.csv_pool; + } static fromJSON(json: UploadDTO): Upload { - return new Upload( - new Date(json.created), - json.filename, - json.id, - json.recordcount, - json.status, - json.type, - json.csv_pool, - ); + return new Upload({ + ...json, + created: new Date(json.created), + }); } } diff --git a/ts/view/configuration/UploadDetail.vue b/ts/view/configuration/UploadDetail.vue index 5f51f3b1..d4c352c3 100644 --- a/ts/view/configuration/UploadDetail.vue +++ b/ts/view/configuration/UploadDetail.vue @@ -150,12 +150,11 @@ tr.has-error {

loading

- + />
@@ -202,8 +201,13 @@ tr.has-error {