Skip to content

Commit

Permalink
Merge pull request #1413 from adamdecaf/read-validate-opts-v2
Browse files Browse the repository at this point in the history
feat: standardize validate opts query params, accept on GET/POST validate
  • Loading branch information
adamdecaf committed Apr 26, 2024
2 parents ee5c9fa + d31ce8e commit 25bb896
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 105 deletions.
8 changes: 5 additions & 3 deletions docs/create-file.md
Expand Up @@ -48,18 +48,20 @@ Example: `POST /files/create?requireABAOrigin=true&bypassDestination=true`
| `allowInvalidCheckDigit` | `AllowInvalidCheckDigit` |
| `allowMissingFileControl` | `AllowMissingFileControl` |
| `allowMissingFileHeader` | `AllowMissingFileHeader` |
| `allowUnorderedBatchNumbers` | `AllowUnorderedBatchNumbers` |
| `allowZeroBatches` | `AllowZeroBatches` |
| `bypassCompanyIdentificationMatch` | `BypassCompanyIdentificationMatch` |
| `bypassDestination` | `BypassDestinationValidation` |
| `bypassOrigin` | `BypassOriginValidation` |
| `bypassDestinationValidation` | `BypassDestinationValidation` |
| `bypassOriginValidation` | `BypassOriginValidation` |
| `customReturnCodes` | `CustomReturnCodes` |
| `customTraceNumbers` | `CustomTraceNumbers` |
| `preserveSpaces` | `PreserveSpaces` |
| `requireABAOrigin` | `RequireABAOrigin` |
| `skipAll` | `SkipAll` |
| `unequalAddendaCounts` | `UnequalAddendaCounts` |
| `unequalServiceClassCode` | `UnequalServiceClassCode` |
| `unorderedBatchNumbers` | `AllowUnorderedBatchNumbers` |

> Note: `bypassDestination`, `bypassOrigin`, and `unorderedBatchNumbers` are deprecated query parameters replace by identical named parameters.
## Upload a raw ACH file

