Skip to content

Commit a4be8ee

Browse files
authored
Merge pull request #6 from thedavidweng/feat/store-perf
2 parents 3a45ad9 + 2157fb3 commit a4be8ee

14 files changed

Lines changed: 738 additions & 128 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jobs:
2020
go-version-file: 'go.mod'
2121
cache: true
2222

23+
- run: test -z "$(gofmt -l .)"
2324
- run: go vet ./...
2425
- run: go test ./... -race
2526
- run: go build ./...

CONTRIBUTING.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ go mod download
2828
### Build and Test
2929

3030
```bash
31+
# Format check (CI will fail if any files are unformatted)
32+
test -z "$(gofmt -l .)"
33+
3134
# Static analysis
3235
go vet ./...
3336

@@ -41,7 +44,7 @@ go test ./... -cover
4144
go build ./cmd/money
4245
```
4346

44-
CI runs `go vet`, `go test -race`, and `go build` on both `ubuntu-latest` and `macos-latest` for every push and pull request.
47+
CI runs `gofmt -l`, `go vet`, `go test -race`, and `go build` on both `ubuntu-latest` and `macos-latest` for every push and pull request.
4548

4649
### Release Workflow
4750

internal/cli/cli.go

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ If this is your first time, run:
105105
This creates your config, encryption key, and database. After setup, you can
106106
link financial institutions and sync transactions locally.
107107
`,
108-
Version: fmt.Sprintf("%s (commit %s)", Version, Commit),
108+
Version: fmt.Sprintf("%s (commit %s)", Version, Commit),
109109
Example: ` money setup
110110
money link "Chase"
111111
money sync
@@ -473,8 +473,8 @@ func newAccountsCommand(ctx context.Context, state *runtimeState, stdout io.Writ
473473
Example: " money accounts list\n money accounts list --verbose --json\n money accounts create-manual --name Savings --type depository --balance 1000.00 --currency USD --confirm",
474474
}
475475
listCmd := &cobra.Command{
476-
Use: "list",
477-
Short: "List accounts",
476+
Use: "list",
477+
Short: "List accounts",
478478
Example: " money accounts list\n money accounts list --verbose\n money accounts list --json",
479479
RunE: func(cmd *cobra.Command, args []string) error {
480480
activeStore, err := requireStore(state)
@@ -625,7 +625,7 @@ func newTransactionsCommand(ctx context.Context, state *runtimeState, stdout io.
625625
Use: "search <query>",
626626
Short: "Search transactions",
627627
Example: " money transactions search \"whole foods\"\n money transactions search uber --limit 10",
628-
Args: cobra.ExactArgs(1),
628+
Args: cobra.ExactArgs(1),
629629
RunE: func(cmd *cobra.Command, args []string) error {
630630
activeStore, err := requireStore(state)
631631
if err != nil {
@@ -727,8 +727,8 @@ func newCategoriesCommand(ctx context.Context, state *runtimeState, stdout io.Wr
727727
Example: " money categories list\n money categories list --verbose --json",
728728
}
729729
listCmd := &cobra.Command{
730-
Use: "list",
731-
Short: "List categories",
730+
Use: "list",
731+
Short: "List categories",
732732
Example: " money categories list\n money categories list --verbose",
733733
RunE: func(cmd *cobra.Command, args []string) error {
734734
activeStore, err := requireStore(state)
@@ -1038,16 +1038,16 @@ func newImportCommand(ctx context.Context, state *runtimeState, stdout io.Writer
10381038
Short: "Import accounts and transactions from " + sourceName,
10391039
Args: cobra.ExactArgs(1),
10401040
RunE: func(cmd *cobra.Command, args []string) error {
1041-
if state.json && !dryRun && !confirm {
1042-
return cliError{
1043-
command: "import." + sourceName,
1044-
code: "CONFIRMATION_REQUIRED",
1045-
message: "JSON import writes require --dry-run or --confirm",
1046-
category: contracts.CategoryValidation,
1047-
retryable: false,
1048-
exitCode: 2,
1041+
if state.json && !dryRun && !confirm {
1042+
return cliError{
1043+
command: "import." + sourceName,
1044+
code: "CONFIRMATION_REQUIRED",
1045+
message: "JSON import writes require --dry-run or --confirm",
1046+
category: contracts.CategoryValidation,
1047+
retryable: false,
1048+
exitCode: 2,
1049+
}
10491050
}
1050-
}
10511051
if batchID == "" {
10521052
batchID = time.Now().UTC().Format("20060102T150405Z")
10531053
}
@@ -1587,7 +1587,6 @@ func newPlaidLogoutCommand(state *runtimeState, stdout io.Writer, commandName st
15871587
return cmd
15881588
}
15891589

1590-
15911590
type providerAvailabilityRow struct {
15921591
Provider string
15931592
Status string

internal/cli/cli_link.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,4 +502,3 @@ func selectLinkInstitution(institutions []providers.Institution, institutionID s
502502
}
503503
return institutions[0], nil
504504
}
505-

internal/cli/link_flow_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,8 @@ func TestRunPlaidLinkFlowReturnsCLIErrorOnCancel(t *testing.T) {
348348
return fakeLinkSessionServer{
349349
url: "http://127.0.0.1:4000",
350350
callback: providers.LinkCallback{
351-
State: state,
352-
Status: "cancel",
351+
State: state,
352+
Status: "cancel",
353353
Metadata: providers.LinkMetadata{LinkSessionID: "sess-cancel"},
354354
},
355355
}, nil
@@ -396,9 +396,9 @@ func TestRunPlaidLinkFlowReturnsCLIErrorOnLinkError(t *testing.T) {
396396
return fakeLinkSessionServer{
397397
url: "http://127.0.0.1:4000",
398398
callback: providers.LinkCallback{
399-
State: state,
400-
Status: "error",
401-
Error: providers.LinkError{Type: "INSTITUTION_ERROR", Code: "INSUFFICIENT_CREDENTIALS", Message: "user entered invalid credentials"},
399+
State: state,
400+
Status: "error",
401+
Error: providers.LinkError{Type: "INSTITUTION_ERROR", Code: "INSUFFICIENT_CREDENTIALS", Message: "user entered invalid credentials"},
402402
Metadata: providers.LinkMetadata{RequestID: "req-123", LinkSessionID: "sess-456"},
403403
},
404404
}, nil

internal/importsource/monarch.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import (
1414

1515
// MonarchImporter reads Monarch JSON exports and maps them to canonical records.
1616
// Expected JSON shape:
17-
// {
18-
// "accounts": [...],
19-
// "transactions": [...]
20-
// }
17+
//
18+
// {
19+
// "accounts": [...],
20+
// "transactions": [...]
21+
// }
22+
//
2123
// Each account has: id, name, type, subtype, balance, currency.
2224
// Each transaction has: id, account_id, date, amount, name, merchant_name, category, pending.
2325
type MonarchImporter struct{}

internal/plaidlogin/callback_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ func TestCallbackHandlerAcceptsValidCodeOnce(t *testing.T) {
4343

4444
func TestCallbackHandlerRejectsOAuthErrorMissingCodeWrongStateAndWrongMethod(t *testing.T) {
4545
tests := []struct {
46-
name string
47-
method string
48-
target string
49-
status int
46+
name string
47+
method string
48+
target string
49+
status int
5050
wantWaitErr bool // true if Wait should return immediately with an error
5151
}{
5252
{name: "oauth error", method: http.MethodGet, target: "/oauth/callback?error=access_denied&state=state-ok", status: http.StatusBadRequest, wantWaitErr: true},

internal/providers/provider.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ import (
1010
// so that provider adapters can use them without importing core directly.
1111
// The store package imports core directly — it no longer imports providers.
1212
type (
13-
Institution = core.Institution
14-
ProviderItem = core.ProviderItem
15-
FinancialAccount = core.FinancialAccount
16-
Transaction = core.ProviderTransaction
17-
Recurring = core.ProviderRecurring
18-
SyncRun = core.SyncRun
19-
SyncResult = core.SyncResult
20-
InvestmentHolding = core.InvestmentHolding
21-
InvestmentSecurity = core.InvestmentSecurity
22-
Liability = core.Liability
23-
SyncSink = core.SyncSink
13+
Institution = core.Institution
14+
ProviderItem = core.ProviderItem
15+
FinancialAccount = core.FinancialAccount
16+
Transaction = core.ProviderTransaction
17+
Recurring = core.ProviderRecurring
18+
SyncRun = core.SyncRun
19+
SyncResult = core.SyncResult
20+
InvestmentHolding = core.InvestmentHolding
21+
InvestmentSecurity = core.InvestmentSecurity
22+
Liability = core.Liability
23+
SyncSink = core.SyncSink
2424
)
2525

2626
type Provider interface {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Composite index for CashflowSummary: filters by removed, pending, currency, then range on date
2+
CREATE INDEX IF NOT EXISTS idx_transactions_cashflow ON transactions (removed, pending, currency, date);
3+
4+
-- Composite index for filtered ListTransactions by account
5+
CREATE INDEX IF NOT EXISTS idx_transactions_account_removed_date ON transactions (removed, account_id, date DESC);

0 commit comments

Comments
 (0)