diff --git a/Makefile b/Makefile index d791f58a..b2612e66 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ build: .PHONY: setup setup: - docker-compose up -d --force-recreate --remove-orphans + docker compose up -d --force-recreate --remove-orphans .PHONY: check check: @@ -50,7 +50,7 @@ endif .PHONY: teardown teardown: - -docker-compose down --remove-orphans + -docker compose down --remove-orphans docker: update docker build --pull --build-arg VERSION=${VERSION} -t moov/ach-test-harness:${VERSION} -f Dockerfile . diff --git a/go.mod b/go.mod index 5a80c4aa..2026f4cf 100644 --- a/go.mod +++ b/go.mod @@ -16,12 +16,17 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/gobuffalo/here v0.6.7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/igrmk/treemap/v2 v2.0.1 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -44,12 +49,23 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect golang.org/x/net v0.27.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 12356c16..3134d3f8 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -17,6 +19,11 @@ github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/gobuffalo/here v0.6.7 h1:hpfhh+kt2y9JLDfhYUxxCRxQol540jsVfKUZzjlbp8o= github.com/gobuffalo/here v0.6.7/go.mod h1:vuCfanjqckTuRlqAitJz6QC4ABNnS27wLb816UhsPcc= @@ -27,6 +34,8 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/igrmk/treemap/v2 v2.0.1 h1:Jhy4z3yhATvYZMWCmxsnHO5NnNZBdueSzvxh6353l+0= @@ -112,6 +121,22 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= goftp.io/server v0.4.1 h1:x7KG4HIxSMdK/rpYhExMinRN/aO/T9icvaG/B5e/XfY= @@ -141,6 +166,13 @@ golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/genproto v0.0.0-20240701130421-f6361c86f094 h1:6whtk83KtD3FkGrVb2hFXuQ+ZMbCNdakARIn/aHMmG8= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/batches/api_batch.go b/pkg/batches/api_batch.go index 8f59d855..f4002b9e 100644 --- a/pkg/batches/api_batch.go +++ b/pkg/batches/api_batch.go @@ -5,8 +5,10 @@ import ( "net/http" "github.com/moov-io/base/log" + "github.com/moov-io/base/telemetry" "github.com/gorilla/mux" + "go.opentelemetry.io/otel/attribute" ) func NewBatchController(logger log.Logger, service BatchService) *batchController { @@ -33,9 +35,17 @@ func (c *batchController) AppendRoutes(router *mux.Router) *mux.Router { func (c *batchController) Search() func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { + ctx, span := telemetry.StartSpan(r.Context(), "api-batch-search") + defer span.End() + w.Header().Set("Content-Type", "application/json; charset=utf-8") - batches, err := c.service.Search(readSearchOptions(r)) + opts := readSearchOptions(r) + span.SetAttributes( + attribute.String("search.trace_number", opts.TraceNumber), + ) + + batches, err := c.service.Search(ctx, opts) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) diff --git a/pkg/batches/repository_batch.go b/pkg/batches/repository_batch.go index deee18fd..64aa1bda 100644 --- a/pkg/batches/repository_batch.go +++ b/pkg/batches/repository_batch.go @@ -1,6 +1,7 @@ package batches import ( + "context" "fmt" "io/fs" "path/filepath" @@ -9,10 +10,14 @@ import ( "github.com/moov-io/ach" "github.com/moov-io/ach-test-harness/pkg/response/match" "github.com/moov-io/ach-test-harness/pkg/service" + "github.com/moov-io/base/telemetry" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) type BatchRepository interface { - Search(opts SearchOptions) ([]ach.Batcher, error) + Search(ctx context.Context, opts SearchOptions) ([]ach.Batcher, error) } type batchRepository struct { @@ -25,9 +30,14 @@ func NewFTPRepository(cfg *service.FTPConfig) *batchRepository { } } -func (r *batchRepository) Search(opts SearchOptions) ([]ach.Batcher, error) { +func (r *batchRepository) Search(ctx context.Context, opts SearchOptions) ([]ach.Batcher, error) { + _, span := telemetry.StartSpan(ctx, "repo-batch-search") + defer span.End() + out := make([]ach.Batcher, 0) + var filesProcessed int + //nolint:gosimple var search fs.WalkDirFunc search = func(path string, d fs.DirEntry, err error) error { @@ -42,14 +52,26 @@ func (r *batchRepository) Search(opts SearchOptions) ([]ach.Batcher, error) { if strings.ToLower(filepath.Ext(path)) != ".ach" { return nil } + filesProcessed += 1 batches, err := filterBatches(path, opts) if err != nil { return err } - out = append(out, batches...) + + if len(batches) > 0 { + span.AddEvent("found-batches", trace.WithAttributes( + attribute.Int("search.batches", len(batches)), + attribute.String("search.filename", path), + )) + + out = append(out, batches...) + } return nil } + span.SetAttributes( + attribute.Int("search.files_processed", filesProcessed), + ) var walkingPath = r.rootPath if opts.Path != "" { diff --git a/pkg/batches/repository_batch_test.go b/pkg/batches/repository_batch_test.go index 4dade574..a92136da 100644 --- a/pkg/batches/repository_batch_test.go +++ b/pkg/batches/repository_batch_test.go @@ -1,6 +1,7 @@ package batches import ( + "context" "testing" "github.com/moov-io/ach-test-harness/pkg/service" @@ -8,6 +9,8 @@ import ( ) func TestRepository(t *testing.T) { + ctx := context.Background() + repo := NewFTPRepository(&service.FTPConfig{ RootPath: "./testdata", Paths: service.Paths{ @@ -17,12 +20,12 @@ func TestRepository(t *testing.T) { }) // return all - batches, err := repo.Search(SearchOptions{}) + batches, err := repo.Search(ctx, SearchOptions{}) require.NoError(t, err) require.Len(t, batches, 2) // search by account number - batches, err = repo.Search(SearchOptions{ + batches, err = repo.Search(ctx, SearchOptions{ AccountNumber: "987654321", }) @@ -32,7 +35,7 @@ func TestRepository(t *testing.T) { // search by timestamp in our files: // returned/2.ach was created on 1908161055 and has 1 entry // outbound/1.ach was created on 1908161059 and has 2 batches - batches, err = repo.Search(SearchOptions{ + batches, err = repo.Search(ctx, SearchOptions{ CreatedAfter: "2019-08-16T10:56:00+00:00", }) @@ -42,7 +45,7 @@ func TestRepository(t *testing.T) { // search by subdirectory in our files: // outbound/1.ach was created on 1908161059 and has 2 batches - batches, err = repo.Search(SearchOptions{ + batches, err = repo.Search(ctx, SearchOptions{ Path: "outbound", }) diff --git a/pkg/batches/service_batch.go b/pkg/batches/service_batch.go index 55c9664d..c1410c50 100644 --- a/pkg/batches/service_batch.go +++ b/pkg/batches/service_batch.go @@ -1,6 +1,7 @@ package batches import ( + "context" "fmt" "time" @@ -9,7 +10,7 @@ import ( ) type BatchService interface { - Search(ops SearchOptions) ([]ach.Batcher, error) + Search(ctx context.Context, ops SearchOptions) ([]ach.Batcher, error) } type batchService struct { @@ -31,8 +32,8 @@ type SearchOptions struct { Path string } -func (s *batchService) Search(opts SearchOptions) ([]ach.Batcher, error) { - return s.repository.Search(opts) +func (s *batchService) Search(ctx context.Context, opts SearchOptions) ([]ach.Batcher, error) { + return s.repository.Search(ctx, opts) } func (opts SearchOptions) fileTooOld(file *ach.File) (bool, error) { diff --git a/pkg/entries/api_entry.go b/pkg/entries/api_entry.go index f0ec98db..8c51ac54 100644 --- a/pkg/entries/api_entry.go +++ b/pkg/entries/api_entry.go @@ -6,8 +6,10 @@ import ( "strconv" "github.com/moov-io/base/log" + "github.com/moov-io/base/telemetry" "github.com/gorilla/mux" + "go.opentelemetry.io/otel/attribute" ) func NewEntryController(logger log.Logger, service EntryService) *entryController { @@ -34,9 +36,17 @@ func (c *entryController) AppendRoutes(router *mux.Router) *mux.Router { func (c *entryController) Search() func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { + ctx, span := telemetry.StartSpan(r.Context(), "api-entry-search") + defer span.End() + w.Header().Set("Content-Type", "application/json; charset=utf-8") - entries, err := c.service.Search(readSearchOptions(r)) + opts := readSearchOptions(r) + span.SetAttributes( + attribute.String("search.trace_number", opts.TraceNumber), + ) + + entries, err := c.service.Search(ctx, opts) if err != nil { w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{"error": err.Error()}) diff --git a/pkg/entries/api_test.go b/pkg/entries/api_entry_test.go similarity index 100% rename from pkg/entries/api_test.go rename to pkg/entries/api_entry_test.go diff --git a/pkg/entries/repository_entry.go b/pkg/entries/repository_entry.go index 818fca63..31c6627c 100644 --- a/pkg/entries/repository_entry.go +++ b/pkg/entries/repository_entry.go @@ -1,6 +1,7 @@ package entries import ( + "context" "fmt" "io/fs" "path/filepath" @@ -9,10 +10,14 @@ import ( "github.com/moov-io/ach" "github.com/moov-io/ach-test-harness/pkg/response/match" "github.com/moov-io/ach-test-harness/pkg/service" + "github.com/moov-io/base/telemetry" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) type EntryRepository interface { - Search(opts SearchOptions) ([]*ach.EntryDetail, error) + Search(ctx context.Context, opts SearchOptions) ([]*ach.EntryDetail, error) } type ftpRepository struct { @@ -25,9 +30,14 @@ func NewFTPRepository(cfg *service.FTPConfig) *ftpRepository { } } -func (r *ftpRepository) Search(opts SearchOptions) ([]*ach.EntryDetail, error) { +func (r *ftpRepository) Search(ctx context.Context, opts SearchOptions) ([]*ach.EntryDetail, error) { + _, span := telemetry.StartSpan(ctx, "repo-entry-search") + defer span.End() + out := make([]*ach.EntryDetail, 0) + var filesProcessed int + //nolint:gosimple var search fs.WalkDirFunc search = func(path string, d fs.DirEntry, err error) error { @@ -42,14 +52,26 @@ func (r *ftpRepository) Search(opts SearchOptions) ([]*ach.EntryDetail, error) { if strings.ToLower(filepath.Ext(path)) != ".ach" { return nil } + filesProcessed += 1 entries, err := filterEntries(path, opts) if err != nil { return err } - out = append(out, entries...) + + if len(entries) > 0 { + span.AddEvent("found-entries", trace.WithAttributes( + attribute.Int("search.entries", len(entries)), + attribute.String("search.filename", path), + )) + + out = append(out, entries...) + } return nil } + span.SetAttributes( + attribute.Int("search.files_processed", filesProcessed), + ) var walkingPath = r.rootPath if opts.Path != "" { diff --git a/pkg/entries/repository_entry_test.go b/pkg/entries/repository_entry_test.go index d1e66f32..2b268857 100644 --- a/pkg/entries/repository_entry_test.go +++ b/pkg/entries/repository_entry_test.go @@ -1,6 +1,7 @@ package entries import ( + "context" "testing" "github.com/moov-io/ach-test-harness/pkg/service" @@ -8,6 +9,8 @@ import ( ) func TestRepository(t *testing.T) { + ctx := context.Background() + repo := NewFTPRepository(&service.FTPConfig{ RootPath: "./testdata", Paths: service.Paths{ @@ -17,12 +20,12 @@ func TestRepository(t *testing.T) { }) // return all - entries, err := repo.Search(SearchOptions{}) + entries, err := repo.Search(ctx, SearchOptions{}) require.NoError(t, err) require.Len(t, entries, 3) // search by account number - entries, err = repo.Search(SearchOptions{ + entries, err = repo.Search(ctx, SearchOptions{ AccountNumber: "987654321", }) @@ -32,7 +35,7 @@ func TestRepository(t *testing.T) { // search by timestamp in our files: // returned/2.ach was created on 1908161055 and has 1 entry // outbound/1.ach was created on 1908161059 and has 2 entries - entries, err = repo.Search(SearchOptions{ + entries, err = repo.Search(ctx, SearchOptions{ CreatedAfter: "2019-08-16T10:56:00+00:00", }) @@ -42,7 +45,7 @@ func TestRepository(t *testing.T) { // search by subdirectory in our files: // outbound/1.ach was created on 1908161059 and has 2 entries - entries, err = repo.Search(SearchOptions{ + entries, err = repo.Search(ctx, SearchOptions{ Path: "outbound", }) diff --git a/pkg/entries/service_entry.go b/pkg/entries/service_entry.go index e8a8225c..969a8e3c 100644 --- a/pkg/entries/service_entry.go +++ b/pkg/entries/service_entry.go @@ -1,6 +1,7 @@ package entries import ( + "context" "fmt" "time" @@ -9,7 +10,7 @@ import ( ) type EntryService interface { - Search(ops SearchOptions) ([]*ach.EntryDetail, error) + Search(ctx context.Context, ops SearchOptions) ([]*ach.EntryDetail, error) } type entryService struct { @@ -57,6 +58,6 @@ func parseTimestamp(when string) (time.Time, error) { return time.Time{}, fmt.Errorf("unable to parse '%s'", when) } -func (s *entryService) Search(opts SearchOptions) ([]*ach.EntryDetail, error) { - return s.repository.Search(opts) +func (s *entryService) Search(ctx context.Context, opts SearchOptions) ([]*ach.EntryDetail, error) { + return s.repository.Search(ctx, opts) } diff --git a/pkg/response/batch_mirror.go b/pkg/response/batch_mirror.go index 5a01f736..0dc1354f 100644 --- a/pkg/response/batch_mirror.go +++ b/pkg/response/batch_mirror.go @@ -2,6 +2,7 @@ package response import ( "bytes" + "context" "fmt" "path/filepath" "sort" @@ -9,6 +10,9 @@ import ( "github.com/moov-io/ach" "github.com/moov-io/ach-test-harness/pkg/service" + "github.com/moov-io/base/telemetry" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) // batchMirror is an object that will save batches @@ -56,17 +60,24 @@ func newBatchMirror(w FileWriter) *batchMirror { } } -func (bm *batchMirror) saveEntry(b *ach.Batcher, copy *service.Copy, ed *ach.EntryDetail) { +func (bm *batchMirror) saveEntry(ctx context.Context, b *ach.Batcher, copy *service.Copy, ed *ach.EntryDetail) { if b == nil || copy == nil || ed == nil { return } + ctx, span := telemetry.StartSpan(ctx, "batch-mirror-save-entry") + defer span.End() + batcher := *b // Get the batchMirrorKey key := batchMirrorKey{ path: copy.Path, companyID: batcher.GetHeader().CompanyIdentification, } + span.SetAttributes( + attribute.String("batch.company_id", key.companyID), + ) + // Create a new batchMirrorBatch map if this key does not exist if _, exists := bm.batches[key]; !exists { bm.batches[key] = make(map[string]*batchMirrorBatch) @@ -83,11 +94,16 @@ func (bm *batchMirror) saveEntry(b *ach.Batcher, copy *service.Copy, ed *ach.Ent bm.batches[key][batcher.GetHeader().BatchNumberField()].entries = append(bm.batches[key][batcher.GetHeader().BatchNumberField()].entries, ed) } -func (bm *batchMirror) saveFiles() error { +func (bm *batchMirror) saveFiles(ctx context.Context) error { if len(bm.batches) == 0 { return nil } + _, span := telemetry.StartSpan(ctx, "batch-mirror-save-files", trace.WithAttributes( + attribute.Int("mirror.batches", len(bm.batches)), + )) + defer span.End() + // Write files by Path/CompanyID for key, mirror := range bm.batches { var buf bytes.Buffer diff --git a/pkg/response/entry_transformer.go b/pkg/response/entry_transformer.go index 8b34b58d..12bf8132 100644 --- a/pkg/response/entry_transformer.go +++ b/pkg/response/entry_transformer.go @@ -1,23 +1,28 @@ package response import ( + "context" "fmt" "github.com/moov-io/ach" "github.com/moov-io/ach-test-harness/internal/achx" "github.com/moov-io/ach-test-harness/pkg/service" + "github.com/moov-io/base/telemetry" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) type EntryTransformer interface { - MorphEntry(fh ach.FileHeader, bh *ach.BatchHeader, ed *ach.EntryDetail, action *service.Action) (*ach.EntryDetail, error) + MorphEntry(ctx context.Context, fh ach.FileHeader, bh *ach.BatchHeader, ed *ach.EntryDetail, action *service.Action) (*ach.EntryDetail, error) } type EntryTransformers []EntryTransformer -func (et EntryTransformers) MorphEntry(fh ach.FileHeader, bh *ach.BatchHeader, ed *ach.EntryDetail, action *service.Action) (*ach.EntryDetail, error) { +func (et EntryTransformers) MorphEntry(ctx context.Context, fh ach.FileHeader, bh *ach.BatchHeader, ed *ach.EntryDetail, action *service.Action) (*ach.EntryDetail, error) { var err error for i := range et { - ed, err = et[i].MorphEntry(fh, bh, ed, action) + ed, err = et[i].MorphEntry(ctx, fh, bh, ed, action) if err != nil { return ed, fmt.Errorf("%T: %v", et, err) } @@ -27,11 +32,17 @@ func (et EntryTransformers) MorphEntry(fh ach.FileHeader, bh *ach.BatchHeader, e type CorrectionTransformer struct{} -func (t *CorrectionTransformer) MorphEntry(fh ach.FileHeader, bh *ach.BatchHeader, ed *ach.EntryDetail, action *service.Action) (*ach.EntryDetail, error) { +func (t *CorrectionTransformer) MorphEntry(ctx context.Context, fh ach.FileHeader, bh *ach.BatchHeader, ed *ach.EntryDetail, action *service.Action) (*ach.EntryDetail, error) { if action.Correction == nil { return ed, nil } + _, span := telemetry.StartSpan(ctx, "entry-correction-transformer", trace.WithAttributes( + attribute.String("action.correction_code", action.Correction.Code), + attribute.String("entry.trace_number", ed.TraceNumber), + )) + defer span.End() + out := ach.NewEntryDetail() // Set the TransactionCode from the EntryDetail @@ -101,11 +112,17 @@ func generateCorrectedData(cor *service.Correction) string { type ReturnTransformer struct{} -func (t *ReturnTransformer) MorphEntry(fh ach.FileHeader, bh *ach.BatchHeader, ed *ach.EntryDetail, action *service.Action) (*ach.EntryDetail, error) { +func (t *ReturnTransformer) MorphEntry(ctx context.Context, fh ach.FileHeader, bh *ach.BatchHeader, ed *ach.EntryDetail, action *service.Action) (*ach.EntryDetail, error) { if action.Return == nil { return ed, nil } + _, span := telemetry.StartSpan(ctx, "entry-return-transformer", trace.WithAttributes( + attribute.String("action.return_code", action.Return.Code), + attribute.String("entry.trace_number", ed.TraceNumber), + )) + defer span.End() + out := ach.NewEntryDetail() // Set the TransactionCode from the EntryDetail diff --git a/pkg/response/entry_transformer_test.go b/pkg/response/entry_transformer_test.go index 6e9e8240..9c330870 100644 --- a/pkg/response/entry_transformer_test.go +++ b/pkg/response/entry_transformer_test.go @@ -1,6 +1,7 @@ package response import ( + "context" "fmt" "path/filepath" "testing" @@ -26,7 +27,7 @@ func TestMorphEntry__Correction(t *testing.T) { } bh := file.Batches[0].GetHeader() ed := file.Batches[0].GetEntries()[0] - out, err := xform.MorphEntry(file.Header, bh, ed, &action) + out, err := xform.MorphEntry(context.Background(), file.Header, bh, ed, &action) require.NoError(t, err) if out.Addenda98 == nil { @@ -60,7 +61,7 @@ func TestMorphEntry__Return(t *testing.T) { } bh := file.Batches[0].GetHeader() ed := file.Batches[0].GetEntries()[0] - out, err := xform.MorphEntry(file.Header, bh, ed, &action) + out, err := xform.MorphEntry(context.Background(), file.Header, bh, ed, &action) require.NoError(t, err) if out.Addenda98 != nil { @@ -102,7 +103,7 @@ func TestMorphEntry__Prenote(t *testing.T) { ed.TransactionCode = ach.CheckingPrenoteCredit xform := &CorrectionTransformer{} - out, err := xform.MorphEntry(file.Header, bh, ed, &action) + out, err := xform.MorphEntry(context.Background(), file.Header, bh, ed, &action) require.NoError(t, err, msg) require.Equal(t, ach.CheckingReturnNOCCredit, out.TransactionCode, msg) @@ -130,7 +131,7 @@ func TestMorphEntry__Prenote(t *testing.T) { ed.TransactionCode = ach.CheckingPrenoteCredit xform := &ReturnTransformer{} - out, err := xform.MorphEntry(file.Header, bh, ed, &action) + out, err := xform.MorphEntry(context.Background(), file.Header, bh, ed, &action) require.NoError(t, err, msg) require.Equal(t, ach.CheckingReturnNOCCredit, out.TransactionCode, msg) diff --git a/pkg/response/file_transformer.go b/pkg/response/file_transformer.go index c4d597e4..add27f23 100644 --- a/pkg/response/file_transformer.go +++ b/pkg/response/file_transformer.go @@ -1,6 +1,7 @@ package response import ( + "context" "fmt" "math/rand" "path/filepath" @@ -10,6 +11,7 @@ import ( "github.com/moov-io/ach-test-harness/pkg/response/match" "github.com/moov-io/ach-test-harness/pkg/service" "github.com/moov-io/base/log" + "github.com/moov-io/base/telemetry" ) type FileTransfomer struct { @@ -37,7 +39,10 @@ func NewFileTransformer(logger log.Logger, cfg *service.Config, responses []serv return xform } -func (ft *FileTransfomer) Transform(file *ach.File) error { +func (ft *FileTransfomer) Transform(ctx context.Context, file *ach.File) error { + ctx, span := telemetry.StartSpan(ctx, "file-transform") + defer span.End() + // Track ach.File objects to write based on different delay durations, including a default of "0s" var outFiles = outFiles{} @@ -53,13 +58,13 @@ func (ft *FileTransfomer) Transform(file *ach.File) error { entries := file.Batches[i].GetEntries() for j := range entries { // Check if there's a matching Action and perform it. There may also be a future-dated action to execute. - copyAction, processAction := ft.Matcher.FindAction(entries[j]) + copyAction, processAction := ft.Matcher.FindAction(ctx, entries[j]) if copyAction != nil { // Save this Entry - mirror.saveEntry(&file.Batches[i], copyAction.Copy, entries[j]) + mirror.saveEntry(ctx, &file.Batches[i], copyAction.Copy, entries[j]) } if processAction != nil { - entry, err := ft.Entry.MorphEntry(file.Header, bh, entries[j], processAction) + entry, err := ft.Entry.MorphEntry(ctx, file.Header, bh, entries[j], processAction) if err != nil { return fmt.Errorf("transform batch[%d] morph entry[%d] error: %v", i, j, err) } @@ -103,7 +108,7 @@ func (ft *FileTransfomer) Transform(file *ach.File) error { } // Save off the entries as requested - if err := mirror.saveFiles(); err != nil { + if err := mirror.saveFiles(ctx); err != nil { return fmt.Errorf("problem saving entries: %v", err) } diff --git a/pkg/response/file_transformer_test.go b/pkg/response/file_transformer_test.go index 5e463968..9eca1e89 100644 --- a/pkg/response/file_transformer_test.go +++ b/pkg/response/file_transformer_test.go @@ -1,6 +1,7 @@ package response import ( + "context" "os" "path/filepath" "sort" @@ -94,7 +95,7 @@ func TestFileTransformer_NoMatch(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify no "returned" files created @@ -117,7 +118,7 @@ func TestFileTransformer_CopyOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify no "returned" files created @@ -148,7 +149,7 @@ func TestFileTransformer_CopyOnlyAndCopyOnly_SingleBatch(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify no "returned" files created @@ -184,7 +185,7 @@ func TestFileTransformer_CopyOnlyAndCopyOnly_MultipleBatches(t *testing.T) { require.Len(t, achIn.Batches, 3) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify no "returned" files created @@ -240,7 +241,7 @@ func TestFileTransformer_ReturnOnly(t *testing.T) { require.Equal(t, "10200002", achIn.Batches[0].GetEntries()[0].RDFIIdentification) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -299,7 +300,7 @@ func TestFileTransformer_CorrectionOnly(t *testing.T) { require.Equal(t, "10200002", achIn.Batches[0].GetEntries()[0].RDFIIdentification) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -353,7 +354,7 @@ func TestFileTransformer_ReturnOnlyAndCopyOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -406,7 +407,7 @@ func TestFileTransformer_CorrectionOnlyAndCopyOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -457,7 +458,7 @@ func TestFileTransformer_DelayReturnOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -498,7 +499,7 @@ func TestFileTransformer_DelayCorrectionOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -540,7 +541,7 @@ func TestFileTransformer_DelayReturnOnlyAndCopyOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -592,7 +593,7 @@ func TestFileTransformer_DelayCorrectionOnlyAndCopyOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -643,7 +644,7 @@ func TestFileTransformer_CopyAndDelayReturn(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -692,7 +693,7 @@ func TestFileTransformer_CopyAndDelayCorrection(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -741,7 +742,7 @@ func TestFileTransformer_CopyAndDelayReturnAndCopyOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -790,7 +791,7 @@ func TestFileTransformer_CopyAndDelayCorrectionAndCopyOnly(t *testing.T) { require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -844,7 +845,7 @@ func TestFileTransformer_DelayCorrectionOnlyAndDelayReturnOnly_sameDelay(t *test require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -903,7 +904,7 @@ func TestFileTransformer_DelayCorrectionOnlyAndDelayReturnOnly_differentDelay(t require.NotNil(t, achIn) // transform the file - err = fileTransformer.Transform(achIn) + err = fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) // verify the "returned" file created @@ -996,7 +997,7 @@ func TestFileTransformer_CTX(t *testing.T) { t.Run("return", func(t *testing.T) { // transform - err := fileTransformer.Transform(achIn) + err := fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) retdir := filepath.Join(dir, "returned") @@ -1021,7 +1022,7 @@ func TestFileTransformer_CTX(t *testing.T) { t.Run("correction", func(t *testing.T) { // transform - err := fileTransformer.Transform(achIn) + err := fileTransformer.Transform(context.Background(), achIn) require.NoError(t, err) retdir := filepath.Join(dir, "returned") diff --git a/pkg/response/ftp_watcher.go b/pkg/response/ftp_watcher.go index 8971bfb4..1f54bb94 100644 --- a/pkg/response/ftp_watcher.go +++ b/pkg/response/ftp_watcher.go @@ -1,11 +1,15 @@ package response import ( + "context" "fmt" "github.com/moov-io/ach" "github.com/moov-io/base/log" + "github.com/moov-io/base/telemetry" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ftp "goftp.io/server/core" ) @@ -35,6 +39,12 @@ type FTPWatcher struct { } func (notify *FTPWatcher) AfterFilePut(conn *ftp.Conn, dstPath string, size int64, err error) { + ctx, span := telemetry.StartSpan(context.Background(), "after-file-put", trace.WithAttributes( + attribute.String("ftp.destination", dstPath), + attribute.Int64("ftp.file_size_bytes", size), + )) + defer span.End() + notify.logger.Info().Log(fmt.Sprintf("accepting file at %s", dstPath)) if err != nil { @@ -58,13 +68,14 @@ func (notify *FTPWatcher) AfterFilePut(conn *ftp.Conn, dstPath string, size int6 file, err := reader.Read() if err != nil { + span.RecordError(err) notify.logger.Error().Log(fmt.Sprintf("ftp: error reading ACH file %s: %v", dstPath, err)) } if err := file.Create(); err != nil { notify.logger.Error().Log(fmt.Sprintf("ftp: error creating file %s: %v", dstPath, err)) } - if err := notify.transformer.Transform(&file); err != nil { + if err := notify.transformer.Transform(ctx, &file); err != nil { notify.logger.Error().Log(fmt.Sprintf("ftp: error transforming file %s: %v", dstPath, err)) } } diff --git a/pkg/response/match/matcher.go b/pkg/response/match/matcher.go index e06f8f6b..bdc25981 100644 --- a/pkg/response/match/matcher.go +++ b/pkg/response/match/matcher.go @@ -1,6 +1,7 @@ package match import ( + "context" "fmt" "strings" @@ -8,6 +9,10 @@ import ( "github.com/moov-io/ach-test-harness/internal/achx" "github.com/moov-io/ach-test-harness/pkg/service" "github.com/moov-io/base/log" + "github.com/moov-io/base/telemetry" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" ) type Matcher struct { @@ -28,7 +33,12 @@ func New(logger log.Logger, cfg service.Matching, responses []service.Response) } } -func (m Matcher) FindAction(ed *ach.EntryDetail) (copyAction *service.Action, processAction *service.Action) { +func (m Matcher) FindAction(ctx context.Context, ed *ach.EntryDetail) (copyAction *service.Action, processAction *service.Action) { + ctx, span := telemetry.StartSpan(ctx, "matcher-find-action", trace.WithAttributes( + attribute.String("entry.trace_number", ed.TraceNumber), + )) + defer span.End() + /* * See https://github.com/moov-io/ach-test-harness#config-schema for more details on how to configure. */ @@ -156,6 +166,11 @@ func (m Matcher) FindAction(ed *ach.EntryDetail) (copyAction *service.Action, pr if m.Debug { logger.Log(b.String()) } + span.AddEvent("match-results", trace.WithAttributes( + attribute.String("results", b.String()), + attribute.String("match.positive", strings.Join(positiveMatchers, ", ")), + attribute.String("match.negative", strings.Join(negativeMatchers, ", ")), + )) // Return the Action if we've still matched if negative == 0 && positive > 0 { diff --git a/pkg/response/match/matcher_test.go b/pkg/response/match/matcher_test.go index 600e2407..03b09639 100644 --- a/pkg/response/match/matcher_test.go +++ b/pkg/response/match/matcher_test.go @@ -1,6 +1,7 @@ package match import ( + "context" "path/filepath" "testing" "time" @@ -191,6 +192,8 @@ func TestMatchTraceNumber(t *testing.T) { // TransactionCode RDFIIdentification AccountNumber Amount Name TraceNumber Category // 22 27397636 273976369 100 Incorrect Name 273976367520469 func TestMultiMatch(t *testing.T) { + ctx := context.Background() + var delay, err = time.ParseDuration("12h") require.NoError(t, err) @@ -238,12 +241,12 @@ func TestMultiMatch(t *testing.T) { entries := file.Batches[0].GetEntries() // Find our Action - copyAction, processAction := matcher.FindAction(entries[0]) + copyAction, processAction := matcher.FindAction(ctx, entries[0]) require.Nil(t, copyAction) require.Nil(t, processAction) // Find our Action - copyAction, processAction = matcher.FindAction(entries[1]) + copyAction, processAction = matcher.FindAction(ctx, entries[1]) require.Nil(t, copyAction) require.Nil(t, processAction) }) @@ -266,12 +269,12 @@ func TestMultiMatch(t *testing.T) { entries := file.Batches[0].GetEntries() // Find our Action - copyAction, processAction := matcher.FindAction(entries[0]) + copyAction, processAction := matcher.FindAction(ctx, entries[0]) require.Nil(t, copyAction) require.Nil(t, processAction) // Find our Action - copyAction, processAction = matcher.FindAction(entries[1]) + copyAction, processAction = matcher.FindAction(ctx, entries[1]) require.NotNil(t, copyAction) require.Equal(t, actionCopy, *copyAction) require.Nil(t, processAction) @@ -295,12 +298,12 @@ func TestMultiMatch(t *testing.T) { entries := file.Batches[0].GetEntries() // Find our Action - copyAction, processAction := matcher.FindAction(entries[0]) + copyAction, processAction := matcher.FindAction(ctx, entries[0]) require.Nil(t, copyAction) require.Nil(t, processAction) // Find our Action - copyAction, processAction = matcher.FindAction(entries[1]) + copyAction, processAction = matcher.FindAction(ctx, entries[1]) require.Nil(t, copyAction) require.NotNil(t, processAction) require.Equal(t, actionReturn, *processAction) @@ -332,12 +335,12 @@ func TestMultiMatch(t *testing.T) { entries := file.Batches[0].GetEntries() // Find our Action - copyAction, processAction := matcher.FindAction(entries[0]) + copyAction, processAction := matcher.FindAction(ctx, entries[0]) require.Nil(t, copyAction) require.Nil(t, processAction) // Find our Action - copyAction, processAction = matcher.FindAction(entries[1]) + copyAction, processAction = matcher.FindAction(ctx, entries[1]) require.NotNil(t, copyAction) require.Equal(t, actionCopy, *copyAction) require.NotNil(t, processAction) diff --git a/pkg/service/environment.go b/pkg/service/environment.go index 3984b4e0..41623002 100644 --- a/pkg/service/environment.go +++ b/pkg/service/environment.go @@ -3,14 +3,17 @@ package service import ( - ftp "goftp.io/server/core" + "context" + "fmt" - "github.com/gorilla/mux" + achtestharness "github.com/moov-io/ach-test-harness" "github.com/moov-io/base/config" "github.com/moov-io/base/log" "github.com/moov-io/base/stime" + "github.com/moov-io/base/telemetry" - _ "github.com/moov-io/ach-test-harness" + "github.com/gorilla/mux" + ftp "goftp.io/server/core" ) // Environment - Contains everything thats been instantiated for this service. @@ -50,6 +53,16 @@ func NewEnvironment(env *Environment) (*Environment, error) { env.TimeService = stime.NewSystemTimeService() } + telemetryShutdownFunc, err := telemetry.SetupTelemetry(context.Background(), env.Config.Telemetry, achtestharness.Version) + if err != nil { + return env, fmt.Errorf("setting up telemetry failed: %w", err) + } + prev := env.Shutdown + env.Shutdown = func() { + prev() + telemetryShutdownFunc() + } + return env, nil } diff --git a/pkg/service/model_config.go b/pkg/service/model_config.go index 818799e3..f00cb214 100644 --- a/pkg/service/model_config.go +++ b/pkg/service/model_config.go @@ -9,6 +9,7 @@ import ( "github.com/moov-io/ach" "github.com/moov-io/base/log" + "github.com/moov-io/base/telemetry" ) type GlobalConfig struct { @@ -21,7 +22,9 @@ func (gc *GlobalConfig) Validate() error { // Config defines all the configuration for the app type Config struct { - Servers ServerConfig + Servers ServerConfig + Telemetry telemetry.Config + ValidateOpts *ach.ValidateOpts Matching Matching Responses []Response