diff --git a/forms/record_upsert.go b/forms/record_upsert.go index 46b95076..ab617e63 100644 --- a/forms/record_upsert.go +++ b/forms/record_upsert.go @@ -728,6 +728,8 @@ func (form *RecordUpsert) DrySubmit(callback func(txDao *daos.Dao) error) error // You can optionally provide a list of InterceptorFunc to further // modify the form behavior before persisting it. func (form *RecordUpsert) Submit(interceptors ...InterceptorFunc[*models.Record]) error { + oldRecord := form.record.OriginalCopy() + if err := form.ValidateAndFill(); err != nil { return err } @@ -749,10 +751,23 @@ func (form *RecordUpsert) Submit(interceptors ...InterceptorFunc[*models.Record] return fmt.Errorf("failed to save the record: %w", err) } - // @todo exec before the record save (it is after because of eventual record id change)? - // // upload new files (if any) + // + // note1: executed outside of transaction to avoid keeping a lock for too long + // note2: executed after the record save to allow record id change with a before model hook if err := form.processFilesToUpload(); err != nil { + if oldRecord.IsNew() { + // delete previously inserted record + if err := form.dao.DeleteRecord(form.record); err != nil && form.app.IsDebug() { + log.Println(err) + } + } else { + // revert record changes + if err := form.dao.SaveRecord(oldRecord); err != nil && form.app.IsDebug() { + log.Println(err) + } + } + return fmt.Errorf("failed to process the uploaded files: %w", err) } diff --git a/forms/record_upsert_test.go b/forms/record_upsert_test.go index 64b7fe6e..a93cc517 100644 --- a/forms/record_upsert_test.go +++ b/forms/record_upsert_test.go @@ -1037,3 +1037,51 @@ func TestRecordUpsertAddAndRemoveFiles(t *testing.T) { t.Fatalf("Expected file_many to be 5, got %v", fileMany) } } + +func TestRecordUpsertUploadFailure(t *testing.T) { + app, _ := tests.NewTestApp() + defer app.Cleanup() + + collection, err := app.Dao().FindCollectionByNameOrId("demo3") + if err != nil { + t.Fatal(err) + } + + // create with invalid file + { + new := models.NewRecord(collection) + new.Id = "123456789012341" + + form := forms.NewRecordUpsert(app, new) + form.LoadData(map[string]any{"title": "new_test"}) + form.AddFiles("files", &filesystem.File{Reader: &filesystem.PathReader{Path: "/tmp/__missing__"}}) + + if err := form.Submit(); err == nil { + t.Fatal("Expected error, got nil") + } + + if r, err := app.Dao().FindRecordById(collection.Id, new.Id); err == nil { + t.Fatalf("Expected the inserted record to be deleted, found \n%v", r.PublicExport()) + } + } + + // update with invalid file + { + record, err := app.Dao().FindRecordById(collection.Id, "1tmknxy2868d869") + if err != nil { + t.Fatal(err) + } + + form := forms.NewRecordUpsert(app, record) + form.LoadData(map[string]any{"title": "update_test"}) + form.AddFiles("files", &filesystem.File{Reader: &filesystem.PathReader{Path: "/tmp/__missing__"}}) + + if err := form.Submit(); err == nil { + t.Fatal("Expected error, got nil") + } + + if r, _ := app.Dao().FindRecordById(collection.Id, record.Id); r == nil || r.GetString("title") == "update_test" { + t.Fatalf("Expected the record changes to be reverted, got \n%v", r.PublicExport()) + } + } +}