Skip to content

Commit

Permalink
Merge pull request #1073 from adamdecaf/new-segment-endpoint
Browse files Browse the repository at this point in the history
feat: new segment endpoint
  • Loading branch information
adamdecaf authored Aug 22, 2022
2 parents fc57391 + f6fbfea commit ff9c07d
Show file tree
Hide file tree
Showing 7 changed files with 282 additions and 47 deletions.
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,7 @@ golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j
golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 h1:dtndE8FcEta75/4kHF3AbpuWzV6f1LjnLrM4pe2SZrw=
golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down
85 changes: 75 additions & 10 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,9 @@ paths:
/files/{fileID}/segment:
post:
tags: ['ACH Files']
summary: Segment File
description: Split one File into two. One with only debits and one with only credits.
operationId: segmentFile
summary: Segment FileID
description: Split one FileID into two. One with only debits and one with only credits.
operationId: segmentFileID
parameters:
- name: X-Request-ID
in: header
Expand All @@ -370,13 +370,13 @@ paths:
schema:
type: string
example: "3f2d23ee214"
# requestBody:
# description: Optional configuration for segmenting files
# required: false
# content:
# application/json:
# schema:
# $ref: '#/components/schemas/SegmentFileConfiguration'
requestBody:
description: Optional configuration for segmenting files
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/SegmentFileConfiguration'
responses:
'200':
description: An ID of each new ACH file
Expand Down Expand Up @@ -576,6 +576,63 @@ paths:
description: Batch deleted
'404':
description: Batch or File not found
/segment:
post:
tags: ['ACH Files']
summary: Segment File
description: Split one File into two. One with only debits and one with only credits.
operationId: segmentFile
parameters:
- name: X-Request-ID
in: header
description: Optional Request ID allows application developer to trace requests through the system's logs
example: "rs4f9915"
schema:
type: string
- name: X-Idempotency-Key
in: header
description: Idempotent key in the header which expires after 24 hours. These strings should contain enough entropy to not collide with each other in your requests.
example: "a4f88150"
required: false
schema:
type: string
requestBody:
description: ACH file (in Nacha or JSON formatting) along with optional segment configuration
required: false
content:
text/plain:
schema:
description: A plaintext ACH file
type: string
example: |
101 23138010401210428821906240000A094101Federal Reserve Bank My Bank Name
5225Name on Account 121042882 PPDREG.SALARY 190625 1121042880000001
62723138010412345678 0100000000 Receiver Account Name 0121042880000001
82250000010023138010000100000000000000000000121042882 121042880000001
9000001000001000000010023138010000100000000000000000000
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
application/json:
schema:
$ref: '#/components/schemas/SegmentFile'
responses:
'200':
description: An ID of each new ACH file
content:
application/json:
schema:
$ref: '#/components/schemas/SegmentedFiles'
'400':
description: See error in response body
content:
application/json:
schema:
$ref: 'https://raw.githubusercontent.com/moov-io/base/master/api/common.yaml#/components/schemas/Error'
'404':
description: A resource with the specified ID was not found

components:
schemas:
Expand Down Expand Up @@ -2211,3 +2268,11 @@ components:
type: boolean
default: false
description: Allow a file to be read with unordered batch numbers.
SegmentFileConfiguration:
properties: {} # TODO: Are there any config options people need?
SegmentFile:
properties:
file:
$ref: '#/components/schemas/File'
opts:
$ref: '#/components/schemas/SegmentFileConfiguration'
113 changes: 101 additions & 12 deletions server/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,14 +591,14 @@ func decodeBalanceFileRequest(_ context.Context, r *http.Request) (interface{},
}, nil
}

