Skip to content

Commit

Permalink
Fix exchange rate fetching
Browse files Browse the repository at this point in the history
  • Loading branch information
binary-koan committed Mar 2, 2024
1 parent ee853e2 commit 0957728
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 45 deletions.
3 changes: 2 additions & 1 deletion app/controllers/frontend_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class FrontendController < ApplicationController
def index
render html: File.read(Rails.root.join("public/index.html")).html_safe
path = Rails.env.production? ? "public/index.html" : "web/build/index.html"
render html: File.read(Rails.root.join(path)).html_safe
end
end
5 changes: 4 additions & 1 deletion app/models/category_budget.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ class CategoryBudget < ApplicationRecord
validates :budget_cents, numericality: { greater_than_or_equal_to: 0 }

scope :for_date_scope, ->(date) { order(date_from: :asc).where("date_from <= :date AND (date_to >= :date OR date_to IS NULL)", date: date) }
scope :for_date, ->(date) { for_date_scope(date).first }

def self.for_date(date)
for_date_scope(date).first
end

def budget
Money.new(amount_cents: budget_cents, currency:)
Expand Down
23 changes: 12 additions & 11 deletions app/services/exchange_rates/convert_money.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,27 @@ def call
private

def exchange_rates
@exchange_rates ||= existing_exchange_rates + sync_exchange_rates
@exchange_rates ||= existing_exchange_rates.merge(sync_exchange_rates)
end

def sync_exchange_rates
(needed_exchange_rates - existing_exchange_rates.keys).map do |date, from_currency|
ExchangeRates::Fetch.call(from_currency, date).find do |rate|
index_rates((needed_exchange_rates - existing_exchange_rates.keys).map do |date, from_currency|
ExchangeRates::Fetch.new(from_currency, date).call.find do |rate|
rate.to_currency == to_currency
end
end
end)
end

def existing_exchange_rates
@existing_exchange_rates ||= ExchangeRateValue.
@existing_exchange_rates ||= index_rates(ExchangeRateValue.
includes(:from_currency, :to_currency).
where(to_currency:, date: money_on_dates.compact.map(&:date).uniq).
flat_map do |rate|
rate.exchange_rate_values.map do |value|
{ [rate.date, value.from_currency] => value.rate }
end
end
where(to_currency:, date: money_on_dates.compact.map(&:date).uniq))
end

def index_rates(rates)
rates.to_h do |rate|
[[rate.date, rate.from_currency], rate.rate]
end
end

def needed_exchange_rates
Expand Down
12 changes: 7 additions & 5 deletions app/services/exchange_rates/fetch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,31 @@ def initialize(from_currency, date)
end

def call
response = fetch_exchange_rate(date) || fetch_exchange_rate("latest") || raise(FetchFailedError, "Failed to fetch exchange rate")
response = fetch_exchange_rate(formatted_date) || fetch_exchange_rate("latest") || raise(FetchFailedError, "Failed to fetch exchange rate")
return if response.nil?

save_exchange_rates(response)
end

private

def formatted_date
date.strftime("%Y.%-m.%-d")
end

def save_exchange_rates(response)
Currency.all.map do |currency|
next if currency == from_currency

ExchangeRateValue.create!(
from_currency:,
to_currency: currency,
date:,
rate: response[from_currency.code.downcase][currency.code.downcase]
rate: currency == from_currency ? 1 : response[from_currency.code.downcase][currency.code.downcase]
)
end
end

def fetch_exchange_rate(date)
response = Faraday.get("https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/#{date}/currencies/#{from_currency.code.downcase}.json")
response = Faraday.get("https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@#{date}/v1/currencies/#{from_currency.code.downcase}.json")
return if response.status != 200

JSON.parse(response.body)
Expand Down
6 changes: 6 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,10 @@

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true

Rails.application.config.middleware.insert_after(
ActionDispatch::Static,
ActionDispatch::Static,
Rails.root.join("web/build").to_s
)
end
21 changes: 13 additions & 8 deletions db/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
decimal_digits: 2
)

gbp = Currency.find_by(code: "GBP") || Currency.create!(
code: "GBP",
name: "Pounds Sterling",
symbol: "£",
decimal_digits: 2
)

