Add error display to file upload
This commit is contained in:
parent
344f4bcaa5
commit
66d35428fa
7 changed files with 93 additions and 28 deletions
|
|
@ -114,6 +114,15 @@ var FileuploadFiles = Table[
|
||||||
Generated: false,
|
Generated: false,
|
||||||
AutoIncr: false,
|
AutoIncr: false,
|
||||||
},
|
},
|
||||||
|
Error: column{
|
||||||
|
Name: "error",
|
||||||
|
DBType: "text",
|
||||||
|
Default: "",
|
||||||
|
Comment: "",
|
||||||
|
Nullable: false,
|
||||||
|
Generated: false,
|
||||||
|
AutoIncr: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Indexes: fileuploadFileIndexes{
|
Indexes: fileuploadFileIndexes{
|
||||||
FilePkey: index{
|
FilePkey: index{
|
||||||
|
|
@ -184,11 +193,12 @@ type fileuploadFileColumns struct {
|
||||||
SizeBytes column
|
SizeBytes column
|
||||||
FileUUID column
|
FileUUID column
|
||||||
Committer column
|
Committer column
|
||||||
|
Error column
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c fileuploadFileColumns) AsSlice() []column {
|
func (c fileuploadFileColumns) AsSlice() []column {
|
||||||
return []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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
db/migrations/00135_fileupload_file_error.sql
Normal file
6
db/migrations/00135_fileupload_file_error.sql
Normal file
|
|
@ -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;
|
||||||
|
|
@ -38,6 +38,7 @@ type FileuploadFile struct {
|
||||||
SizeBytes int32 `db:"size_bytes" `
|
SizeBytes int32 `db:"size_bytes" `
|
||||||
FileUUID uuid.UUID `db:"file_uuid" `
|
FileUUID uuid.UUID `db:"file_uuid" `
|
||||||
Committer null.Val[int32] `db:"committer" `
|
Committer null.Val[int32] `db:"committer" `
|
||||||
|
Error string `db:"error" `
|
||||||
|
|
||||||
R fileuploadFileR `db:"-" `
|
R fileuploadFileR `db:"-" `
|
||||||
}
|
}
|
||||||
|
|
@ -65,7 +66,7 @@ type fileuploadFileR struct {
|
||||||
func buildFileuploadFileColumns(alias string) fileuploadFileColumns {
|
func buildFileuploadFileColumns(alias string) fileuploadFileColumns {
|
||||||
return fileuploadFileColumns{
|
return fileuploadFileColumns{
|
||||||
ColumnsExpr: expr.NewColumnsExpr(
|
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"),
|
).WithParent("fileupload.file"),
|
||||||
tableAlias: alias,
|
tableAlias: alias,
|
||||||
ID: psql.Quote(alias, "id"),
|
ID: psql.Quote(alias, "id"),
|
||||||
|
|
@ -79,6 +80,7 @@ func buildFileuploadFileColumns(alias string) fileuploadFileColumns {
|
||||||
SizeBytes: psql.Quote(alias, "size_bytes"),
|
SizeBytes: psql.Quote(alias, "size_bytes"),
|
||||||
FileUUID: psql.Quote(alias, "file_uuid"),
|
FileUUID: psql.Quote(alias, "file_uuid"),
|
||||||
Committer: psql.Quote(alias, "committer"),
|
Committer: psql.Quote(alias, "committer"),
|
||||||
|
Error: psql.Quote(alias, "error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,6 +98,7 @@ type fileuploadFileColumns struct {
|
||||||
SizeBytes psql.Expression
|
SizeBytes psql.Expression
|
||||||
FileUUID psql.Expression
|
FileUUID psql.Expression
|
||||||
Committer psql.Expression
|
Committer psql.Expression
|
||||||
|
Error psql.Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c fileuploadFileColumns) Alias() string {
|
func (c fileuploadFileColumns) Alias() string {
|
||||||
|
|
@ -121,10 +124,11 @@ type FileuploadFileSetter struct {
|
||||||
SizeBytes omit.Val[int32] `db:"size_bytes" `
|
SizeBytes omit.Val[int32] `db:"size_bytes" `
|
||||||
FileUUID omit.Val[uuid.UUID] `db:"file_uuid" `
|
FileUUID omit.Val[uuid.UUID] `db:"file_uuid" `
|
||||||
Committer omitnull.Val[int32] `db:"committer" `
|
Committer omitnull.Val[int32] `db:"committer" `
|
||||||
|
Error omit.Val[string] `db:"error" `
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s FileuploadFileSetter) SetColumns() []string {
|
func (s FileuploadFileSetter) SetColumns() []string {
|
||||||
vals := make([]string, 0, 11)
|
vals := make([]string, 0, 12)
|
||||||
if s.ID.IsValue() {
|
if s.ID.IsValue() {
|
||||||
vals = append(vals, "id")
|
vals = append(vals, "id")
|
||||||
}
|
}
|
||||||
|
|
@ -158,6 +162,9 @@ func (s FileuploadFileSetter) SetColumns() []string {
|
||||||
if !s.Committer.IsUnset() {
|
if !s.Committer.IsUnset() {
|
||||||
vals = append(vals, "committer")
|
vals = append(vals, "committer")
|
||||||
}
|
}
|
||||||
|
if s.Error.IsValue() {
|
||||||
|
vals = append(vals, "error")
|
||||||
|
}
|
||||||
return vals
|
return vals
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -195,6 +202,9 @@ func (s FileuploadFileSetter) Overwrite(t *FileuploadFile) {
|
||||||
if !s.Committer.IsUnset() {
|
if !s.Committer.IsUnset() {
|
||||||
t.Committer = s.Committer.MustGetNull()
|
t.Committer = s.Committer.MustGetNull()
|
||||||
}
|
}
|
||||||
|
if s.Error.IsValue() {
|
||||||
|
t.Error = s.Error.MustGet()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *FileuploadFileSetter) Apply(q *dialect.InsertQuery) {
|
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) {
|
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() {
|
if s.ID.IsValue() {
|
||||||
vals[0] = psql.Arg(s.ID.MustGet())
|
vals[0] = psql.Arg(s.ID.MustGet())
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -270,6 +280,12 @@ func (s *FileuploadFileSetter) Apply(q *dialect.InsertQuery) {
|
||||||
vals[10] = psql.Raw("DEFAULT")
|
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, "", ", ", "")
|
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 {
|
func (s FileuploadFileSetter) Expressions(prefix ...string) []bob.Expression {
|
||||||
exprs := make([]bob.Expression, 0, 11)
|
exprs := make([]bob.Expression, 0, 12)
|
||||||
|
|
||||||
if s.ID.IsValue() {
|
if s.ID.IsValue() {
|
||||||
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
|
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
|
return exprs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1074,6 +1097,7 @@ type fileuploadFileWhere[Q psql.Filterable] struct {
|
||||||
SizeBytes psql.WhereMod[Q, int32]
|
SizeBytes psql.WhereMod[Q, int32]
|
||||||
FileUUID psql.WhereMod[Q, uuid.UUID]
|
FileUUID psql.WhereMod[Q, uuid.UUID]
|
||||||
Committer psql.WhereNullMod[Q, int32]
|
Committer psql.WhereNullMod[Q, int32]
|
||||||
|
Error psql.WhereMod[Q, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fileuploadFileWhere[Q]) AliasedAs(alias string) fileuploadFileWhere[Q] {
|
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),
|
SizeBytes: psql.Where[Q, int32](cols.SizeBytes),
|
||||||
FileUUID: psql.Where[Q, uuid.UUID](cols.FileUUID),
|
FileUUID: psql.Where[Q, uuid.UUID](cols.FileUUID),
|
||||||
Committer: psql.WhereNull[Q, int32](cols.Committer),
|
Committer: psql.WhereNull[Q, int32](cols.Committer),
|
||||||
|
Error: psql.Where[Q, string](cols.Error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -192,9 +192,11 @@ func JobImport(ctx context.Context, txn bob.Executor, file_id int32) error {
|
||||||
err = importCSV(ctx, file_id, parseCSVFlyover, processCSVFlyover)
|
err = importCSV(ctx, file_id, parseCSVFlyover, processCSVFlyover)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debug().Err(err).Msg("failed to import CSV")
|
||||||
_, err := psql.Update(
|
_, err := psql.Update(
|
||||||
um.Table("fileupload.file"),
|
um.Table("fileupload.file"),
|
||||||
um.SetCol("status").ToArg("error"),
|
um.SetCol("status").ToArg("error"),
|
||||||
|
um.SetCol("error").ToArg(err.Error()),
|
||||||
um.Where(psql.Quote("id").EQ(psql.Arg(file_id))),
|
um.Where(psql.Quote("id").EQ(psql.Arg(file_id))),
|
||||||
).Exec(ctx, db.PGInstance.BobDB)
|
).Exec(ctx, db.PGInstance.BobDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ const (
|
||||||
|
|
||||||
type Upload struct {
|
type Upload struct {
|
||||||
Created time.Time `db:"created" json:"created"`
|
Created time.Time `db:"created" json:"created"`
|
||||||
|
Error string `db:"error" json:"error"`
|
||||||
Filename string `db:"filename" json:"filename"`
|
Filename string `db:"filename" json:"filename"`
|
||||||
ID int32 `db:"id" json:"id"`
|
ID int32 `db:"id" json:"id"`
|
||||||
RecordCount int `db:"recordcount" json:"recordcount"`
|
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()),
|
Created: omit.From(time.Now()),
|
||||||
CreatorID: omit.From(int32(u.ID)),
|
CreatorID: omit.From(int32(u.ID)),
|
||||||
Deleted: omitnull.FromPtr[time.Time](nil),
|
Deleted: omitnull.FromPtr[time.Time](nil),
|
||||||
|
Error: omit.From(""),
|
||||||
Name: omit.From(upload.Name),
|
Name: omit.From(upload.Name),
|
||||||
OrganizationID: omit.From(u.Organization.ID),
|
OrganizationID: omit.From(u.Organization.ID),
|
||||||
Status: omit.From(enums.FileuploadFilestatustypeUploaded),
|
Status: omit.From(enums.FileuploadFilestatustypeUploaded),
|
||||||
|
|
@ -167,6 +169,7 @@ func UploadList(ctx context.Context, org Organization) ([]Upload, error) {
|
||||||
"file.created AS created",
|
"file.created AS created",
|
||||||
//"file.creator_id",
|
//"file.creator_id",
|
||||||
//"file.deleted",
|
//"file.deleted",
|
||||||
|
"file.error AS error",
|
||||||
"file.id AS id",
|
"file.id AS id",
|
||||||
"file.name AS filename",
|
"file.name AS filename",
|
||||||
//"file.organization_id",
|
//"file.organization_id",
|
||||||
|
|
@ -235,9 +238,9 @@ func getUploadDetailPool(ctx context.Context, file *models.FileuploadFile) (*Upl
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
log.Debug().Str("status", file.Status.String()).Int32("id", file.ID).Msg("returning")
|
|
||||||
return &Upload{
|
return &Upload{
|
||||||
Created: file.Created,
|
Created: file.Created,
|
||||||
|
Error: file.Error,
|
||||||
Filename: file.Name,
|
Filename: file.Name,
|
||||||
ID: file.ID,
|
ID: file.ID,
|
||||||
RecordCount: len(pool_rows),
|
RecordCount: len(pool_rows),
|
||||||
|
|
|
||||||
|
|
@ -559,6 +559,17 @@ export interface ReviewTaskListResponse {
|
||||||
}
|
}
|
||||||
export interface UploadDTO {
|
export interface UploadDTO {
|
||||||
created: string;
|
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;
|
filename: string;
|
||||||
id: number;
|
id: number;
|
||||||
recordcount: number;
|
recordcount: number;
|
||||||
|
|
@ -567,25 +578,29 @@ export interface UploadDTO {
|
||||||
csv_pool?: CSVPoolDetail;
|
csv_pool?: CSVPoolDetail;
|
||||||
}
|
}
|
||||||
export class Upload {
|
export class Upload {
|
||||||
constructor(
|
created: Date;
|
||||||
public created: Date,
|
error: string;
|
||||||
public filename: string,
|
filename: string;
|
||||||
public id: number,
|
id: number;
|
||||||
public recordcount: number,
|
recordcount: number;
|
||||||
public status: string,
|
status: string;
|
||||||
public type: string,
|
type: string;
|
||||||
public csv_pool?: CSVPoolDetail,
|
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 {
|
static fromJSON(json: UploadDTO): Upload {
|
||||||
return new Upload(
|
return new Upload({
|
||||||
new Date(json.created),
|
...json,
|
||||||
json.filename,
|
created: new Date(json.created),
|
||||||
json.id,
|
});
|
||||||
json.recordcount,
|
|
||||||
json.status,
|
|
||||||
json.type,
|
|
||||||
json.csv_pool,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,12 +150,11 @@ tr.has-error {
|
||||||
<p>loading</p>
|
<p>loading</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<MapMultipoint
|
<MapLocator
|
||||||
:bounds="session.organization!.service_area"
|
|
||||||
:markers="[]"
|
:markers="[]"
|
||||||
:organizationId="session.organization!.id"
|
:organizationId="session.organization!.id"
|
||||||
:tegola="session.urls?.tegola ?? ''"
|
:tegola="session.urls?.tegola ?? ''"
|
||||||
></MapMultipoint>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -202,8 +201,13 @@ tr.has-error {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
<div v-if="upload.error" class="alert alert-error" role="alert">
|
||||||
|
<i class="bi bi-exclamation-triangle me-2"></i>
|
||||||
|
<strong>Error:</strong> Your upload failed to parse correctly. The
|
||||||
|
specific error was: '{{ upload.error }}'
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-else-if="
|
||||||
!upload.csv_pool?.pools || upload.csv_pool.pools.length === 0
|
!upload.csv_pool?.pools || upload.csv_pool.pools.length === 0
|
||||||
"
|
"
|
||||||
class="alert alert-warning"
|
class="alert alert-warning"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue