ynab / ynab-sdk-ruby

YNAB API Client for Ruby
https://api.ynab.com
Apache License 2.0
65 stars 12 forks source link

Flag color validation fails on some existing transactions fetched through the API #81

Open davidstosik opened 1 month ago

davidstosik commented 1 month ago

This might have been caused by a change in data format, combined with new validations added in recent versions of the gem.

I'm trying to fetch all transactions in a budget using the API:

ynab_api = YNAB::API.new(YNAB_ACCESS_TOKEN)
budget = ynab_api.budgets.get_budgets.data.budgets.find { _1.name == YNAB_BUDGET_NAME }
transactions = ynab_api.transactions.get_transactions(budget.id)

And I get the following error:

.../ynab-3.4.0/lib/ynab/models/transaction_flag_color.rb:39:in `build_from_hash': Invalid ENUM value  for class #TransactionFlagColor (RuntimeError)
        from .../ynab-3.4.0/lib/ynab/models/transaction_flag_color.rb:31:in `build_from_hash'
        from .../ynab-3.4.0/lib/ynab/models/transaction_detail.rb:431:in `_deserialize'
(continued...) ``` from .../ynab-3.4.0/lib/ynab/models/transaction_detail.rb:386:in `block in build_from_hash' from .../ynab-3.4.0/lib/ynab/models/transaction_detail.rb:376:in `each_pair' from .../ynab-3.4.0/lib/ynab/models/transaction_detail.rb:376:in `build_from_hash' from .../ynab-3.4.0/lib/ynab/models/transactions_response_data.rb:171:in `_deserialize' from .../ynab-3.4.0/lib/ynab/models/transactions_response_data.rb:123:in `block (2 levels) in build_from_hash' from .../ynab-3.4.0/lib/ynab/models/transactions_response_data.rb:123:in `map' from .../ynab-3.4.0/lib/ynab/models/transactions_response_data.rb:123:in `block in build_from_hash' from .../ynab-3.4.0/lib/ynab/models/transactions_response_data.rb:116:in `each_pair' from .../ynab-3.4.0/lib/ynab/models/transactions_response_data.rb:116:in `build_from_hash' from .../ynab-3.4.0/lib/ynab/models/transactions_response.rb:158:in `_deserialize' from .../ynab-3.4.0/lib/ynab/models/transactions_response.rb:113:in `block in build_from_hash' from .../ynab-3.4.0/lib/ynab/models/transactions_response.rb:103:in `each_pair' from .../ynab-3.4.0/lib/ynab/models/transactions_response.rb:103:in `build_from_hash' from .../ynab-3.4.0/lib/ynab/api_client.rb:282:in `convert_to_type' from .../ynab-3.4.0/lib/ynab/api_client.rb:242:in `deserialize' from .../ynab-3.4.0/lib/ynab/api_client.rb:73:in `call_api' from .../ynab-3.4.0/lib/ynab/api/transactions_api.rb:296:in `get_transactions_with_http_info' from .../ynab-3.4.0/lib/ynab/api/transactions_api.rb:240:in `get_transactions' ```

I've also confirmed that the error started happening in the version 3.0.0 of the gem.

Hacking into the gem a little bit, I was able to find that my budget contains a transaction which flag is an empty string, when returned from the API, then I confirmed it by also querying the API directly:

$ curl --no-progress-meter -X 'GET' \
  'https://api.ynab.com/v1/budgets/0a08ffe7-0121-43c9-a9c6-7484e9138bd5/transactions/9aced28e-a240-47ff-b108-2d672872536c' \
  -H 'accept: application/json' \
  -H "Authorization: Bearer $YNAB_ACCESS_TOKEN" | \
jq '.data.transaction'
{
  "id": "9aced28e-a240-47ff-b108-2d672872536c",
  "date": "2022-11-15",
  "flag_color": "", // 💥
  "flag_name": null,
  "account_id": "48d5416c-2351-49ab-ab4e-1d367b9756c7",
  "category_id": "98a88345-80e8-4015-9f4c-8b53858c84b3",
  // ...
}

A transaction with an empty string for flag color is invalid, according to the OpenAPI spec:

https://github.com/ynab/ynab-sdk-ruby/blob/825a49af31fa8bd10ee7406fc40bf72955a1b406/open_api_spec.yaml#L3276-L3287

I can picture three ways to fix this problem:

  1. update the OpenAPI spec (and rebuild the gem), to mark the empty string as a valid value for flag_color
  2. update the YNAB API server implementation to return null values when it meets an empty string from the database
  3. run a migration on the YNAB user data to update all transactions with an invalid flag color

To me, the real fix would be number 3.

bradymholt commented 1 month ago

Thanks @davidstosik - I have a PR here to address this. I'd prefer to gracefully handle this situation in the client for now.