From cccb8522c5287ff4d426eae8a4cd4f742c67f45a Mon Sep 17 00:00:00 2001 From: ofacklam <oliver.facklam@gmail.com> Date: Sun, 23 Jun 2019 23:27:04 +0200 Subject: [PATCH] [User resolvers] finish resolvers closes #53 move dislike query rename user_like_group table to user_dislike_group -> closes #66 bugfixes in messagemodel --- ...190511130512_create_user_dislike_group.js} | 8 +- db/seeds/08_dummy_dislikes.js | 22 ++ db/seeds/09_dummy_questions.js | 33 +++ src/graphql/models/groupModel.ts | 4 +- src/graphql/models/messageModel.ts | 91 ++++---- src/graphql/models/userModel.ts | 61 ++++- src/graphql/object_resolvers/users.ts | 29 ++- src/graphql/resolvers.ts | 9 + src/graphql/typeDefs/actions.graphql | 1 + src/graphql/typeDefs/objects.graphql | 1 - test/resolvers/data/onplatal/users.ts | 208 ++++++++++++++++++ 11 files changed, 401 insertions(+), 66 deletions(-) rename db/migrations/{20190511130512_create_user_like_group.js => 20190511130512_create_user_dislike_group.js} (66%) create mode 100644 db/seeds/08_dummy_dislikes.js create mode 100644 db/seeds/09_dummy_questions.js diff --git a/db/migrations/20190511130512_create_user_like_group.js b/db/migrations/20190511130512_create_user_dislike_group.js similarity index 66% rename from db/migrations/20190511130512_create_user_like_group.js rename to db/migrations/20190511130512_create_user_dislike_group.js index d390e81..1058e76 100644 --- a/db/migrations/20190511130512_create_user_like_group.js +++ b/db/migrations/20190511130512_create_user_dislike_group.js @@ -1,6 +1,6 @@ exports.up = function (knex, Promise) { - return knex.schema.createTable('user_like_group', function (table) { + return knex.schema.createTable('user_dislike_group', function (table) { table.increments('id'); //autoincrementing (non-nullable) primary key table.string('uid').notNullable() @@ -10,13 +10,9 @@ exports.up = function (knex, Promise) { table.string('gid').notNullable() .references('gid').inTable('groups') .onDelete('CASCADE'); - - table.unique(['uid', 'gid']); - - table.boolean('isDislike').notNullable(); }); }; exports.down = function (knex, Promise) { - return knex.schema.dropTable('user_like_group'); + return knex.schema.dropTable('user_dislike_group'); }; diff --git a/db/seeds/08_dummy_dislikes.js b/db/seeds/08_dummy_dislikes.js new file mode 100644 index 0000000..c9bc2d2 --- /dev/null +++ b/db/seeds/08_dummy_dislikes.js @@ -0,0 +1,22 @@ + +exports.seed = async function (knex, Promise) { + // Deletes ALL existing entries + await knex('user_dislike_group').del(); + // Inserts seed entries + const users = [{ + uid: 'hadrien.renaud', + gid: 'jtx' + }, { + uid: 'timothee.darcet', + gid: 'bobar' + }, { + uid: 'oliver.facklam', + gid: 'politix' + } + + ]; + + await knex('user_dislike_group').insert(users); + console.log("finished running 08_dummy_dislikes"); + +}; diff --git a/db/seeds/09_dummy_questions.js b/db/seeds/09_dummy_questions.js new file mode 100644 index 0000000..ac366cb --- /dev/null +++ b/db/seeds/09_dummy_questions.js @@ -0,0 +1,33 @@ + +exports.seed = async function (knex, Promise) { + // Deletes ALL existing entries + // done by deleting all messages + + // Inserts seed entries + const questions = [{ + title: "Au secours !", + content: "Quand sont les perms BR ? J'arrive pas a me connecter a eduroam :'(", + author: "oliver.facklam", + recipient: "br" + }, + { + title: "Prépass ?", + content: "Quand commence la prépasse du binet ?", + author: "timothee.darcet", + recipient : "subaisse" + }, + ]; + + for (let q of questions) { + let mid = (await knex('messages').insert({ type: 'question' }, ['mid']))[0].mid; + await knex('messages_questions').insert({ + mid: mid, + title: q.title, + content: q.content, + author: q.author, + recipient: q.recipient + }); + } + + console.log("finished running 09_dummy_questions"); +}; diff --git a/src/graphql/models/groupModel.ts b/src/graphql/models/groupModel.ts index 476a073..d6f5248 100644 --- a/src/graphql/models/groupModel.ts +++ b/src/graphql/models/groupModel.ts @@ -9,7 +9,7 @@ import { SimpleGroup } from "../object_resolvers/groups/simpleGroups"; import { MetaGroup } from "../object_resolvers/groups/metaGroups"; import { Request } from "../object_resolvers/requests/requests"; import { User } from "../object_resolvers/users"; -import knex from "../../../db/knex_router" +import knex from "../../../db/knex_router"; import { GroupCollection, GroupSet, Tools } from "../../utils/tools"; import { createSubgroupArgs, editGroupArgs } from "../typeDefs/queries"; import {Cache} from "../../utils/cache"; @@ -58,6 +58,7 @@ export class GroupModel { * @function getSimpleGroup * @summary Fonction qui renvoie un groupe simple donné. * @arg {string} gid - Identifiant demandé. + * @arg {boolean} load - Si load vaut false, les données ne seront pas chargées * @return {Promise(SimpleGroup)} Renvoie le groupe dont l'identifiant est 'gid' * @async * @rights connectedOrOnplatal @@ -71,6 +72,7 @@ export class GroupModel { * @function getMetaGroup * @summary Fonction qui renvoie un méta-groupe donné. * @arg {string} gid - Identifiant demandé. + * @arg {boolean} load - Si load vaut false, les données ne seront pas chargées * @return {Promise(MetaGroup)} Renvoie le groupe dont l'identifiant est 'gid' * @async * @rights connectedOrOnplatal diff --git a/src/graphql/models/messageModel.ts b/src/graphql/models/messageModel.ts index e0cc950..d23531d 100644 --- a/src/graphql/models/messageModel.ts +++ b/src/graphql/models/messageModel.ts @@ -86,20 +86,6 @@ export class MessageModel { return Question.tryCreate(mid); } - /** - * @memberof GraphQL.Group# - * @function questions - * @summary Renvoie les questions adressées a ce groupe - * @arg {string} gid - * @return {Promise(Question[])} - * @rights viewer - * @async - */ - async getAllQuestionsReceived(gid: string): Promise<Question[]> { - let result:Question[] = await knex('messages_questions').select().where('recipient', gid ); - return result; - } - /** * @memberof GraphQL.MessageModel# * @function getAnswer @@ -113,21 +99,6 @@ export class MessageModel { return Answer.tryCreate(mid); } - /** - * @memberof GraphQL.Group# - * @function answers - * @summary Renvoie les réponses de ce groupe - * @arg {string} gid - * @return {Promise(Answer[])} - * @rights viewer - * @async - */ - async getAllAnswersSent(gid: string): Promise<Answer[]> { - // let received_messages = await selectors.recievedMessages(user, groupUID); - let result:Answer[] = await knex('messages_answers').select().where('recipient', gid); - return result; - } - /** * @memberof GraphQL.MessageModel# * @function getAllMessages @@ -175,7 +146,7 @@ export class MessageModel { async getAllAnnouncementsSent(groups: GroupSet): Promise<Announcement[]> { let result = await knex.select('mid').from('announcements_authors').whereIn('gid', Array.from(groups)); - return result.map(async obj => await this.getAnnouncement(obj.mid)); + return Promise.all(result.map(obj => this.getAnnouncement(obj.mid))); } /** @@ -190,7 +161,7 @@ export class MessageModel { async getAllAnnouncementsReceived(groups: GroupSet): Promise<Announcement[]> { let result = await knex.select('mid').from('announcements_recipients').whereIn('gid', Array.from(groups)); - return result.map(async obj => await this.getAnnouncement(obj.mid)); + return Promise.all(result.map(obj => this.getAnnouncement(obj.mid))); } /** @@ -251,7 +222,7 @@ export class MessageModel { async getAllEventsFrom(groups: GroupSet): Promise<Event[]> { let result = await knex.select('mid').from('events_authors').whereIn('gid', Array.from(groups)); - return result.map(async obj => await this.getEvent(obj.mid)); + return Promise.all(result.map(obj => this.getEvent(obj.mid))); } /** @@ -266,7 +237,7 @@ export class MessageModel { async getAllEventsTo(groups: GroupSet): Promise<Event[]> { let result = await knex.select('mid').from('events_recipients').whereIn('gid', Array.from(groups)); - return result.map(async obj => await this.getEvent(obj.mid)); + return Promise.all(result.map(obj => this.getEvent(obj.mid))); } /** @@ -339,7 +310,7 @@ export class MessageModel { async getAllPrivatePosts(groups: GroupSet): Promise<PrivatePost[]> { let result = await knex.select('mid').from('messages_privateposts').whereIn('recipient', Array.from(groups)); - return result.map(async obj => await this.getPrivatePost(obj.mid)); + return Promise.all(result.map(obj => this.getPrivatePost(obj.mid))); } /** @@ -347,23 +318,50 @@ export class MessageModel { * @function getAllQuestions * @summary Fonction qui renvoie toutes les questions posées aux groupes. * @arg {GroupSet} groups - Un ensemble d'identifiants, supposés valides. - * @return {Promise(Announcement[])} Renvoie toutes les annonces émises par ces groupes + * @return {Promise(Announcement[])} Renvoie toutes les questions posées a ces groupes * @async * @rights member of groups */ async getAllQuestions(groups: GroupSet): Promise<Announcement[]> { let result = await knex.select('mid').from('messages_questions').whereIn('recipient', Array.from(groups)); - return result.map(async obj => await this.getQuestion(obj.mid)); + return Promise.all(result.map(obj => this.getQuestion(obj.mid))); + } + + /** + * @memberof GraphQL.Group# + * @function getAllQuestionsReceived + * @summary Renvoie les questions adressées a ce groupe + * @arg {string} gid + * @return {Promise(Question[])} + * @rights viewer + * @async + */ + async getAllQuestionsReceived(gid: string): Promise<Question[]> { + let result: Question[] = await knex('messages_questions').select().where('recipient', gid); + return result; + } + + /** + * @memberof GraphQL.Group# + * @function getQuestionsFromUser + * @summary Renvoie les questions posées par le user + * @arg {string} uid + * @return {Promise(Question[])} + * @rights viewer + * @async + */ + async getQuestionsFromUser(uid: string): Promise<Question[]> { + let result = await knex('messages_questions').select('mid').where('author', uid); + return Promise.all(result.map(obj => this.getQuestion(obj.mid))); } - /** * @memberof GraphQL.MessageModel# * @function getAllAnswers * @summary Fonction qui renvoie toutes les réponses données par le groupe. * @arg {GroupSet} groups - Un ensemble d'identifiants, supposés valides. - * @return {Promise(Announcement[])} Renvoie toutes les annonces émises par ces groupes + * @return {Promise(Announcement[])} Renvoie toutes les réponses données par ces groupes * @async * @rights member of groups */ @@ -374,10 +372,23 @@ export class MessageModel { .innerJoin('messages_questions', 'messages_answers.for_question', 'messages_questions.mid') .whereIn('messages_questions.recipient', groups); - return result.map(async obj => await this.getAnswer(obj.mid)); + return Promise.all(result.map(obj => this.getAnswer(obj.mid))); } - + /** + * @memberof GraphQL.Group# + * @function getAllAnswersSent + * @summary Renvoie les réponses de ce groupe + * @arg {string} gid + * @return {Promise(Answer[])} + * @rights viewer + * @async + */ + async getAllAnswersSent(gid: string): Promise<Answer[]> { + // let received_messages = await selectors.recievedMessages(user, groupUID); + let result: Answer[] = await knex('messages_answers').select().where('recipient', gid); + return result; + } /** * @memberof GraphQL.MessageModel# diff --git a/src/graphql/models/userModel.ts b/src/graphql/models/userModel.ts index 777a5be..036c7e6 100644 --- a/src/graphql/models/userModel.ts +++ b/src/graphql/models/userModel.ts @@ -5,10 +5,14 @@ */ import { User } from "../object_resolvers/users"; -import { User as UT, userData } from "../../ldap/export/user" +import { User as UT, userData } from "../../ldap/export/user"; +import { Group as GT, groupData } from "../../ldap/export/group"; + +import knex from "../../../db/knex_router"; import { searchTOLArgs, editProfileArgs } from "../typeDefs/queries"; import { ApolloError } from "apollo-server-core"; import {Cache} from "../../utils/cache"; +import { GroupCollection, Tools, GroupSet } from "../../utils/tools"; export class UserModel { @@ -33,7 +37,7 @@ export class UserModel { * @function getUser * @summary Fonction qui renvoie l'utilisateur demandé. * @arg {string} uid - Identifiant demandé. - * @arg {any} hints - Parametres supplémentaires. + * @arg {boolean} load - Si load vaut false, les données ne seront pas chargées. * @return {Promise(User)} Renvoie l'utilisateur dont l'identifiant est 'uid' * @async * @rights connectedOrOnplatal @@ -42,6 +46,59 @@ export class UserModel { return this.cache.get(uid, (id) => User.tryCreate(id, load)); } + /** + * @memberof GraphQL.UserModel# + * @function getMemberOf + * @summary Fonction qui renvoie les groupes stricts du user. + * @arg {string} uid - Identifiant demandé. + * @return {Promise(GroupCollection)} Renvoie les identifiants des groupes dont ce user est membre strict. + * @async + * @rights connectedOrOnplatal + */ + async getMemberOf(uid: string): Promise<GroupCollection> { + let gd = new groupData(); + gd.members = [uid]; + let data = await GT.search(gd); + + let simple = new GroupSet(data); + let meta = await Tools.metaGroupsOfGroups(simple); + + return {simpleGroups: simple, metaGroups: meta}; + } + + /** + * @memberof GraphQL.UserModel# + * @function getAdminOf + * @summary Fonction qui renvoie les groupes dont le user est admin strict. + * @arg {string} uid - Identifiant demandé. + * @return {Promise(GroupCollection)} Renvoie les identifiants des groupes dont ce user est admin strict. + * @async + * @rights connectedOrOnplatal + */ + async getAdminOf(uid: string): Promise<GroupCollection> { + let gd = new groupData(); + gd.admins = [uid]; + let data = await GT.search(gd); + + let simple = new GroupSet(data); + let meta = await Tools.metaGroupsOfGroups(simple); + + return { simpleGroups: simple, metaGroups: meta }; + } + + /** + * @memberof GraphQL.UserModel# + * @function getDislikes + * @summary Fonction qui renvoie les groupes que le contextUser veut cacher. + * @return {Promise(GroupSet)} Renvoie les identifiants des groupes que le contextUser veut cacher. + * @async + * @rights connected + */ + async getDislikes(): Promise<GroupSet> { + let data = await knex.select('gid').from('user_dislike_group').where('uid', this.contextUser); + return new GroupSet(data.map(obj => obj.gid)); + } + /** * @memberof GraphQL.UserModel# * @function searchTOL diff --git a/src/graphql/object_resolvers/users.ts b/src/graphql/object_resolvers/users.ts index e19600c..a033770 100644 --- a/src/graphql/object_resolvers/users.ts +++ b/src/graphql/object_resolvers/users.ts @@ -286,7 +286,8 @@ export class User { * @async */ async memberOf(args, context: Context, info): Promise<Group[]> { - throw "Not implemented"; + let groups = await context.models.user.getMemberOf(this.uid); + return context.models.group.getAllGroupsByCollection(groups); } /** @@ -340,7 +341,8 @@ export class User { * @async */ async adminOf(args, context: Context, info): Promise<Group[]> { - throw "Not implemented"; + let groups = await context.models.user.getAdminOf(this.uid); + return context.models.group.getAllGroupsByCollection(groups); } /** @@ -385,18 +387,6 @@ export class User { return {simpleGroups: simple, metaGroups: meta}; } - /** - * @memberof GraphQL.User# - * @function dislikes - * @summary Renvoie les groupes qu'il veut cacher - * @return {Promise(Group[])} - * @rights connectedOrOnplatal - * @async - */ - async dislikes(args, context: Context, info): Promise<Group[]> { - throw "Not implemented"; - } - /** * @memberof GraphQL.User# * @function questionsFromUser @@ -406,7 +396,14 @@ export class User { * @async */ async questionsFromUser(args, context: Context, info): Promise<Question[]> { - await this.m_loader.load(); - throw "Not implemented"; + let questions = await context.models.message.getQuestionsFromUser(this.uid); + let res = new Array<Question>(); + + for(let q of questions) { + if(await context.models.auth.isViewer(await q.getRecipient())) + res.push(q); + } + + return res; } } diff --git a/src/graphql/resolvers.ts b/src/graphql/resolvers.ts index 6165fdd..f0c6e82 100644 --- a/src/graphql/resolvers.ts +++ b/src/graphql/resolvers.ts @@ -211,6 +211,15 @@ export const resolvers = { return context.models.group.getAllSimpleGroups(visibleGroupCollection.simpleGroups); }, + // @rights connected + dislikes: async function (root, args, context: Context): Promise<Group[]> { + if(context.models.auth.isAuthenticated()) { + let groups = await context.models.user.getDislikes(); + return context.models.group.getAllGroupsBySet(groups); + } + throw new AuthenticationError("Not connected"); + }, + // TOL // @rights connectedOrOnplatal searchTOL: async function (root, args, context: Context): Promise<User[]> { diff --git a/src/graphql/typeDefs/actions.graphql b/src/graphql/typeDefs/actions.graphql index ae91a48..91c8713 100644 --- a/src/graphql/typeDefs/actions.graphql +++ b/src/graphql/typeDefs/actions.graphql @@ -33,6 +33,7 @@ type Query { # Correspondrait au sous-champ "viewerOf" de User, volontairement non-défini comme tel. Tous les autres cas de figure sont couverts par les sous-champs "<permission>Of" de User allGroups: [Group] allSimpleGroups: [SimpleGroup] + dislikes: [Group] # Groupes que l'utilisateur veut cacher # TOL searchTOL( diff --git a/src/graphql/typeDefs/objects.graphql b/src/graphql/typeDefs/objects.graphql index 48e8335..766ac7b 100644 --- a/src/graphql/typeDefs/objects.graphql +++ b/src/graphql/typeDefs/objects.graphql @@ -54,7 +54,6 @@ type User { adminOf: [Group] # Admin strict inheritedAdminOf: [Group] # Admin hérité likes: [Group] # Groupes dont l'utilisateur est sympathisant (purement indicatif, pas d'influence sur les niveaux de droit) - dislikes: [Group] # Groupes que l'utilisateur veut cacher # Les Message dont il est l'auteur questionsFromUser: [Question] # Les seuls Messages publics où `authors` est de type User diff --git a/test/resolvers/data/onplatal/users.ts b/test/resolvers/data/onplatal/users.ts index 1981e37..32ad47e 100644 --- a/test/resolvers/data/onplatal/users.ts +++ b/test/resolvers/data/onplatal/users.ts @@ -141,6 +141,22 @@ export const tests: TestSet = { } },*/ + { + description: "dislikes not authorized", + query: ` + query { + dislikes { + gid + } + } + `, + result: { + "data": { + "dislikes": null + } + } + }, + { description: "firstName, lastName, nickName", query: ` @@ -203,5 +219,197 @@ export const tests: TestSet = { } } }, + + { + description: "inheritedMemberOf", + query: ` + query { + user(uid: "oliver.facklam") { + inheritedMemberOf { + gid + } + } + } + `, + result: { + "data": { + "user": { + "inheritedMemberOf": [ + { + "gid": "br" + }, + { + "gid": "faerix" + }, + { + "gid": "xy" + }, + { + "gid": "chorale" + }, + { + "gid": "subaisse" + }, + { + "gid": "sport_judo" + }, + { + "gid": "on_platal" + }, + { + "gid": "formation_x" + }, + { + "gid": "promo_2017" + }, + { + "gid": "promo_x2017" + }, + { + "gid": "groupe_sigma" + }, + { + "gid": "root_br" + }, + { + "gid": "artificial_intelligence" + }, + { + "gid": "federez" + } + ] + } + } + } + }, + + { + description: "speakerOf", + query: ` + query { + user(uid: "oliver.facklam") { + speakerOf { + gid + } + } + } + `, + result: { + "data": { + "user": { + "speakerOf": [ + { + "gid": "groupe_sigma" + } + ] + } + } + } + }, + + { + description: "inheritedAdminOf", + query: ` + query { + user(uid: "oliver.facklam") { + inheritedAdminOf { + gid + } + } + } + `, + result: { + "data": { + "user": { + "inheritedAdminOf": [ + { + "gid": "groupe_sigma" + } + ] + } + } + } + }, + + { + description: "likes", + query: ` + query { + user(uid: "timothee.darcet") { + likes { + gid + } + } + } + `, + result: { + "data": { + "user": { + "likes": [ + { + "gid": "impro" + }, + { + "gid": "montagne" + }, + { + "gid": "x-courseaularge" + }, + { + "gid": "wep" + }, + { + "gid": "xonthemun" + }, + { + "gid": "skiclub" + }, + { + "gid": "bspp" + }, + { + "gid": "ratemyclass" + }, + { + "gid": "ratatouille" + }, + { + "gid": "badas" + }, + { + "gid": "aventuriers" + }, + { + "gid": "cordix" + }, + { + "gid": "ionsat" + } + ] + } + } + } + }, + + { + description: "questionsFromUser empty (only for viewers of groups)", + query: ` + query { + user(uid: "timothee.darcet") { + questionsFromUser { + title + content + } + } + } + `, + result: { + "data": { + "user": { + "questionsFromUser": [] + } + } + } + }, ] } \ No newline at end of file -- GitLab