Skip to content

Commit

Permalink
Merge 480fcef into 9ed0b04
Browse files Browse the repository at this point in the history
  • Loading branch information
MCFrank16 committed Jul 10, 2019
2 parents 9ed0b04 + 480fcef commit 7addc6e
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 7 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
"moment": "^2.24.0",
"morgan": "^1.9.1",
"multer": "^1.4.1",
"open": "^6.4.0",
"passport": "^0.4.0",
"passport-facebook": "^3.0.0",
"passport-google-oauth": "^2.0.0",
Expand Down
36 changes: 33 additions & 3 deletions src/api/controllers/articleController.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import errorSender from '../../helpers/error.sender';
import generatePagination from '../../helpers/generate.pagination.details';

const {
Article, Category, User, Report, Highlight
Article, Category, User, Report, Share, Highlight
} = models;

const { CREATED, BAD_REQUEST } = statusCodes;
Expand Down Expand Up @@ -239,8 +239,8 @@ export default class ArticleController {
'Sorry, but that reason does not exist, Thanks');
}
}
/*

/**
* allow a user to highlight a text in an article
*
* @author Alain Burindi
Expand Down Expand Up @@ -270,4 +270,34 @@ export default class ArticleController {
errorSender(BAD_REQUEST, res, 'text', errorMessage.textMatch);
}
}

/**
* allow an author to report a certain article as inappropriate
*
* @author Frank Mutabazi
* allow a user to share an article on social media
*
* @static
* @param {object} req the request
* @param {object} res the response to be sent
* @memberof ArticleController
* @returns {Object} res
*/
static async socialShareArticle(req, res) {
const { user: { id } } = req.user;
const { slug } = req.params;
const { sharedOn } = req;
const { title } = req;

await Share.create({
userId: id,
articleSlug: slug,
sharedOn
});

return res.status(statusCodes.CREATED).json({
status: statusCodes.CREATED,
message: `${title} has been shared successfully on ${sharedOn}, Thanks`
});
}
}
49 changes: 49 additions & 0 deletions src/api/migrations/20190704140437-create-share.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

export default {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Shares', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
allowNull: false,
unique: false
},
articleSlug: {
type: Sequelize.STRING,
references: {
model: 'Articles',
key: 'slug'
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
allowNull: false,
unique: false
},
sharedOn: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
})
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Shares');
}
};
1 change: 1 addition & 0 deletions src/api/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const models = {
Rating: sequelize.import('./rating'),
Report: sequelize.import('./report'),
Reason: sequelize.import('./reasons'),
Share: sequelize.import('./share'),
Highlight: sequelize.import('./highlight'),
Following: sequelize.import('./following')
};
Expand Down
33 changes: 33 additions & 0 deletions src/api/models/share.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export default (sequelize, DataTypes) => {
const Share = sequelize.define('Share', {
userId:
{
type: DataTypes.INTEGER,
onDelete: 'CASCADE',
allowNull: false,
references: {
model: 'Users',
key: 'id'
}
},
articleSlug: {
type: DataTypes.STRING,
onDelete: 'CASCADE',
allowNull: false,
references: {
model: 'Articles',
key: 'slug'
}
} ,
sharedOn: {
type: DataTypes.STRING,
onDelete: 'CASCADE',
allowNull: false
}
}, {});
Share.associate = ({ User, Article }) => {
Share.belongsTo(User, { foreignKey: 'userId'});
Share.belongsTo(Article, { foreignKey: 'articleSlug'});
};
return Share;
};
26 changes: 26 additions & 0 deletions src/api/routes/articleRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import checkArticleOwner from '../../middlewares/checkArticleOwnership';
import checkExistingRates from '../../middlewares/checkExistingRating';
import uploadImage from '../../middlewares/upload';
import errorHandler from '../../middlewares/errorHandler';
import shareArticle from '../../middlewares/shareArticle';
import validateRatingsRoute from '../../middlewares/validations/ratings.routes';

const articleRouter = new Router();
Expand Down Expand Up @@ -62,4 +63,29 @@ articleRouter.post('/:slug/report',
checkArticleOwner.checkOwner,
articleController.reportAnArticle);

articleRouter.post('/:slug/share/facebook',
checkValidToken,
checkArticle.getArticle,
shareArticle,
articleController.socialShareArticle);

articleRouter.post('/:slug/share/twitter',
checkValidToken,
checkArticle.getArticle,
shareArticle,
articleController.socialShareArticle);

articleRouter.post('/:slug/share/linkedin',
checkValidToken,
checkArticle.getArticle,
shareArticle,
articleController.socialShareArticle);

articleRouter.post('/:slug/share/gmail',
checkValidToken,
checkArticle.getArticle,
shareArticle,
articleController.socialShareArticle);


