Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change RubyMoney bank to currencylayer #10520

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

danieljames-dj
Copy link
Member

The bank is changed so that all our currencies will be supported.

@danieljames-dj danieljames-dj force-pushed the currencylayer branch 3 times, most recently from 32be5f1 to cca9d8e Compare January 3, 2025 17:17
lib/dues_calculator.rb Show resolved Hide resolved
mclb.currencylayer = true
mclb.ttl_in_seconds = 86_400
mclb.cache = 'rails_money_cache.txt' # To be replaced with proper cache path.
mclb.update_rates
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would mean rates are only updated once per server boot. We sometimes go for days without redeploying, which in some cases may mean exchange rates are up to ~10% out of sync.

(See also my other comment about more dynamic updating below)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find a proper documentation on these part, but my best guess is that if ttl_in_seconds is set, whenever we ask the library to do an exchange, if the previous exchange was fetched before x seconds, it will invalidate the cache and fetch the newer rates. I feel so because if this is not how it works, it doesn't make sense to have ttl_in_seconds. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just verified from the library's source code that my guess is correct.

mclb.access_key = ENV.fetch("CURRENCY_LAYER_API_KEY", "")
mclb.currencylayer = true
mclb.ttl_in_seconds = 86_400
mclb.cache = 'rails_money_cache.txt' # To be replaced with proper cache path.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the gem only support file caching? Would love to use Redis for this instead

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I didn't knew we were having redis. I couldn't get to see anywhere officially how to use it.

I tried mclb.cache = :redis_cache_store, { url: EnvConfig.CACHE_REDIS_URL } and I am not sure how to test if it worked.

mclb = Money::Bank::CurrencylayerBank.new
mclb.access_key = ENV.fetch("CURRENCY_LAYER_API_KEY", "")
mclb.currencylayer = true
mclb.ttl_in_seconds = 86_400
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did this value come from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is number of seconds in 1 day, I just copy-pasted from the documentation. I thought 1 day will be a good ttl for this case.

Money.default_bank = eu_bank
mclb = Money::Bank::CurrencylayerBank.new
mclb.access_key = ENV.fetch("CURRENCY_LAYER_API_KEY", "")
mclb.currencylayer = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this flag do and why is it needed? From my perspective as an outside reviewer who hasn't done any research on CurrencyLayer, this line of code reads like "Oh dear CurrencyLayer gem, which gives me a class called CurrencylayerBank, please really really access CurrencyLayer by the way!".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's almost correct. Here's the link to documentation: LINK.

There are two endpoints - new (https://apilayer.com/marketplace/currency_data-api) and old (https://currencylayer.com/product). I created the access key in old, and to use old, I need to make it true. If I don't set this, it will point to new by default.

I'm fine with switching to new if needed, I don't have any personal preference. The only reason why I chose old is that it directly talks with currencylayer, while the new will first go to apilayer and then it goes to currencylayer and hence increases the latency a bit.

eu_bank = EuCentralBank.new
Money.default_bank = eu_bank
mclb = Money::Bank::CurrencylayerBank.new
mclb.access_key = ENV.fetch("CURRENCY_LAYER_API_KEY", "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be AppSecrets.CURRENCYLAYER_API_KEY instead. We have a top-level file called app_secrets.rb where you can see how we configure secrets access.

In dev and test, the values for these secrets are loaded from environment variables, which means placing it in .env.development and .env.test is correct. For production (and staging), you tell me the real access key through a (semi-)secure channel, and I insert it into a Vault runtime that feeds our production servers

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks. I've changed it.

@@ -35,3 +35,4 @@ STAGING_OAUTH_SECRET=example-secret
AVATARS_PUBLIC_STORAGE=local
AVATARS_PRIVATE_STORAGE=local_private
DUMP_HOST=https://assets.worldcubeassociation.org
CURRENCY_LAYER_API_KEY=9d254ec37e1591eec964dac947fafa09
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these keys (here in test and same in dev) random dummy values or are they actual access keys?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are actual access keys from my test account. I understand that exposing actual access keys is risky, but I went ahead since my test account is a free account and I don't mind even if the account gets deactivated.

The reason why I added actual access key is that if I give a dummy access key, it will throw error in CI/CD.

I tried if there is any way to fix this with a dummy access key, but couldn't find any way. Can you please let me know whether you faced this problem while initializing env variables for paypal or stripe or anything similar?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We actually use access keys from testing or dummy accounts for Stripe and PayPal. Look into .env.test, you will see that a lot of PAYPAL_* variables exist. These are from "real" accounts, which means we have actual username/password to log in to PayPal dashboard. But if you do actually log in, you will see that these accounts are marked as test accounts by PayPal, and you cannot send actual, real money with them. Only sandbox money. So that's why the accounts are not real, they are only "real".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I think currencylayer doesn't have a test account as it doesn't do any sensitive things. So will it make sense to create two currencylayer accounts for WCA - one for test and one for production?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, now that I think about it, I have another idea...
What would you think about using the if Rails.env.test? check to use the EuCentralBank in local environments and switch on the CurrencyLayer in production only?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can introduce a flag in EnvConfig (file env_config.rb) where developers can switch on CurrencyLayer manually if they want, for local testing and stuff

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a great idea, I've now implemented it. :-)

@danieljames-dj danieljames-dj force-pushed the currencylayer branch 3 times, most recently from 32309df to d9cffea Compare January 5, 2025 07:12
mclb.access_key = AppSecrets.CURRENCY_LAYER_API_KEY
mclb.currencylayer = true
mclb.ttl_in_seconds = 86_400
mclb.cache = proc.new do |payload|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be Proc.new (note the capitalization)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually rubocop asked to replace Proc.new with proc. After re-reading again, I realized it's proc and not proc.new. I cannot test it right now as my access key has expired. Once the budget is approved, I'll test again using a new test key created with WCA's account.

eu_bank = EuCentralBank.new
Money.default_bank = eu_bank
if Rails.env.test? || Rails.env.development?
eu_bank = EuCentralBank.new
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also update_rates the same way you're doing for the new CurrencyLayer bank below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, done.

Money.locale_backend = :i18n

eu_bank = EuCentralBank.new
Money.default_bank = eu_bank
if Rails.env.test? || Rails.env.development?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is Rails.env.local? which is exactly an equivalent of "test or dev"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's nice, updated.

Copy link
Member Author

@danieljames-dj danieljames-dj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @gregorbg for your review. I hope this is now fine codewise, let me know if not. I'll ping you once the budget is approved for the same so that I can test it in staging.

eu_bank = EuCentralBank.new
Money.default_bank = eu_bank
if Rails.env.test? || Rails.env.development?
eu_bank = EuCentralBank.new
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, done.

Money.locale_backend = :i18n

eu_bank = EuCentralBank.new
Money.default_bank = eu_bank
if Rails.env.test? || Rails.env.development?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's nice, updated.

mclb.access_key = AppSecrets.CURRENCY_LAYER_API_KEY
mclb.currencylayer = true
mclb.ttl_in_seconds = 86_400
mclb.cache = proc.new do |payload|
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually rubocop asked to replace Proc.new with proc. After re-reading again, I realized it's proc and not proc.new. I cannot test it right now as my access key has expired. Once the budget is approved, I'll test again using a new test key created with WCA's account.

Copy link
Member

@gregorbg gregorbg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code fine, pending valid API keys

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants