Skip to content

Commit

Permalink
Merge df3514f into f35b173
Browse files Browse the repository at this point in the history
  • Loading branch information
Anguandia committed Aug 28, 2019
2 parents f35b173 + df3514f commit d401ce1
Show file tree
Hide file tree
Showing 14 changed files with 2,255 additions and 1,247 deletions.
2,426 changes: 1,206 additions & 1,220 deletions package-lock.json

Large diffs are not rendered by default.

136 changes: 136 additions & 0 deletions src/controllers/bookmarks.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/* eslint-disable require-jsdoc */
import Util from '../helpers/util';
import dbService from '../services/data.service';
import models from '../models';

const {
checkItem, ensureBookMark, getUserBookMarks, addToCollection,
findCollection, renameCollection, updateItem, deleteCollection,
unCollect, getBookMarkName
} = dbService;

const util = new Util();

const notFound = (msg) => {
util.setError(404, `${msg} not found`);
return util;
};

class BookMarkController {
static async createBookMark(req, res) {
const article = await checkItem(req.body.articleId, 'Article');
const name = req.body.name || `${article.title}-${new Date()}`;
const bookmark = await ensureBookMark(req.body.articleId, name, req.auth.id);
return res.status(201).json({
message: `bookmark '${name}' created`, data: bookmark[0]
});
}

static async editBookMark(req, res) {
const oldName = req.params.name || req.data.name;
const name = req.body.name || req.data.newName;
const updated = updateItem(oldName, name, 'BookMark');
return res.status(200).json({
message: `bookmark ${oldName} updated to ${name}`,
data: updated[1]
});
}

static async createCollection(req, res) {
const { name, articleId, collection } = req.body;
const added = await addToCollection(articleId, collection, name, req.auth.id);
if (added[1]) {
return res.status(201).json({
message: `bookmark '${name}' added to collection '${collection}'`
});
}
res.status(200).json({
message: `bookmark '${name}' already in collection '${collection}'`
});
}

static async getCollection(req, res) {
const collection = await findCollection(req.params.collection, req.auth.id, [models.Article]);
return res.status(200).json({ collection });
}

static async getCollections(req, res) {
const all = await getUserBookMarks(req.auth.id, [models.Article]);
const collections = all.filter(x => x.collection);
if (collections.length) {
// reduce/map collections to collection: bookmark-count object array
return res.status(200).json({ collections });
}
notFound('collections').send(res);
}

static async updateCollection(req, res) {
const { collection } = req.params;
const updated = await renameCollection(
req.body.collection, collection, req.auth.id
);
return res.status(200).json({
message: `collection '${collection}' updated to ${collection}`,
data: updated
});
}

// delete collection and content
static async deleteCollection(req, res) {
const { collection } = req.params;
await deleteCollection(collection);
return res.status(200).json({ message: `collection '${collection}' deleted` });
}

// delete from collection
static async unCollect(req, res) {
const { collection, name } = req.params;
await unCollect(collection, name, req.auth.id);
return res.status(200).json({
message: `bookmark ${name} deleted from collection ${collection}`
});
}

static async getUserBookMarks(req, res) {
const bookmarks = await getUserBookMarks(req.auth.id);
return res.status(200).json({
message: `${bookmarks.length} bookmarks found`,
data: bookmarks
});
}

static async getUserBookMark(req, res) {
const bookmark = await getBookMarkName(req.auth.id, req.params.name);
return res.status(200).json({
message: `bookmark '${bookmark.name}' found`,
data: bookmark
});
}

static async deleteUserBookMark(req, res) {
const bookmark = await getBookMarkName(req.auth.id, req.params.name);
bookmark.destroy();
return res.status(200).json({
message: `bookmark '${req.params.name}' deleted`
});
}

static async deleteUserBookMarks(req, res) {
const reader = await checkItem(req.auth.id, 'user', ['articles']);
const deleted = await reader.removeArticles(reader.articles);
res.status(200).json({ message: `${deleted} bookmarks deleted` });
}

static async copyBookmark(req, res) {
const {
articleId, name, newName, userId
} = req.data;
const copy = await ensureBookMark(articleId, newName, userId);
res.status(201).json({
message: `copy of bookmark ${name} created as ${newName}`,
data: copy
});
}
}

export default BookMarkController;
105 changes: 105 additions & 0 deletions src/middlewares/bookmarks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/* eslint-disable require-jsdoc */
import Util from '../helpers/util';
import dbService from '../services/data.service';

