diff --git a/assignments/HackYourTemperature/.gitignore b/assignments/HackYourTemperature/.gitignore new file mode 100644 index 000000000..d16f63d9b --- /dev/null +++ b/assignments/HackYourTemperature/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.env \ No newline at end of file diff --git a/assignments/HackYourTemperature/app.js b/assignments/HackYourTemperature/app.js new file mode 100644 index 000000000..4de1623f2 --- /dev/null +++ b/assignments/HackYourTemperature/app.js @@ -0,0 +1,43 @@ +import express from "express"; +import keys from "./sources/keys.js"; + +const app = express(); +app.use(express.json()); + +app.get("/", (req, res) => { +res.send("Hello, from backend to frontend!"); +}); + +app.post("/weather", async (req, res) => { +const { cityName } = req.body; + +if (!cityName) { +return res.status(400).json({ + weatherText: "City name is required!", +}); +} + +try { +const response = await fetch( + `https://api.openweathermap.org/data/2.5/weather?q=${cityName}&appid=${keys.API_KEY}&units=metric`, +); + +const data = await response.json(); + +if (data.cod === "404") { + return res.status(404).json({ + weatherText: "City is not found!", + }); +} + +res.json({ + weatherText: `The temperature in ${data.name} is ${data.main.temp}°C.`, +}); +} catch (error) { +res.status(500).json({ + weatherText: "Error fetching weather data.", +}); +} +}); + +export default app; diff --git a/assignments/HackYourTemperature/babel.config.cjs b/assignments/HackYourTemperature/babel.config.cjs new file mode 100644 index 000000000..fbb629af6 --- /dev/null +++ b/assignments/HackYourTemperature/babel.config.cjs @@ -0,0 +1,13 @@ +module.exports = { + presets: [ + [ + // This is a configuration, here we are telling babel what configuration to use + "@babel/preset-env", + { + targets: { + node: "current", + }, + }, + ], + ], +}; diff --git a/assignments/HackYourTemperature/jest.config.js b/assignments/HackYourTemperature/jest.config.js new file mode 100644 index 000000000..6f3736221 --- /dev/null +++ b/assignments/HackYourTemperature/jest.config.js @@ -0,0 +1,9 @@ +export default { + // Tells jest that any file that has 2 .'s in it and ends with either js or jsx should be run through the babel-jest transformer + transform: { + "^.+\\.jsx?$": "babel-jest", + }, + // By default our `node_modules` folder is ignored by jest, this tells jest to transform those as well + transformIgnorePatterns: [], + testEnvironment: "node", +}; diff --git a/assignments/HackYourTemperature/package.json b/assignments/HackYourTemperature/package.json new file mode 100644 index 000000000..fbed1b48d --- /dev/null +++ b/assignments/HackYourTemperature/package.json @@ -0,0 +1,29 @@ +{ + "name": "hackyourtemperature", + "version": "1.0.0", + "description": "", + "main": "server.js", + "type": "module", + "scripts": { + "test": "jest", + "start": "node server.js", + "prettier:check": "prettier --check .", + "prettier:write": "prettier --write ." + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "dotenv": "^17.2.3", + "express": "^5.1.0", + "express-handlebars": "^8.0.3" + }, + "devDependencies": { + "@babel/preset-env": "^7.28.5", + "babel-jest": "^30.2.0", + "husky": "^9.1.7", + "jest": "^30.2.0", + "lint-staged": "^16.2.6", + "supertest": "^7.1.4" + } +} diff --git a/assignments/HackYourTemperature/server.js b/assignments/HackYourTemperature/server.js new file mode 100644 index 000000000..713601cf1 --- /dev/null +++ b/assignments/HackYourTemperature/server.js @@ -0,0 +1,7 @@ +import app from "./app.js"; + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => { +console.log(`Server running on http://localhost:${PORT}`); +}); diff --git a/assignments/HackYourTemperature/sources/keys.js b/assignments/HackYourTemperature/sources/keys.js new file mode 100644 index 000000000..6a26bd40e --- /dev/null +++ b/assignments/HackYourTemperature/sources/keys.js @@ -0,0 +1,8 @@ +import dotenv from 'dotenv'; +dotenv.config(); + +const keys = { + API_KEY: process.env.API_KEY +}; + +export default keys; \ No newline at end of file diff --git a/assignments/HackYourTemperature/tests/app.test.js b/assignments/HackYourTemperature/tests/app.test.js new file mode 100644 index 000000000..bccd7af0c --- /dev/null +++ b/assignments/HackYourTemperature/tests/app.test.js @@ -0,0 +1,28 @@ +import app from "../app.js"; +import supertest from "supertest"; + +const request = supertest(app); + +describe("POST /weather", () => { + it("should return 400 if no cityName is provided", async () => { + const response = await request.post("/weather").send({}); + expect(response.status).toBe(400); + expect(response.body.weatherText).toBe("City name is required!"); + }); + + it("should return 404 if city is not found", async () => { + const response = await request + .post("/weather") + .send({ cityName: "UnknownCity" }); + expect(response.status).toBe(404); + expect(response.body.weatherText).toBe("City is not found!"); + }); + + it("should return weather data for a valid city", async () => { + const response = await request + .post("/weather") + .send({ cityName: "Amsterdam" }); + expect(response.status).toBe(200); + expect(response.body.weatherText).toContain("Amsterdam"); + }); +});