renamed
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
PORT=3003
|
||||
MONGODB_URI='mongodb+srv://admin:admin@cluster1.ciuxwxd.mongodb.net/blogApp?retryWrites=true&w=majority'
|
||||
TEST_MONGODB_URI='mongodb+srv://admin:admin@cluster1.ciuxwxd.mongodb.net/testBlogApp?retryWrites=true&w=majority'
|
||||
SECRET='kdsajf90u234ijkasl;sdafds90812jpsakdy3894ioeklj321;ljx098zxxc'
|
||||
@@ -0,0 +1 @@
|
||||
missing testing execise 4.23 maybe even 4.8 -4.14
|
||||
@@ -0,0 +1,22 @@
|
||||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
const mongoose = require("mongoose");
|
||||
const blogsRouter = require("./controllers/blogs");
|
||||
const usersRouter = require("./controllers/users");
|
||||
const loginRouter = require("./controllers/login");
|
||||
const config = require("./utils/config");
|
||||
const middleware = require("./utils/middleware");
|
||||
|
||||
const app = express();
|
||||
|
||||
mongoose.connect(config.MONGODB_URI).then(() => {
|
||||
console.log("connected to mongodb");
|
||||
});
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(middleware.tokenExtractor);
|
||||
app.use("/api/blogs" , blogsRouter);
|
||||
app.use("/api/users", usersRouter);
|
||||
app.use("/api/login", loginRouter);
|
||||
|
||||
module.exports = app;
|
||||
@@ -0,0 +1,56 @@
|
||||
const blogsRouter = require("express").Router();
|
||||
const Blog = require("../models/blog");
|
||||
const User = require("../models/user");
|
||||
const middleware = require("../utils/middleware");
|
||||
|
||||
blogsRouter.get("", (request, response) => {
|
||||
Blog.find({})
|
||||
.populate("user", { name: 1 })
|
||||
.then((blogs) => {
|
||||
response.json(blogs);
|
||||
});
|
||||
});
|
||||
|
||||
blogsRouter.delete("/:id", middleware.userExtractor, async (req, res) => {
|
||||
const id = req.params.id;
|
||||
const blog = await Blog.findById(id);
|
||||
|
||||
let result = null;
|
||||
const userId = req.user._id.toString();
|
||||
|
||||
if (blog.user.toString() === userId) {
|
||||
const newUser = {
|
||||
...req.user._doc,
|
||||
blogs: [...req.user._doc.blogs.filter((el) => el.toString() !== id)],
|
||||
};
|
||||
await User.findOneAndUpdate({ _id: userId }, newUser);
|
||||
result = await Blog.findByIdAndDelete(id);
|
||||
return res.json(result);
|
||||
}
|
||||
return res.end();
|
||||
});
|
||||
|
||||
blogsRouter.put("/:id", (req, res) => {
|
||||
const id = req.params.id;
|
||||
Blog.findOneAndUpdate({ _id: id }, req.body).then((result) => {
|
||||
res.json(result);
|
||||
});
|
||||
});
|
||||
|
||||
blogsRouter.post("", middleware.userExtractor, async (req, res) => {
|
||||
const { title, url } = req.body;
|
||||
const user = req.user;
|
||||
const blog = new Blog({
|
||||
title,
|
||||
author: user.name,
|
||||
url,
|
||||
likes: 0,
|
||||
user: user.id,
|
||||
});
|
||||
const savedBlog = await blog.save();
|
||||
user.blogs = user.blogs.concat(savedBlog._id);
|
||||
user.save();
|
||||
res.json(savedBlog);
|
||||
});
|
||||
|
||||
module.exports = blogsRouter;
|
||||
@@ -0,0 +1,24 @@
|
||||
const loginRouter = require("express").Router();
|
||||
const jwt = require("jsonwebtoken");
|
||||
const bcrypt = require("bcrypt");
|
||||
const User = require("../models/user");
|
||||
const config = require("../utils/config");
|
||||
|
||||
loginRouter.post("", async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
const user = await User.findOne({ username });
|
||||
const passwordCorrect =
|
||||
user === null ? false : await bcrypt.compare(password, user.password);
|
||||
if (!(user && passwordCorrect))
|
||||
return res.status(401).json({ error: "invalid username or passowrd" });
|
||||
|
||||
const userForToken = {
|
||||
username: user.username,
|
||||
id: user._id,
|
||||
};
|
||||
const token = jwt.sign(userForToken, config.SECRET);
|
||||
res.status(200).send({ token, username: user.username, name: user.name });
|
||||
});
|
||||
|
||||
module.exports = loginRouter;
|
||||
@@ -0,0 +1,36 @@
|
||||
const usersRouter = require("express").Router();
|
||||
const User = require("../models/user");
|
||||
const bcrypt = require("bcrypt");
|
||||
|
||||
usersRouter.get("", (request, response) => {
|
||||
User.find({}).then((users) => {
|
||||
response.json(users);
|
||||
});
|
||||
});
|
||||
/*
|
||||
usersRouter.delete("/:id", (request, response) => {
|
||||
const id = request.params.id;
|
||||
User.findByIdAndDelete(id).then((users) => response.json(users));
|
||||
});
|
||||
|
||||
usersRouter.put("/:id", (request, response) => {
|
||||
const id = request.params.id;
|
||||
const data = { ...request.body };
|
||||
User.findOneAndUpdate({ _id: id }, data).then((result) => {
|
||||
response.json(result);
|
||||
});
|
||||
});
|
||||
*/
|
||||
usersRouter.post("", async (request, response) => {
|
||||
const { username, password, name } = request.body;
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
const user = new User({ username, password: hashedPassword, name });
|
||||
try {
|
||||
const savedUser = await user.save();
|
||||
response.status(201).json(savedUser);
|
||||
} catch (err) {
|
||||
response.status(400).send(err._message);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = usersRouter;
|
||||
@@ -0,0 +1,25 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const blogSchema = new mongoose.Schema({
|
||||
title: String,
|
||||
author: String,
|
||||
url: String,
|
||||
likes: Number,
|
||||
comments: [String],
|
||||
user: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "User",
|
||||
},
|
||||
});
|
||||
|
||||
blogSchema.set("toJSON", {
|
||||
transform: (document, returnedObject) => {
|
||||
returnedObject.id = returnedObject._id.toString();
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
},
|
||||
});
|
||||
|
||||
const Blog = mongoose.model("Blog", blogSchema);
|
||||
|
||||
module.exports = Blog;
|
||||
@@ -0,0 +1,26 @@
|
||||
const mongoose = require("mongoose");
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
username: { type: String, minlength: 3, require: true },
|
||||
password: { type: String, require: true },
|
||||
name: String,
|
||||
blogs: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "Blog",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
userSchema.set("toJSON", {
|
||||
transform: (document, returnedObject) => {
|
||||
returnedObject.id = returnedObject._id.toString();
|
||||
delete returnedObject._id;
|
||||
delete returnedObject.__v;
|
||||
delete returnedObject.password;
|
||||
},
|
||||
});
|
||||
|
||||
const User = mongoose.model("User", userSchema);
|
||||
|
||||
module.exports = User;
|
||||
Generated
+9280
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "bloglistpt4",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "NODE_ENV=developmnet nodemon server.js",
|
||||
"test": "NODE_ENV=test jest --verbose"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.1.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.1.4",
|
||||
"express": "^4.18.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"mongoose": "^7.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.5.0",
|
||||
"nodemon": "^2.0.22",
|
||||
"supertest": "^6.3.3"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
const config = require('./utils/config')
|
||||
const logger = require('./utils/logger')
|
||||
const app = require('./app')
|
||||
app.listen(config.PORT, () => {
|
||||
logger.info(`Server running on port ${config.PORT}`)
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
const mongoose = require('mongoose')
|
||||
|
||||
|
||||
|
||||
|
||||
console.log(mongoose.Schema)
|
||||
console.log(mongoose.SchemaType)
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
curl http://localhost:3003/api/blogs | json_pp -json_opt pretty,canonical
|
||||
|
||||
curl http://localhost:3003/api/users | json_pp -json_opt pretty,canonical
|
||||
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"title":"title1"}' http://localhost:3003/api/blogs | json_pp -json_opt pretty,canonical
|
||||
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"username":"tomy1213","password":"tommy123","name":"Tonny Montana"}' http://localhost:3003/api/users | json_pp -json_opt pretty,canonical
|
||||
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"username":"tommy","password":"tommy123"}' http://localhost:3003/api/login | json_pp -json_opt pretty,canonical
|
||||
|
||||
curl -H "Content-Type: application/json" -H "Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRvbW15IiwiaWQiOiI2NDg5NGQwOGVjOGQ0ZTgxMGRlZDQzZDEiLCJpYXQiOjE2ODY3MTk4MDl9.Vb0E6JNbWZTSiHXtXUnt7pT_yLMy8novvFAOqxoYKFA" -X POST -d '{"title":"title1"}' http://localhost:3003/api/blogs | json_pp -json_opt pretty,canonical
|
||||
@@ -0,0 +1,8 @@
|
||||
const listHelper = require("../utils/list_helper");
|
||||
|
||||
test("dummy returns one", () => {
|
||||
const blogs = [];
|
||||
const result = listHelper.dummy(blogs);
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
const listHelper = require("../utils/list_helper");
|
||||
|
||||
describe("total likes", () => {
|
||||
const blogs = [
|
||||
{
|
||||
_id: "5a422a851b54a676234d17f7",
|
||||
title: "React patterns",
|
||||
author: "Michael Chan",
|
||||
url: "https://reactpatterns.com/",
|
||||
likes: 7,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422aa71b54a676234d17f8",
|
||||
title: "Go To Statement Considered Harmful",
|
||||
author: "Edsger W. Dijkstra",
|
||||
url: "http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html",
|
||||
likes: 5,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422b3a1b54a676234d17f9",
|
||||
title: "Canonical string reduction",
|
||||
author: "Edsger W. Dijkstra",
|
||||
url: "http://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD808.html",
|
||||
likes: 12,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422b891b54a676234d17fa",
|
||||
title: "First class tests",
|
||||
author: "Robert C. Martin",
|
||||
url: "http://blog.cleancoder.com/uncle-bob/2017/05/05/TestDefinitions.htmll",
|
||||
likes: 10,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422ba71b54a676234d17fb",
|
||||
title: "TDD harms architecture",
|
||||
author: "Robert C. Martin",
|
||||
url: "http://blog.cleancoder.com/uncle-bob/2017/03/03/TDD-Harms-Architecture.html",
|
||||
likes: 0,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422bc61b54a676234d17fc",
|
||||
title: "Type wars",
|
||||
author: "Robert C. Martin",
|
||||
url: "http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.html",
|
||||
likes: 2,
|
||||
__v: 0,
|
||||
},
|
||||
];
|
||||
test('testing likes',()=>{
|
||||
const result= listHelper.totalLikes(blogs)
|
||||
expect(result).toBe(36)
|
||||
})
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
const listHelper = require("../utils/list_helper");
|
||||
|
||||
describe("maxLikes", () => {
|
||||
const blogs = [
|
||||
{
|
||||
_id: "5a422a851b54a676234d17f7",
|
||||
title: "React patterns",
|
||||
author: "Michael Chan",
|
||||
url: "https://reactpatterns.com/",
|
||||
likes: 7,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422aa71b54a676234d17f8",
|
||||
title: "Go To Statement Considered Harmful",
|
||||
author: "Edsger W. Dijkstra",
|
||||
url: "http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html",
|
||||
likes: 5,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422b3a1b54a676234d17f9",
|
||||
title: "Canonical string reduction",
|
||||
author: "Edsger W. Dijkstra",
|
||||
url: "http://www.cs.utexas.edu/~EWD/transcriptions/EWD08xx/EWD808.html",
|
||||
likes: 12,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422b891b54a676234d17fa",
|
||||
title: "First class tests",
|
||||
author: "Robert C. Martin",
|
||||
url: "http://blog.cleancoder.com/uncle-bob/2017/05/05/TestDefinitions.htmll",
|
||||
likes: 10,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422ba71b54a676234d17fb",
|
||||
title: "TDD harms architecture",
|
||||
author: "Robert C. Martin",
|
||||
url: "http://blog.cleancoder.com/uncle-bob/2017/03/03/TDD-Harms-Architecture.html",
|
||||
likes: 0,
|
||||
__v: 0,
|
||||
},
|
||||
{
|
||||
_id: "5a422bc61b54a676234d17fc",
|
||||
title: "Type wars",
|
||||
author: "Robert C. Martin",
|
||||
url: "http://blog.cleancoder.com/uncle-bob/2016/05/01/TypeWars.html",
|
||||
likes: 2,
|
||||
__v: 0,
|
||||
},
|
||||
];
|
||||
test("most Liked Blog", () => {
|
||||
const result = listHelper.favoriteBlog(blogs);
|
||||
expect(result).toBe(7);
|
||||
});
|
||||
test("most Blogs", () => {
|
||||
const result = listHelper.mostBlogs(blogs);
|
||||
expect(result).toBe(3);
|
||||
});
|
||||
test("most Liked author", () => {
|
||||
const result = listHelper.mostLikes(blogs);
|
||||
expect(result).toBe(17);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
const mongoose = require('mongoose')
|
||||
const supertest = require('supertest')
|
||||
const app = require('../app')
|
||||
|
||||
const api = supertest(app)
|
||||
|
||||
test('blogs are returned as json', async () => {
|
||||
await api
|
||||
.get('/api/blogs')
|
||||
.expect(200)
|
||||
.expect('Content-Type', /application\/json/)
|
||||
})
|
||||
test('blog is being posted',async()=>{
|
||||
await api.post('/api/blogs').send({title:'title2'})
|
||||
const response = await api.get('api/blogs')
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await mongoose.connection.close()
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
require("dotenv").config();
|
||||
|
||||
const MONGODB_URI =
|
||||
process.env.NODE_ENV === "test"
|
||||
? process.env.TEST_MONGODB_URI
|
||||
: process.env.MONGODB_URI;
|
||||
|
||||
module.exports = {
|
||||
SECRET:process.env.SECRET,
|
||||
MONGODB_URI,
|
||||
PORT: process.env.PORT,
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
const dummy = (blogs) => {
|
||||
return 1;
|
||||
};
|
||||
const totalLikes = (blogs) => {
|
||||
return blogs.reduce((acc, cur) => acc + cur.likes, 0);
|
||||
};
|
||||
const favoriteBlog = (blogs) => {
|
||||
let result = blogs.sort((a, b) => a.score - b.score);
|
||||
return result[0].likes;
|
||||
};
|
||||
|
||||
const mostLikes = (blogs) => {
|
||||
let authors = new Map();
|
||||
blogs.forEach((el) => {
|
||||
if (authors.has(el.author))
|
||||
authors.set(el.author, authors.get(el.author) + el.likes);
|
||||
else authors.set(el.author, el.likes);
|
||||
});
|
||||
return Math.max(...authors.values())
|
||||
};
|
||||
const mostBlogs = (blogs) => {
|
||||
let authors = new Map();
|
||||
blogs.forEach((el) => {
|
||||
if (authors.has(el.author))
|
||||
authors.set(el.author, authors.get(el.author) + 1);
|
||||
else authors.set(el.author, 1);
|
||||
});
|
||||
return Math.max(...authors.values())
|
||||
};
|
||||
module.exports = { dummy, mostLikes, mostBlogs, totalLikes, favoriteBlog };
|
||||
@@ -0,0 +1,12 @@
|
||||
const info = (...params) => {
|
||||
console.log(...params);
|
||||
};
|
||||
|
||||
const error = (...params) => {
|
||||
console.error(...params);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
info,
|
||||
error,
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
const User = require("../models/user");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const config = require("./config");
|
||||
|
||||
const tokenExtractor = (req, res, next) => {
|
||||
const authorization = req.get("authorization");
|
||||
if (authorization && authorization.startsWith("Bearer "))
|
||||
req.token = authorization.replace("Bearer ", "");
|
||||
next();
|
||||
};
|
||||
|
||||
const userExtractor = async (req, res, next) => {
|
||||
const decodedToken = jwt.verify(req.token, config.SECRET);
|
||||
|
||||
if (!decodedToken.id) return res.status(401).json({ error: "invalid token" });
|
||||
|
||||
const user = await User.findById(decodedToken.id);
|
||||
console.log('user from the middleware',user)
|
||||
req.user = user;
|
||||
next();
|
||||
};
|
||||
module.exports = { tokenExtractor, userExtractor };
|
||||
Reference in New Issue
Block a user