Expand Down
99 changes: 99 additions & 0 deletions openapi.yaml
Expand Up @@ -89,11 +89,23 @@ paths:
description: Optional parameter to configure ImmediateOrigin validation
schema:
type: boolean
deprecated: true
- name: bypassOriginValidation
in: query
description: Optional parameter to configure ImmediateOrigin validation
schema:
type: boolean
- name: bypassDestination
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
deprecated: true
- name: bypassDestinationValidation
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: customTraceNumbers
in: query
description: Optional parameter to configure ImmediateDestination validation
Expand Down Expand Up @@ -134,6 +146,12 @@ paths:
description: Allow a file to be read with unordered batch numbers.
schema:
type: boolean
deprecated: true
- name: allowUnorderedBatchNumbers
in: query
description: Allow a file to be read with unordered batch numbers.
schema:
type: boolean
- name: allowInvalidCheckDigit
in: query
description: Allow the CheckDigit field in EntryDetail to differ from the expected calculation
Expand Down Expand Up @@ -304,6 +322,87 @@ paths:
schema:
$ref: '#/components/schemas/RawFile'
/files/{fileID}/validate:
parameters:
- name: skipAll
in: query
description: Optional parameter to disable all validation checks for a File
schema:
type: boolean
- name: requireABAOrigin
in: query
description: Optional parameter to configure ImmediateOrigin validation
schema:
type: boolean
- name: bypassOriginValidation
in: query
description: Optional parameter to configure ImmediateOrigin validation
schema:
type: boolean
- name: bypassDestinationValidation
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: customTraceNumbers
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: allowZeroBatches
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: allowMissingFileHeader
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: allowMissingFileControl
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: bypassCompanyIdentificationMatch
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: customReturnCodes
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: unequalServiceClassCode
in: query
description: Optional parameter to configure ImmediateDestination validation
schema:
type: boolean
- name: allowUnorderedBatchNumbers
in: query
description: Allow a file to be read with unordered batch numbers.
schema:
type: boolean
- name: allowInvalidCheckDigit
in: query
description: Allow the CheckDigit field in EntryDetail to differ from the expected calculation
schema:
type: boolean
- name: unequalAddendaCounts
in: query
description: Optional parameter to configure UnequalAddendaCounts validation
schema:
type: boolean
- name: preserveSpaces
in: query
description: Optional parameter to save all padding spaces
schema:
type: boolean
- name: allowInvalidAmounts
in: query
description: Optional parameter to save all padding spaces
schema:
type: boolean
get:
tags: ['ACH Files']
summary: Validate File
Expand Down
117 changes: 15 additions & 102 deletions server/files.go
Expand Up @@ -25,7 +25,6 @@ import (
"fmt"
"io"
"net/http"
"strconv"
"strings"

"github.com/moov-io/ach"
Expand Down Expand Up @@ -117,104 +116,17 @@ func createFileEndpoint(s Service, r Repository, logger log.Logger) endpoint.End
func decodeCreateFileRequest(_ context.Context, request *http.Request) (interface{}, error) {
var r io.Reader
req := createFileRequest{
File: ach.NewFile(),
requestID: moovhttp.GetRequestID(request),
validateOpts: &ach.ValidateOpts{},
}

const (
skipAll = "skipAll"
requireABAOrigin = "requireABAOrigin"
bypassOrigin = "bypassOrigin"
bypassDestination = "bypassDestination"
customTraceNumbers = "customTraceNumbers"
allowZeroBatches = "allowZeroBatches"
allowMissingFileHeader = "allowMissingFileHeader"
allowMissingFileControl = "allowMissingFileControl"
bypassCompanyIdentificationMatch = "bypassCompanyIdentificationMatch"
customReturnCodes = "customReturnCodes"
unequalServiceClassCode = "unequalServiceClassCode"
unorderedBatchNumbers = "unorderedBatchNumbers"
allowInvalidCheckDigit = "allowInvalidCheckDigit"
unequalAddendaCounts = "unequalAddendaCounts"
preserveSpaces = "preserveSpaces"
allowInvalidAmounts = "allowInvalidAmounts"
)

validationNames := []string{
skipAll,
requireABAOrigin,
bypassOrigin,
bypassDestination,
customTraceNumbers,
allowZeroBatches,
allowMissingFileHeader,
allowMissingFileControl,
bypassCompanyIdentificationMatch,
customReturnCodes,
unequalServiceClassCode,
unorderedBatchNumbers,
allowInvalidCheckDigit,
unequalAddendaCounts,
preserveSpaces,
allowInvalidAmounts,
}

for _, name := range validationNames {
q := request.URL.Query()
if q == nil {
continue
}
input := q.Get(name)
if input == "" {
continue
}

ok, err := strconv.ParseBool(input)
if err != nil {
return nil, fmt.Errorf("invalid bool: %v", err)
}
if !ok {
continue
}

switch name {
case skipAll:
req.validateOpts.SkipAll = true
case requireABAOrigin:
req.validateOpts.RequireABAOrigin = true
case bypassOrigin:
req.validateOpts.BypassOriginValidation = true
case bypassDestination:
req.validateOpts.BypassDestinationValidation = true
case customTraceNumbers:
req.validateOpts.CustomTraceNumbers = true
case allowZeroBatches:
req.validateOpts.AllowZeroBatches = true
case allowMissingFileHeader:
req.validateOpts.AllowMissingFileHeader = true
case allowMissingFileControl:
req.validateOpts.AllowMissingFileControl = true
case bypassCompanyIdentificationMatch:
req.validateOpts.BypassCompanyIdentificationMatch = true
case customReturnCodes:
req.validateOpts.CustomReturnCodes = true
case unequalServiceClassCode:
req.validateOpts.UnequalServiceClassCode = true
case unorderedBatchNumbers:
req.validateOpts.AllowUnorderedBatchNumbers = true
case allowInvalidCheckDigit:
req.validateOpts.AllowInvalidCheckDigit = true
case unequalAddendaCounts:
req.validateOpts.UnequalAddendaCounts = true
case preserveSpaces:
req.validateOpts.PreserveSpaces = true
case allowInvalidAmounts:
req.validateOpts.AllowInvalidAmounts = true
}
}

bs, err := io.ReadAll(request.Body)
File: ach.NewFile(),
requestID: moovhttp.GetRequestID(request),
}

body, validateOpts, err := readValidateOpts(request)
if err != nil {
return nil, err
}
req.validateOpts = validateOpts

bs, err := io.ReadAll(body)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -538,10 +450,11 @@ func decodeValidateFileRequest(_ context.Context, r *http.Request) (interface{},
requestID: moovhttp.GetRequestID(r),
}

var opts ach.ValidateOpts
if err := json.NewDecoder(r.Body).Decode(&opts); err == nil {
req.opts = &opts
_, validateOpts, err := readValidateOpts(r)
if err != nil {
return nil, err
}
req.opts = validateOpts

return req, nil
}
Expand Down
13 changes: 13 additions & 0 deletions server/test/unordered_batches/unordered_batch_test.go
Expand Up @@ -51,6 +51,19 @@ func TestUnorderedBatches(t *testing.T) {
req = httptest.NewRequest("POST", fmt.Sprintf("/files/%s/validate", response.ID), &buf)
server.Handler.ServeHTTP(w, req)
w.Flush()
require.Equal(t, http.StatusOK, w.Code)

// Try POST /validate with ?unorderedBatchNumbers
w = httptest.NewRecorder()
req = httptest.NewRequest("POST", fmt.Sprintf("/files/%s/validate?unorderedBatchNumbers=true", response.ID), nil)
server.Handler.ServeHTTP(w, req)
w.Flush()
require.Equal(t, http.StatusOK, w.Code)

// Try POST /validate with ?allowUnorderedBatchNumbers
w = httptest.NewRecorder()
req = httptest.NewRequest("POST", fmt.Sprintf("/files/%s/validate?allowUnorderedBatchNumbers=true", response.ID), nil)
server.Handler.ServeHTTP(w, req)
w.Flush()
require.Equal(t, http.StatusOK, w.Code)
}

0 comments on commit 25bb896

Please sign in to comment.