-
Notifications
You must be signed in to change notification settings - Fork 6
Assignments week 2 Nikita #12
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import app from '../app.js' | ||
import supertest from 'supertest' | ||
import fetch from 'node-fetch' | ||
|
||
jest.mock('node-fetch', () => jest.fn()) | ||
|
||
const request = supertest(app) | ||
|
||
describe('POST /', () => { | ||
it('returns temperature for a valid city', async () => { | ||
const fakeGeoData = [{ lat: 52.37, lon: 4.89 }] | ||
const fakeWeatherData = { | ||
main: { | ||
temp: 283.15, | ||
}, | ||
} | ||
|
||
fetch | ||
.mockResolvedValueOnce({ | ||
json: async () => fakeGeoData, | ||
}) | ||
.mockResolvedValueOnce({ | ||
json: async () => fakeWeatherData, | ||
}) | ||
|
||
const response = await request.post('/').send({ cityName: 'Amsterdam' }) | ||
|
||
expect(response.statusCode).toBe(200) | ||
expect(response.body).toEqual({ | ||
cityName: 'Amsterdam', | ||
temperature: '10.0', | ||
}) | ||
}) | ||
|
||
it('returns error message for unknown city', async () => { | ||
fetch.mockResolvedValueOnce({ | ||
json: async () => [], | ||
}) | ||
|
||
const response = await request.post('/').send({ cityName: 'FakeCityXYZ' }) | ||
|
||
expect(response.statusCode).toBe(200) | ||
expect(response.body).toBe('City is not found') | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import express from 'express' | ||
import { engine } from 'express-handlebars' | ||
import fetch from 'node-fetch' | ||
import { API_KEY } from './sources/keys.js' | ||
|
||
|
||
const app = express() | ||
|
||
app.engine('handlebars', engine()) | ||
app.set('view engine', 'handlebars') | ||
app.set('views', './views') | ||
|
||
app.get('/', (req, res) => { | ||
res.render('home') | ||
}) | ||
|
||
app.use(express.json()) | ||
|
||
app.post('/', async (req, res) => { | ||
try { | ||
const cityName = req.body.cityName | ||
const geoData = await fetch( | ||
`http://api.openweathermap.org/geo/1.0/direct?q=${cityName}&limit=1&appid=${API_KEY}` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whenever you have to write domains multiple times, it's good to turn them into variables/constants so they can be switched out, and so you can make sure theirs no typos in the project. e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! |
||
) | ||
const geoResponse = await geoData.json() | ||
console.log(geoResponse) | ||
if (geoResponse.length == 0) { | ||
throw new Error('City is not found') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is best not to use errors in "normal" functionality (a user typing a city that doesn't exist is usual behaviour). it is best to just write here: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it! Makes sense |
||
} | ||
|
||
const lat = geoResponse[0].lat | ||
const lon = geoResponse[0].lon | ||
console.log(lat, lon) | ||
|
||
const weatherData = await fetch( | ||
`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}` | ||
) | ||
const weatherResponse = await weatherData.json() | ||
res.json({ | ||
cityName: cityName, | ||
temperature: (weatherResponse.main.temp - 273.15).toFixed(1) | ||
}); | ||
} catch (err) { | ||
res.json(err.message) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Best to null check this, as in theory you can "throw" anything, (even though it should be an Error by convention). e.g the line "throw 0;" is valid, even if silly/should not be used. Therfore, we should update this to res.json(err?.message ?? 'unknownError'). incase of null/undefined messages. Also, when we return an error, it's good to set a different status (i.e. not 2XX) response codes, https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status A full list can be found here, but the common ones are 404 - not found, 403 - forbidden/ not logged in, 500 - server error, so 500 is a good catch all for unexpected errors like this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thnx, Edward! I’ll add the null check and set the status to 500 for unknown errors |
||
} | ||
}) | ||
|
||
export default app |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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", | ||
}, | ||
}, | ||
], | ||
], | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
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: [], | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"name": "hackyourtemperature", | ||
"version": "1.0.0", | ||
"main": "server.js", | ||
"type": "module", | ||
"scripts": { | ||
"test": "jest", | ||
"start": "node server.js", | ||
"dev": "nodemon server.js" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"description": "", | ||
"dependencies": { | ||
"@babel/preset-env": "^7.27.2", | ||
"babel-jest": "^29.7.0", | ||
"express": "^5.1.0", | ||
"express-handlebars": "^8.0.3", | ||
"jest": "^29.7.0", | ||
"node-fetch": "^3.3.2", | ||
"nodemon": "^3.1.10", | ||
"supertest": "^7.1.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import app from './app.js' | ||
|
||
app.listen(3000, () => { | ||
console.log(`server started on http://localhost:3000`) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const API_KEY = '7d47d5f41dcdf3b026655cb15bf3ff0a' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's good practice to not push up API keys to repo's to keep them safe. two ways to do this are: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're totally right — I’ll move the key to a .env file and add it to .gitignore. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<h1>Hello from backend to frontend!</h1> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,43 @@ | ||
/** | ||
* Exercise 3: Create an HTTP web server | ||
*/ | ||
|
||
const http = require('http'); | ||
const fs = require('fs').promises | ||
const path = require('path') | ||
const http = require('http') | ||
|
||
//create a server | ||
let server = http.createServer(function (req, res) { | ||
let server = http.createServer(async function (req, res) { | ||
// YOUR CODE GOES IN HERE | ||
res.write('Hello World!'); // Sends a response back to the client | ||
res.end(); // Ends the response | ||
}); | ||
try { | ||
if (req.url === '/') { | ||
const data = await fs.readFile(path.join(__dirname, 'index.html')) | ||
res.writeHead(200, { | ||
'Content-Type': 'text/html', | ||
}) | ||
res.end(data) | ||
} else if (req.url === '/index.js') { | ||
const data = await fs.readFile(path.join(__dirname, 'index.js')) | ||
res.writeHead(200, { | ||
'Content-Type': 'application/javascript', | ||
}) | ||
res.end(data) | ||
} else { | ||
res.writeHead(404, { | ||
'Content-Type': 'text/plain', | ||
}) | ||
res.end('404 not found') | ||
} | ||
} catch (err) { | ||
res.writeHead(500, { | ||
'Content-Type': 'text/plain', | ||
}) | ||
res.end('server error') | ||
console.error(err) | ||
} | ||
// res.write('Hello World!') // Sends a response back to the client | ||
// res.end() // Ends the response | ||
}) | ||
|
||
server.listen(3000); // The server starts to listen on port 3000 | ||
server.listen(3000, () => { | ||
console.log('server is running (listen on port 3000)') | ||
}) // The server starts to listen on port 3000 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "1-joke-api", | ||
"type": "module", | ||
"version": "1.0.0", | ||
"description": "Did you know that there is an API for Chuck Norris jokes? That's incredible, right!?", | ||
"main": "script.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"dependencies": { | ||
"node-fetch": "^3.3.2" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,25 @@ | ||
/** | ||
* 1. Chuck Norris programs do not accept input | ||
* | ||
* | ||
* `GET` a random joke inside the function, using the API: http://www.icndb.com/api/ | ||
* (use `node-fetch`) and print it to the console. | ||
* (use `node-fetch`) and print it to the console. | ||
* Make use of `async/await` and `try/catch` | ||
* | ||
* | ||
* Hints | ||
* - To install node dependencies you should first initialize npm | ||
* - Print the entire response to the console to see how it is structured. | ||
*/ | ||
import fetch from 'node-fetch' | ||
|
||
function printChuckNorrisJoke() { | ||
// YOUR CODE GOES IN HERE | ||
|
||
async function printChuckNorrisJoke() { | ||
try { | ||
const response = await fetch('https://api.chucknorris.io/jokes/random') | ||
// const body = await response.text() | ||
const data = await response.json() | ||
// console.log(response); | ||
console.log(data.value) | ||
} catch (err) { | ||
console.error(err) | ||
} | ||
} | ||
|
||
printChuckNorrisJoke(); | ||
printChuckNorrisJoke() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Lorem ipsum |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,64 @@ | ||
const express = require('express') | ||
const app = express(); | ||
|
||
const app = express() | ||
const fs = require('fs') | ||
|
||
app.use(express.json()) // to read requests format in JSON | ||
|
||
app.post('/blogs', (req, res) => { | ||
// const content = req.body.content | ||
// const title = req.body.title | ||
const { title, content } = req.body | ||
fs.writeFileSync(title, content) | ||
res.end('ok') | ||
}) | ||
|
||
app.put('/posts/:title', (req, res) => { | ||
// console.log(req.query) | ||
const { title, content } = req.body | ||
if (title || content) { | ||
if (fs.existsSync(title)) { | ||
fs.writeFileSync(title, content) | ||
res.end('ok') | ||
} else { | ||
res.end('This post does not exist!') | ||
} | ||
} else { | ||
res.end('the request does not have a title and/or content') | ||
} | ||
}) | ||
|
||
app.delete('/blogs/:title', (req, res) => { | ||
// console.log(req.params.title) | ||
const title = req.params.title | ||
if (fs.existsSync(title)) { | ||
fs.unlinkSync(title); | ||
res.end('ok'); | ||
} else { | ||
res.end('This blog does not exist!') | ||
} | ||
}) | ||
|
||
app.get('/blogs/:title', (req, res) => { | ||
|
||
// How to get the title from the url parameters? | ||
const title = req.params.title | ||
// check if post exists | ||
if (fs.existsSync(title)) { | ||
const post = fs.readFileSync(title); | ||
res.end(`Your post content is: ${post}`) | ||
} else { | ||
res.end('This post does not exist!') | ||
} | ||
|
||
|
||
// send response | ||
}) | ||
|
||
// YOUR CODE GOES IN HERE | ||
app.get('/', function (req, res) { | ||
res.send('Hello World') | ||
}) | ||
|
||
app.listen(3000) | ||
|
||
app.listen(3000, () => { | ||
console.log(`server started on http://localhost:3000`) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good use of mocking systems!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot! :)