Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable fedach and fedwire download from proxy #268

Merged
24 changes: 11 additions & 13 deletions cmd/server/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,18 @@ func fedWireDataFile(logger log.Logger) (io.Reader, error) {
func attemptFileDownload(logger log.Logger, listName string) (io.Reader, error) {
routingNumber := os.Getenv("FRB_ROUTING_NUMBER")
downloadCode := os.Getenv("FRB_DOWNLOAD_CODE")

if routingNumber != "" && downloadCode != "" {
logger.Logf("download: attempting %s", listName)
client, err := download.NewClient(&download.ClientOpts{
RoutingNumber: routingNumber,
DownloadCode: downloadCode,
})
if err != nil {
return nil, fmt.Errorf("client setup: %v", err)
}
return client.GetList(listName)
downloadURL := os.Getenv("FRB_DOWNLOAD_URL_TEMPLATE")
adamdecaf marked this conversation as resolved.
Show resolved Hide resolved

logger.Logf("download: attempting %s", listName)
client, err := download.NewClient(&download.ClientOpts{
RoutingNumber: routingNumber,
DownloadCode: downloadCode,
DownloadURL: downloadURL,
})
if err != nil {
return nil, fmt.Errorf("client setup: %v", err)
}

return nil, nil
return client.GetList(listName)
}

func readDataFilepath(env, fallback string) string {
Expand Down
20 changes: 15 additions & 5 deletions pkg/download/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ type Client struct {

routingNumber string // X_FRB_EPAYMENTS_DIRECTORY_ORG_ID header
downloadCode string // X_FRB_EPAYMENTS_DIRECTORY_DOWNLOAD_CD
downloadURL string // defaults to "https://frbservices.org/EPaymentsDirectory/directories/%s?format=json" where %s is the list name

}

type ClientOpts struct {
HTTPClient *http.Client
RoutingNumber, DownloadCode string
HTTPClient *http.Client
RoutingNumber, DownloadCode, DownloadURL string
}

func NewClient(opts *ClientOpts) (*Client, error) {
Expand All @@ -45,17 +47,22 @@ func NewClient(opts *ClientOpts) (*Client, error) {
return nil, errors.New("missing download code")
}

if opts.DownloadURL == "" {
opts.DownloadURL = "https://frbservices.org/EPaymentsDirectory/directories/%s?format=json"
adamdecaf marked this conversation as resolved.
Show resolved Hide resolved
}

return &Client{
httpClient: opts.HTTPClient,
routingNumber: opts.RoutingNumber,
downloadCode: opts.DownloadCode,
downloadURL: opts.DownloadURL,
}, nil
}

// GetList downloads an FRB list and saves it into an io.Reader.
// Example listName values: fedach, fedwire
func (c *Client) GetList(listName string) (io.Reader, error) {
where, err := url.Parse(fmt.Sprintf("https://frbservices.org/EPaymentsDirectory/directories/%s?format=json", listName))
where, err := url.Parse(fmt.Sprintf(c.downloadURL, listName))
if err != nil {
return nil, fmt.Errorf("url: %v", err)
}
Expand All @@ -64,8 +71,11 @@ func (c *Client) GetList(listName string) (io.Reader, error) {
if err != nil {
return nil, fmt.Errorf("building %s url: %v", listName, err)
}
req.Header.Set("X_FRB_EPAYMENTS_DIRECTORY_ORG_ID", c.routingNumber)
req.Header.Set("X_FRB_EPAYMENTS_DIRECTORY_DOWNLOAD_CD", c.downloadCode)

if c.downloadCode != "" && c.routingNumber != "" {
req.Header.Set("X_FRB_EPAYMENTS_DIRECTORY_ORG_ID", c.routingNumber)
req.Header.Set("X_FRB_EPAYMENTS_DIRECTORY_DOWNLOAD_CD", c.downloadCode)
}

// perform our request
resp, err := c.httpClient.Do(req)
Expand Down
75 changes: 75 additions & 0 deletions pkg/download/download_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ package download

import (
"bytes"
"fmt"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -34,6 +38,41 @@ func TestClient__fedach(t *testing.T) {
}
}

func TestClient__fedach_custom_url(t *testing.T) {
file, err := os.ReadFile(filepath.Join("..", "..", "data", "fedachdir.json"))
if err != nil {
t.Fatal(err)
}

mockHTTPServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, string(file))
}))
defer mockHTTPServer.Close()

t.Setenv("FRB_DOWNLOAD_URL_TEMPLATE", mockHTTPServer.URL+"/%s")
t.Setenv("FRB_ROUTING_NUMBER", "123456789")
t.Setenv("FRB_DOWNLOAD_CODE", "a1b2c3d4-123b-9876-1234-z1x2y3a1b2c3")

client := setupClient(t)

fedach, err := client.GetList("fedach")
if err != nil {
t.Fatal(err)
}

buf, ok := fedach.(*bytes.Buffer)
require.True(t, ok)

if n := buf.Len(); n < 1024 {
t.Errorf("unexpected size of %d bytes", n)
}

bs, _ := io.ReadAll(io.LimitReader(fedach, 10024))
if !bytes.Equal(bs, file) {
t.Errorf("unexpected output:\n%s", string(bs))
}
}

func TestClient__fedwire(t *testing.T) {
client := setupClient(t)

Expand All @@ -55,18 +94,54 @@ func TestClient__fedwire(t *testing.T) {
}
}

func TestClient__wire_custom_url(t *testing.T) {
file, err := os.ReadFile(filepath.Join("..", "..", "data", "fedachdir.json"))
if err != nil {
t.Fatal(err)
}
mockHTTPServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, string(file))
}))
defer mockHTTPServer.Close()

t.Setenv("FRB_DOWNLOAD_URL_TEMPLATE", mockHTTPServer.URL+"/%s")
t.Setenv("FRB_ROUTING_NUMBER", "123456789")
t.Setenv("FRB_DOWNLOAD_CODE", "a1b2c3d4-123b-9876-1234-z1x2y3a1b2c3")

client := setupClient(t)

fedach, err := client.GetList("fedwire")
if err != nil {
t.Fatal(err)
}

buf, ok := fedach.(*bytes.Buffer)
require.True(t, ok)

if n := buf.Len(); n < 1024 {
t.Errorf("unexpected size of %d bytes", n)
}

bs, _ := io.ReadAll(io.LimitReader(fedach, 10024))
if !bytes.Equal(bs, file) {
t.Errorf("unexpected output:\n%s", string(bs))
}
}

func setupClient(t *testing.T) *Client {
t.Helper()

routingNumber := os.Getenv("FRB_ROUTING_NUMBER")
downloadCode := os.Getenv("FRB_DOWNLOAD_CODE")
downloadURL := os.Getenv("FRB_DOWNLOAD_URL_TEMPLATE")
if routingNumber == "" || downloadCode == "" {
t.Skip("missing FRB routing number or download code")
}

client, err := NewClient(&ClientOpts{
RoutingNumber: routingNumber,
DownloadCode: downloadCode,
DownloadURL: downloadURL,
})
if err != nil {
t.Fatal(err)
Expand Down
Loading