Initial commit

This commit is contained in:
Ace Nassri 2018-06-22 13:43:39 -07:00
parent a1b141d5b3
commit 28a6fbb8f1
9 changed files with 1260 additions and 0 deletions

View File

@ -0,0 +1 @@
node_modules/

1
src/currencyservice/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

View 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" ]

View 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}`);
}
});

View 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

File diff suppressed because it is too large Load Diff

View 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"
}
}

View 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;
}

View 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();