Project C
- Released
-
28 October 2021 - Submission
-
Deadline
12 November 2021 (for Feedback; provided on best-effort basis) 25 November 2021 @ 5 PM EDT Where Web Submit Name C Files
index.js dao.js - Version
-
1.0 28 October 2021 Initial Release
Table of contents
Executive Summary
This project is similar to Project B (in that you will build a number of web services) except you will use Node JS instead of Tomcat. This allows us to augment our TCP (Project A) and Java (Project B) backend with JavaScript services. You will use all these services in the next two projects to build a shopping-cart web application.
The Environment
It is highly recommended that you use your red
account or RemoteLab for this project rather than your own computer. Since we will be using CLI (the command-line interface) for this project, it is very easy to work remotely by ssh’ing to your account. To that end, use your favorite non-GUI editor (such as nano, vim, or emacs) or GUI text editor (VS Code, Atom, or Gedit) to edit your source files.
Setting up your Environment for Node JS
To setup Node JS in your home directory, run these command:
$ eval `4413Load`
$ 4413Load -rc >> ~/.cshrc # or >> ~/.bashrc
$ 4413Go2
This adds the node_modules
directory to your home directory as a symbolic link to a shared copy of the libraries and dependencies your Node JS project might require, and adds the path of the Node JS installation and related files to your PATH
environment variable and startup script.
To create a Node JS project named projC
in your home directory and create your main (index
) script, issue these commands:
$ nodeGo ~/projC
$ cd ~/projC
$ touch index.js
You will be prompted to confirm several options. Accept all their defaults by pressing ENTER
and then typing yes
at the end of the prompts. This will create a directory named projC
directly under your home directory and set it up. Your “servlet” file should be created in it under the name index.js
. Once created, you run it using command:
$ node index.js
Since index.js
is intended to instantiate a service, the above command will block until you press ^C
(Ctrl+C
). Any logging on standard output (done in JavaScript via the console.log
function) will appear under the above command along with any exceptions thrown.
The Services
This Project asks that you develop and test three microservices with the following functional specifications:
GeoNode
This service is the Node counterpart of the Java GeoWeb service of Project B and the Geo2
service of Project A. It receives two URL-encoded parameters lat
and lng
containing the GPS coordinates of a place on Earth. If this is the first time this client has made such a request then the return would be the text/plain
payload “RECEIVED”. If not, then the return would be the text/plain
payload
The distance from (lat1, lng1) to (lat2, lng2) is: XXX km
where XXX
is the geodesic distance between the previous place (lat1
and lng1
, sent in the last request) and the current one (lat2
and lng2
, sent in this request). This can continue (to a 3rd, 4th, … places) as long as the requests are made in the same session. All computations must be made by delegating to the Geo
service of Project A, not Geo2
of Project A nor GeoWeb
of Project B; i.e. this web service does not do any math.
Making TCP connections from Node JS
To make an TCP connection, use the
net
modules.const client = net.createConnection(port, host); client.on('error', (err) => { /* handle error */ }); client.on('connect', () => { client.write('...'); // Send a request client.on('data', (data) => { /* use data */ }); client.end(); // close the socket });
For the full Node JS documentation and API reference, refer to here.
Catalog
This service receives one URL parameter id
containing an integer and returns the id
and name
of the row corresponding to that id
in the Category
table of the Models_R_US.db
database (which resides in ~/4413/pkg/sqlite
). The response should be mimetyped as application/json
in the response’s content type. If the id
parameter is missing then the response should be an array of json objects for all rows in the table. Here is an example of the response when the request parameter id
is 2:
[{"id":2,"name":"Classic Cars"}]
Trip
This service receives two URL parameters from
and to
containing the start and end addresses of a trip. It returns the optimal distance (in km) and time (in minutes) between them given the current traffic conditions. To get this information, send a request to the MapQuest Directions API (http://www.mapquestapi.com/directions/v2/route?
) and supply the following parameters:
Parameter | Description |
---|---|
key | Your API key, which is needed to make requests to MapQuest services. |
from | The starting location of a Route Request. |
to | The ending location of a Route Request. |
unit | Specifies the type of units to use when calculating distance. Give k for kilometres. |
timeType | Specifies the time type to use. Give 1 to use the current date/time when generating a route. |
useTraffic | Set this to true to use historical and realtime traffic speeds to influence the route. |
For example, the given request:
GET /Trip?from=4700+Keele+Street%2C+Toronto&to=Canada%27s+Wonderland%2C+Vaughan%2C+Ontario
Your service should response with something like this:
{ "distance": 9.2344, "time": 12.3 }
If no route can be computed, i.e. the response’s info.statuscode
attribute is non-zero and the info.messages
array attribute contains “Unable to calculate route.”, your service should respond with:
{ "distance": 0, "time": 0 }
Getting a MapQuest API Key
You will need a MapQuest Developer account. To create a new account, register to here. Once you have completed the form and click
Sign Me Up
, you will be taken to your Profile.Click Manage Keys on the left navigation to go to your API keys. MapQuest automatically creates you an API key when you first create your account. You can find it by expanding
My Application
where you will find yourConsumer Key
. This is your API key, you will use in your GET request.
Whitespaces in the Location Query
Your
from
andto
query parameters should permit whitespaces to be contained within it. However, URLs cannot contain whitespaces. To get around this, we encode our URLs. See here and here. You can use the built-in global JavaScript functionencodeURIComponent
or thestringify
function in thequerystring
module.
Testing Tip
You can use an API explorer tool like hoppscotch.io to explore the MapQuest API. For security reasons, by default, Hoppscotch only works with HTTPS requests. To make HTTP requests, you will need to install the Hoppscotch Firefox plugin. Once installed, you should be able to test the MapQuest API and your own web services.
Service Implementation
Use the design patterns, methodologies, and hints demonstrated in lecture in order to speed up development and learn best-practice approaches.
Develop all 3 services in one project, but each in its own middleware (i.e. app.get
) function. The route (URL mapping) should be the same as the service name. Use the following fragment as a template for your index.js
:
const express = require('express');
const session = require('express-session');
const dao = require('./dao.js');
const app = express();
const port = process.argv[2] || 3000;
app.enable('trust proxy');
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
proxy: true
}));
app.get('/GeoNode', function (req, res) { /* ... */ });
app.get('/Catalog', function (req, res) { /* ... */ });
app.get('/Trip', function (req, res) { /* ... */ });
const server = app.listen(port, function () {
const host = server.address().address;
const port = server.address().port;
console.log(`server listening to ${host}:${port}`);
});
And in your dao.js
:
const os = require('os');
const path = require('path');
const sqlite3 = require('sqlite3');
const dbfile = '4413/pkg/sqlite/Models_R_US.db';
const dbpath = path.join(os.homedir(), ...dbfile.split('/'));
const db = new (sqlite3.verbose()).Database(dbpath);
module.exports = {
getCatalog(id) {
/* ... */
}
};
Testing & Deployment
Test your services through a browser using http://host:port?qs
, where qs
is the query string. Use localhost
with port 3000
(or a port specified via the command-line arguments) when testing on a workstation.
Once everything is working, start the the service on a workstation, copy down the hostname (e.g.: ea75
) and port number, log into red
(rather than a workstation) by simply ssh
‘ing to it, run firefox
(you will need to close any opened Firefox instances on your workstation) and then connect to the running web service on the workstation from red
(e.g.: make requests to the services at http://ea75.eecs.yorku.ca:3000/
). Please DO NOT run your service directly on red
as the non-standard ports are blocked and red
is a shared environment.
Clean-Up
Do not leave services running unnecessarily in the background on time-shared servers — they will consume system resources. Once you are done with a service, terminate it. Use the fg
/bg
commands or ps -u
and kill
to list and terminate all you services on red
, red1
, and red2
.
Submitting Your Work
Simply upload your index.js
and dao.js
to the course cloud so you can use it during tests. (You can also upload it to your Github, Google Drive, DropBox, S3, or some other cloud service).
$ submit 4413 C index.js dao.js