Skip to content
This repository has been archived by the owner on Oct 1, 2019. It is now read-only.

Commit

Permalink
Add backend implementation of user conversations (UserMessage)
Browse files Browse the repository at this point in the history
  • Loading branch information
voidxnull committed Dec 18, 2016
1 parent f5f7723 commit b6125a2
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 2 deletions.
16 changes: 16 additions & 0 deletions migrations/20161217202823_user_messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export async function up(knex) {
await knex.schema.createTable('user_messages', table => {
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
table.timestamp('created_at', true).defaultTo(knex.raw("(now() at time zone 'utc')"));
table.timestamp('updated_at', true).defaultTo(knex.raw("(now() at time zone 'utc')"));
table.uuid('sender_id').references('id').inTable('users').onDelete('cascade').onUpdate('cascade');
table.uuid('reciever_id').references('id').inTable('users').onDelete('cascade').onUpdate('cascade');
table.text('text');

table.index(['sender_id', 'reciever_id']);
});
}

export async function down(knex) {
await knex.schema.dropTable('user_messages');
}
10 changes: 10 additions & 0 deletions src/api/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,16 @@ export default class ApiClient
return await response.json();
}

async sendMessage(userId, text) {
const response = await this.postJSON(`/api/v1/user/${userId}/messages`, { text });
return await response.json();
}

async userMessages(userId) {
const response = await this.get(`/api/v1/user/${userId}/messages`);
return await response.json();
}

async registerUser(userData) {
const response = await this.post(`/api/v1/users`, userData);
return await response.json();
Expand Down
72 changes: 71 additions & 1 deletion src/api/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ import {
User as UserValidators,
School as SchoolValidators,
Hashtag as HashtagValidators,
Geotag as GeotagValidators
Geotag as GeotagValidators,
UserMessage as UserMessageValidators
} from './db/validators';


Expand Down Expand Up @@ -3261,8 +3262,77 @@ export default class ApiController {
await this.getPostComments(ctx);
};

sendMessage = async (ctx) => {
if (!ctx.session || !ctx.session.user) {
ctx.status = 403;
ctx.body = { error: 'You are not authorized' };
return;
}

if (!this.areMutuallyFollowed(ctx.session.user, ctx.params.id)) {
ctx.status = 403;
ctx.body = { error: 'You must be mutually followed with this user to be able to message them' };
return;
}

try {
await new Checkit(UserMessageValidators).run(ctx.request.body);
} catch (e) {
ctx.status = 400;
ctx.body = { error: e.toJSON() };
return;
}

const User = this.bookshelf.model('User');

const currentUser = await new User({ id: ctx.session.user }).fetch({ require: true });
const message = await currentUser.outbox().create({
reciever_id: ctx.params.id,
text: ctx.request.body.text
});

ctx.body = await message.fetch();
}

/**
* Gets a chain of messages between the current user and the specified in params.
*/
getUserMessages = async (ctx) => {
if (!ctx.session || !ctx.session.user) {
ctx.status = 403;
ctx.body = { error: 'You are not authorized' };
return;
}

const UserMessage = this.bookshelf.model('UserMessage');

const messages = await UserMessage.collection()
.query(qb => {
qb
.where({ sender_id: ctx.session.user, reciever_id: ctx.params.id })
.orWhere({ sender_id: ctx.params.id, reciever_id: ctx.session.user })
.orderBy('created_at', 'ASC');
})
.fetch();

ctx.body = messages;
}

// ========== Helpers ==========

async areMutuallyFollowed(user1Id, user2Id) {
const knex = this.bookshelf.knex;

const userFollows = await knex('followers')
.where({ user_id: user1Id, following_user_id: user2Id })
.count();
const userBeingFollowed = await knex('followers')
.where({ user_id: user2Id, following_user_id: user1Id })
.count();

return userFollows.count != '0' && userBeingFollowed.count != '0';
}

countComments = async (posts) => {
const ids = posts.map(post => {
return post.get('id');
Expand Down
17 changes: 17 additions & 0 deletions src/api/db/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ export function initBookshelfFromKnex(knex) {
post_subscriptions() {
return this.belongsToMany(Post, 'post_subscriptions');
},
inbox() {
return this.hasMany(UserMessage, 'reciever_id');
},
outbox() {
return this.hasMany(UserMessage, 'sender_id');
},
virtuals: {
gravatarHash() {
const email = this.get('email');
Expand Down Expand Up @@ -599,6 +605,16 @@ export function initBookshelfFromKnex(knex) {
tableName: 'quotes'
});

const UserMessage = bookshelf.Model.extend({
tableName: 'user_messages',
sender() {
return this.belongsTo(User, 'sender_id');
},
reciever() {
return this.belongsTo(User, 'reciever_id');
}
});

const Posts = bookshelf.Collection.extend({
model: Post
});
Expand All @@ -615,6 +631,7 @@ export function initBookshelfFromKnex(knex) {
bookshelf.model('Geotag', Geotag);
bookshelf.model('Comment', Comment);
bookshelf.model('Quote', Quote);
bookshelf.model('UserMessage', UserMessage);
bookshelf.collection('Posts', Posts);

return bookshelf;
Expand Down
6 changes: 5 additions & 1 deletion src/api/db/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,8 @@ const Hashtag = {
}
};

export { User, School, Hashtag, Geotag };
const UserMessage = {
text: ['string', 'minLength:1', 'required']
};

export { User, School, Hashtag, Geotag, UserMessage };
2 changes: 2 additions & 0 deletions src/api/routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export function initApi(bookshelf, sphinx) {
api.head('/user/:username', controller.checkUserExists);
api.get('/user/:id/following', controller.getFollowedUsers);
api.get('/user/:id/mutual-follows', controller.getMutualFollows);
api.get('/user/:id/messages', controller.getUserMessages);
api.post('/user/:id/messages', controller.sendMessage);
api.head('/user/email/:email', controller.checkEmailTaken);
api.get('/user/available-username/:username', controller.getAvailableUsername);
api.get('/user/:username', controller.getUser);
Expand Down

0 comments on commit b6125a2

Please sign in to comment.