Category.find_by(name: "First") || Category.create!(
name: "First",
color: "red",
Expand All @@ -31,11 +38,9 @@

account = Account.find_by(name: "Test") || Account.create!(name: "Test", currency: usd)

(ENV["USER_EMAILS"]&.split(",") || []).each do |email|
User.find_or_initialize_by(email:) || User.create!(
email:,
password: "changeme",
default_currency: usd,
default_account: account
)
end
User.find_by(email: "[email protected]") || User.create!(
email: "[email protected]",
password: "changeme",
default_currency: usd,
default_account: account
)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"prettier-plugin-tailwindcss": "^0.1.13"
},
"scripts": {
"dev": "concurrently -n api,web,api-codegen,web-codegen -c blue,magenta,green,yellow 'cd api && bun run --watch bin/dev.ts' 'cd web && bun run watch' 'cd api && bun graphql-codegen --watch' 'cd web && bun graphql-codegen --watch'",
"dev": "concurrently -n api,web,codegen -c blue,magenta,yellow 'rails s' 'cd web && bun run watch' 'cd web && bun graphql-codegen --watch'",
"dev:test": "cd api && ENV_TYPE=test PORT=4445 bun run bin/dev-test.ts",
"deploy": "bun run deploy.js",
"typecheck": "web && bun typecheck",
Expand Down
19 changes: 6 additions & 13 deletions web/src/components/transactions/NewTransactionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ export const NewTransactionModal: Component<{
}
})

let memoInput: HTMLInputElement | undefined
let shopInput: HTMLInputElement | undefined

createEffect(() => {
if (isDateSelected()) {
memoInput?.focus()
shopInput?.focus()
}
})

Expand All @@ -99,8 +99,7 @@ export const NewTransactionModal: Component<{
: amountType === "expense"
? -integerAmount
: integerAmount,
date: stripTime(new Date(date)),
includeInReports: Boolean(data.includeInReports)
date: stripTime(new Date(date))
}

if (shopCurrencyId && shopAmount) {
Expand Down Expand Up @@ -149,7 +148,7 @@ export const NewTransactionModal: Component<{
<>
<Show when={!splittingTransaction()}>
<Modal isOpen={props.isOpen} onClickOutside={props.onClose}>
<ModalContent class="flex h-[28rem] flex-col">
<ModalContent class="flex h-[31rem] flex-col">
<ModalTitle>
New Transaction
<ModalCloseButton onClick={props.onClose} />
Expand All @@ -170,7 +169,7 @@ export const NewTransactionModal: Component<{
<FormInput
placeholderLabel={true}
of={form}
ref={memoInput}
ref={shopInput}
label="Where?"
name="shop"
onBlur={(e) => {
Expand All @@ -189,13 +188,7 @@ export const NewTransactionModal: Component<{
}}
/>

<FormInput
placeholderLabel={true}
of={form}
ref={memoInput}
label="What?"
name="memo"
/>
<FormInput placeholderLabel={true} of={form} label="What?" name="memo" />

<Show
when={getValue(form, "shopCurrencyId")}
Expand Down
5 changes: 3 additions & 2 deletions web/src/components/transactions/TransactionActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,13 @@ export const TransactionActions: Component<{ transaction: ListingTransactionFrag
</DropdownMenuItem>

<DropdownMenuItem
onClick={() =>
onClick={() => {
updateTransaction({
id: props.transaction.id,
input: { includeInReports: !props.transaction.includeInReports }
})
}
onToggle(false)
}}
>
{props.transaction.includeInReports ? (
<>
Expand Down
8 changes: 5 additions & 3 deletions web/src/utils/graphqlClient/requestGraphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const requestGraphql = async <Result>(
return data
}

const maxRetries = 10
const maxRetries = 2

const fetchWithRetry = async (fetch: () => Promise<Response>, retries = 0): Promise<Response> => {
const response = await fetch()
Expand All @@ -51,8 +51,10 @@ const fetchWithRetry = async (fetch: () => Promise<Response>, retries = 0): Prom
}

if (retries < maxRetries) {
return fetchWithRetry(fetch, retries + 1)
return new Promise((resolve) => setTimeout(resolve, 2000)).then(() =>
fetchWithRetry(fetch, retries + 1)
)
}

throw new Error("Still getting 50x errors after max retries exceeded. Plesae try again later.")
throw new Error("Still getting 50x errors after max retries exceeded. Please try again later.")
}

0 comments on commit 0957728

Please sign in to comment.