type segmentFileRequest struct {
type segmentFileIDRequest struct {
fileID string
requestID string

opts *ach.SegmentFileConfiguration
}

type segmentFileResponse struct {
type segmentedFilesResponse struct {
CreditFileID string `json:"creditFileID"`
CreditFile *ach.File `json:"creditFile"`

Expand All @@ -608,31 +608,31 @@ type segmentFileResponse struct {
Err error `json:"error"`
}

func segmentFileEndpoint(s Service, r Repository, logger log.Logger) endpoint.Endpoint {
func segmentFileIDEndpoint(s Service, r Repository, logger log.Logger) endpoint.Endpoint {
return func(_ context.Context, request interface{}) (interface{}, error) {
req, ok := request.(segmentFileRequest)
req, ok := request.(segmentFileIDRequest)
if !ok {
return segmentFileResponse{Err: ErrFoundABug}, ErrFoundABug
return segmentedFilesResponse{Err: ErrFoundABug}, ErrFoundABug
}

creditFile, debitFile, err := s.SegmentFile(req.fileID, req.opts)
creditFile, debitFile, err := s.SegmentFileID(req.fileID, req.opts)

if logger != nil {
logger.With(log.Fields{
"files": log.String("segmentFile"),
"files": log.String("segmentFileID"),
"requestID": log.String(req.requestID),
})
if err != nil {
logger.Error().LogError(err)
} else {
logger.Info().Log("segment file")
logger.Info().Log("segment fileID")
}
}
if err != nil {
return segmentFileResponse{Err: err}, err
return segmentedFilesResponse{Err: err}, err
}

var resp segmentFileResponse
var resp segmentedFilesResponse

if creditFile.ID != "" {
err = r.StoreFile(creditFile)
Expand Down Expand Up @@ -664,14 +664,14 @@ func segmentFileEndpoint(s Service, r Repository, logger log.Logger) endpoint.En
}
}

func decodeSegmentFileRequest(_ context.Context, r *http.Request) (interface{}, error) {
func decodeSegmentFileIDRequest(_ context.Context, r *http.Request) (interface{}, error) {
vars := mux.Vars(r)
fileID, ok := vars["fileID"]
if !ok {
return nil, ErrBadRouting
}

req := segmentFileRequest{
req := segmentFileIDRequest{
fileID: fileID,
requestID: moovhttp.GetRequestID(r),
}
Expand All @@ -684,6 +684,95 @@ func decodeSegmentFileRequest(_ context.Context, r *http.Request) (interface{},
return req, nil
}

type segmentFileRequest struct {
File *ach.File
requestID string

opts *ach.SegmentFileConfiguration
}

func segmentFileEndpoint(s Service, r Repository, logger log.Logger) endpoint.Endpoint {
return func(_ context.Context, request interface{}) (interface{}, error) {
req, ok := request.(segmentFileRequest)
if !ok {
return segmentedFilesResponse{Err: ErrFoundABug}, ErrFoundABug
}

creditFile, debitFile, err := s.SegmentFile(req.File, req.opts)
if logger != nil {
logger.With(log.Fields{
"files": log.String("segmentFile"),
"requestID": log.String(req.requestID),
})
if err != nil {
logger.Error().LogError(err)
} else {
logger.Info().Log("segment file")
}
}
if err != nil {
return segmentedFilesResponse{Err: err}, err
}

var resp segmentedFilesResponse

if creditFile.ID != "" {
err = r.StoreFile(creditFile)
if logger != nil && err != nil {
logger.With(log.Fields{
"files": log.String("storeCreditFile"),
"requestID": log.String(req.requestID),
}).LogError(err)
}
resp.CreditFile = creditFile
resp.CreditFileID = creditFile.ID
}

if debitFile.ID != "" {
err = r.StoreFile(debitFile)
if logger != nil && err != nil {
logger.With(log.Fields{
"files": log.String("storeDebitFile"),
"requestID": log.String(req.requestID),
}).LogError(err)
}
resp.DebitFile = debitFile
resp.DebitFileID = debitFile.ID
}

resp.Err = err

return resp, nil
}
}

func decodeSegmentFileRequest(_ context.Context, r *http.Request) (interface{}, error) {
var wrapper struct {
File *ach.File `json:"file"`
Opts *ach.SegmentFileConfiguration `json:"opts"`
}

header := strings.ToLower(r.Header.Get("content-type"))
if strings.Contains(header, "application/json") {
err := json.NewDecoder(r.Body).Decode(&wrapper)
if err != nil {
return segmentedFilesResponse{Err: err}, err
}
} else {
file, err := ach.NewReader(r.Body).Read()
if err != nil {
return segmentedFilesResponse{Err: err}, err
}
wrapper.File = &file
}

return segmentFileRequest{
File: wrapper.File,
requestID: moovhttp.GetRequestID(r),
opts: wrapper.Opts,
}, nil
}

type flattenBatchesRequest struct {
fileID string
requestID string
Expand Down
Loading

0 comments on commit ff9c07d

Please sign in to comment.