Initial commit
This commit is contained in:
parent
a1b141d5b3
commit
28a6fbb8f1
9 changed files with 1260 additions and 0 deletions
1
src/currencyservice/.dockerignore
Normal file
1
src/currencyservice/.dockerignore
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules/
|
1
src/currencyservice/.gitignore
vendored
Normal file
1
src/currencyservice/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules/
|
7
src/currencyservice/Dockerfile
Normal file
7
src/currencyservice/Dockerfile
Normal file
|
@ -0,0 +1,7 @@
|
|||
FROM node:8
|
||||
WORKDIR /usr/src/app
|
||||
COPY package*.json ./
|
||||
RUN npm install --only=production
|
||||
COPY . .
|
||||
EXPOSE 31337
|
||||
CMD [ "node", "server.js" ]
|
47
src/currencyservice/client.js
Normal file
47
src/currencyservice/client.js
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2015 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const grpc = require('grpc');
|
||||
|
||||
const PROTO_PATH = path.join(__dirname, './proto/currency_service.proto');
|
||||
const PORT = 31337;
|
||||
|
||||
const shopProto = grpc.load(PROTO_PATH).hipstershop;
|
||||
const client = new shopProto.CurrencyService(`localhost:${PORT}`,
|
||||
grpc.credentials.createInsecure());
|
||||
|
||||
const request = {
|
||||
from: {
|
||||
currency_code: 'USD',
|
||||
amount: {
|
||||
decimal: 300,
|
||||
fractional: 0
|
||||
}
|
||||
},
|
||||
to_code: 'CHF'
|
||||
};
|
||||
|
||||
client.convert(request, function (err, response) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
const amount = response.amount;
|
||||
console.log(`OUTPUT: ${amount.decimal}.${amount.fractional}`);
|
||||
}
|
||||
});
|
29
src/currencyservice/currency_service.proto
Normal file
29
src/currencyservice/currency_service.proto
Normal file
|
@ -0,0 +1,29 @@
|
|||
syntax = "proto3";
|
||||
package hipstershop;
|
||||
|
||||
message Empty {}
|
||||
|
||||
service CurrencyService {
|
||||
rpc GetSupportedCurrencies(Empty) returns (GetSupportedCurrenciesResponse) {}
|
||||
rpc Convert(CurrencyConversionRequest) returns (Money) {}
|
||||
}
|
||||
// Describes a money amount without currency. For example, decimal=2 and
|
||||
// fractional=500 (or fractional=5) makes up 2.5 units.
|
||||
message MoneyAmount {
|
||||
uint32 decimal = 1;
|
||||
uint32 fractional = 2;
|
||||
}
|
||||
message Money {
|
||||
// The 3-letter currency code defined in ISO 4217.
|
||||
string currency_code = 1;
|
||||
MoneyAmount amount = 2;
|
||||
}
|
||||
message GetSupportedCurrenciesResponse {
|
||||
// The 3-letter currency code defined in ISO 4217.
|
||||
repeated string currency_codes = 1;
|
||||
}
|
||||
message CurrencyConversionRequest {
|
||||
Money from = 1;
|
||||
// The 3-letter currency code defined in ISO 4217.
|
||||
string to_code = 2;
|
||||
}
|
1020
src/currencyservice/package-lock.json
generated
Normal file
1020
src/currencyservice/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
14
src/currencyservice/package.json
Normal file
14
src/currencyservice/package.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "grpc-currency-service",
|
||||
"version": "0.1.0",
|
||||
"description": "A gRPC currency conversion microservice",
|
||||
"repository": "TODO",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"async": "^1.5.2",
|
||||
"google-protobuf": "^3.0.0",
|
||||
"grpc": "^1.0.0",
|
||||
"request": "^2.87.0",
|
||||
"xml2js": "^0.4.19"
|
||||
}
|
||||
}
|
29
src/currencyservice/proto/currency_service.proto
Normal file
29
src/currencyservice/proto/currency_service.proto
Normal file
|
@ -0,0 +1,29 @@
|
|||
syntax = "proto3";
|
||||
package hipstershop;
|
||||
|
||||
message Empty {}
|
||||
|
||||
service CurrencyService {
|
||||
rpc GetSupportedCurrencies(Empty) returns (GetSupportedCurrenciesResponse) {}
|
||||
rpc Convert(CurrencyConversionRequest) returns (Money) {}
|
||||
}
|
||||
// Describes a money amount without currency. For example, decimal=2 and
|
||||
// fractional=500 (or fractional=5) makes up 2.5 units.
|
||||
message MoneyAmount {
|
||||
uint32 decimal = 1;
|
||||
uint32 fractional = 2;
|
||||
}
|
||||
message Money {
|
||||
// The 3-letter currency code defined in ISO 4217.
|
||||
string currency_code = 1;
|
||||
MoneyAmount amount = 2;
|
||||
}
|
||||
message GetSupportedCurrenciesResponse {
|
||||
// The 3-letter currency code defined in ISO 4217.
|
||||
repeated string currency_codes = 1;
|
||||
}
|
||||
message CurrencyConversionRequest {
|
||||
Money from = 1;
|
||||
// The 3-letter currency code defined in ISO 4217.
|
||||
string to_code = 2;
|
||||
}
|
112
src/currencyservice/server.js
Normal file
112
src/currencyservice/server.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright 2018 Google LLC.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const grpc = require('grpc');
|
||||
const request = require('request');
|
||||
const xml2js = require('xml2js');
|
||||
|
||||
const PROTO_PATH = path.join(__dirname, './proto/currency_service.proto');
|
||||
const PORT = 31337;
|
||||
const DATA_URL = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml';
|
||||
const shopProto = grpc.load(PROTO_PATH).hipstershop;
|
||||
|
||||
/**
|
||||
* Helper function that gets currency data from an XML webpage
|
||||
* Uses public data from European Central Bank
|
||||
*/
|
||||
function _getCurrencyData (callback) {
|
||||
request(DATA_URL, (err, res) => {
|
||||
if (err) {
|
||||
throw new Error(`Error getting data: ${err}`);
|
||||
}
|
||||
|
||||
const body = res.body.split('\n').slice(7, -2).join('\n');
|
||||
xml2js.parseString(body, (err, resJs) => {
|
||||
if (err) {
|
||||
throw new Error(`Error parsing HTML: ${err}`);
|
||||
}
|
||||
|
||||
const array = resJs['Cube']['Cube'].map(x => x['$']);
|
||||
const results = array.reduce((acc, x) => {
|
||||
acc[x['currency']] = x['rate'];
|
||||
return acc;
|
||||
}, { 'EUR': '1.0' });
|
||||
callback(results);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that handles decimal/fractional carrying
|
||||
*/
|
||||
function _carry (amount) {
|
||||
amount.fractional += (amount.decimal % 1) * 100;
|
||||
amount.decimal = Math.floor(amount.decimal + amount.fractional / 100);
|
||||
amount.fractional = amount.fractional % 100;
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the supported currencies
|
||||
*/
|
||||
function getSupportedCurrencies (call, callback) {
|
||||
_getCurrencyData((data) => {
|
||||
callback(null, {currency_codes: data.keys()});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts between currencies
|
||||
*/
|
||||
function convert (call, callback) {
|
||||
try {
|
||||
_getCurrencyData((data) => {
|
||||
const request = call.request;
|
||||
|
||||
// Convert: from_currency --> EUR
|
||||
const from = request.from;
|
||||
const euros = _carry({
|
||||
decimal: from.amount.decimal / data[from.currency_code],
|
||||
fractional: from.amount.fractional / data[from.currency_code]
|
||||
});
|
||||
|
||||
// Convert: EUR --> to_currency
|
||||
const target = _carry({
|
||||
decimal: euros.decimal * data[request.to_code],
|
||||
fractional: euros.fractional * data[request.to_code]
|
||||
});
|
||||
target.fractional = Math.round(target.fractional);
|
||||
|
||||
callback(null, {amount: target});
|
||||
});
|
||||
} catch (err) {
|
||||
callback(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an RPC server that receives requests for the
|
||||
* CurrencyConverter service at the sample server port
|
||||
*/
|
||||
function main () {
|
||||
var server = new grpc.Server();
|
||||
server.addService(shopProto.CurrencyService.service, {getSupportedCurrencies, convert});
|
||||
server.bind(`0.0.0.0:${PORT}`, grpc.ServerCredentials.createInsecure());
|
||||
server.start();
|
||||
}
|
||||
|
||||
main();
|
Loading…
Reference in a new issue