Skip to content
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
1 change: 1 addition & 0 deletions assignments/hackyourtemperature/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
34 changes: 34 additions & 0 deletions assignments/hackyourtemperature/__tests__/app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import app from '../app.js';
import supertest from 'supertest';

const request = supertest(app);

describe('POST /weather', () => {
test('400 when cityName missing', async () => {
const res = await request.post('/weather').send({});
expect(res.status).toBe(400);
expect(typeof res.body.error).toBe('string');
expect(res.body.error.toLowerCase()).toContain('cityname');
});

test('404 when city not found', async () => {
const res = await request
.post('/weather')
.send({ cityName: '___nope___' });
expect(res.status).toBe(404);
expect(typeof res.body.weatherText).toBe('string');
expect(res.body.weatherText.toLowerCase()).toContain('not found');
});

test('200 for a valid city returns fields', async () => {
const res = await request
.post('/weather')
.send({ cityName: 'Amsterdam' });
expect(res.status).toBe(200);
expect(res.body.cityName.toLowerCase()).toContain('amsterdam');
expect(typeof res.body.weatherText).toBe('string');
expect(
typeof res.body.temperature === 'number' || res.body.temperature === null
).toBe(true);
});
});
47 changes: 47 additions & 0 deletions assignments/hackyourtemperature/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import express from 'express';
import fetch from 'node-fetch';
import keys from './sources/keys.js';

const app = express();
app.use(express.json());

app.get('/', (_req, res) => {
res.type('text/plain').send('hello from backend to frontend');
});

app.post('/weather', async (req, res) => {
const { cityName } = req.body ?? {};
if (!cityName || typeof cityName !== 'string' || !cityName.trim()) {
return res.status(400).json({
error: 'Send JSON like { "cityName": "Amsterdam" }',
});
}

try {
const url =
`https://api.openweathermap.org/data/2.5/weather` +
`?q=${encodeURIComponent(cityName.trim())}` +
`&appid=${keys.API_KEY}&units=metric`;

const r = await fetch(url);
const data = await r.json();

if (r.status === 404 || data?.cod === '404') {
return res.status(404).json({ weatherText: 'City is not found!' });
}

const name = data?.name ?? cityName.trim();
const temp = typeof data?.main?.temp === 'number' ? data.main.temp : null;

return res.json({
cityName: name,
temperature: temp,
weatherText: `${name}: ${temp}°C`,
});
} catch (err) {
console.error(err);
return res.status(500).json({ error: 'Server error contacting API' });
}
});

export default app;
12 changes: 12 additions & 0 deletions assignments/hackyourtemperature/babel.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current'
},
},
],
],
};
8 changes: 8 additions & 0 deletions assignments/hackyourtemperature/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default {
transform: {
'^.+\\.jsx?$': 'babel-jest'
},

testEnvironment: 'node',
transformIgnorePatterns: [],
};
27 changes: 27 additions & 0 deletions assignments/hackyourtemperature/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "hackyourtemperature",
"version": "1.0.0",
"type": "module",
"main": "server.js",
"description": "",
"scripts": {
"test": "jest",
"start": "node server.js",
"dev": "nodemon --ext js,json --watch . server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^5.1.0",
"express-handlebars": "^8.0.3",
"node-fetch": "^3.3.2"
},
"devDependencies": {
"@babel/preset-env": "^7.28.5",
"babel-jest": "^30.2.0",
"jest": "^30.2.0",
"nodemon": "^3.1.10",
"supertest": "^7.1.4"
}
}
6 changes: 6 additions & 0 deletions assignments/hackyourtemperature/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import app from './app.js';

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}`);
});
3 changes: 3 additions & 0 deletions assignments/hackyourtemperature/sources/keys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
API_KEY: 'f6f12ccf08763d2f26c3ea0142cc94ee',
};