const {
getUserBookMarks, getUserBookMark, checkExisting,
checkName, checkCollection, getBookMarkName, checkItem
} = dbService;

const util = new Util();

const notFound = (msg) => {
util.setError(404, `${msg} not found`);
return util;
};

let data;
class BookMarkWare {
static async checkBookmark(req, res, next) {
const articleId = req.params.articleId || req.body.articleId;
const name = req.body.oldName ? req.body.oldName : req.params.name || req.body.name;
const bookmark = await getUserBookMark(req.auth.id, name, articleId);

if (!bookmark) {
return notFound(`bookmark ${name} with article ID ${articleId}`).send(res);
}
if (req.method === 'PATCH' && req.body.name === req.params.name) {
return res.status(400).json({ message: 'update aborted, old and new name the same' });
}
next();
}

static async checkUserBookMarks(req, res, next) {
const bookmarks = await getUserBookMarks(req.auth.id);
if (!bookmarks.length) {
return notFound('user bookmarks').send(res);
}
next();
}

static async checkCollection(req, res, next) {
const oldCollection = req.body.oldCollection || req.params.collection;
const found = await checkCollection(
oldCollection, req.auth.id
);
if (!found) {
return notFound(`collection '${oldCollection}'`).send(res);
}
next();
}

static async checkDuplicate(req, res, next) {
const { articleId, name } = req.body;
const existing = await checkExisting(req.auth.id, articleId);
const existingName = await checkName(name, 'BookMark');
const bookmark = await getUserBookMark(req.auth.id, name, articleId);
const article = await checkItem(articleId, 'Article');
if (!article) {
return notFound(`article with id ${articleId}`).send(res);
}
if (bookmark) {
return res.status(200).json({
message: `bookmark '${name}' exists`, data: bookmark
});
}
if (existing && existing.name) {
data = existing;
data.newName = name;
return res.status(200).json({
message: `bookmark existing as '${existing.name}'`,
options: {
1: `create copy with new name ${name} on /api/v1/users/copy`,
2: `update the name to ${name} on /api/v1/users/update`
},
data: existing
});
}
if (existingName) {
res.status(409).json({
message: `another bookmark with name ${name}`,
data: existingName
});
}
next();
}

static async createCopy(req, res, next) {
req.data = data;
next();
}

static async checkBookmarkName(req, res, next) {
const { name } = req.params;
const bookmark = await getBookMarkName(req.auth.id, name);
if (!bookmark) {
return notFound(`bookmark '${name}'`).send(res);
}
if (req.body.name === name) {
return res.status(400).json({ message: 'update aborted, old and new name the same' });
}
next();
}
}

export default BookMarkWare;
37 changes: 37 additions & 0 deletions src/migrations/create-bookmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('BookMarks', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
articleId: {
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
collection: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('BookMarks');
}
};

5 changes: 5 additions & 0 deletions src/models/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ module.exports = (sequelize, DataTypes) => {
foreignKey: 'articleId',
as: 'tags'
})
Article.belongsToMany(models.user, {
through: 'BookMarks',
foreignKey: 'articleId',
as: 'readers'
})
};
return Article;
};
15 changes: 15 additions & 0 deletions src/models/bookmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const BookMark = sequelize.define('BookMark', {
articleId: DataTypes.INTEGER,
userId: DataTypes.INTEGER,
name: DataTypes.STRING,
collection: DataTypes.STRING
}, {});
BookMark.associate = function (models) {
BookMark.belongsTo(models.Article, { foreignKey: 'articleId' })
BookMark.belongsTo(models.user, { foreignKey: 'userId' })
};
return BookMark;
};

7 changes: 7 additions & 0 deletions src/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ module.exports = (sequelize, DataTypes) => {
as: 'author',
foreignKey: 'authorId'
})
user.belongsToMany(Article, {
through: 'BookMarks',
as: 'articles',
foreignKey: 'userId',
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
})
};
return user;
};
Empty file added src/routes/api/article/doc.js
Empty file.
10 changes: 10 additions & 0 deletions src/routes/api/bookmarks/bookmarks.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import express from 'express';
import BookMarkController from '../../../controllers/bookmarks.controller';
import auth from '../../../middlewares/auth';

const router = express.Router();

const { createBookMark, getUserBookMarks, editBookMark } = BookMarkController;

router.post('/users/bookmarks/:articleId', auth, createBookMark);
router.get('/users/bookmarks', auth, getUserBookMarks);
Loading

0 comments on commit d401ce1

Please sign in to comment.