From ab5c6619475b92fc3a56459e217e2138cf8ae3d3 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Tue, 10 Sep 2024 20:42:04 +0000 Subject: [PATCH 1/3] 20654-new --- cmd/generate-payment-request-edi/main.go | 19 ++- migrations/app/migrations_manifest.txt | 2 + ...0204924_add_upload_type_application.up.sql | 2 + ...d_payment_request_edi_uploads_table.up.sql | 10 ++ pkg/models/payment_request_edi_upload.go | 16 +++ pkg/models/upload.go | 4 + pkg/payment_request/send_to_syncada.go | 16 +-- pkg/payment_request/send_to_syncada_test.go | 16 +-- pkg/services/invoice/store_invoice.go | 52 +++---- .../payment_request_reviewed_processor.go | 12 +- pkg/uploader/payment_request_edi_uploader.go | 131 ++++++++++++++++++ 11 files changed, 226 insertions(+), 54 deletions(-) create mode 100644 migrations/app/schema/migrations/app/schema/20240820204924_add_upload_type_application.up.sql create mode 100644 migrations/app/schema/migrations/app/schema/migrations/app/schema/20240820210949_add_payment_request_edi_uploads_table.up.sql create mode 100644 pkg/models/payment_request_edi_upload.go create mode 100644 pkg/uploader/payment_request_edi_uploader.go diff --git a/cmd/generate-payment-request-edi/main.go b/cmd/generate-payment-request-edi/main.go index b91144340b7..4f8d7022c0e 100644 --- a/cmd/generate-payment-request-edi/main.go +++ b/cmd/generate-payment-request-edi/main.go @@ -7,6 +7,7 @@ import ( "log" "os" "strings" + "time" "github.com/benbjohnson/clock" "github.com/spf13/pflag" @@ -51,6 +52,8 @@ func initFlags(flag *pflag.FlagSet) { // Logging Levels cli.InitLoggingFlags(flag) + cli.InitStorageFlags(flag) + // Don't sort flags flag.SortFlags = false } @@ -125,7 +128,10 @@ func main() { if envFlag == "production" || envFlag == "prod" || envFlag == "prd" { isProd = true } - + if err != nil { + logger.Fatal("Failed to get next ICN value", zap.Error(err)) + } + fileName := fmt.Sprintf("%s_edi858.txt", time.Now().Format("2006_01_02T15_04_05Z07_00")) edi858c, err := generator.Generate(appCtx, paymentRequest, isProd) if err != nil { logger.Fatal(err.Error()) @@ -135,6 +141,15 @@ func main() { if err != nil { logger.Fatal(err.Error()) } + logger.Info(edi858String) + storeInvoice := invoice.StoreInvoice858C{} + verrs, err := storeInvoice.Call(appCtx, edi858String, &paymentRequest, fileName) + if verrs != nil && verrs.HasAny() { + logger.Fatal(verrs.Error()) + } - fmt.Print(edi858String) + if err != nil { + logger.Fatal(err.Error()) + } + logger.Info("Successfully stored invoice") } diff --git a/migrations/app/migrations_manifest.txt b/migrations/app/migrations_manifest.txt index ab0cc2e13f5..2138864989d 100644 --- a/migrations/app/migrations_manifest.txt +++ b/migrations/app/migrations_manifest.txt @@ -993,3 +993,5 @@ 20240819164156_update_pws_violations_pt3.up.sql 20240820125856_allow_pptas_migration.up.sql 20240820151043_add_gsr_role.up.sql +20240820204924_add_upload_type_application.up.sql +20240820210949_add_payment_request_edi_uploads_table.up.sql diff --git a/migrations/app/schema/migrations/app/schema/20240820204924_add_upload_type_application.up.sql b/migrations/app/schema/migrations/app/schema/20240820204924_add_upload_type_application.up.sql new file mode 100644 index 00000000000..0a43e6e6bd2 --- /dev/null +++ b/migrations/app/schema/migrations/app/schema/20240820204924_add_upload_type_application.up.sql @@ -0,0 +1,2 @@ +ALTER TYPE upload_type ADD VALUE 'APP'; +COMMENT ON COLUMN uploads.upload_type IS 'Who created the upload: USER, PRIME, OFFICE, or APPLICATION'; \ No newline at end of file diff --git a/migrations/app/schema/migrations/app/schema/migrations/app/schema/20240820210949_add_payment_request_edi_uploads_table.up.sql b/migrations/app/schema/migrations/app/schema/migrations/app/schema/20240820210949_add_payment_request_edi_uploads_table.up.sql new file mode 100644 index 00000000000..753bc904a39 --- /dev/null +++ b/migrations/app/schema/migrations/app/schema/migrations/app/schema/20240820210949_add_payment_request_edi_uploads_table.up.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS payment_request_edi_uploads ( + id uuid primary key, + upload_id uuid not null constraint payment_request_edi_uploads_id_fkey references uploads, + created_at timestamp not null, + updated_at timestamp not null, + deleted_at timestamp with time zone +); + +COMMENT ON TABLE payment_request_edi_uploads IS 'Stores uploads from the application that are run in background from generated EDI files'; +COMMENT ON COLUMN payment_request_edi_uploads.upload_id IS 'Foreign key of the uploads table'; \ No newline at end of file diff --git a/pkg/models/payment_request_edi_upload.go b/pkg/models/payment_request_edi_upload.go new file mode 100644 index 00000000000..2dc6bb0f804 --- /dev/null +++ b/pkg/models/payment_request_edi_upload.go @@ -0,0 +1,16 @@ +package models + +import ( + "time" + + "github.com/gofrs/uuid" +) + +type PaymentRequestEdiUpload struct { + ID uuid.UUID `db:"id"` + UploadID uuid.UUID `db:"upload_id"` + Upload Upload `belongs_to:"uploads" fk_id:"upload_id"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` + DeletedAt *time.Time `db:"deleted_at"` +} diff --git a/pkg/models/upload.go b/pkg/models/upload.go index d6afc2d0d4a..5b89eb4a3ab 100644 --- a/pkg/models/upload.go +++ b/pkg/models/upload.go @@ -23,6 +23,8 @@ const ( UploadTypePRIME UploadType = "PRIME" // UploadTypePRIME string OFFICE UploadTypeOFFICE UploadType = "OFFICE" + // UploadTypeAPP string APP + UploadTypeAPP UploadType = "APP" ) // An Upload represents an uploaded file, such as an image or PDF. @@ -54,6 +56,7 @@ func (u *Upload) Validate(_ *pop.Connection) (*validate.Errors, error) { string(UploadTypeUSER), string(UploadTypePRIME), string(UploadTypeOFFICE), + string(UploadTypeAPP), }}) vs = append(vs, &validators.StringIsPresent{Field: u.Filename, Name: "Filename"}, @@ -117,6 +120,7 @@ func (ut UploadType) Valid() bool { string(UploadTypeUSER), string(UploadTypePRIME), string(UploadTypeOFFICE), + string(UploadTypeAPP), } { if string(ut) == value { return true diff --git a/pkg/payment_request/send_to_syncada.go b/pkg/payment_request/send_to_syncada.go index 0111db7ea67..c4cf295a33f 100644 --- a/pkg/payment_request/send_to_syncada.go +++ b/pkg/payment_request/send_to_syncada.go @@ -13,32 +13,32 @@ import ( ) // SendToSyncada send EDI file to Syncada for processing -func SendToSyncada(appCtx appcontext.AppContext, edi string, icn int64, gexSender services.GexSender, sftpSender services.SyncadaSFTPSender, sendEDIFile bool) error { +func SendToSyncada(appCtx appcontext.AppContext, edi string, icn int64, gexSender services.GexSender, sftpSender services.SyncadaSFTPSender, sendEDIFile bool) (string, error) { logger := appCtx.Logger() syncadaFileName := fmt.Sprintf("%s_%d_edi858.txt", time.Now().Format("2006_01_02T15_04_05Z07_00"), icn) if !sendEDIFile { logger.Info("SendToSyncada() is in do not send mode, syncadaFileName: " + syncadaFileName + "") - return nil + return "", nil } if (gexSender == nil) && (sftpSender == nil) { - return fmt.Errorf("cannot send to Syncada, SendToSyncada() senders are nil") + return "", fmt.Errorf("cannot send to Syncada, SendToSyncada() senders are nil") } if gexSender != nil { logger.Info("SendToSyncada() is in send mode using GEX, sending syncadaFileName: " + syncadaFileName) resp, err := gexSender.SendToGex(services.GEXChannelInvoice, edi, syncadaFileName) if err != nil { logger.Error("GEX Sender encountered an error", zap.Error(err)) - return fmt.Errorf("GEX sender encountered an error: %w", err) + return "", fmt.Errorf("GEX sender encountered an error: %w", err) } if resp == nil { logger.Error("GEX Sender receieved no response from GEX") - return fmt.Errorf("no response when sending EDI to GEX") + return "", fmt.Errorf("no response when sending EDI to GEX") } if resp.StatusCode != http.StatusOK { logger.Error("func SendToSyncada() failed send to GEX with", zap.Int("StatusCode", resp.StatusCode), zap.String("Status", resp.Status)) - return fmt.Errorf("received error response when sending EDI to GEX %v", resp) + return "", fmt.Errorf("received error response when sending EDI to GEX %v", resp) } logger.Info( "SUCCESS: EDI858 Processor sent a new file to syncada for Payment Request, using GEX", @@ -50,9 +50,9 @@ func SendToSyncada(appCtx appcontext.AppContext, edi string, icn int64, gexSende logger.Info("SendToSyncada() is in send mode, sending syncadaFileName: " + syncadaFileName + "") _, err := sftpSender.SendToSyncadaViaSFTP(appCtx, edi858String, syncadaFileName) if err != nil { - return err + return "", err } logger.Info("SUCCESS: EDI858 Processor sent new file to syncada for Payment Request", zap.String("syncadaFileName", syncadaFileName)) } - return nil + return syncadaFileName, nil } diff --git a/pkg/payment_request/send_to_syncada_test.go b/pkg/payment_request/send_to_syncada_test.go index 7d94dbb6e33..7c65c1d187a 100644 --- a/pkg/payment_request/send_to_syncada_test.go +++ b/pkg/payment_request/send_to_syncada_test.go @@ -21,14 +21,14 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() { suite.Run("returns no error if send is false", func() { sftpSender := services.SyncadaSFTPSender(nil) gexSender := services.GexSender(nil) - err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, false) + _, err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, false) suite.NoError(err) }) suite.Run("returns error if no sender", func() { sftpSender := services.SyncadaSFTPSender(nil) gexSender := services.GexSender(nil) - err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, true) + _, err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, true) suite.Error(err) suite.Equal("cannot send to Syncada, SendToSyncada() senders are nil", err.Error()) }) @@ -40,7 +40,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() { gexSender := &mocks.GexSender{} gexSender. On("SendToGex", services.GEXChannelInvoice, fakeEdi, filenameMatcher).Return(response, nil) - err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) + _, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) suite.NoError(err) }) @@ -51,7 +51,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() { gexSender := &mocks.GexSender{} gexSender. On("SendToGex", services.GEXChannelInvoice, fakeEdi, filenameMatcher).Return(response, nil) - err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) + _, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) suite.Error(err) suite.Contains("received error response when sending EDI to GEX &{ 403 0 0 map[] 0 [] false false map[] }", err.Error()) }) @@ -62,7 +62,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() { gexSender := &mocks.GexSender{} gexSender. On("SendToGex", services.GEXChannelInvoice, fakeEdi, filenameMatcher).Return(nil, nil) - err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) + _, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) suite.Error(err) suite.Contains("no response when sending EDI to GEX", err.Error()) }) @@ -73,7 +73,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() { gexSender := &mocks.GexSender{} gexSender. On("SendToGex", services.GEXChannelInvoice, fakeEdi, filenameMatcher).Return(nil, fmt.Errorf("gex send threw error")) - err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) + _, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) suite.Error(err) suite.Contains("GEX sender encountered an error: gex send threw error", err.Error()) }) @@ -85,7 +85,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() { sftpSender. On("SendToSyncadaViaSFTP", mock.AnythingOfType("*appcontext.appContext"), mock.Anything, filenameMatcher).Return(bytesSent, fmt.Errorf("test error")) gexSender := services.GexSender(nil) - err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, true) + _, err := SendToSyncada(suite.AppContextForTest(), "edi string", 12345, gexSender, sftpSender, true) suite.Error(err) suite.Equal("test error", err.Error()) }) @@ -98,7 +98,7 @@ func (suite *PaymentRequestHelperSuite) TestSendToSyncada() { sftpSender. On("SendToSyncadaViaSFTP", mock.AnythingOfType("*appcontext.appContext"), strings.NewReader(fakeEdi), filenameMatcher).Return(bytesSent, nil) gexSender := services.GexSender(nil) - err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) + _, err := SendToSyncada(suite.AppContextForTest(), fakeEdi, 12345, gexSender, sftpSender, true) suite.NoError(err) }) } diff --git a/pkg/services/invoice/store_invoice.go b/pkg/services/invoice/store_invoice.go index 7f2831bbc05..9c109f5c8c1 100644 --- a/pkg/services/invoice/store_invoice.go +++ b/pkg/services/invoice/store_invoice.go @@ -6,7 +6,6 @@ import ( "github.com/gobuffalo/validate/v3" "github.com/gobuffalo/validate/v3/validators" - "github.com/gofrs/uuid" "github.com/pkg/errors" "github.com/spf13/afero" "go.uber.org/zap" @@ -19,25 +18,23 @@ import ( // StoreInvoice858C is a service object to store an invoice's EDI in S3. type StoreInvoice858C struct { - Storer *storage.FileStorer } // Call stores the EDI/Invoice to S3. -func (s StoreInvoice858C) Call(appCtx appcontext.AppContext, edi string, invoice *models.Invoice, userID uuid.UUID) (*validate.Errors, error) { +func (s StoreInvoice858C) Call(appCtx appcontext.AppContext, edi string, paymentRequest *models.PaymentRequest, fileName string) (*validate.Errors, error) { verrs := validate.NewErrors() // Create path for EDI file - // {application-bucket}/app/invoice/{invoice_id}.edi - invoiceID := invoice.ID.String() - ediFilename := invoiceID + ".edi" - ediFilePath := "/app/invoice/" - ediTmpFile := path.Join(ediFilePath, ediFilename) - + // {application-bucket}/app/payment-request-{payment_request_id}/mto-{move_id}/{payment_request_id}.txt + paymentRequestId := paymentRequest.ID.String() + ediFilePath := "app/payment-request-uploads/" + "payment-request-" + paymentRequestId + "/mto-" + paymentRequest.MoveTaskOrderID.String() + "/edi" + ediTmpFile := path.Join(ediFilePath, fileName) + appCtx.Logger().Warn("attemping to store it to:" + ediFilePath) fs := afero.NewMemMapFs() f, err := fs.Create(ediTmpFile) if err != nil { - return verrs, errors.Wrapf(err, "afero.Create Failed in StoreInvoice858C() invoice ID: %s", invoiceID) + return verrs, errors.Wrapf(err, "afero.Create Failed in StoreInvoice858C() payment request ID: %s", paymentRequestId) } defer func() { @@ -48,7 +45,7 @@ func (s StoreInvoice858C) Call(appCtx appcontext.AppContext, edi string, invoice _, err = io.WriteString(f, edi) if err != nil { - return verrs, errors.Wrapf(err, "io.WriteString(edi) Failed in StoreInvoice858C() invoice ID: %s", invoiceID) + return verrs, errors.Wrapf(err, "io.WriteString(edi) Failed in StoreInvoice858C() invoice ID: %s", paymentRequestId) } err = f.Sync() @@ -57,42 +54,27 @@ func (s StoreInvoice858C) Call(appCtx appcontext.AppContext, edi string, invoice } // Create UserUpload - loader, err := uploader.NewUserUploader(*s.Storer, uploader.MaxCustomerUserUploadFileSizeLimit) + loader, err := uploader.NewPaymentRequestEDIUploader(storage.NewFilesystem(storage.FilesystemParams{}), uploader.MaxCustomerUserUploadFileSizeLimit) if err != nil { appCtx.Logger().Fatal("could not instantiate uploader", zap.Error(err)) } // Set Storagekey path for S3 loader.SetUploadStorageKey(ediTmpFile) - // Delete of previous upload, if it exist - // If Delete of UserUpload fails, ignoring this error because we still have a new UserUpload that needs to be saved - // to the Invoice - err = UploadUpdater{UserUploader: loader}.DeleteUpload(appCtx, invoice) - if err != nil { - logStr := "" - if invoice != nil && invoice.UserUploadID != nil { - logStr = invoice.UserUploadID.String() - } - appCtx.Logger().Info("Errors encountered for while deleting previous UserUpload:"+logStr, - zap.Any("verrors", verrs.Error())) + // Create and save UserUpload to s3 + filePointer := &uploader.File{ + File: f, } - // Create and save UserUpload to s3 - userUpload, verrs2, err := loader.CreateUserUpload(appCtx, userID, uploader.File{File: f}, uploader.AllowedTypesText) + // Then pass the pointer to the function + appUpload, verrs2, err := loader.CreatePaymentRequestEDIUploadForDocument(appCtx, filePointer, uploader.AllowedTypesText) verrs.Append(verrs2) if err != nil { - return verrs, errors.Wrapf(err, "Failed to Create UserUpload for StoreInvoice858C(), invoice ID: %s", invoiceID) - } - - if userUpload == nil { - return verrs, errors.New("Failed to Create and Save new UserUpload object in database, invoice ID: " + invoiceID) + return verrs, errors.Wrapf(err, "Failed to Create AppUpload for StoreInvoice858C(), invoice ID: %s", paymentRequestId) } - // Save UserUpload to Invoice - verrs2, err = UploadUpdater{UserUploader: loader}.Call(appCtx, invoice, userUpload) - verrs.Append(verrs2) - if err != nil { - return verrs, errors.New("Failed to save UserUpload to Invoice: " + invoiceID) + if appUpload == nil { + return verrs, errors.New("Failed to Create and Save new ApUpload object in database, invoice ID: " + paymentRequestId) } if verrs.HasAny() { diff --git a/pkg/services/payment_request/payment_request_reviewed_processor.go b/pkg/services/payment_request/payment_request_reviewed_processor.go index 882f7097ec8..e030ee12f52 100644 --- a/pkg/services/payment_request/payment_request_reviewed_processor.go +++ b/pkg/services/payment_request/payment_request_reviewed_processor.go @@ -133,7 +133,8 @@ func (p *paymentRequestReviewedProcessor) ProcessAndLockReviewedPR(appCtx appcon ) // Send EDI string to Syncada // If sent successfully to GEX, update payment request status to SENT_TO_GEX. - err = paymentrequesthelper.SendToSyncada(txnAppCtx, edi858cString, icn, p.gexSender, p.sftpSender, p.runSendToSyncada) + var fileName string + fileName, err = paymentrequesthelper.SendToSyncada(txnAppCtx, edi858cString, icn, p.gexSender, p.sftpSender, p.runSendToSyncada) if err != nil { return GexSendError{paymentRequestID: lockedPR.ID, err: err} } @@ -145,6 +146,15 @@ func (p *paymentRequestReviewedProcessor) ProcessAndLockReviewedPR(appCtx appcon if err != nil { return fmt.Errorf("failure updating payment request status: %w", err) } + storeInvoice := invoice.StoreInvoice858C{} + verrs, err := storeInvoice.Call(appCtx, edi858cString, &lockedPR, fileName) + + if err != nil { + return fmt.Errorf("failure storing invoice: %w", err) + } + if verrs != nil && verrs.HasAny() { + return fmt.Errorf("validation errors while storing invoice: %w", verrs) + } return nil }) diff --git a/pkg/uploader/payment_request_edi_uploader.go b/pkg/uploader/payment_request_edi_uploader.go new file mode 100644 index 00000000000..1b1082b0796 --- /dev/null +++ b/pkg/uploader/payment_request_edi_uploader.go @@ -0,0 +1,131 @@ +package uploader + +import ( + "fmt" + "io" + "path" + + "github.com/gobuffalo/validate/v3" + "github.com/gofrs/uuid" + "github.com/pkg/errors" + "github.com/spf13/afero" + "go.uber.org/zap" + + "github.com/transcom/mymove/pkg/appcontext" + "github.com/transcom/mymove/pkg/models" + "github.com/transcom/mymove/pkg/storage" +) + +type PaymentRequestEDIUploader struct { + uploader *Uploader +} + +// NewPaymentRequestEdiUploader creates and returns a new uploader +func NewPaymentRequestEDIUploader(storer storage.FileStorer, fileSizeLimit ByteSize) (*PaymentRequestEDIUploader, error) { + uploader, err := NewUploader(storer, fileSizeLimit, models.UploadTypeAPP) + if err != nil { + if err == ErrFileSizeLimitExceedsMax { + return nil, err + } + return nil, fmt.Errorf("could not create uploader.PaymentRequestEdiUploader for PaymentRequestEdiUpload: %w", err) + } + return &PaymentRequestEDIUploader{ + uploader: uploader, + }, nil +} + +// PrepareFileForUpload called Uploader.PrepareFileForUpload +func (u *PaymentRequestEDIUploader) PrepareFileForUpload(appCtx appcontext.AppContext, file io.ReadCloser, filename string) (afero.File, error) { + // Read the incoming data into a temporary afero.File for consumption + return u.uploader.PrepareFileForUpload(appCtx, file, filename) +} + +func (u *PaymentRequestEDIUploader) createAndStore(appCtx appcontext.AppContext, file *File, allowedTypes AllowedFileTypes) (*models.PaymentRequestEdiUpload, *validate.Errors, error) { + // If storage key is not set assign a default + id := uuid.Must(uuid.NewV4()) + if u.GetUploadStorageKey() == "" { + u.uploader.DefaultStorageKey = path.Join("app", id.String()) + } + //aFile, err := u.PrepareFileForUpload(appCtx, file.File, file.File.Name()) + + newUpload, verrs, err := u.uploader.CreateUpload(appCtx, *file, allowedTypes) + if verrs.HasAny() || err != nil { + appCtx.Logger().Error("error creating and storing new upload", zap.Error(err)) + return nil, verrs, err + } + + newUploadForApp := &models.PaymentRequestEdiUpload{ + ID: id, + UploadID: newUpload.ID, + Upload: *newUpload, + } + + verrs, err = appCtx.DB().ValidateAndCreate(newUploadForApp) + if err != nil || verrs.HasAny() { + appCtx.Logger().Error("error creating new app upload", zap.Error(err)) + return nil, verrs, err + } + + return newUploadForApp, &validate.Errors{}, nil +} +func (u *PaymentRequestEDIUploader) CreatePaymentRequestEDIUploadForDocument(appCtx appcontext.AppContext, file *File, allowedTypes AllowedFileTypes) (*models.PaymentRequestEdiUpload, *validate.Errors, error) { + + if u.uploader == nil { + return nil, &validate.Errors{}, errors.New("Did not call NewPaymentRequestEDIUploader before calling this function") + } + + if file != nil && file.File != nil { + tags := "858c" + file.Tags = &tags + } + + paymentRequestEDIUpload, verrs, err := u.createAndStore(appCtx, file, allowedTypes) + return paymentRequestEDIUpload, verrs, err + +} + +func (u *PaymentRequestEDIUploader) PresignedURL(appCtx appcontext.AppContext, paymentRequestEDIUpload *models.PaymentRequestEdiUpload) (string, error) { + if paymentRequestEDIUpload == nil { + appCtx.Logger().Error("failed to get PaymentRequestEDIUploader presigned url") + return "", errors.New("failed to get PaymentRequestEDIUploader presigned url") + } + url, err := u.uploader.PresignedURL(appCtx, &paymentRequestEDIUpload.Upload) + if err != nil { + appCtx.Logger().Error("failed to get PaymentRequestEDIUploader presigned url", zap.Error(err)) + return "", err + } + return url, nil +} + +// FileSystem return Uploader file system +func (u *PaymentRequestEDIUploader) FileSystem() *afero.Afero { + return u.uploader.Storer.FileSystem() +} + +// Uploader return Uploader +func (u *PaymentRequestEDIUploader) Uploader() *Uploader { + return u.uploader +} + +// SetUploadStorageKey set the PaymentRequestEDIUpload.Upload.StorageKey member +func (u *PaymentRequestEDIUploader) SetUploadStorageKey(key string) { + if u.uploader != nil { + u.uploader.SetUploadStorageKey(key) + } +} + +// GetUploadStorageKey returns the PaymentRequestEDIUpload.Upload.StorageKey member +func (u *PaymentRequestEDIUploader) GetUploadStorageKey() string { + if u.uploader == nil { + return "" + } + return u.uploader.UploadStorageKey +} + +// Download fetches an Upload's file and stores it in a tempfile. The path to this +// file is returned. +// +// It is the caller's responsibility to delete the tempfile. +func (u *PaymentRequestEDIUploader) Download(appCtx appcontext.AppContext, paymentRequestEDIUpload *models.PaymentRequestEdiUpload) (io.ReadCloser, error) { + return u.uploader.Download(appCtx, &paymentRequestEDIUpload.Upload) +} From c1d9a78f046a2104ae777946f38ca1fb98ba0062 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Tue, 10 Sep 2024 23:49:14 +0000 Subject: [PATCH 2/3] b-20654 --- ...0204924_add_upload_type_application.up.sql | 0 ...d_payment_request_edi_uploads_table.up.sql | 0 pkg/uploader/payment_request_edi_uploader.go | 33 +++++++++++++++++-- 3 files changed, 30 insertions(+), 3 deletions(-) rename migrations/app/schema/{migrations/app/schema => }/20240820204924_add_upload_type_application.up.sql (100%) rename migrations/app/schema/{migrations/app/schema/migrations/app/schema => }/20240820210949_add_payment_request_edi_uploads_table.up.sql (100%) diff --git a/migrations/app/schema/migrations/app/schema/20240820204924_add_upload_type_application.up.sql b/migrations/app/schema/20240820204924_add_upload_type_application.up.sql similarity index 100% rename from migrations/app/schema/migrations/app/schema/20240820204924_add_upload_type_application.up.sql rename to migrations/app/schema/20240820204924_add_upload_type_application.up.sql diff --git a/migrations/app/schema/migrations/app/schema/migrations/app/schema/20240820210949_add_payment_request_edi_uploads_table.up.sql b/migrations/app/schema/20240820210949_add_payment_request_edi_uploads_table.up.sql similarity index 100% rename from migrations/app/schema/migrations/app/schema/migrations/app/schema/20240820210949_add_payment_request_edi_uploads_table.up.sql rename to migrations/app/schema/20240820210949_add_payment_request_edi_uploads_table.up.sql diff --git a/pkg/uploader/payment_request_edi_uploader.go b/pkg/uploader/payment_request_edi_uploader.go index 1b1082b0796..bcfe14a45f7 100644 --- a/pkg/uploader/payment_request_edi_uploader.go +++ b/pkg/uploader/payment_request_edi_uploader.go @@ -37,18 +37,45 @@ func NewPaymentRequestEDIUploader(storer storage.FileStorer, fileSizeLimit ByteS // PrepareFileForUpload called Uploader.PrepareFileForUpload func (u *PaymentRequestEDIUploader) PrepareFileForUpload(appCtx appcontext.AppContext, file io.ReadCloser, filename string) (afero.File, error) { // Read the incoming data into a temporary afero.File for consumption + + // Convert io.ReadCloser to io.ReadSeeker + seeker, ok := file.(io.ReadSeeker) + if !ok { + appCtx.Logger().Error("file is not seekable") + return nil, errors.New("file is not seekable") + } + + fileSize, err := seeker.Seek(0, io.SeekEnd) + if err != nil { + appCtx.Logger().Error("error getting file size", zap.Error(err)) + return nil, err + } + _, err = seeker.Seek(0, io.SeekStart) + if err != nil { + appCtx.Logger().Error("error resetting file position", zap.Error(err)) + return nil, err + } + + appCtx.Logger().Info("File size", zap.Int64("size", fileSize)) + return u.uploader.PrepareFileForUpload(appCtx, file, filename) } - func (u *PaymentRequestEDIUploader) createAndStore(appCtx appcontext.AppContext, file *File, allowedTypes AllowedFileTypes) (*models.PaymentRequestEdiUpload, *validate.Errors, error) { // If storage key is not set assign a default id := uuid.Must(uuid.NewV4()) if u.GetUploadStorageKey() == "" { u.uploader.DefaultStorageKey = path.Join("app", id.String()) } - //aFile, err := u.PrepareFileForUpload(appCtx, file.File, file.File.Name()) + aFile, err := u.PrepareFileForUpload(appCtx, file.File, file.File.Name()) + if err != nil { + appCtx.Logger().Error("error preparing file for upload", zap.Error(err)) + return nil, nil, err + } + preppedFile := &File{ + File: aFile, + } - newUpload, verrs, err := u.uploader.CreateUpload(appCtx, *file, allowedTypes) + newUpload, verrs, err := u.uploader.CreateUpload(appCtx, *preppedFile, allowedTypes) if verrs.HasAny() || err != nil { appCtx.Logger().Error("error creating and storing new upload", zap.Error(err)) return nil, verrs, err From 3ae80e758ce5fbefb33eedecd48d051b7e7a8730 Mon Sep 17 00:00:00 2001 From: deandreJones Date: Wed, 11 Sep 2024 01:50:27 +0000 Subject: [PATCH 3/3] fix upload_test --- pkg/models/upload_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/models/upload_test.go b/pkg/models/upload_test.go index 93bb7906009..1cb9e64024c 100644 --- a/pkg/models/upload_test.go +++ b/pkg/models/upload_test.go @@ -29,7 +29,7 @@ func (suite *ModelSuite) Test_UploadValidationErrors() { "bytes": {"Bytes can not be blank."}, "filename": {"Filename can not be blank."}, "content_type": {"ContentType can not be blank."}, - "upload_type": {"UploadType is not in the list [USER, PRIME, OFFICE]."}, + "upload_type": {"UploadType is not in the list [USER, PRIME, OFFICE, APP]."}, } suite.verifyValidationErrors(upload, expErrors)