Compare commits
10 commits
8bb31432c7
...
b839aeba78
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b839aeba78 | ||
|
|
e51f318929 | ||
| 0309a2cc95 | |||
| 160402f147 | |||
| a41907fe4b | |||
|
|
9bd54f4719 | ||
|
|
f58ef53347 | ||
|
|
0d97a86ac8 | ||
|
|
09a320a5da | ||
|
|
41ec39c961 |
13 changed files with 387 additions and 34 deletions
|
|
@ -5,7 +5,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/app.js",
|
"start": "node src/app.js",
|
||||||
"dev": "node --watch src/app.js",
|
"dev": "node --watch src/app.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "node --test"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,12 @@
|
||||||
const express = require('express')
|
const express = require("express");
|
||||||
const mongoose = require('mongoose')
|
const { addRoutes } = require("./routes");
|
||||||
|
const app = express();
|
||||||
|
|
||||||
const app = express()
|
app.use(express.json());
|
||||||
|
|
||||||
const blogSchema = mongoose.Schema({
|
addRoutes(app);
|
||||||
title: String,
|
|
||||||
author: String,
|
|
||||||
url: String,
|
|
||||||
likes: Number,
|
|
||||||
})
|
|
||||||
|
|
||||||
const Blog = mongoose.model('Blog', blogSchema)
|
const PORT = 3003;
|
||||||
|
|
||||||
const mongoUrl = 'mongodb://localhost/bloglist'
|
|
||||||
mongoose.connect(mongoUrl)
|
|
||||||
|
|
||||||
app.use(express.json())
|
|
||||||
|
|
||||||
app.get('/api/blogs', (request, response) => {
|
|
||||||
Blog.find({}).then((blogs) => {
|
|
||||||
response.json(blogs)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
app.post('/api/blogs', (request, response) => {
|
|
||||||
const blog = new Blog(request.body)
|
|
||||||
|
|
||||||
blog.save().then((result) => {
|
|
||||||
response.status(201).json(result)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const PORT = 3003
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`Server running on port ${PORT}`)
|
console.log(`Server running on port ${PORT}`);
|
||||||
})
|
});
|
||||||
|
|
|
||||||
15
parts/4/blogApp/src/db.js
Normal file
15
parts/4/blogApp/src/db.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
|
const blogSchema = mongoose.Schema({
|
||||||
|
title: String,
|
||||||
|
author: String,
|
||||||
|
url: String,
|
||||||
|
likes: Number,
|
||||||
|
})
|
||||||
|
|
||||||
|
const Blog = mongoose.model('Blog', blogSchema)
|
||||||
|
|
||||||
|
const mongoUrl = 'mongodb://localhost/bloglist'
|
||||||
|
mongoose.connect(mongoUrl)
|
||||||
|
|
||||||
|
module.exports = {models: {Blog}}
|
||||||
22
parts/4/blogApp/src/routes.js
Normal file
22
parts/4/blogApp/src/routes.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
const { models } = require("./db");
|
||||||
|
const Blog = models.Blog;
|
||||||
|
|
||||||
|
BASE_API_PATH = "/api";
|
||||||
|
|
||||||
|
const addRoutes = (app) => {
|
||||||
|
app.get(`${BASE_API_PATH}/blogs`, (request, response) => {
|
||||||
|
Blog.find({}).then((blogs) => {
|
||||||
|
response.json(blogs);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post(`${BASE_API_PATH}/blogs`, (request, response) => {
|
||||||
|
const blog = new Blog(request.body);
|
||||||
|
|
||||||
|
blog.save().then((result) => {
|
||||||
|
response.status(201).json(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { addRoutes };
|
||||||
89
parts/4/blogApp/src/utils.js
Normal file
89
parts/4/blogApp/src/utils.js
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
const reverse = (string) => {
|
||||||
|
return string.split("").reverse().join("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const average = (array) => {
|
||||||
|
const reducer = (sum, item) => {
|
||||||
|
return sum + item;
|
||||||
|
};
|
||||||
|
|
||||||
|
return array.length === 0 ? 0 : array.reduce(reducer, 0) / array.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
const listHelper = (posts) => {
|
||||||
|
console.log("lol");
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const totalLikes = (posts) => {
|
||||||
|
if (!posts) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const likeCount = posts
|
||||||
|
.map((post) => post.likes)
|
||||||
|
.reduce((cum, value) => cum + value, 0);
|
||||||
|
|
||||||
|
return likeCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
const favoritePost = (posts) => {
|
||||||
|
if (!posts || posts.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const highestLikes = posts.reduce(
|
||||||
|
(max, post) => (post.likes > max ? (max = post.likes) : max),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
const favoritePost = posts.find((post) => post.likes == highestLikes);
|
||||||
|
|
||||||
|
return favoritePost;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mostPosts = (posts) => {
|
||||||
|
if (!posts || posts.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const countMap = posts.reduce((acc, post) => {
|
||||||
|
acc[post.author] = (acc[post.author] || 0) + 1;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const [author, postsCount] = Object.entries(countMap).reduce(
|
||||||
|
(max, entry) => (entry[1] > max[1] ? entry : max),
|
||||||
|
['', 0]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { author, posts: postsCount };
|
||||||
|
};
|
||||||
|
|
||||||
|
const mostLikes = (posts) => {
|
||||||
|
if (!posts || posts.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const likesMap = posts.reduce((acc, post) => {
|
||||||
|
acc[post.author] = (acc[post.author] || 0) + post.likes;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const [author, likesCount] = Object.entries(likesMap).reduce(
|
||||||
|
(max, entry) => (entry[1] > max[1] ? entry : max),
|
||||||
|
['', 0]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { author, likes: likesCount };
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
reverse,
|
||||||
|
average,
|
||||||
|
listHelper,
|
||||||
|
totalLikes,
|
||||||
|
favoritePost,
|
||||||
|
mostPosts,
|
||||||
|
mostLikes
|
||||||
|
};
|
||||||
18
parts/4/blogApp/tests/average.test.js
Normal file
18
parts/4/blogApp/tests/average.test.js
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
const { test, describe } = require('node:test')
|
||||||
|
const assert = require('node:assert')
|
||||||
|
|
||||||
|
const average = require('../src/utils').average
|
||||||
|
|
||||||
|
describe('average', () => {
|
||||||
|
test('of one value is the value itself', () => {
|
||||||
|
assert.strictEqual(average([1]), 1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('of many is calculated right', () => {
|
||||||
|
assert.strictEqual(average([1, 2, 3, 4, 5, 6]), 3.5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('of empty array is zero', () => {
|
||||||
|
assert.strictEqual(average([]), 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
43
parts/4/blogApp/tests/favoritePost.test.js
Normal file
43
parts/4/blogApp/tests/favoritePost.test.js
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
const { test, describe } = require("node:test");
|
||||||
|
const assert = require("node:assert");
|
||||||
|
const { favoritePost } = require("../src/utils");
|
||||||
|
|
||||||
|
describe("favoritePost ", () => {
|
||||||
|
const posts = [
|
||||||
|
{
|
||||||
|
_id: "5a422aa71b54a676234d17f8",
|
||||||
|
title: "Go To Statement Considered Harmful",
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 5,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "5a422aa71b54a676234d17f8",
|
||||||
|
title: "Go To Statement Considered Harmful",
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 2,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const emptyArray = [];
|
||||||
|
|
||||||
|
const gibberish = "asdaSd123asd";
|
||||||
|
|
||||||
|
test("finds top properly", () => {
|
||||||
|
assert.strictEqual(favoritePost(posts), posts[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("works fine with empty array", () => {
|
||||||
|
assert.strictEqual(favoritePost(emptyArray), null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("fails with gibberish input", () => {
|
||||||
|
const failedCall = () => {
|
||||||
|
favoritePost(gibberish);
|
||||||
|
};
|
||||||
|
assert.throws(failedCall, Error);
|
||||||
|
});
|
||||||
|
});
|
||||||
10
parts/4/blogApp/tests/listHelper.test.js
Normal file
10
parts/4/blogApp/tests/listHelper.test.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
const { test, describe } = require("node:test");
|
||||||
|
const assert = require("node:assert");
|
||||||
|
const { listHelper } = require("../src/utils");
|
||||||
|
|
||||||
|
test("dummy returns one", () => {
|
||||||
|
const blogs = [];
|
||||||
|
|
||||||
|
const result = listHelper(blogs);
|
||||||
|
assert.strictEqual(result, 1);
|
||||||
|
});
|
||||||
54
parts/4/blogApp/tests/mostLikes.test.js
Normal file
54
parts/4/blogApp/tests/mostLikes.test.js
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
const { test, describe } = require("node:test");
|
||||||
|
const assert = require("node:assert");
|
||||||
|
const { mostLikes } = require("../src/utils");
|
||||||
|
|
||||||
|
describe("most likes ", () => {
|
||||||
|
const posts = [
|
||||||
|
{
|
||||||
|
_id: "5a422aa71b54a676234d17f8",
|
||||||
|
title: "Go To Statement Considered Harmful",
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 5,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "5a422aa71b54a676234d17f8",
|
||||||
|
title: "Go To Statement Considered Harmful",
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 2,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "123",
|
||||||
|
title: "Lololo",
|
||||||
|
author: "John Doe",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 1,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const emptyArray = [];
|
||||||
|
|
||||||
|
const gibberish = "asdaSd123asd";
|
||||||
|
|
||||||
|
test("finds top author properly", () => {
|
||||||
|
assert.deepStrictEqual(mostLikes(posts), {
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
likes: 7,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("works fine with empty array", () => {
|
||||||
|
assert.strictEqual(mostLikes(emptyArray), null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("fails with gibberish input", () => {
|
||||||
|
const failedCall = () => {
|
||||||
|
mostLikes(gibberish);
|
||||||
|
};
|
||||||
|
assert.throws(failedCall, Error);
|
||||||
|
});
|
||||||
|
});
|
||||||
54
parts/4/blogApp/tests/mostPosts.test.js
Normal file
54
parts/4/blogApp/tests/mostPosts.test.js
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
const { test, describe } = require("node:test");
|
||||||
|
const assert = require("node:assert");
|
||||||
|
const { mostPosts } = require("../src/utils");
|
||||||
|
|
||||||
|
describe("most posts ", () => {
|
||||||
|
const posts = [
|
||||||
|
{
|
||||||
|
_id: "5a422aa71b54a676234d17f8",
|
||||||
|
title: "Go To Statement Considered Harmful",
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 5,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "5a422aa71b54a676234d17f8",
|
||||||
|
title: "Go To Statement Considered Harmful",
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 2,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "123",
|
||||||
|
title: "Lololo",
|
||||||
|
author: "John Doe",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 1,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const emptyArray = [];
|
||||||
|
|
||||||
|
const gibberish = "asdaSd123asd";
|
||||||
|
|
||||||
|
test("finds top author properly", () => {
|
||||||
|
assert.deepStrictEqual(mostPosts(posts), {
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
posts: 2,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("works fine with empty array", () => {
|
||||||
|
assert.strictEqual(mostPosts(emptyArray), null);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("fails with gibberish input", () => {
|
||||||
|
const failedCall = () => {
|
||||||
|
mostPosts(gibberish);
|
||||||
|
};
|
||||||
|
assert.throws(failedCall, Error);
|
||||||
|
});
|
||||||
|
});
|
||||||
22
parts/4/blogApp/tests/reverse.test.js
Normal file
22
parts/4/blogApp/tests/reverse.test.js
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
const { test } = require('node:test')
|
||||||
|
const assert = require('node:assert')
|
||||||
|
|
||||||
|
const reverse = require('../src/utils.js').reverse
|
||||||
|
|
||||||
|
test('reverse of a', () => {
|
||||||
|
const result = reverse('a')
|
||||||
|
|
||||||
|
assert.strictEqual(result, 'a')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('reverse of react', () => {
|
||||||
|
const result = reverse('react')
|
||||||
|
|
||||||
|
assert.strictEqual(result, 'tcaer')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('reverse of saippuakauppias', () => {
|
||||||
|
const result = reverse('saippuakauppias')
|
||||||
|
|
||||||
|
assert.strictEqual(result, 'saippuakauppias')
|
||||||
|
})
|
||||||
43
parts/4/blogApp/tests/totalLikes.test.js
Normal file
43
parts/4/blogApp/tests/totalLikes.test.js
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
const { test, describe } = require("node:test");
|
||||||
|
const assert = require("node:assert");
|
||||||
|
const { totalLikes } = require("../src/utils");
|
||||||
|
|
||||||
|
describe("total likes ", () => {
|
||||||
|
const posts = [
|
||||||
|
{
|
||||||
|
_id: "5a422aa71b54a676234d17f8",
|
||||||
|
title: "Go To Statement Considered Harmful",
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 5,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "5a422aa71b54a676234d17f8",
|
||||||
|
title: "Go To Statement Considered Harmful",
|
||||||
|
author: "Edsger W. Dijkstra",
|
||||||
|
url: "https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf",
|
||||||
|
likes: 2,
|
||||||
|
__v: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const emptyArray = [];
|
||||||
|
|
||||||
|
const gibberish = "asdaSd123asd";
|
||||||
|
|
||||||
|
test("counts likes properly", () => {
|
||||||
|
assert.strictEqual(totalLikes(posts), 7);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("works fine with empty array", () => {
|
||||||
|
assert.strictEqual(totalLikes(emptyArray), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("fails with gibberish input", () => {
|
||||||
|
const failedCall = () => {
|
||||||
|
totalLikes(gibberish);
|
||||||
|
};
|
||||||
|
assert.throws(failedCall, Error);
|
||||||
|
});
|
||||||
|
});
|
||||||
8
parts/4/notes.md
Normal file
8
parts/4/notes.md
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
Exercises:
|
||||||
|
* [X] 4.1
|
||||||
|
* [X] 4.2
|
||||||
|
* [X] 4.3
|
||||||
|
* [X] 4.4
|
||||||
|
* [X] 4.5
|
||||||
|
* [X] 4.6
|
||||||
|
* [X] 4.7
|
||||||
Loading…
Add table
Add a link
Reference in a new issue