Skip to content

Commit

Permalink
Merge 2cd6a07 into f2742f1
Browse files Browse the repository at this point in the history
  • Loading branch information
JuwonAbiola committed Sep 11, 2019
2 parents f2742f1 + 2cd6a07 commit 58e973f
Show file tree
Hide file tree
Showing 14 changed files with 481 additions and 6 deletions.
78 changes: 76 additions & 2 deletions server/controllers/Articles.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import models from '../database/models';
import { imageUpload, serverResponse, serverError } from '../helpers';
import {
imageUpload,
serverResponse,
serverError,
articleResponse
} from '../helpers';
import Tags from './Tags';
import middlewares from '../middlewares';

const {
Article, Like, Dislike, Category
Article, Like, Dislike, Category, Comment, Tag, User
} = models;
const { verifyToken, getSessionFromToken } = middlewares;

/**
* Returns server response for the article like/dislike operation
Expand Down Expand Up @@ -432,6 +439,7 @@ const createLikeOrDislike = async (userAction, userId, article) => {
}
}


/**
* gets all author's article
*
Expand Down Expand Up @@ -461,6 +469,72 @@ const createLikeOrDislike = async (userAction, userId, article) => {
return serverError(res);
}
}


/**
*
* @name viewArticle
* @async
* @static
* @memberof Articles
* @param {Object} req express request object
* @param {Object} res express response object
* @returns {JSON} Details of the article and the updated
*/
static async viewArticle(req, res) {
try {
const {
params: { slug },
headers: { authorization }
} = req;
const article = await Article.findOne({
where: { slug },
include: [
{ model: Category, attributes: ['name'] },
{
model: User,
as: 'Author',
attributes: ['avatarUrl', 'firstName', 'lastName', 'userName']
},
{ model: Tag, as: 'tags' },
{ model: Comment, as: 'comments' }
]
});
if (authorization) {
return verifyToken(req, res, () => getSessionFromToken(req, res, () => Articles.authView(req, res, article)));
}
if (!article || article.isArchived || !article.publishedAt) {
return serverResponse(res, 404, { error: 'article not found' });
}
await Article.update({ views: article.views + 1 }, { where: { slug } });
return articleResponse(res, 200, article);
} catch (error) {
return serverError(res);
}
}

/**
* @name authView
* @description allows a user to view their own article
* @param {object} req
* @param {object} res
* @param {object} article
* @returns {json} the json response returned by the server
* @memberof ProfilesController
*/
static authView(req, res, article) {
const {
user: { id: userId }
} = req;

if (article && userId === article.authorId) {
return serverResponse(res, 200, { article });
}
if (!article || article.isArchived || !article.publishedAt) {
return serverResponse(res, 404, { error: 'article not found' });
}
return articleResponse(res, 200, article);
}
}

export default Articles;
3 changes: 3 additions & 0 deletions server/database/migrations/20190813135136-create-article.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ export default {
likesCount: {
type: Sequelize.INTEGER
},
views: {
type: Sequelize.INTEGER
},
dislikesCount: {
type: Sequelize.INTEGER
},
Expand Down
13 changes: 13 additions & 0 deletions server/database/models/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ export default (sequelize, DataTypes) => {
}
}
},
views: {
type: DataTypes.INTEGER,
defaultValue: 0,
validate: {
isInt: {
msg: 'likes count must be integers'
},
min: {
args: [0],
msg: 'articles likes count must not be less than 0'
}
}
},
dislikesCount: {
type: DataTypes.INTEGER,
defaultValue: 0,
Expand Down
31 changes: 31 additions & 0 deletions server/database/seeds/20190731140735-articles-seeds.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,37 @@ export default {
isArchived: false,
createdAt: new Date(),
updatedAt: new Date()
},
{
slug: 'how-to-train-your-drag-6',
title: 'How to train your dragon 6',
description: 'So toothless',
articleBody: 'It is a dragon',
authorId: 1,
likesCount: 0,
dislikesCount: 0,
publishedAt: null,
isArchived: true,
createdAt: new Date(),
updatedAt: new Date()
},
{
slug: 'thrhhr',
title: 'thrhhr',
description: null,
authorId: 1,
image:
'http://res.cloudinary.com/teamrambo50/image/upload/v1565884519/fazaithupzfod35wxwys.png',
articleBody:
'dxrcftyugvihbjlnk;;;;;;;;;;jbihuvgycfvgubionpbivugcyftxdfcgvuhiboj',
likesCount: 0,
views: 36,
categoryId: 3,
isArchived: true,
dislikesCount: 0,
publishedAt: '2019-09-10T14:22:04.482Z',
createdAt: '2019-09-10T14:22:04.486Z',
updatedAt: '2019-09-10T14:55:06.895Z'
}
],
{}
Expand Down
69 changes: 69 additions & 0 deletions server/docs/authors-haven-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,36 @@ paths:
application/json:
schema:
"$ref": "#/components/schemas/updateArticleErrorResponse"
/api/v1/articles/{slug}:
get:
parameters:
- name: slug
in: path
required: true
description: article slug
schema:
type: string
summary: Route for viewing an article
description: Allow users to view an article
responses:
200:
description: article fetched successfully
content:
application/json:
schema:
'$ref': '#/components/schemas/viewArticle'
404:
description: article not found
content:
application/json:
schema:
'$ref': '#/components/schemas/errorResponse'
500:
description: Internal server error
content:
application/json:
schema:
'$ref': '#/components/schemas/serverResponse'

