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

Feature/submission #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Weather Tracking API

## Objective
Implement a NodeJS REST API for a weather tracking system with data persistence and integration with a third-party weather API.
Implement a NodeJA REST API for a weather tracking system with data persistence and integration with a third-party weather API.

You have total control over frameworks, tools, and libraries. Consider the Evaluation Criteria, but also try to have fun — this isn't a test of the _right_ or _wrong_ approach; it's about getting an understanding of _your_ individual approach.

Expand All @@ -14,7 +14,7 @@ You have total control over frameworks, tools, and libraries. Consider the Evalu
- `PUT /cities/:id`: Update details of a specific city by ID.
- `DELETE /cities/:id`: Delete a city by ID.
- `GET /cities/:id/weather`: Retrieve the current weather for a specific city by ID using a third-party weather API.
- Persist city data using a datastore of your choice (can be in memory, or file-based if you like).
- Persist city data using a datastore of your choice.
- Fetch current weather information from a third-party weather API.
- Write tests for the API endpoints.
- Ensure the code is well-documented and follows best practices.
Expand All @@ -35,3 +35,12 @@ You have total control over frameworks, tools, and libraries. Consider the Evalu
- Documentation and clarity in the README

🚀 Happy coding!

## Instructions

- CD to /server
- run npm install
- run npm start to initialise the server
- npm test to run the test suite
- An initial city has been added using the ID 2641170

47 changes: 47 additions & 0 deletions dataStore.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"cities": {
"2641170": {
"coord": {
"lon": -1.1505,
"lat": 52.9536
},
"weather": [
{
"id": 803,
"main": "Clouds",
"description": "broken clouds",
"icon": "04d"
}
],
"base": "stations",
"main": {
"temp": 286.18,
"feels_like": 285.55,
"temp_min": 284.38,
"temp_max": 287.42,
"pressure": 1018,
"humidity": 77
},
"visibility": 10000,
"wind": {
"speed": 3.6,
"deg": 220
},
"clouds": {
"all": 75
},
"dt": 1716543390,
"sys": {
"type": 2,
"id": 2093695,
"country": "GB",
"sunrise": 1716522812,
"sunset": 1716581384
},
"timezone": 3600,
"id": 2641170,
"name": "Nottingham",
"cod": 200
}
}
}
Binary file added server/.DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions server/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}
21 changes: 21 additions & 0 deletions server/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const express = require('express');
const logger = require('morgan');

const citiesRouter = require('./routes/index');
const { notFoundHandler, errorHandler } = require('./errorHandler');

const app = express();
// Middleware setup
app.use(logger('dev'));
app.use(express.json());

// Routes
app.use('/api', citiesRouter);

// Catch 404 and forward to error handler
app.use(notFoundHandler);

// Error handler
app.use(errorHandler);

module.exports = app;
90 changes: 90 additions & 0 deletions server/bin/www
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env node

/**
* Module dependencies.
*/

var app = require('../app');
Copy link
Contributor

Choose a reason for hiding this comment

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

The use of var in this file makes me think it is copy/pasted boilerplate 😉. I have no problem with that, and appreciate that this is supposed to be a quick. But just for info: I would usually be asking for this to be changed, before reviewing the rest of the PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

That said: since this is intended to be a bin file: you could have a good reason to use var?

var debug = require('debug')('server:server');
var http = require('http');

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
* Normalize a port into a number, string, or false.
*/

function normalizePort(val) {
var port = parseInt(val, 10);

if (isNaN(port)) {
// named pipe
return val;
}

if (port >= 0) {
// port number
return port;
}

return false;
}

/**
* Event listener for HTTP server "error" event.
*/

function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}

var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}

/**
* Event listener for HTTP server "listening" event.
*/

function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
3 changes: 3 additions & 0 deletions server/dataStore.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"cities": {}
}
20 changes: 20 additions & 0 deletions server/errorHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const createError = require('http-errors');

const notFoundHandler = (req, res, next) => {
next(createError(404));
};

const errorHandler = (err, req, res, next) => {
// Set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};

// Render the error page
res.status(err.status || 500);
res.send(err.message);
};

module.exports = {
notFoundHandler,
errorHandler,
};
5 changes: 5 additions & 0 deletions server/jest-preset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
preset: '@babel/preset-env',
testEnvironment: 'node',
testMatch: ["**/__tests__/**/*.test.js"],
};
13 changes: 13 additions & 0 deletions server/middleware/loadData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Middleware to load data
const { readDataFromFile} = require('../utils/files.js');

async function loadData(req, res, next) {
try {
req.data = await readDataFromFile();
next();
} catch (error) {
res.status(500).send('Failed to read data from file');
}
}

module.exports = loadData;
13 changes: 13 additions & 0 deletions server/middleware/saveData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Middleware to save data
const { writeDataToFile } = require('../utils/files.js');

async function saveData(req, res, next) {
try {
await writeDataToFile(req.data);
next();
} catch (error) {
res.status(500).send('Failed to write data to file');
}
}

module.exports = saveData;
10 changes: 10 additions & 0 deletions server/middleware/validateCityId.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function validateCityId(req, res, next) {
const cityId = req.params.id || req.query.id;
if (!cityId) {
return res.status(400).send('City ID is required');
}
req.cityId = cityId;
next();
}

module.exports = validateCityId;
Loading