From 0933546f88540a83c2005e5f763af9eaf560ab04 Mon Sep 17 00:00:00 2001 From: Brooke E Kline Jr Date: Tue, 16 Apr 2019 13:56:26 -0400 Subject: [PATCH] mandatory fedWireMessage fields mandatory fedWireMessage fields --- beneficiaryIntermediaryFI_test.go | 4 +- fedWireMessage.go | 36 +++-- file.go | 3 + go.mod | 1 + go.sum | 1 + reader.go | 14 +- reader_test.go | 8 +- writer.go | 69 ++++---- writer_test.go | 254 ++++++++++++++++++++++++++---- 9 files changed, 302 insertions(+), 88 deletions(-) diff --git a/beneficiaryIntermediaryFI_test.go b/beneficiaryIntermediaryFI_test.go index 5d0f0b06..2bc5d25a 100644 --- a/beneficiaryIntermediaryFI_test.go +++ b/beneficiaryIntermediaryFI_test.go @@ -1,6 +1,6 @@ package wire -// mockBeneficiaryIntermediaryFI creates a BeneficiaryIntermediaryFI +// mockBeneficiaryIntermediaryFI creates a BeneficiaryIntermediaryFI func mockBeneficiaryIntermediaryFI() *BeneficiaryIntermediaryFI { bifi := NewBeneficiaryIntermediaryFI() bifi.FinancialInstitution.IdentificationCode = DemandDepositAccountNumber @@ -10,4 +10,4 @@ func mockBeneficiaryIntermediaryFI() *BeneficiaryIntermediaryFI { bifi.FinancialInstitution.Address.AddressLineTwo = "Address Two" bifi.FinancialInstitution.Address.AddressLineThree = "Address Three" return bifi -} \ No newline at end of file +} diff --git a/fedWireMessage.go b/fedWireMessage.go index d21fa5aa..4e88b254 100644 --- a/fedWireMessage.go +++ b/fedWireMessage.go @@ -6,8 +6,6 @@ package wire -//ToDo: omitEmpty - // FedWireMessage is a FedWire Message type FedWireMessage struct { // ID @@ -142,34 +140,40 @@ func NewFedWireMessage() FedWireMessage { // verify checks basic valid NACHA batch rules. Assumes properly parsed records. This does not mean it is a valid batch as validity is tied to each batch type func (fwm *FedWireMessage) verify() error { - // ToDo: Add errors - if fwm.SenderSupplied == nil { - return nil + if err := fwm.isMandatory(); err != nil { + return err } + /* if err := batch.isBatchAmount(); err != nil { + return err + }*/ + + return nil +} + +func (fwm *FedWireMessage) isMandatory() error { + + if fwm.SenderSupplied == nil { + return fieldError("SenderSupplied", ErrFieldRequired) + } if fwm.TypeSubType == nil { - return nil + return fieldError("TypeSubType", ErrFieldRequired) } - if fwm.InputMessageAccountabilityData == nil { - return nil + return fieldError("InputMessageAccountabilityData", ErrFieldRequired) } - if fwm.Amount == nil { - return nil + return fieldError("Amount", ErrFieldRequired) } - if fwm.SenderDepositoryInstitution == nil { - return nil + return fieldError("SenderDepositoryInstitution", ErrFieldRequired) } - if fwm.ReceiverDepositoryInstitution == nil { - return nil + return fieldError("ReceiverDepositoryInstitution", ErrFieldRequired) } - if fwm.BusinessFunctionCode == nil { - return nil + return fieldError("BusinessFunctionCode", ErrFieldRequired) } return nil diff --git a/file.go b/file.go index 81ff5022..148a517c 100644 --- a/file.go +++ b/file.go @@ -34,5 +34,8 @@ func (f *File) Create() error { // Validate will never modify the file. func (f *File) Validate() error { + if err := f.FedWireMessage.verify(); err != nil { + return err + } return nil } diff --git a/go.mod b/go.mod index 24b03db2..5851f49a 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6 github.com/moov-io/ach v1.0.0 github.com/moov-io/base v0.9.0 + github.com/pkg/errors v0.8.0 github.com/rickar/cal v1.0.1 // indirect golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 golang.org/x/text v0.3.0 diff --git a/go.sum b/go.sum index 0833f719..42e54e5c 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,7 @@ github.com/moov-io/base v0.8.0/go.mod h1:pPu/TAc9PkaaegbREVEeDHsGqyAlvji9vqTuARu github.com/moov-io/base v0.9.0 h1:7bp5Jpola4EunGmOVVaw3WkWOqcSnyVZJEoOmYLKdhw= github.com/moov-io/base v0.9.0/go.mod h1:pPu/TAc9PkaaegbREVEeDHsGqyAlvji9vqTuARuAnd0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= diff --git a/reader.go b/reader.go index 84e1be04..d2069358 100644 --- a/reader.go +++ b/reader.go @@ -21,6 +21,7 @@ type Reader struct { File File // line is the current line being parsed from the input r line string + // ToDo: Do we need a current FEDWireMessage, just use FedWireMessage // currentFedWireMessage is the current FedWireMessage being parsed currentFedWireMessage FedWireMessage // lineNum is the line number of the file being parsed @@ -74,6 +75,8 @@ func (r *Reader) Read() (File, error) { return r.File, err } } + r.File.AddFedWireMessage(r.currentFedWireMessage) + r.currentFedWireMessage = NewFedWireMessage() return r.File, nil } @@ -321,6 +324,7 @@ func (r *Reader) parseSenderSupplied() error { if err := ss.Validate(); err != nil { return r.parseError(err) } + r.currentFedWireMessage.SetSenderSupplied(ss) return nil } @@ -335,6 +339,7 @@ func (r *Reader) parseTypeSubType() error { if err := tst.Validate(); err != nil { return r.parseError(err) } + r.currentFedWireMessage.SetTypeSubType(tst) return nil } @@ -349,6 +354,7 @@ func (r *Reader) parseInputMessageAccountabilityData() error { if err := imad.Validate(); err != nil { return r.parseError(err) } + r.currentFedWireMessage.SetInputMessageAccountabilityData(imad) return nil } @@ -363,12 +369,13 @@ func (r *Reader) parseAmount() error { if err := amt.Validate(); err != nil { return r.parseError(err) } + r.currentFedWireMessage.SetAmount(amt) return nil } func (r *Reader) parseSenderDepositoryInstitution() error { r.tagName = "SenderDepositoryInstitution" -/* if len(r.line) < 15 { + /* if len(r.line) < 15 { r.errors.Add(r.parseError(NewTagWrongLengthErr(15, len(r.line)))) return r.errors }*/ @@ -377,12 +384,13 @@ func (r *Reader) parseSenderDepositoryInstitution() error { if err := sdi.Validate(); err != nil { return r.parseError(err) } + r.currentFedWireMessage.SetSenderDepositoryInstitution(sdi) return nil } func (r *Reader) parseReceiverDepositoryInstitution() error { r.tagName = "ReceiverDepositoryInstitution" -/* if len(r.line) < 15 { + /* if len(r.line) < 15 { r.errors.Add(r.parseError(NewTagWrongLengthErr(15, len(r.line)))) return r.errors }*/ @@ -391,6 +399,7 @@ func (r *Reader) parseReceiverDepositoryInstitution() error { if err := rdi.Validate(); err != nil { return r.parseError(err) } + r.currentFedWireMessage.SetReceiverDepositoryInstitution(rdi) return nil } @@ -405,6 +414,7 @@ func (r *Reader) parseBusinessFunctionCode() error { if err := bfc.Validate(); err != nil { return r.parseError(err) } + r.currentFedWireMessage.SetBusinessFunctionCode(bfc) return nil } diff --git a/reader_test.go b/reader_test.go index c5adaa14..299e8908 100644 --- a/reader_test.go +++ b/reader_test.go @@ -13,12 +13,14 @@ func TestFedWireMessageFileRead(t *testing.T) { } defer f.Close() r := NewReader(f) - _, err = r.Read() - if _, err := r.Read(); err != nil { + fwmFile, err := r.Read() + if err != nil { t.Errorf("%T: %s", err, err) } - if err = r.File.Validate(); err != nil { + // ensure we have a validated file structure + if err = fwmFile.Validate(); err != nil { t.Errorf("%T: %s", err, err) } + } diff --git a/writer.go b/writer.go index b297e2cd..2f14ba7d 100644 --- a/writer.go +++ b/writer.go @@ -9,10 +9,10 @@ import ( "io" ) -// A Writer writes an x9.file to an encoded file. +// A Writer writes an fedWireMessage to an encoded file. // -// As returned by NewWriter, a Writer writes x9file structs into -// x9 formatted files. +// As returned by NewWriter, a Writer writes FedWireMessage file structs into +// FedWireMessage formatted files. // Writer struct type Writer struct { @@ -27,7 +27,7 @@ func NewWriter(w io.Writer) *Writer { } } -// Writer writes a single x9.file record to w +// Writer writes a single FedWireMessage record to w func (w *Writer) Write(file *File) error { if err := file.Validate(); err != nil { return err @@ -49,44 +49,43 @@ func (w *Writer) Flush() { } func (w *Writer) writeFedWireMessage(file *File) error { - fwm := file.FedWireMessage - if err := w.writeMandatory(fwm); err != nil { - return err - } - if err := w.writeOtherTransferInfo(fwm); err != nil { - return err - } - if err := w.writeBeneficiary(fwm); err != nil { - return err - } - if err := w.writeOriginator(fwm); err != nil { - return err - } - if err := w.writeFinancialInstitution(fwm); err != nil { - return err - } + fwm := file.FedWireMessage + if err := w.writeMandatory(fwm); err != nil { + return err + } + if err := w.writeOtherTransferInfo(fwm); err != nil { + return err + } + if err := w.writeBeneficiary(fwm); err != nil { + return err + } + if err := w.writeOriginator(fwm); err != nil { + return err + } + if err := w.writeFinancialInstitution(fwm); err != nil { + return err + } - if err := w.writeCoverPayment(fwm); err != nil { - return err - } + if err := w.writeCoverPayment(fwm); err != nil { + return err + } - if fwm.UnstructuredAddenda != nil { - if _, err := w.w.WriteString(fwm.GetUnstructuredAddenda().String() + "\n"); err != nil { - return err - } + if fwm.UnstructuredAddenda != nil { + if _, err := w.w.WriteString(fwm.GetUnstructuredAddenda().String() + "\n"); err != nil { + return err } - if err := w.writeRemittance(fwm); err != nil { - return err - } - if fwm.ServiceMessage != nil { - if _, err := w.w.WriteString(fwm.GetServiceMessage().String() + "\n"); err != nil { - return err - } + } + if err := w.writeRemittance(fwm); err != nil { + return err + } + if fwm.ServiceMessage != nil { + if _, err := w.w.WriteString(fwm.GetServiceMessage().String() + "\n"); err != nil { + return err } + } return nil } - func (w *Writer) writeMandatory(fwm FedWireMessage) error { if fwm.SenderSupplied != nil { if _, err := w.w.WriteString(fwm.GetSenderSupplied().String() + "\n"); err != nil { diff --git a/writer_test.go b/writer_test.go index fd84ed06..844f86b2 100644 --- a/writer_test.go +++ b/writer_test.go @@ -2,14 +2,14 @@ package wire import ( "bytes" + "github.com/moov-io/base" "log" "os" - "path/filepath" "strings" "testing" ) -// TestFedWireMessage writes an FedWireMessage toa file +// TestFedWireMessage writes an FedWireMessage to a file func TestFedWireMessageWrite(t *testing.T) { file := NewFile() fwm := NewFedWireMessage() @@ -163,47 +163,241 @@ func TestFedWireMessageWrite(t *testing.T) { t.Errorf("%T: %s", err, err) } - fd, err := os.Create(filepath.Join("", "test/testdata", "fedWireMessage.txt")) - if err != nil { - log.Fatalf("Unexpected error creating output file: %s\n", err) - } - defer func() { - fd.Sync() - fd.Close() - }() - w := NewWriter(fd) - if err := w.Write(file); err != nil { - log.Fatalf("Unexpected error: %s\n", err) - } - // We want to write the file to an io.Writer - w = NewWriter(os.Stdout) + w := NewWriter(os.Stdout) if err := w.Write(file); err != nil { log.Fatalf("Unexpected error: %s\n", err) } w.Flush() r := NewReader(strings.NewReader(b.String())) - _, err = r.Read() + + fwmFile, err := r.Read() if err != nil { t.Errorf("%T: %s", err, err) } - if err = r.File.Validate(); err != nil { + // ensure we have a validated file structure + if err = fwmFile.Validate(); err != nil { + t.Errorf("%T: %s", err, err) + } + +} + +func TestSenderSupplied_Mandatory(t *testing.T) { + file := NewFile() + fwm := NewFedWireMessage() + + // Mandatory Fields + tst := mockTypeSubType() + fwm.SetTypeSubType(tst) + imad := mockInputMessageAccountabilityData() + fwm.SetInputMessageAccountabilityData(imad) + amt := mockAmount() + fwm.SetAmount(amt) + rdi := mockReceiverDepositoryInstitution() + fwm.SetReceiverDepositoryInstitution(rdi) + sdi := mockSenderDepositoryInstitution() + fwm.SetSenderDepositoryInstitution(sdi) + bfc := mockBusinessFunctionCode() + fwm.SetBusinessFunctionCode(bfc) + + file.AddFedWireMessage(fwm) + + // Create file + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + if !base.Match(err, ErrFieldRequired) { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestTypeSubType_Mandatory(t *testing.T) { + file := NewFile() + fwm := NewFedWireMessage() + + // Mandatory Fields + ss := mockSenderSupplied() + fwm.SetSenderSupplied(ss) + imad := mockInputMessageAccountabilityData() + fwm.SetInputMessageAccountabilityData(imad) + amt := mockAmount() + fwm.SetAmount(amt) + rdi := mockReceiverDepositoryInstitution() + fwm.SetReceiverDepositoryInstitution(rdi) + sdi := mockSenderDepositoryInstitution() + fwm.SetSenderDepositoryInstitution(sdi) + bfc := mockBusinessFunctionCode() + fwm.SetBusinessFunctionCode(bfc) + + file.AddFedWireMessage(fwm) + + // Create file + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + if !base.Match(err, ErrFieldRequired) { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestInputMessageAccountabilityData_Mandatory(t *testing.T) { + file := NewFile() + fwm := NewFedWireMessage() + + // Mandatory Fields + ss := mockSenderSupplied() + fwm.SetSenderSupplied(ss) + tst := mockTypeSubType() + fwm.SetTypeSubType(tst) + amt := mockAmount() + fwm.SetAmount(amt) + rdi := mockReceiverDepositoryInstitution() + fwm.SetReceiverDepositoryInstitution(rdi) + sdi := mockSenderDepositoryInstitution() + fwm.SetSenderDepositoryInstitution(sdi) + bfc := mockBusinessFunctionCode() + fwm.SetBusinessFunctionCode(bfc) + + file.AddFedWireMessage(fwm) + + // Create file + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + if !base.Match(err, ErrFieldRequired) { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestAmount_Mandatory(t *testing.T) { + file := NewFile() + fwm := NewFedWireMessage() + + // Mandatory Fields + ss := mockSenderSupplied() + fwm.SetSenderSupplied(ss) + tst := mockTypeSubType() + fwm.SetTypeSubType(tst) + imad := mockInputMessageAccountabilityData() + fwm.SetInputMessageAccountabilityData(imad) + amt := mockAmount() + fwm.SetAmount(amt) + rdi := mockReceiverDepositoryInstitution() + fwm.SetReceiverDepositoryInstitution(rdi) + sdi := mockSenderDepositoryInstitution() + fwm.SetSenderDepositoryInstitution(sdi) + bfc := mockBusinessFunctionCode() + fwm.SetBusinessFunctionCode(bfc) + + file.AddFedWireMessage(fwm) + + // Create file + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + if !base.Match(err, ErrFieldRequired) { + t.Errorf("%T: %s", err, err) + } + } +} + +func TestSenderDepositoryInstitution_Mandatory(t *testing.T) { + file := NewFile() + fwm := NewFedWireMessage() + + // Mandatory Fields + ss := mockSenderSupplied() + fwm.SetSenderSupplied(ss) + tst := mockTypeSubType() + fwm.SetTypeSubType(tst) + imad := mockInputMessageAccountabilityData() + fwm.SetInputMessageAccountabilityData(imad) + amt := mockAmount() + fwm.SetAmount(amt) + rdi := mockReceiverDepositoryInstitution() + fwm.SetReceiverDepositoryInstitution(rdi) + bfc := mockBusinessFunctionCode() + fwm.SetBusinessFunctionCode(bfc) + + file.AddFedWireMessage(fwm) + + // Create file + if err := file.Create(); err != nil { t.Errorf("%T: %s", err, err) } + if err := file.Validate(); err != nil { + if !base.Match(err, ErrFieldRequired) { + t.Errorf("%T: %s", err, err) + } + } } -/*// write the file to std out. Anything io.Writer -fd, err := os.Create(filepath.Join("..", "ach-rck-read", "rck-debit.ach")) -if err != nil { -log.Fatalf("Unexpected error creating output file: %s\n", err) +func TestReceiverDepositoryInstitution_Mandatory(t *testing.T) { + file := NewFile() + fwm := NewFedWireMessage() + + // Mandatory Fields + ss := mockSenderSupplied() + fwm.SetSenderSupplied(ss) + tst := mockTypeSubType() + fwm.SetTypeSubType(tst) + imad := mockInputMessageAccountabilityData() + fwm.SetInputMessageAccountabilityData(imad) + amt := mockAmount() + fwm.SetAmount(amt) + sdi := mockSenderDepositoryInstitution() + fwm.SetSenderDepositoryInstitution(sdi) + bfc := mockBusinessFunctionCode() + fwm.SetBusinessFunctionCode(bfc) + + file.AddFedWireMessage(fwm) + + // Create file + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + if !base.Match(err, ErrFieldRequired) { + t.Errorf("%T: %s", err, err) + } + } } -defer func() { - fd.Sync() - fd.Close() -}() -w := ach.NewWriter(fd) -if err := w.Write(file); err != nil { -log.Fatalf("Unexpected error: %s\n", err) + +func TestBusinessFunctionCode_Mandatory(t *testing.T) { + file := NewFile() + fwm := NewFedWireMessage() + + // Mandatory Fields + ss := mockSenderSupplied() + fwm.SetSenderSupplied(ss) + tst := mockTypeSubType() + fwm.SetTypeSubType(tst) + imad := mockInputMessageAccountabilityData() + fwm.SetInputMessageAccountabilityData(imad) + amt := mockAmount() + fwm.SetAmount(amt) + sdi := mockSenderDepositoryInstitution() + fwm.SetSenderDepositoryInstitution(sdi) + rdi := mockReceiverDepositoryInstitution() + fwm.SetReceiverDepositoryInstitution(rdi) + + file.AddFedWireMessage(fwm) + + // Create file + if err := file.Create(); err != nil { + t.Errorf("%T: %s", err, err) + } + if err := file.Validate(); err != nil { + if !base.Match(err, ErrFieldRequired) { + t.Errorf("%T: %s", err, err) + } + } } -w.Flush()*/