Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions migrations/20180223033914-create-contacts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('contacts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER
},
friendId: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('contacts');
}
};
11 changes: 11 additions & 0 deletions models/contacts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
var contacts = sequelize.define('contacts', {
userId: DataTypes.INTEGER,
friendId: DataTypes.INTEGER
}, {});
contacts.associate = function(models) {
// associations can be defined here
};
return contacts;
};
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@
"hapi": "^16.6.2",
"hapi-auth-basic": "^4.2.0",
"hapi-auth-jwt2": "^4.0.0",
"hapi-swagger": "^7.10.0",
"inert": "^4.2.1",
"joi": "^13.1.2",
"jsonwebtoken": "^8.1.1",
"jwt-decode": "^2.2.0",
"pg": "^6.4.2",
"hapi-swagger": "^7.10.0",
"inert": "^4.2.1",
"joi": "^13.1.2",
"sequelize": "^4.32.6",
"vision": "^4.1.1"
}
Expand Down
100 changes: 100 additions & 0 deletions routes/contacts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const model = require('../models');
const Joi = require('joi');

const contactSwagger = {
responses: {
200: {
description: 'Success',
schema: Joi.object({
message: Joi.array().example([{ userId: 1, name: 'John_Doe' }, { userId: 2, name: 'Jane_Doe' }]),
}).label('Result'),
},
401: { description: 'Unauthorized' },
},
};

const headerValidation = Joi.object({
authorization: Joi.string(),
}).unknown();

const contactAddSwagger = {
responses: {
200: {
description: 'Success',
schema: Joi.object({
message: Joi.string().example('Added a friend'),
}).label('Result'),
},
400: { description: 'Bad Request' },
401: { description: 'Unauthorized' },
},
};

const contactAddValidation = Joi.object({
friendId: Joi.number().example(2),
});

module.exports = [{
method: 'GET',
path: '/contacts',
config: {
tags: ['api'],
description: 'get contacts of the users',
notes: 'find all the contacts the user has added',
plugins: {
'hapi-swagger': contactSwagger,
},
validate: { headers: headerValidation },
},
handler: (request, reply) => {
model.contacts.findAll({
where:
{ userId: (request.auth.credentials.userId) },
}).then((result) => {
const resultArrPromise = [];
result.forEach(({ friendId }) => {
resultArrPromise.push(model.users.findOne({ where: { userId: friendId } }));
});

const detailsArr = [];
Promise.all(resultArrPromise).then((resultArr) => {
resultArr.forEach(({ userName, userId }) => {
detailsArr.push({
name: userName,
id: userId,
});
});

reply(detailsArr);
});
});
},
}, {
method: 'POST',
path: '/contacts',
config: {
tags: ['api'],
description: 'add a contact for the current user',
notes: 'inerst into database the id of the contact',
plugins: {
'hapi-swagger': contactAddSwagger,
},
validate: { headers: headerValidation, payload: contactAddValidation },
},
handler: (request, reply) => {
const { friendId } = request.payload;
const { userId } = request.auth.credentials;

if (friendId === userId) {
reply({ message: 'Can\'t add yourself' }).code(400);
}

model.users.findOne({ where: { userId: friendId } }).then((result) => {
if (result === null) {
reply({ message: 'User doesn\'t exist' }).code(400);
} else {
model.contacts.findOrCreate({ where: { userId, friendId }, defaults: { userId, friendId } }).then(() => reply({ message: 'Successfully added' }).code(200));
}
});
},
}];
3 changes: 2 additions & 1 deletion routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ const userRegistration = require('./userRegister');
const userLogin = require('./userLogin');
const auth = require('./auth');
const history = require('./history');
const contacts = require('./contacts');

module.exports = [].concat(ping, userRegistration, userLogin, auth, history);
module.exports = [].concat(ping, userRegistration, userLogin, auth, history, contacts);
34 changes: 34 additions & 0 deletions seeders/20180223055734-demo-contacts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@


module.exports = {
up: (queryInterface, Sequelize) => queryInterface.bulkInsert('contacts', [
{
userId: 1,
friendId: 2,
createdAt: new Date(),
updatedAt: new Date(),
},
{
userId: 1,
friendId: 3,
createdAt: new Date(),
updatedAt: new Date(),
}, {
userId: 2,
friendId: 1,
createdAt: new Date(),
updatedAt: new Date(),
}, {
userId: 2,
friendId: 3,
createdAt: new Date(),
updatedAt: new Date(),
}, {
userId: 3,
friendId: 2,
createdAt: new Date(),
updatedAt: new Date(),
}], {}),

down: (queryInterface, Sequelize) => queryInterface.bulkDelete('contacts', null, {}),
};
114 changes: 114 additions & 0 deletions tests/routes/contacts.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
const server = require('../../server');

const userId = 1;
const userName = 'John_Doe';

describe('GET contacts list', () => {
test('should get 401 status code if passed without auth', (done) => {
const request = {
method: 'GET',
url: '/contacts',
};
server.inject(request, (reply) => {
expect(reply.statusCode).toEqual(401);
done();
});
});

test('should get 200 status code if passed with auth', (done) => {
const request = {
method: 'GET',
url: '/contacts',
credentials: { userId, userName },
};
server.inject(request, (reply) => {
expect(reply.statusCode).toEqual(200);
done();
});
});

test('should get array of objects on success', (done) => {
const request = {
method: 'GET',
url: '/contacts',
credentials: { userId, userName },
};
server.inject(request, (reply) => {
expect.assertions(3);
expect(reply.result).toBeInstanceOf(Array);
expect(reply.result[0]).toHaveProperty('id');
expect(reply.result[0]).toHaveProperty('name');
done();
});
});
});

describe('POST a new contact to the contact list', () => {
test('should get 401 status code if passed without auth', (done) => {
const request = {
method: 'POST',
url: '/contacts',
};
server.inject(request, (reply) => {
expect(reply.statusCode).toEqual(401);
done();
});
});

test('should get 200 status code if passed with auth', (done) => {
const request = {
method: 'POST',
url: '/contacts',
credentials: { userId, userName },
payload: { friendId: 2 },
};
server.inject(request, (reply) => {
expect(reply.statusCode).toEqual(200);
done();
});
});


test('should get Successfully on adding a friend', (done) => {
const request = {
method: 'POST',
url: '/contacts',
credentials: { userId, userName },
payload: { friendId: 2 },
};
server.inject(request, (reply) => {
expect(reply.result.message).toEqual('Successfully added');
done();
});
});

test('should get error if passing user\'s own id', (done) => {
const request = {
method: 'POST',
url: '/contacts',
credentials: { userId, userName },
payload: { friendId: userId },
};
server.inject(request, (reply) => {
expect.assertions(2);
expect(reply.result.message).toEqual('Can\'t add yourself');
expect(reply.statusCode).toEqual(400);
done();
});
});

test('should get error if passsing non-existent id', (done) => {
const request = {
method: 'POST',
url: '/contacts',
credentials: { userId, userName },
payload: { friendId: 23 },
};
server.inject(request, (reply) => {
expect.assertions(2);
expect(reply.result.message).toEqual('User doesn\'t exist');
expect(reply.statusCode).toEqual(400);
done();
});
});
});