/api/v1/articles/{slug}/comments/{commentId}:
patch:
Expand Down Expand Up @@ -1656,3 +1686,42 @@ components:
message:
type: string
example: no token provided
viewArticle:
type: object
properties:
id:
type: integer
example: 1
title:
type: string
example: seven
description:
type: string
example: pride
image:
type: string
example: 'https://res.cloudinary.com/teamrambo50/image/upload/v1565160704/avatar-1577909_1280_xsoxql.png'
articleBody:
type: string
example: prider
likesCount:
type: integer
example: 1
dislikesCount:
type: integer
example: 0
publishedAt:
type: string
format: date-time
example: 2017-07-21T17:32:28Z
createdAt:
type: string
format: date-time
example: 2017-07-21T17:32:28Z
updatedAt:
type: string
format: date-time
example: 2017-07-21T17:32:28Z
authorId:
type: integer
example: 1
26 changes: 26 additions & 0 deletions server/helpers/articleResponse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @name articleResponse
* @param {Object} res express response object
* @param {Number} code status code to return
* @param {Object} article object with response details
* @param {Object} token strings
* @returns {JSON} JSON response with status and response information
*/
const articleResponse = (res, code, article) => {
const {
dataValues: {
isArchived,
Category,
categoryId,
authorId,
tags,
...articleData
}
} = article;
if (Category) {
articleData.category = Category.name;
}
articleData.tagList = tags.map(({ name }) => name);
return res.status(code).json({ article: articleData });
};
export default articleResponse;
4 changes: 3 additions & 1 deletion server/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
} from './tagHelpers';
import isFollowing from './isFollowing';
import searchCategorizer from './searchCategorizer';
import articleResponse from './articleResponse';

const { expiryDate } = dateHelper;
const { sendResetPasswordEmail, sendVerificationEmail } = emailTemplates;
Expand Down Expand Up @@ -46,5 +47,6 @@ export {
formatTag,
removeDuplicateTags,
isFollowing,
searchCategorizer
searchCategorizer,
articleResponse
};
1 change: 1 addition & 0 deletions server/routes/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ router.delete(
protectedRoutesMiddlewares,
Articles.removeDislike
);
router.get('/read/:slug', Articles.viewArticle);

router.patch(
'/update/:slug',
Expand Down
11 changes: 10 additions & 1 deletion test/articles/__mocks__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ const authenticatedUser = {
confirmPassword: 'authors.haven@haven.com'
};

const checkmateArticleData = {
title: ' checkmate ',
description: 'ext ever since the 1500s, when an unknown printer',
articleBody: 'ext ever since the 1500s, when an unknown printer took a ga',
status: 'publish',
category: 'sport'
};

export {
ArticleData,
ArticleData2,
Expand All @@ -176,5 +184,6 @@ export {
authenticatedUser,
categoryDetails,
ArticleData3,
surplusTagArticleData
surplusTagArticleData,
checkmateArticleData
};
4 changes: 3 additions & 1 deletion test/articles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import * as likeOrDislikeArticle from './likeOrDislikeArticle.test';
import * as removeArticleLikeOrDislike from './removeArticleLikeOrDislike.test';
import * as updateArticle from './update.test';
import * as getUserArticle from './getUserArticles.test';
import * as viewArticle from './viewArticle.test';

export {
createArticle,
likeOrDislikeArticle,
removeArticleLikeOrDislike,
updateArticle,
getUserArticle
getUserArticle,
viewArticle
};
Loading

0 comments on commit 58e973f

Please sign in to comment.