diff --git a/assignments/__tests__/app.test.js b/assignments/__tests__/app.test.js new file mode 100644 index 000000000..8b9c6d5d2 --- /dev/null +++ b/assignments/__tests__/app.test.js @@ -0,0 +1,33 @@ +import supertest from "supertest" +import { app } from "../app.js" + +const request = supertest(app) + +describe("POST /api/weather", () => { + it("should return 400 if cityName is missing", async () => { + const response = await request + .post("/api/weather") + .send({}) + + expect(response.status).toBe(400) + expect(response.body.error).toBe("cityName is required") + }) + + it("should return 404 if cityName is gibberish", async () => { + const response = await request + .post("/api/weather") + .send({ cityName: "asdasdasdqweqwe" }) + + expect(response.status).toBe(404) + expect(response.body.error || response.body.weatherText).toContain("not found") + }) + + it("should return 200 and weather text for a valid city", async () => { + const response = await request + .post("/api/weather") + .send({ cityName: "London" }) + + expect(response.status).toBe(200) + expect(response.body.weatherText).toContain("The temperature in London") + }) +}) \ No newline at end of file diff --git a/assignments/app.js b/assignments/app.js new file mode 100644 index 000000000..04dd5a271 --- /dev/null +++ b/assignments/app.js @@ -0,0 +1,6 @@ +import express from "express"; +import {generalRouter} from "./routes/routes.js"; + +export const app = express() +app.use(express.json()) +app.use('/api', generalRouter) \ No newline at end of file diff --git a/assignments/config-files/babel.config.cjs b/assignments/babel.config.cjs similarity index 100% rename from assignments/config-files/babel.config.cjs rename to assignments/babel.config.cjs diff --git a/assignments/controllers/mainController.js b/assignments/controllers/mainController.js new file mode 100644 index 000000000..af53a112a --- /dev/null +++ b/assignments/controllers/mainController.js @@ -0,0 +1,22 @@ +import {getWeatherService} from "../services/getWeatherService.js"; + +export const returnHelloText = (req, res) => { + return res.status(200).json({ message: 'hello from backend to frontend!' }) +} + + +export const getCityWeather = async (req, res) => { + const {cityName} = req.body + + if (!cityName) { + return res.status(400).json({ error: 'cityName is required'}) + } + + const result = await getWeatherService(cityName) + + if (result.error) { + return res.status(404).json(result) + } + + return res.status(200).json(result) +} \ No newline at end of file diff --git a/assignments/index.js b/assignments/index.js new file mode 100644 index 000000000..3b2cbab8e --- /dev/null +++ b/assignments/index.js @@ -0,0 +1,14 @@ +import {app} from "./app.js"; + +const PORT = 3000; + +const startApp = async () => { + try { + app.listen(PORT, () => { + console.log(`Server listening on port ${PORT}`) + }) + } catch (e) { + console.log(e) + } +} +startApp() \ No newline at end of file diff --git a/assignments/config-files/jest.config.js b/assignments/jest.config.js similarity index 100% rename from assignments/config-files/jest.config.js rename to assignments/jest.config.js diff --git a/assignments/package.json b/assignments/package.json new file mode 100644 index 000000000..3bbb5af67 --- /dev/null +++ b/assignments/package.json @@ -0,0 +1,27 @@ +{ + "name": "assignments", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "scripts": { + "test": "jest", + "dev": "nodemon --inspect index.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "axios": "^1.13.1", + "express": "5.1.0", + "express-handlebars": "8.0.1", + "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" + } +} diff --git a/assignments/routes/routes.js b/assignments/routes/routes.js new file mode 100644 index 000000000..e249c6bdf --- /dev/null +++ b/assignments/routes/routes.js @@ -0,0 +1,7 @@ +import {Router} from "express"; +import {getCityWeather, returnHelloText} from "../controllers/mainController.js"; + +export const generalRouter = Router() + +generalRouter.get('/', returnHelloText) +generalRouter.post('/weather', getCityWeather) \ No newline at end of file diff --git a/assignments/services/getWeatherService.js b/assignments/services/getWeatherService.js new file mode 100644 index 000000000..bc57914ee --- /dev/null +++ b/assignments/services/getWeatherService.js @@ -0,0 +1,33 @@ +import axios from "axios" +import {keys} from "../sources/keys.js" + +const baseURL = "https://api.openweathermap.org/data/2.5/weather" + +export const getWeatherService = async (cityName) => { + try { + const response = await axios.get(`${baseURL}`, { + params: { + q: cityName, + appid: keys.API_KEY, + units: "metric", + lang: "en" + } + }) + + const temperature = response.data.main.temp.toFixed(0) + const weatherText = `The temperature in ${response.data.name} is ${temperature}°C.` + + return { + cityName: response.data.name, + temperature, + weatherText, + } + + } catch (error) { + if (error.response?.status === 404) { + return { error: "City is not found!" } + } + console.error("Weather API error:", error.message) + return { error: "Failed to fetch weather data" } + } +} \ No newline at end of file diff --git a/assignments/sources/keys.js b/assignments/sources/keys.js new file mode 100644 index 000000000..cbeac2443 --- /dev/null +++ b/assignments/sources/keys.js @@ -0,0 +1,3 @@ +export const keys = { + API_KEY: '10f7a7d2be30dc7eaab9c6101911a9e2' +} \ No newline at end of file