{
settings: {
name: "posts",
version: 1,
namespace: "posts",
rest: true,
ws: true,
graphql: true,
permission: C.PERM_LOGGEDIN,
role: "user",
collection: Post,
modelPropFilter: "code title content author votes voters views createdAt updatedAt",
modelPopulates: {
"author": "persons",
"voters": "persons"
}
},
actions: {
find: {
cache: true,
handler(ctx) {
let filter = {};
if (ctx.params.filter == "my")
filter.author = ctx.user.id;
else if (ctx.params.author != null) {
filter.author = this.personService.decodeID(ctx.params.author);
}
let query = Post.find(filter);
return ctx.queryPageSort(query).exec().then( (docs) => {
return this.toJSON(docs);
})
.then((json) => {
return this.populateModels(json);
});
}
},
// return a model by ID
get: {
cache: true, // if true, we don't increment the views!
permission: C.PERM_PUBLIC,
handler(ctx) {
ctx.assertModelIsExist(ctx.t("app:PostNotFound"));
return Post.findByIdAndUpdate(ctx.modelID, { $inc: { views: 1 } }).exec().then( (doc) => {
return this.toJSON(doc);
})
.then((json) => {
return this.populateModels(json);
});
}
},
create: {
handler(ctx) {
this.validateParams(ctx, true);
let post = new Post({
title: ctx.params.title,
content: ctx.params.content,
author: ctx.user.id
});
return post.save()
.then((doc) => {
return this.toJSON(doc);
})
.then((json) => {
return this.populateModels(json);
})
.then((json) => {
this.notifyModelChanges(ctx, "created", json);
return json;
});
}
},
update: {
permission: C.PERM_OWNER,
handler(ctx) {
ctx.assertModelIsExist(ctx.t("app:PostNotFound"));
this.validateParams(ctx);
return this.collection.findById(ctx.modelID).exec()
.then((doc) => {
if (ctx.params.title != null)
doc.title = ctx.params.title;
if (ctx.params.content != null)
doc.content = ctx.params.content;
return doc.save();
})
.then((doc) => {
return this.toJSON(doc);
})
.then((json) => {
return this.populateModels(json);
})
.then((json) => {
this.notifyModelChanges(ctx, "updated", json);
return json;
});
}
},
remove: {
permission: C.PERM_OWNER,
handler(ctx) {
ctx.assertModelIsExist(ctx.t("app:PostNotFound"));
return Post.remove({ _id: ctx.modelID })
.then(() => {
return ctx.model;
})
.then((json) => {
this.notifyModelChanges(ctx, "removed", json);
return json;
});
}
},
vote(ctx) {
ctx.assertModelIsExist(ctx.t("app:PostNotFound"));
return this.collection.findById(ctx.modelID).exec()
.then((doc) => {
// Check user is on voters
if (doc.voters.indexOf(ctx.user.id) !== -1)
throw ctx.errorBadRequest(C.ERR_ALREADY_VOTED, ctx.t("app:YouHaveAlreadyVotedThisPost"));
return doc;
})
.then((doc) => {
// Add user to voters
return Post.findByIdAndUpdate(doc.id, { $addToSet: { voters: ctx.user.id } , $inc: { votes: 1 }}, { "new": true });
})
.then((doc) => {
return this.toJSON(doc);
})
.then((json) => {
return this.populateModels(json);
})
.then((json) => {
this.notifyModelChanges(ctx, "voted", json);
return json;
});
},
unvote(ctx) {
ctx.assertModelIsExist(ctx.t("app:PostNotFound"));
return this.collection.findById(ctx.modelID).exec()
.then((doc) => {
// Check user is on voters
if (doc.voters.indexOf(ctx.user.id) == -1)
throw ctx.errorBadRequest(C.ERR_NOT_VOTED_YET, ctx.t("app:YouHaveNotVotedThisPostYet"));
return doc;
})
.then((doc) => {
// Remove user from voters
return Post.findByIdAndUpdate(doc.id, { $pull: { voters: ctx.user.id } , $inc: { votes: -1 }}, { "new": true });
})
.then((doc) => {
return this.toJSON(doc);
})
.then((json) => {
return this.populateModels(json);
})
.then((json) => {
this.notifyModelChanges(ctx, "unvoted", json);
return json;
});
}
},
methods: {
/**
* Validate params of context.
* We will call it in `create` and `update` actions
*
* @param {Context} ctx context of request
* @param {boolean} strictMode strictMode. If true, need to exists the required parameters
*/
validateParams(ctx, strictMode) {
if (strictMode || ctx.hasParam("title"))
ctx.validateParam("title").trim().notEmpty(ctx.t("app:PostTitleCannotBeEmpty")).end();
if (strictMode || ctx.hasParam("content"))
ctx.validateParam("content").trim().notEmpty(ctx.t("app:PostContentCannotBeEmpty")).end();
if (ctx.hasValidationErrors())
throw ctx.errorBadRequest(C.ERR_VALIDATION_ERROR, ctx.validationErrors);
}
},
/**
* Check the owner of model
*
* @param {any} ctx Context of request
* @returns {Promise}
*/
ownerChecker(ctx) {
return new Promise((resolve, reject) => {
ctx.assertModelIsExist(ctx.t("app:PostNotFound"));
if (ctx.model.author.code == ctx.user.code || ctx.isAdmin())
resolve();
else
reject();
});
},
// Fired when start the service
init(ctx) {
this.personService = ctx.services("persons");
// Add custom error types
C.append([
"ALREADY_VOTED",
"NOT_VOTED_YET"
], "ERR");
},
socket: {
afterConnection(socket, io) {
// Fired when a new client connected via websocket
}
},
graphql: {
query: `
posts(limit: Int, offset: Int, sort: String): [Post]
post(code: String): Post
`,
types: `
type Post {
code: String!
title: String
content: String
author: Person!
views: Int
votes: Int,
voters(limit: Int, offset: Int, sort: String): [Person]
createdAt: Timestamp
updatedAt: Timestamp
}
`,
mutation: `
postCreate(title: String!, content: String!): Post
postUpdate(code: String!, title: String, content: String): Post
postRemove(code: String!): Post
postVote(code: String!): Post
postUnvote(code: String!): Post
`,
resolvers: {
Query: {
posts: "find",
post: "get"
},
Mutation: {
postCreate: "create",
postUpdate: "update",
postRemove: "remove",
postVote: "vote",
postUnvote: "unvote"
}
}
}
};