export default articleRouter;
13 changes: 9 additions & 4 deletions src/configs/environments.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const testDatabase = process.env.TEST_DATABASE;
const host = process.env.HOST;
const dialect = 'postgres';
const port = process.env.DB_PORT;
const { APP_URL_FRONTEND } = process.env;

const environments = [
{
Expand All @@ -31,7 +32,8 @@ const environments = [
database: testDatabase,
host,
dialect,
port
port,
APP_URL_FRONTEND
},
{
name: 'development',
Expand All @@ -47,7 +49,8 @@ const environments = [
database: devDatabase,
host,
dialect,
port
port,
APP_URL_FRONTEND
},
{
name: 'production',
Expand All @@ -62,7 +65,8 @@ const environments = [
password,
host,
dialect,
port
port,
APP_URL_FRONTEND
},
{
name: 'stagging',
Expand All @@ -79,7 +83,8 @@ const environments = [
database: devDatabase,
host,
dialect,
port
port,
APP_URL_FRONTEND
}
];

Expand Down
41 changes: 41 additions & 0 deletions src/middlewares/shareArticle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

import open from 'open';
import dotenv from 'dotenv';
import env from '../configs/environments';
import models from '../api/models';

const { Article } = models;

dotenv.config();

const minimumValue = 0;
const { APP_URL_FRONTEND } = env;
const facebookShareURL = `https://web.facebook.com/sharer/sharer.php?u=${APP_URL_FRONTEND}/api/articles/`;
const twitterShareURL = `https://twitter.com/intent/tweet?text=${APP_URL_FRONTEND}/api/articles/`;
const linkedinShareURL = `https://www.linkedin.com/sharing/share-offsite/?url=${APP_URL_FRONTEND}/api/articles/`;

export default async (req, res, next) => {
const { slug } = req.params;

const { dataValues: { title } } = await Article.findOne({ where: { slug } });
req.title = title;

if (req.url.search(/\/facebook/g) > minimumValue) {
req.sharedOn = 'facebook';
await open(`${facebookShareURL}${slug}`, { wait: false });
} else if (req.url.search(/\/twitter/g) > minimumValue) {
req.sharedOn = 'twitter';
await open(`${twitterShareURL}${slug}`, { wait: false });
} else if (req.url.search(/\/linkedin/g) > minimumValue) {
req.sharedOn = 'linkedin';
await open(`${linkedinShareURL}${slug}`, { wait: false });
} else if (req.url.search(/\/gmail/g) > minimumValue) {
req.sharedOn = 'gmail';
await open(`mailto:?subject=${title}&body=${APP_URL_FRONTEND}/articles/${slug}`,
{
wait: false
});
}

next();
};
62 changes: 62 additions & 0 deletions tests/socialShare.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import app from '../src/index';
import status from '../src/helpers/constants/status.codes';
import signupUser from '../src/helpers/tests/signup';

const { expect } = chai;
chai.use(chaiHttp);

const userToken = signupUser();
const slug = 'What-is-a-Version-1-UUID';
const title = 'What is a Version 1 UUID';

describe(' testing the social share endpoints', () => {
it('should share via facebook', (done) => {
chai
.request(app)
.post(`/api/articles/${slug}/share/facebook`)
.set('Authorization', userToken)
.end((err, res) => {
expect(res.body).to.have.property('status').eql(status.CREATED);
expect(res.body).to.have.property('message').eql(`${title} has been shared successfully on facebook, Thanks`);
done();
});
});

it('should share via twitter', (done) => {
chai
.request(app)
.post(`/api/articles/${slug}/share/twitter`)
.set('Authorization', userToken)
.end((err, res) => {
expect(res.body).to.have.property('status').eql(status.CREATED);
expect(res.body).to.have.property('message').eql(`${title} has been shared successfully on twitter, Thanks`);
done();
});
});

it('should share via linkedin', (done) => {
chai
.request(app)
.post(`/api/articles/${slug}/share/linkedin`)
.set('Authorization', userToken)
.end((err, res) => {
expect(res.body).to.have.property('status').eql(status.CREATED);
expect(res.body).to.have.property('message').eql(`${title} has been shared successfully on linkedin, Thanks`);
done();
});
});

it('should share via gmail', (done) => {
chai
.request(app)
.post(`/api/articles/${slug}/share/gmail`)
.set('Authorization', userToken)
.end((err, res) => {
expect(res.body).to.have.property('status').eql(status.CREATED);
expect(res.body).to.have.property('message').eql(`${title} has been shared successfully on gmail, Thanks`);
done();
});
});
});

0 comments on commit 7addc6e

Please sign in to comment.