Skip to content

Commit

Permalink
Merge 6defa56 into da24335
Browse files Browse the repository at this point in the history
  • Loading branch information
rafmme authored Jan 28, 2019
2 parents da24335 + 6defa56 commit 5900d8b
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 33 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
"methods": "^1.1.2",
"mocha-lcov-reporter": "^1.3.0",
"morgan": "^1.9.1",
"node-boolify": "^1.0.5",
"passport": "^0.4.0",
"passport-facebook": "^2.1.1",
"passport-google-oauth20": "^1.0.0",
Expand Down
87 changes: 65 additions & 22 deletions server/controllers/ArticleController.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Boolify } from 'node-boolify';
import db from '../models';
import ArticleHelper from '../helpers/ArticleHelper';
import Util from '../helpers/Util';
Expand Down Expand Up @@ -35,29 +34,27 @@ class ArticleController {
content,
banner: banner || 'https://unsplash.com/photos/Q7wDdmgCBFg',
tagsList: tagsArray,
isPublished: Boolify(isPublished) || true,
isPublished: Boolean(isPublished),
isReported: false
};

let article = await Article.create(articleData);
article = article.toJSON();
article.tags = articleData.tagsList;
const { createdAt, updatedAt } = article;

if (article) {
article = article.toJSON();
article.tags = articleData.tagsList;
const { createdAt, updatedAt } = article;
await TagHelper.findOrAddTag(article.id, tagsArray);
article.createdAt = Util.formatDate(createdAt);
article.updatedAt = Util.formatDate(updatedAt);
await TagHelper.findOrAddTag(article.id, tagsArray);
article.createdAt = Util.formatDate(createdAt);
article.updatedAt = Util.formatDate(updatedAt);

return response(
res,
201,
'success',
'New article has been successfully created',
null,
article
);
}
return response(
res,
201,
'success',
'New article has been successfully created',
null,
article
);
} catch (error) {
return response(
res,
Expand Down Expand Up @@ -230,10 +227,9 @@ class ArticleController {
}
});
if (result) {
const articleSlug =
result.title.toLowerCase() === req.body.title.toLowerCase()
? result.slug
: ArticleHelper.generateArticleSlug(req.body.title);
const articleSlug = result.title.toLowerCase() === req.body.title.toLowerCase()
? result.slug
: ArticleHelper.generateArticleSlug(req.body.title);

req.body.slug = articleSlug;
let article = await result.update(req.body);
Expand Down Expand Up @@ -316,6 +312,53 @@ class ArticleController {
return response(res, 400, 'failure', 'No search parameters supplied', null, null);
}
}

/**
* @static
* @description this handles the sharing of articles on social media
* @param {object} req HTTP request object
* @param {object} res HTTP response object
* @returns {object} api route response
*/
static async share(req, res) {
const { slug } = req.params;
const { platform } = req.query;
let article = await Article.findOne({ where: { slug, isPublished: true } });

if (!article) {
return response(
res,
404,
'failure',
'not found error',
{ message: 'Article not found' },
null
);
}

article = article.toJSON();
const url = `https://neon-ah-staging.herokuapp.com/articles/${slug}`;
const postContent = {
platform,
title: article.title,
body: article.content,
imageUrl: article.banner,
url
};

const socialShareLink = ArticleHelper.generateSocialShareLink(postContent);
if (socialShareLink === '') {
return response(
res,
400,
'failure',
'bad request error',
{ message: 'Invalid social media platform supplied' },
null
);
}
return res.redirect(socialShareLink);
}
}

export default ArticleController;
36 changes: 36 additions & 0 deletions server/helpers/ArticleHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,42 @@ class ArticleHelper {
return slug;
}
}

/**
* @static
* @description a function for generating social share link
* @param {object} contentData
* @returns {string} returns the social share link
*/
static generateSocialShareLink(contentData) {
let shareLink = '';
const {
platform,
title,
body,
url,
imageUrl
} = contentData;
switch (`${platform}`.toLowerCase()) {
case 'twitter':
shareLink = `https://twitter.com/share?url=${url}&text=${title}\n${body.slice(0, 25)}...\n${imageUrl}\n`;
break;
case 'facebook':
case 'fb':
shareLink = `https://facebook.com/sharer.php?&u=${url}`;
break;
case 'whatsapp':
shareLink = `whatsapp://send?text=*${title}*\n\`\`\`${body.slice(0, 300)}...\`\`\`\n${url}`;
break;
case 'linkedin':
shareLink = `https://linkedin.com/shareArticle?&url=${url}&title=${title}&summary=${body.slice(0, 300)}...`;
break;
default:
shareLink = '';
break;
}
return shareLink;
}
}

export default ArticleHelper;
16 changes: 8 additions & 8 deletions server/middlewares/validations/ArticleValidation.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/* eslint-disable no-useless-escape */
/* eslint-disable no-unused-expressions */
/* eslint-disable prefer-const */

