The Currencies is a microservice, which uses gRPC technology. It supports both unary and bidirectional streaming calls, which allows data update every 6 seconds. The service provides the latest market exchange rates of all currencies from all country. When an error occurs, it handles it in a non-fatal way with an error message.
The whole service is containerized using a Docker engine and the service can be easily launched and deployed with the pre-prepared make
commands in the Makefile.
The Currencies obtains all necessary data from the Business Insider website. The algorithm does not infringe any copyrights nor the website robots exclusion protocol.
The installation process on the Windows machines would be slightly different.
$ git clone https://github.com/chutommy/currencies.git # download repository
$ cd currencies # move to repository dir
$ make build # build docker image
$ make run # initialize service
BTG | KZT | GMD | NMR | PEN | ARDR | DCR | FUN | REQ | IRR |
QSP | ANT | CVE | UAH | MCO | PYG | BTM | DOP | NGN | QTUM |
SDG | XRP | CVC | ICX | LBC | VTC | XVG | COP | CZK | SCR |
GNO | KMF | NXT | IDR | PAB | BSV | BTS | OMG | PPT | BIF |
MKD | XEM | HRK | NET | UYU | INR | MAD | RLC | HKD | LAK |
BCC | BZD | EUR | GBP | MYR | BTC | GIP | LRC | NEO | NULS |
STX | AFN | DATA | ETH | CND | GRID | LKR | TNT | ADA | DRGN |
LINK | PKR | PLN | RWF | SYS | CNY | SNT | WTC | XMR | ETC |
KHR | LRD | LTC | LYD | SOS | THB | MMK | USD | NPR | AED |
ENJ | GTQ | AION | CNX | XAF | BGN | DTR | MONA | RCN | BAT |
BOB | TOP | UTK | BYN | GNT | TJS | WAVES | KCS | LEND | MGA |
REP | ZRX | ARK | ISK | MANA | STORJ | GBYTE | KMD | NIO | HTG |
SZL | AUD | EOS | LA | TWD | DZD | SVC | JPY | KNC | MAID |
PIVX | SGD | USDT | CNH | ELF | GRS | MXN | ALL | FCT | KES |
SBD | ZEN | EDO | BNB | NZD | RDD | XZC | ENG | ILS | ITC |
TMT | BCN | MIOTA | RUB | TZS | BRL | BSD | DNA | ZMW | BAY |
NAS | SLS | XLM | MVR | MWK | NOK | SEK | STRAT | MTL | CLP |
DASH | HNL | TAAS | TRX | JMD | KRW | MOP | SAR | VND | ZEC |
BNT | DKK | ETB | OMR | TTD | XWC | EGP | KIN | MLN | NAD |
PHP | TNB | ZAR | CAD | CHF | MUR | YER | ARS | BDT | DENT |
BMD | CRC | DGB | GNF | STEEM | ETN | MOON | SC | TRY | AE |
HUF | JOD | LBP | LSK | SRD | BND | CUP | GAS | UGX | AMD |
LSL | BHD | DJF | DOGE | GYD | KWD | POWR | QAR |
Note: The Currency request holds the key "Name" and its value is not case sensitive.
GetCurrency provides current data of one certain currency. The data holds the currency code, country of origin, the description, the last currency value change in percentages, the exchange rate to USD and the time of the last update.
GetRateRequest defines the request message for the GetRate call. It needs the base currency and the destination currency. Supported currencies are here.
Base represents the base currency for the exchange rate.
Destination represents the destination currency for the exchange rate.
message GetRateRequest {
string Base = 1;
string Destination = 2;
}
{
"Base":"EUR",
"Destination":"USD"
}
GetRateResponse defines the response message for the GetRate call. It holds only the exchange rate of the request's base and destination.
Rate is the result exchange rate.
message GetRateResponse {
float Rate = 1;
}
"Rate":1.1655
GetRate calculates the exchange rate between the base and the destination. The service takes the latest data from the source.
GetCurrencyRequest defines the request message for the GetCurrency and the SubscribeCurrency calls.
Name stands for the currency code for the currency. The Name value is not case sensitive.
message GetCurrencyRequest {
string Name = 1;
{
"Name":"CAD"
}
GetCurrencyResponse defines the response message for the GetCurrency call and the StreamingSubscribeResponse message.
Name stands for the currency code for the currency. Every Name value is capitalized.
Country holds the name of the country where the currency came from.
Description is the full name of the currency.
Change represents the latest currency change in the percentages.
RateUSD is the exchange rates between the currency and the USD. Both currency values are taken from the lastest source update.
UpdatedAt is the time of the last update of the currency in the source.
message GetCurrencyResponse {
string Name = 1;
string Country = 2;
string Description = 3;
float Change = 4;
float RateUSD = 5;
string UpdatedAt = 6;
}
{
"Name": "CAD",
"Country": "Canada",
"Description": "Canadian Dollar",
"Change": -0.02,
"RateUSD": 1.3416,
"UpdatedAt": "2020-07-25 04:04:00 +0000 UTC"
}
SubscribeCurrency works as the GetCurrency call, except that it does not send a response instantly but wait until the database changes some of its value, then it sends all subscribed currency data to each client.
GetCurrencyRequest defines the request message for the GetCurrency and the SubscribeCurrency calls.
{"Name":"GBP"}
{"Name":"VND"}
StreamingSubscribeResponse defines the response message for the SubscribeCurrency call. It holds either GetCurrencyResponse or the Status error.
Get_currency_response defines the response message with the data about the currency.
Error defines the error status of the problem which occurred.
message StreamingSubscribeResponse {
oneof message{
GetCurrencyResponse GetCurrencyResponse = 1;
google.rpc.Status Error = 2;
}
}
{
"GetCurrencyResponse": {
"Name": "GBP",
"Country": "England",
"Description": "British Pound",
"Change": -0.03,
"RateUSD": 0.7815,
"UpdatedAt": "2020-07-25 04:04:00 +0000 UTC"
}
}
{
"GetCurrencyResponse": {
"Name": "VND",
"Country": "Vietnam",
"Description": "Vietnamese Dong",
"Change": 0.02,
"RateUSD": 23185,
"UpdatedAt": "2020-07-25 04:04:00 +0000 UTC"
}
}
Server logs:
[CURRENCY SERVICE]2020/07/25 10:25:14 [start] listening on 127.0.0.1:10502
[CURRENCY SERVICE]2020/07/25 10:25:23 [success] client successfully subscribed to: GBP
[CURRENCY SERVICE]2020/07/25 10:25:31 [success] client successfully subscribed to: VND
[CURRENCY SERVICE]2020/07/25 10:25:32 [update] currency data updated
For these examples, I am using the tool called gRPCurl to generate binary calls to gRPC servers.
[chutommy@localhost currencies]$ grpcurl --plaintext -d '{"Base":"RUB", "Destination":"USD"}' 127.0.0.1:10502 Currency.GetRate
{
"Rate": 0.0139
}
[chutommy@localhost currencies]$ grpcurl --plaintext -d '{"Base":"GBP", "Destination":"EUR"}' 127.0.0.1:10502 Currency.GetRate
{
"Rate": 1.0979
}
[chutommy@localhost currencies]$ grpcurl --plaintext -d '{"Base":"CZK", "Destination":"CAD"}' 127.0.0.1:10502 Currency.GetRate
{
"Rate": 0.0596
}
[CURRENCY SERVICE]2020/07/25 10:38:01 [start] listening on 127.0.0.1:10502
[CURRENCY SERVICE]2020/07/25 10:38:32 [handle] GetRate call, base: RUB, destination: USD
[CURRENCY SERVICE]2020/07/25 10:38:51 [handle] GetRate call, base: GBP, destination: EUR
[CURRENCY SERVICE]2020/07/25 10:39:11 [handle] GetRate call, base: CZK, destination: CAD
[chutommy@localhost currencies]$ grpcurl --plaintext -d '{"Name":"USD"}' 127.0.0.1:10502 Currency.GetCurrency
{
"Name": "USD",
"Country": "United States of America",
"Description": "United States Dollar",
"RateUSD": 1,
"UpdatedAt": "2020-07-25 10:42:26.385028702 +0200 CEST m=+0.180421634"
}
[chutommy@localhost currencies]$ grpcurl --plaintext -d '{"Name":"GBP"}' 127.0.0.1:10502 Currency.GetCurrency
{
"Name": "GBP",
"Country": "England",
"Description": "British Pound",
"Change": -0.03,
"RateUSD": 0.7815,
"UpdatedAt": "2020-07-25 04:04:00 +0000 UTC"
}
[chutommy@localhost currencies]$ grpcurl --plaintext -d '{"Name":"CAD"}' 127.0.0.1:10502 Currency.GetCurrency
{
"Name": "CAD",
"Country": "Canada",
"Description": "Canadian Dollar",
"Change": -0.02,
"RateUSD": 1.3416,
"UpdatedAt": "2020-07-25 04:04:00 +0000 UTC"
}
[CURRENCY SERVICE]2020/07/25 10:42:10 [start] listening on 127.0.0.1:10502
[CURRENCY SERVICE]2020/07/25 10:42:27 [handle] GetCurrency call: USD
[CURRENCY SERVICE]2020/07/25 10:42:32 [handle] GetCurrency call: GBP
[CURRENCY SERVICE]2020/07/25 10:42:38 [handle] GetCurrency call: CAD
[chutommy@localhost currencies]$ grpcurl --plaintext -d @ 127.0.0.1:10502 Currency.SubscribeCurrency
{"Name":"CAD"}
{"Name":"CZK"}
{"Name":"GBP"}
{
"GetCurrencyResponse": {
"Name": "CAD",
"Country": "Canada",
"Description": "Canadian Dollar",
"Change": -0.02,
"RateUSD": 1.3416,
"UpdatedAt": "2020-07-25 04:04:00 +0000 UTC"
}
}
{
"GetCurrencyResponse": {
"Name": "CZK",
"Country": "Czech Republic",
"Description": "Czech Koruna",
"RateUSD": 22.526,
"UpdatedAt": "2020-07-25 04:04:00 +0000 UTC"
}
}
{
"GetCurrencyResponse": {
"Name": "GBP",
"Country": "England",
"Description": "British Pound",
"Change": -0.03,
"RateUSD": 0.7815,
"UpdatedAt": "2020-07-25 04:04:00 +0000 UTC"
}
}
[CURRENCY SERVICE]2020/07/25 10:46:01 [start] listening on 127.0.0.1:10502
[CURRENCY SERVICE]2020/07/25 10:46:03 [success] client successfully subscribed to: CAD
[CURRENCY SERVICE]2020/07/25 10:46:10 [success] client successfully subscribed to: CZK
[CURRENCY SERVICE]2020/07/25 10:46:15 [success] client successfully subscribed to: GBP
[CURRENCY SERVICE]2020/07/25 10:46:18 [update] currency data updated
[chutommy@localhost currencies]$ grpcurl --plaintext -d '{"Base":"USD", "Destination":"invalid"}' 127.0.0.1:10502 Currency.GetRate
ERROR:
Code: NotFound
Message: Currency was not found: call GetRate: destination currency 'INVALID' not found.
[chutommy@localhost currencies]$ grpcurl --plaintext -d '{"Name":"invalid"}' 127.0.0.1:10502 Currency.GetCurrency
ERROR:
Code: NotFound
Message: Currency "invalid" was not found.
[chutommy@localhost currencies]$ grpcurl --plaintext -d @ 127.0.0.1:10502 Currency.SubscribeCurrency
{"Name":"invalid"}
{
"Error": {
"code": 5,
"message": "Currency \"INVALID\" was not found."
}
}
{"Name":"USD"}
{
"GetCurrencyResponse": {
"Name": "USD",
"Country": "United States of America",
"Description": "United States Dollar",
"RateUSD": 1,
"UpdatedAt": "2020-07-25 11:02:50.145063563 +0200 CEST m=+45.314325138"
}
}
[CURRENCY SERVICE]2020/07/25 11:02:08 [start] listening on 127.0.0.1:10502
[CURRENCY SERVICE]2020/07/25 11:02:10 [error] handle error: call GetRate: destination currency 'INVALID' not found
[CURRENCY SERVICE]2020/07/25 11:02:18 [error] handle error: handling GetCurrency call: currency 'INVALID' not found
[CURRENCY SERVICE]2020/07/25 11:02:35 [error] currency "INVALID" not found
[CURRENCY SERVICE]2020/07/25 11:02:47 [success] client successfully subscribed to: USD
[CURRENCY SERVICE]2020/07/25 11:02:50 [update] currency data updated
All clients can be built with the Protocol Buffer Compiler + gRPC plugin.
The protobuffer of the services: commodity.proto
_
├── config
│ ├── tests
│ │ ├── config_0.yaml
│ │ └── config_1.yaml
│ ├── config.go
│ └── config_test.go
├── data
│ ├── currencies.go
│ ├── currencies_test.go
│ ├── fetching.go
│ └── fetching_test.go
├── models
│ └── currency.go
├── protos
│ ├── currency
│ │ └── currency.pb.go
│ ├── google
│ │ └── rpc
│ │ └── status.proto
│ └── currency.proto
├── server
│ ├── currencies.go
│ ├── currencies_test.go
│ ├── handler.go
│ └── handler_test.go
├── config.yaml
├── Dockerfile
├── go.mod
├── go.sum
├── main.go
├── Makefile
└── README.md