diff --git a/app/init.go b/app/init.go index e23c876..267b617 100644 --- a/app/init.go +++ b/app/init.go @@ -60,7 +60,7 @@ func initImporter(cfg config.Config, cl *mono.Client, a *account.Repository, t * gen := interval.NewGenerator(t) acIm := account.NewImporter(cl, a) txIm := transaction.NewImporter(cl, t, gen) - globalIm := importer.NewImporter(acIm, txIm) + globalIm := importer.NewImporter(txIm, acIm) return importer.NewWorker(globalIm, cfg.MonoAccountID) } diff --git a/importer/importer.go b/importer/importer.go index 5af8f34..41cffe8 100644 --- a/importer/importer.go +++ b/importer/importer.go @@ -2,6 +2,7 @@ package importer import ( "context" + "time" "github.com/rs/zerolog/log" ) @@ -15,7 +16,7 @@ type AccountImporter interface { // TransactionsImporter abstracts transactions import logic implementation. type TransactionsImporter interface { // Import loads transactions in specified interval for specified accountID and saves them to storage. - Import(ctx context.Context, accountID string) error + Import(ctx context.Context, accountID string) (time.Time, time.Time, error) } // Importer loads latest data from bank for specified accountID. @@ -32,17 +33,25 @@ func NewImporter(transactions TransactionsImporter, accounts AccountImporter) *I } } -// Import latest data from bank for specified accountID. +// Import the latest data from bank for specified accountID. func (i *Importer) Import(ctx context.Context, accountID string) { err := i.accounts.Import(ctx, accountID) if err != nil { - log.Err(err).Msg("failed import") + log.Err(err).Msg("failed account import") return } - err = i.transactions.Import(ctx, accountID) + from, to, err := i.transactions.Import(ctx, accountID) if err != nil { - log.Err(err).Msg("failed import") + log.Err(err). + Time("from", from). + Time("to", to). + Msg("failed transactions import") return } + + log.Debug(). + Time("from", from). + Time("to", to). + Msg("import finished") } diff --git a/importer/importer_test.go b/importer/importer_test.go index df69ee9..eee32af 100644 --- a/importer/importer_test.go +++ b/importer/importer_test.go @@ -4,6 +4,7 @@ import ( "context" "io/ioutil" "testing" + "time" "github.com/lungria/spendshelf-backend/importer" "github.com/lungria/spendshelf-backend/importer/mock" @@ -18,8 +19,8 @@ func TestImport_WhenCalled_ImportsAccountsAndTransactions(t *testing.T) { return nil } transactions := &mock.TransactionsImporterMock{} - transactions.ImportFunc = func(ctx context.Context, accountID string) error { - return nil + transactions.ImportFunc = func(ctx context.Context, accountID string) (time.Time, time.Time, error) { + return time.Time{}, time.Time{}, nil } log.Logger = zerolog.New(ioutil.Discard) svc := importer.NewImporter(transactions, accounts) diff --git a/importer/mock/mock.go b/importer/mock/mock.go index 19c5fa2..8032622 100644 --- a/importer/mock/mock.go +++ b/importer/mock/mock.go @@ -5,33 +5,33 @@ package mock import ( "context" - "sync" - "github.com/lungria/spendshelf-backend/importer" + "sync" + "time" ) -// Ensure, that AccountImporterMock does implement AccountImporter. +// Ensure, that TransactionsImporterMock does implement TransactionsImporter. // If this is not the case, regenerate this file with moq. -var _ importer.AccountImporter = &AccountImporterMock{} +var _ importer.TransactionsImporter = &TransactionsImporterMock{} -// AccountImporterMock is a mock implementation of AccountImporter. +// TransactionsImporterMock is a mock implementation of TransactionsImporter. // -// func TestSomethingThatUsesAccountImporter(t *testing.T) { +// func TestSomethingThatUsesTransactionsImporter(t *testing.T) { // -// // make and configure a mocked AccountImporter -// mockedAccountImporter := &AccountImporterMock{ -// ImportFunc: func(ctx context.Context, accountID string) error { -// panic("mock out the Import method") -// }, -// } +// // make and configure a mocked TransactionsImporter +// mockedTransactionsImporter := &TransactionsImporterMock{ +// ImportFunc: func(ctx context.Context, accountID string) (time.Time, time.Time, error) { +// panic("mock out the Import method") +// }, +// } // -// // use mockedAccountImporter in code that requires AccountImporter -// // and then make assertions. +// // use mockedTransactionsImporter in code that requires TransactionsImporter +// // and then make assertions. // -// } -type AccountImporterMock struct { +// } +type TransactionsImporterMock struct { // ImportFunc mocks the Import method. - ImportFunc func(ctx context.Context, accountID string) error + ImportFunc func(ctx context.Context, accountID string) (time.Time, time.Time, error) // calls tracks calls to the methods. calls struct { @@ -47,9 +47,9 @@ type AccountImporterMock struct { } // Import calls ImportFunc. -func (mock *AccountImporterMock) Import(ctx context.Context, accountID string) error { +func (mock *TransactionsImporterMock) Import(ctx context.Context, accountID string) (time.Time, time.Time, error) { if mock.ImportFunc == nil { - panic("AccountImporterMock.ImportFunc: method is nil but AccountImporter.Import was just called") + panic("TransactionsImporterMock.ImportFunc: method is nil but TransactionsImporter.Import was just called") } callInfo := struct { Ctx context.Context @@ -66,8 +66,8 @@ func (mock *AccountImporterMock) Import(ctx context.Context, accountID string) e // ImportCalls gets all the calls that were made to Import. // Check the length with: -// len(mockedAccountImporter.ImportCalls()) -func (mock *AccountImporterMock) ImportCalls() []struct { +// len(mockedTransactionsImporter.ImportCalls()) +func (mock *TransactionsImporterMock) ImportCalls() []struct { Ctx context.Context AccountID string } { @@ -81,26 +81,26 @@ func (mock *AccountImporterMock) ImportCalls() []struct { return calls } -// Ensure, that TransactionsImporterMock does implement TransactionsImporter. +// Ensure, that AccountImporterMock does implement AccountImporter. // If this is not the case, regenerate this file with moq. -var _ importer.TransactionsImporter = &TransactionsImporterMock{} +var _ importer.AccountImporter = &AccountImporterMock{} -// TransactionsImporterMock is a mock implementation of TransactionsImporter. +// AccountImporterMock is a mock implementation of AccountImporter. // -// func TestSomethingThatUsesTransactionsImporter(t *testing.T) { +// func TestSomethingThatUsesAccountImporter(t *testing.T) { // -// // make and configure a mocked TransactionsImporter -// mockedTransactionsImporter := &TransactionsImporterMock{ -// ImportFunc: func(ctx context.Context, accountID string) error { -// panic("mock out the Import method") -// }, -// } +// // make and configure a mocked AccountImporter +// mockedAccountImporter := &AccountImporterMock{ +// ImportFunc: func(ctx context.Context, accountID string) error { +// panic("mock out the Import method") +// }, +// } // -// // use mockedTransactionsImporter in code that requires TransactionsImporter -// // and then make assertions. +// // use mockedAccountImporter in code that requires AccountImporter +// // and then make assertions. // -// } -type TransactionsImporterMock struct { +// } +type AccountImporterMock struct { // ImportFunc mocks the Import method. ImportFunc func(ctx context.Context, accountID string) error @@ -118,9 +118,9 @@ type TransactionsImporterMock struct { } // Import calls ImportFunc. -func (mock *TransactionsImporterMock) Import(ctx context.Context, accountID string) error { +func (mock *AccountImporterMock) Import(ctx context.Context, accountID string) error { if mock.ImportFunc == nil { - panic("TransactionsImporterMock.ImportFunc: method is nil but TransactionsImporter.Import was just called") + panic("AccountImporterMock.ImportFunc: method is nil but AccountImporter.Import was just called") } callInfo := struct { Ctx context.Context @@ -137,8 +137,8 @@ func (mock *TransactionsImporterMock) Import(ctx context.Context, accountID stri // ImportCalls gets all the calls that were made to Import. // Check the length with: -// len(mockedTransactionsImporter.ImportCalls()) -func (mock *TransactionsImporterMock) ImportCalls() []struct { +// len(mockedAccountImporter.ImportCalls()) +func (mock *AccountImporterMock) ImportCalls() []struct { Ctx context.Context AccountID string } { diff --git a/importer/worker.go b/importer/worker.go index d1be95e..3a05463 100644 --- a/importer/worker.go +++ b/importer/worker.go @@ -4,8 +4,6 @@ import ( "context" "sync" "time" - - "github.com/rs/zerolog/log" ) const ( @@ -40,7 +38,6 @@ func (w *Worker) Start() { select { case _ = <-ticker.C: w.executeWithTimeout(w.ctx) - log.Debug().Msg("import finished") case _ = <-w.ctx.Done(): return } diff --git a/transaction/importer.go b/transaction/importer.go index acd8eaf..0f9515c 100644 --- a/transaction/importer.go +++ b/transaction/importer.go @@ -52,10 +52,10 @@ func NewImporter( } // Import loads transactions in specified interval for specified accountID and saves them to storage. -func (i *Importer) Import(ctx context.Context, accountID string) error { +func (i *Importer) Import(ctx context.Context, accountID string) (time.Time, time.Time, error) { from, to, err := i.intervalGen.GetInterval(ctx, accountID) if err != nil { - return fmt.Errorf("failed import transaction for account '%s': %w", accountID, err) + return i.resultWithErr(fmt.Errorf("failed import transaction for account '%s': %w", accountID, err)) } query := mono.GetTransactionsQuery{ @@ -66,21 +66,25 @@ func (i *Importer) Import(ctx context.Context, accountID string) error { monoTransactions, err := i.api.GetTransactions(ctx, query) if err != nil { - return fmt.Errorf("failed import transaction for account '%s': %w", accountID, err) + return i.resultWithErr(fmt.Errorf("failed import transaction for account '%s': %w", accountID, err)) } if len(monoTransactions) == 0 { - return nil + return from, to, nil } transactions := i.mapTransactions(accountID, monoTransactions) err = i.transactions.Save(ctx, transactions) if err != nil { - return fmt.Errorf("failed import transaction for account '%s': %w", accountID, err) + return i.resultWithErr(fmt.Errorf("failed import transaction for account '%s': %w", accountID, err)) } - return nil + return from, to, nil +} + +func (i *Importer) resultWithErr(err error) (time.Time, time.Time, error) { + return time.Time{}, time.Time{}, err } func (i *Importer) mapTransactions(accountID string, src []mono.Transaction) []Transaction { diff --git a/transaction/importer_test.go b/transaction/importer_test.go index b0b6ec5..82bdb90 100644 --- a/transaction/importer_test.go +++ b/transaction/importer_test.go @@ -22,7 +22,7 @@ func TestImport_WhenGetIntervalFails_ReturnsError(t *testing.T) { } svc := transaction.NewImporter(api, storage, gen) - err := svc.Import(context.Background(), "acc") + _, _, err := svc.Import(context.Background(), "acc") assert.True(t, errors.Is(err, testError)) assert.Zero(t, len(storage.SaveCalls())) @@ -43,7 +43,7 @@ func TestImport_WhenApiGetTransactionsFails_ReturnsError(t *testing.T) { } svc := transaction.NewImporter(api, storage, gen) - err := svc.Import(context.Background(), "acc") + _, _, err := svc.Import(context.Background(), "acc") assert.True(t, errors.Is(err, testError)) assert.Zero(t, len(storage.SaveCalls())) @@ -63,7 +63,7 @@ func TestImport_WhenApiGetTransactionsReturnsNothing_StorageNotCalled(t *testing } svc := transaction.NewImporter(api, storage, gen) - err := svc.Import(context.Background(), "acc") + _, _, err := svc.Import(context.Background(), "acc") assert.Nil(t, err) assert.Equal(t, 0, len(storage.SaveCalls())) @@ -93,7 +93,7 @@ func TestImport_WhenStorageSaveReturnsError_ReturnsError(t *testing.T) { } svc := transaction.NewImporter(api, db, gen) - err := svc.Import(context.Background(), "acc") + _, _, err := svc.Import(context.Background(), "acc") saveCalls := db.SaveCalls() assert.Error(t, err) @@ -124,7 +124,7 @@ func TestImport_WhenDataIsSaved_ReturnsNil(t *testing.T) { } svc := transaction.NewImporter(api, db, gen) - err := svc.Import(context.Background(), "acc") + _, _, err := svc.Import(context.Background(), "acc") saveCalls := db.SaveCalls() assert.Nil(t, err) diff --git a/transaction/interval/interval.go b/transaction/interval/interval.go index c104444..c2b9c07 100644 --- a/transaction/interval/interval.go +++ b/transaction/interval/interval.go @@ -49,7 +49,7 @@ func (gen *Generator) GetInterval(ctx context.Context, accountID string) (from, diffSecs := nowUtc.Sub(lastKnownTransactionDate.UTC()).Seconds() if diffSecs > maxAllowedIntervalDuration { - return lastKnownTransactionDate, lastKnownTransactionDate.Add(maxAllowedIntervalDuration / 2), nil + return lastKnownTransactionDate, lastKnownTransactionDate.Add(time.Second * maxAllowedIntervalDuration / 2), nil } return lastKnownTransactionDate.UTC(), nowUtc, nil