import { Boolify } from 'node-boolify';
import db from '../../models';
import Util from '../../helpers/Util';
import response from '../../helpers/response';
Expand Down Expand Up @@ -163,9 +161,9 @@ class ArticleValidation {
req.body.banner = req.body.banner
? Util.removeExtraWhitespace(req.body.banner) : article.banner;
req.body.isPublished = req.body.isPublished !== undefined
? Boolify(req.body.isPublished) : article.isPublished;
? Boolean(req.body.isPublished) : article.isPublished;
req.body.isReported = req.body.isReported !== undefined
? Boolify(req.body.isReported) : article.isReported;
? Boolean(req.body.isReported) : article.isReported;
return next();
}
return response(
Expand Down Expand Up @@ -198,7 +196,7 @@ class ArticleValidation {
return next();
}
if (!Number.isSafeInteger(parseInt(limit, 10))) {
response(res, 400, 'failure', 'There was an issue with your query');
return response(res, 400, 'failure', 'There was an issue with your query');
}
return next();
} catch (error) {
Expand All @@ -207,7 +205,8 @@ class ArticleValidation {
'server error',
{
message: 'Something went wrong on the server'
}, null);
}
);
}
}

Expand All @@ -229,7 +228,7 @@ class ArticleValidation {
return next();
}
if (!Number.isSafeInteger(parseInt(page, 10))) {
response(res, 400, 'failure', 'There was an issue with your query');
return response(res, 400, 'failure', 'There was an issue with your query');
}
return next();
} catch (error) {
Expand All @@ -238,7 +237,8 @@ class ArticleValidation {
'server error',
{
message: 'Something went wrong on the server'
}, null);
}
);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion server/migrations/20190111130326-create-comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default {
allowNull: false
},
articleId: {
type: Sequelize.STRING,
type: Sequelize.UUID,
allowNull: false
},
createdAt: {
Expand Down
4 changes: 4 additions & 0 deletions server/routes/api/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,9 @@ articleRoutes.delete(
ArticleValidation.verifyUserOwnStory,
ArticleController.remove
);
articleRoutes.get(
'/articles/share/:slug',
ArticleController.share
);

export default articleRoutes;
30 changes: 30 additions & 0 deletions server/test/controllers/articles.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,34 @@ describe('API endpoint /articles/', () => {
expect(response.body.data.message).to.eqls('not found error');
});
});

describe('SHARE an article', () => {
it('should successfully share an article with the specified slug', async () => {
const response = await chai
.request(app)
.get('/api/v1/articles/share/how-to-be-a-10x-dev-sGNYfURm?platform=twitter');

expect(response.status).to.eqls(200);
});

it('should return not found if article does not exist', async () => {
const response = await chai
.request(app)
.get('/api/v1/articles/share/jwt-key-use-case-2?platform=whatsapp');

expect(response.status).to.eqls(404);
expect(response.body.status).to.eqls('failure');
expect(response.body.data.message).to.eqls('not found error');
});

it('should return bad request if no social media platform is specified', async () => {
const response = await chai
.request(app)
.get('/api/v1/articles/share/how-to-be-a-10x-dev-sGNYfURm?platform');

expect(response.status).to.eqls(400);
expect(response.body.status).to.eqls('failure');
expect(response.body.data.message).to.eqls('bad request error');
});
});
});
2 changes: 1 addition & 1 deletion server/test/controllers/searchController.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('Search Model', () => {
.request(app)
.get('/api/v1/search?author=s')
.set('Authorization', `Bearer ${token}`)
console.log(response.body)

expect(response.body.data.statusCode).to.equal(500);
stub.restore();
});
Expand Down
60 changes: 60 additions & 0 deletions server/test/helpers/articleHelper.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,64 @@ describe('ArticleHelpers Test', () => {
done();
});
});

describe('generateSocialShareLink function test', () => {
it('should successfully generate a twitter share link', (done) => {
const twitterShareLink = ArticleHelper.generateSocialShareLink({
platform: 'twitter',
title: 'Hello World',
url: 'https://test.com/hello',
body: 'Greetings to the world',
imageUrl:'https://imageUrl.com/img.jpg'
});

expect(twitterShareLink).to.eql('https://twitter.com/share?url=https://test.com/hello&text=Hello World\nGreetings to the world...\nhttps://imageUrl.com/img.jpg\n');
done();
});

it('should successfully generate a facebook share link', (done) => {
const fbShareLink = ArticleHelper.generateSocialShareLink({
platform: 'facebook',
title: 'Hello World',
url: 'https://test.com/hello',
body: 'Greetings to the world',
imageUrl:'https://imageUrl.com/img.jpg'
});

expect(fbShareLink).to.eql('https://facebook.com/sharer.php?&u=https://test.com/hello');
done();
});

it('should successfully generate a linkedIn share link', (done) => {
const lInShareLink = ArticleHelper.generateSocialShareLink({
platform: 'linkedIn',
title: 'Hello World',
url: 'https://test.com/hello',
body: 'Greetings to the world',
imageUrl:'https://imageUrl.com/img.jpg'
});

expect(lInShareLink).to.eql('https://linkedin.com/shareArticle?&url=https://test.com/hello&title=Hello World&summary=Greetings to the world...');
done();
});

it('should successfully generate a whatsapp share link', (done) => {
const whatsappShareLink = ArticleHelper.generateSocialShareLink({
platform: 'whatsapp',
title: 'Hello World',
url: 'https://test.com/hello',
body: 'Greetings to the world',
imageUrl:'https://imageUrl.com/img.jpg'
});

expect(whatsappShareLink).to.eql('whatsapp://send?text=*Hello World*\n```Greetings to the world...```\nhttps://test.com/hello');
done();
});

it('should return empty string for unidentified social platform', (done) => {
const emptyShareLink = ArticleHelper.generateSocialShareLink({});
expect(emptyShareLink).to.eql('');
done();
});
});
});

0 comments on commit 5900d8b

Please sign in to comment.