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": {
|
||||
"start": "node src/app.js",
|
||||
"dev": "node --watch src/app.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "node --test"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
|
|
|||
|
|
@ -1,37 +1,12 @@
|
|||
const express = require('express')
|
||||
const mongoose = require('mongoose')
|
||||
const express = require("express");
|
||||
const { addRoutes } = require("./routes");
|
||||
const app = express();
|
||||
|
||||
const app = express()
|
||||
app.use(express.json());
|
||||
|
||||
const blogSchema = mongoose.Schema({
|
||||
title: String,
|
||||
author: String,
|
||||
url: String,
|
||||
likes: Number,
|
||||
})
|
||||
addRoutes(app);
|
||||
|
||||
const Blog = mongoose.model('Blog', blogSchema)
|
||||
|
||||
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
|
||||
const PORT = 3003;
|
||||
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