Skip to content

Commit

Permalink
fix: import error handling for broken CSV files, matchRuleId is null …
Browse files Browse the repository at this point in the history
…when there is no match
  • Loading branch information
morremeyer committed Dec 1, 2023
1 parent f1fec00 commit b3137a1
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 11 deletions.
32 changes: 31 additions & 1 deletion api/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6274,7 +6274,7 @@ const docTemplate = `{
"description": "List of transaction previews",
"type": "array",
"items": {
"$ref": "#/definitions/importer.TransactionPreview"
"$ref": "#/definitions/importer.TransactionPreviewV3"
}
},
"error": {
Expand Down Expand Up @@ -7089,6 +7089,36 @@ const docTemplate = `{
}
}
},
"importer.TransactionPreviewV3": {
"type": "object",
"properties": {
"destinationAccountName": {
"description": "Name of the destination account from the CSV file",
"type": "string",
"example": "Deutsche Bahn"
},
"duplicateTransactionIds": {
"description": "IDs of transactions that this transaction duplicates",
"type": "array",
"items": {
"type": "string"
}
},
"matchRuleId": {
"description": "ID of the match rule that was applied to this transaction preview",
"type": "string",
"example": "042d101d-f1de-4403-9295-59dc0ea58677"
},
"sourceAccountName": {
"description": "Name of the source account from the CSV file",
"type": "string",
"example": "Employer"
},
"transaction": {
"$ref": "#/definitions/models.TransactionCreate"
}
}
},
"models.AccountCreate": {
"type": "object",
"properties": {
Expand Down
32 changes: 31 additions & 1 deletion api/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -6263,7 +6263,7 @@
"description": "List of transaction previews",
"type": "array",
"items": {
"$ref": "#/definitions/importer.TransactionPreview"
"$ref": "#/definitions/importer.TransactionPreviewV3"
}
},
"error": {
Expand Down Expand Up @@ -7078,6 +7078,36 @@
}
}
},
"importer.TransactionPreviewV3": {
"type": "object",
"properties": {
"destinationAccountName": {
"description": "Name of the destination account from the CSV file",
"type": "string",
"example": "Deutsche Bahn"
},
"duplicateTransactionIds": {
"description": "IDs of transactions that this transaction duplicates",
"type": "array",
"items": {
"type": "string"
}
},
"matchRuleId": {
"description": "ID of the match rule that was applied to this transaction preview",
"type": "string",
"example": "042d101d-f1de-4403-9295-59dc0ea58677"
},
"sourceAccountName": {
"description": "Name of the source account from the CSV file",
"type": "string",
"example": "Employer"
},
"transaction": {
"$ref": "#/definitions/models.TransactionCreate"
}
}
},
"models.AccountCreate": {
"type": "object",
"properties": {
Expand Down
24 changes: 23 additions & 1 deletion api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ definitions:
data:
description: List of transaction previews
items:
$ref: '#/definitions/importer.TransactionPreview'
$ref: '#/definitions/importer.TransactionPreviewV3'
type: array
error:
description: The error, if any occurred for this Match Rule
Expand Down Expand Up @@ -1264,6 +1264,28 @@ definitions:
transaction:
$ref: '#/definitions/models.TransactionCreate'
type: object
importer.TransactionPreviewV3:
properties:
destinationAccountName:
description: Name of the destination account from the CSV file
example: Deutsche Bahn
type: string
duplicateTransactionIds:
description: IDs of transactions that this transaction duplicates
items:
type: string
type: array
matchRuleId:
description: ID of the match rule that was applied to this transaction preview
example: 042d101d-f1de-4403-9295-59dc0ea58677
type: string
sourceAccountName:
description: Name of the source account from the CSV file
example: Employer
type: string
transaction:
$ref: '#/definitions/models.TransactionCreate'
type: object
models.AccountCreate:
properties:
budgetId:
Expand Down
12 changes: 9 additions & 3 deletions pkg/controllers/import_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import (
)

type ImportPreviewListV3 struct {
Data []importer.TransactionPreview `json:"data"` // List of transaction previews
Error *string `json:"error" example:"the specified resource ID is not a valid UUID"` // The error, if any occurred for this Match Rule
Data []importer.TransactionPreviewV3 `json:"data"` // List of transaction previews
Error *string `json:"error" example:"the specified resource ID is not a valid UUID"` // The error, if any occurred for this Match Rule
}

// RegisterImportRoutes registers the routes for imports.
Expand Down Expand Up @@ -211,7 +211,13 @@ func (co Controller) ImportYnabImportPreviewV3(c *gin.Context) {
transactions[i] = transaction
}

c.JSON(http.StatusOK, ImportPreviewListV3{Data: transactions})
// We need to transform the responses for v3
v3Transactions := make([]importer.TransactionPreviewV3, 0, len(transactions))
for _, t := range transactions {
v3Transactions = append(v3Transactions, t.TransformV3())
}

c.JSON(http.StatusOK, ImportPreviewListV3{Data: v3Transactions})
}

// ImportYnab4 imports a YNAB 4 budget
Expand Down
8 changes: 3 additions & 5 deletions pkg/controllers/import_v3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,9 @@ func (suite *TestSuiteStandard) TestImportYnabImportPreviewV3Match() {
assert.Equal(t, tt.destinationAccountIDs[i], transaction.Transaction.DestinationAccountID, "destinationAccountID does not match in line %d", line)
}

assert.Equal(t, matchRuleIDs[i], transaction.MatchRuleID, "Expected match rule has match '%s', actual match rule has match '%s'", matchRuleIDs[i], transaction.MatchRuleID)

// This is kept for backwards compatibility and will be removed with API version 3
// https://github.com/envelope-zero/backend/issues/763
assert.Equal(t, matchRuleIDs[i], transaction.RenameRuleID, "Expected rename rule has match '%s', actual rename rule has match '%s'", matchRuleIDs[i], transaction.MatchRuleID)
if matchRuleIDs[i] != uuid.Nil {
assert.Equal(t, matchRuleIDs[i], *transaction.MatchRuleID, "Expected match rule has match '%s', actual match rule has match '%s'", matchRuleIDs[i], transaction.MatchRuleID)
}

assert.Equal(t, tt.envelopeIDs[i], transaction.Transaction.EnvelopeID, "proposed envelope ID does not match in line %d", line)
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/importer/parser/ynab-import/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func Parse(f io.Reader, account models.Account) ([]importer.TransactionPreview,
headerRow, err := reader.Read()
if err == io.EOF {
return []importer.TransactionPreview{}, nil
} else if err != nil {
return []importer.TransactionPreview{}, err

Check warning on line 32 in pkg/importer/parser/ynab-import/parse.go

View check run for this annotation

Codecov / codecov/patch

pkg/importer/parser/ynab-import/parse.go#L32

Added line #L32 was not covered by tests
}

// Build map for header keys
Expand Down
25 changes: 25 additions & 0 deletions pkg/importer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,28 @@ type TransactionPreview struct {

MatchRuleID uuid.UUID `json:"matchRuleId" example:"042d101d-f1de-4403-9295-59dc0ea58677"` // ID of the match rule that was applied to this transaction preview
}

// transformV3 transforms a TransactionPreview to a TransactionPreviewV3.
func (t TransactionPreview) TransformV3() TransactionPreviewV3 {
id := &t.MatchRuleID
if t.MatchRuleID == uuid.Nil {
id = nil
}

return TransactionPreviewV3{
Transaction: t.Transaction,
SourceAccountName: t.SourceAccountName,
DestinationAccountName: t.DestinationAccountName,
DuplicateTransactionIDs: t.DuplicateTransactionIDs,
MatchRuleID: id,
}
}

// TransactionPreviewV3 is used to preview transactions that will be imported to allow for editing.
type TransactionPreviewV3 struct {
Transaction models.TransactionCreate `json:"transaction"`
SourceAccountName string `json:"sourceAccountName" example:"Employer"` // Name of the source account from the CSV file
DestinationAccountName string `json:"destinationAccountName" example:"Deutsche Bahn"` // Name of the destination account from the CSV file
DuplicateTransactionIDs []uuid.UUID `json:"duplicateTransactionIds"` // IDs of transactions that this transaction duplicates
MatchRuleID *uuid.UUID `json:"matchRuleId" example:"042d101d-f1de-4403-9295-59dc0ea58677"` // ID of the match rule that was applied to this transaction preview
}

0 comments on commit b3137a1

Please sign in to comment.