/** * @file Resolvers des groupes * @author ofacklam */ import {User} from './users'; import {Announcement, Event, PrivatePost, Question, Answer} from './messages'; import {Group as LDAP_Group} from '../../ldap/export/group'; import knex from '../../../db/knex_router'; import { Context } from '../typeDefs/queries'; import { ApolloError, AuthenticationError } from 'apollo-server-core'; import { Request, UserJoinGroup, GroupJoinMetagroup, GroupCoauthorEvent } from './requests'; import { GroupSet } from '../models/tools'; export abstract class Group { /** * @abstract * @memberof GraphQL * @class Group * @summary Resolvers des groupes * @classdesc Une classe abstraite représentant l'interface group du schéma. * Comme Apollo Server, par défaut, appelle la propriété / fonction avec le nom a résoudre, il n'y a pas besoin d'écrire les resolvers explicitement. * @arg {string} gid - Identifiant du groupe, supposé valide * @rights connectedOrOnplatal */ constructor(gid: string) { this.gid = gid; this.m_dataLoaded = false; } /** * @memberof GraphQL.Group# * @function fetchData * @summary Fonction qui va chercher toutes les données sur ce groupe dans le stockage approprié, si ce n'est pas déja fait. * @returns {Promise(boolean)} Renvoie true si le chargement est réussi. * @async * @protected * @abstract */ protected abstract async fetchData(): Promise<boolean> /** * Protected properties. * Ce sont tous les champs triviaux, ie qui peuvent etre récupérés en une seule requete a la base (que ce soit pour SimpleGroup ou MetaGroup), * ce qui permet d'etre plus efficace. * La variable dataLoaded témoigne de si on est déja allés chercher les données. * ATTENTION. Ce ne sont PAS directement les resolvers, il FAUT donc qu'ils aient un nom DIFFÉRENT du nom des champs dans le schéma. */ protected m_dataLoaded: boolean protected m_createdAt: string protected m_updatedAt: string protected m_name: string protected m_description: string protected m_mail: string protected m_website: string protected m_frontPage: string protected m_postsSummary: string /** * Ci-dessous les resolvers a proprement parler. */ __typename : string = "Bad group type"; /** @rights connectedOrOnplatal */ gid: string; /** * @memberof GraphQL.Group# * @function createdAt * @summary Renvoie la date de création * @return {Promise(string)} * @rights connectedOrOnplatal * @async */ async createdAt(args, context: Context, info): Promise<string> { await this.fetchData(); return this.m_createdAt; } /** * @memberof GraphQL.Group# * @function updatedAt * @summary Renvoie la date de mise a jour * @return {Promise(string)} * @rights connectedOrOnplatal * @async */ async updatedAt(args, context: Context, info): Promise<string> { await this.fetchData(); return this.m_updatedAt; } /** * @memberof GraphQL.Group# * @function name * @summary Renvoie le nom du groupe * @return {Promise(string)} * @rights connectedOrOnplatal * @async */ async name(args, context: Context, info): Promise<string> { await this.fetchData(); return this.m_name; } /** * @memberof GraphQL.Group# * @function description * @summary Renvoie la description du groupe * @return {Promise(string)} * @rights viewer * @async */ async description(args, context: Context, info): Promise<string> { if(context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_description; } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.Group# * @function mail * @summary Renvoie l'adresse mail * @return {Promise(string)} * @rights viewer * @async */ async mail(args, context: Context, info): Promise<string> { if (context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_mail; } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.Group# * @function website * @summary Renvoie l'adresse du site web * @return {Promise(string)} * @rights viewer * @async */ async website(args, context: Context, info): Promise<string> { if (context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_website; } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.Group# * @function admins * @summary Renvoie la liste des admins * @return {Promise(User[])} * @rights viewer * @async * @abstract */ abstract async admins(args, context: Context, info): Promise<User[]> /** * @memberof GraphQL.Group# * @function frontPage * @summary Renvoie la page principale * @return {Promise(string)} * @rights viewer * @async */ async frontPage(args, context: Context, info): Promise<string> { if (context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_frontPage; } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.Group# * @function questions * @summary Renvoie les questions adressées a ce groupe * @return {Promise(Question[])} * @rights viewer * @async */ async questions(args, context: Context, info): Promise<Question[]> { if (context.models.auth.isViewer(this.gid)) { throw "Not implemented" // let result = await knex('questions').select().whereIn('id', received_messages); // for(let entry of result){ // entry.type = "Question"; // } // return result; } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.Group# * @function answers * @summary Renvoie les réponses de ce groupe * @return {Promise(Answer[])} * @rights viewer * @async */ async answers(args, context: Context, info): Promise<Answer[]> { if (context.models.auth.isViewer(this.gid)) { throw "Not implemented" // let received_messages = await selectors.recievedMessages(user, groupUID); // let result = await knex('answers').select().whereIn('id', received_messages); // for(let entry of result){ // entry.type = "Answer"; // } // return result; } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.Group# * @function announcementsFromGroup * @summary Renvoie les annonces créées par ce groupe * @return {Promise(Announcement[])} * @rights member * @async */ async announcementsFromGroup(args, context: Context, info): Promise<Announcement[]> { if(context.models.auth.isMember(this.gid)) { //03/03/19 let res = new GroupSet(); res.add(this.gid); return context.models.message.getAllAnnouncementsSent(res); } throw new AuthenticationError("Not a member"); } /** * @memberof GraphQL.Group# * @function announcementsToGroup * @summary Renvoie les annonces adressées a ce groupe * @return {Promise(Announcement[])} * @rights member * @async */ async announcementsToGroup(args, context: Context, info): Promise<Announcement[]> { if(context.models.auth.isMember(this.gid)) { //03/03/19 let res = new GroupSet(); res.add(this.gid); return context.models.message.getAllAnnouncementsReceived(res); } throw new AuthenticationError("Not a member"); } /** * @memberof GraphQL.Group# * @function eventsFromGroup * @summary Renvoie les evenements créés par ce groupe * @return {Promise(Event[])} * @rights member * @async */ async eventsFromGroup(args, context: Context, info): Promise<Event[]> { if (context.models.auth.isMember(this.gid)) { //03/03/19 let res = new GroupSet(); res.add(this.gid); return context.models.message.getAllEventsFrom(res); } throw new AuthenticationError("Not a member"); } /** * @memberof GraphQL.Group# * @function eventsToGroup * @summary Renvoie les evenements adressées a ce groupe * @return {Promise(Event[])} * @rights member * @async */ async eventsToGroup(args, context: Context, info): Promise<Event[]> { if (context.models.auth.isMember(this.gid)) { //03/03/19 let res = new GroupSet(); res.add(this.gid); return context.models.message.getAllEventsTo(res); } throw new AuthenticationError("Not a member"); } /** * @memberof GraphQL.Group# * @function privatePosts * @summary Renvoie les posts internes du groupe * @return {Promise(PrivatePost[])} * @rights member * @async */ async privatePosts(args, context: Context, info): Promise<PrivatePost[]> { if (context.models.auth.isMember(this.gid)) { throw "Not implemented" } throw new AuthenticationError("Not a member"); } /** * @memberof GraphQL.Group# * @function postsSummary * @summary Renvoie le résumé interne * @return {Promise(string)} * @rights member * @async */ async postsSummary(args, context: Context, info): Promise<string> { if (context.models.auth.isMember(this.gid)) { await this.fetchData(); return this.m_postsSummary; } throw new AuthenticationError("Not a member"); } /** * @memberof GraphQL.Group# * @function requestsToGroup * @summary Renvoie toutes les requetes adressées au groupe. * @return {Promise(Request[])} * @rights admin * @async */ async requestsToGroup(args, context: Context, info): Promise<Request[]> { if (context.models.auth.isAdmin(this.gid)) { return context.models.request.getRequestsToGroup(this.gid); } throw new AuthenticationError("Not an admin"); } /** * @memberof GraphQL.Group# * @function userJoinGroupRequestsToGroup * @summary Renvoie toutes les requetes UserJoinGroup adressées au groupe. * @return {Promise(UserJoinGroup[])} * @rights admin * @async */ async userJoinGroupRequestsToGroup(args, context: Context, info): Promise<UserJoinGroup[]> { if (context.models.auth.isAdmin(this.gid)) { return context.models.request.getUserJoinGroupRequestsToGroup(this.gid); } throw new AuthenticationError("Not an admin"); } /** * @memberof GraphQL.Group# * @function groupJoinMetagroupRequestsToGroup * @summary Renvoie toutes les requetes GroupJoinMetagroup adressées au groupe. * @return {Promise(GroupJoinMetagroup[])} * @rights admin * @async */ async groupJoinMetagroupRequestsToGroup(args, context: Context, info): Promise<GroupJoinMetagroup[]> { if (context.models.auth.isAdmin(this.gid)) { return context.models.request.getGroupJoinMetagroupRequestsToGroup(this.gid); } throw new AuthenticationError("Not an admin"); } /** * @memberof GraphQL.Group# * @function groupCoauthorEventRequestsToGroup * @summary Renvoie toutes les requetes GroupCoauthorEvent adressées au groupe. * @return {Promise(GroupCoauthorEvent[])} * @rights admin * @async */ async groupCoauthorEventRequestsToGroup(args, context: Context, info): Promise<GroupCoauthorEvent[]> { if (context.models.auth.isAdmin(this.gid)) { return context.models.request.getGroupCoauthorEventRequestsToGroup(this.gid); } throw new AuthenticationError("Not an admin"); } /** * @memberof GraphQL.Group# * @function visibilityEdges * @summary Renvoie les groupes auxquels ce groupe est visible. * @return {Promise(Group[])} * @rights viewer * @async */ async visibilityEdges(args, context: Context, info): Promise<Group[]> { if (context.models.auth.isViewer(this.gid)) { throw "Not implemented" } throw new AuthenticationError("Not a viewer"); } } export class SimpleGroup extends Group { /** * @memberof GraphQL * @class SimpleGroup * @extends GraphQL.Group * @summary Resolvers des groupes simples * @classdesc Une classe représentant le type SimpleGroup du schéma. * Comme Apollo Server, par défaut, appelle la propriété / fonction avec le nom a résoudre, il n'y a pas besoin d'écrire les resolvers explicitement. * @arg {string} gid - Identifiant du groupe simple, supposé valide. * @rights connectedOrOnplatal */ constructor(gid: string) { super(gid); } /** * @memberof GraphQL.SimpleGroup# * @function tryCreate * @summary Fonction qui va essayer de créer le groupe simple correspondant * @arg {string} gid - Identifiant du groupe simple, sans hypothese sur la validité. * @returns {Promise(SimpleGroup)} - Renvoie le groupe simple créé, ou null en cas d'erreur. * @rights connectedOrOnplatal * @async * @static */ static async tryCreate(gid: string): Promise<SimpleGroup> { let g = new SimpleGroup(gid); if(await g.fetchData()) { return g; } return null; } /** * @memberof GraphQL.SimpleGroup# * @function fetchData * @summary Fonction qui va chercher toutes les données sur ce groupe simple dans le LDAP, si ce n'est pas déja fait. * @returns {Promise(boolean)} Renvoie true si le chargement est réussi. * @async * @protected */ protected async fetchData(): Promise<boolean> { if(!this.m_dataLoaded) { try { let data = await LDAP_Group.peek(this.gid); if(data.gid !== this.gid) { throw "Error in search"; } //this.m_createdAt = data.createdAt; //this.m_updatedAt = data.updatedAt; this.m_name = data.name; this.m_description = data.description; //this.m_mail = data.mail; //this.m_website = data.website; this.m_members = data.members; //this.m_speakers = data.speakers; this.m_admins = data.admins; //this.m_likers = data.likers; //this.m_frontPage = data.frontPage; //this.m_postsSummary = data.postsSummary; //this.m_parent = data.parent; //this.m_children = data.children; //this.m_school = ???; this.m_dataLoaded = true; return true; } catch { return false; } } return true; } /** * Protected properties. * Ce sont tous les champs triviaux, ie qui peuvent etre récupérés en une seule requete a la base (que ce soit pour SimpleGroup ou MetaGroup), * ce qui permet d'etre plus efficace. * La variable dataLoaded témoigne de si on est déja allés chercher les données. * ATTENTION. Ce ne sont PAS directement les resolvers, il FAUT donc qu'ils aient un nom DIFFÉRENT du nom des champs dans le schéma. */ protected m_members: string[] protected m_speakers: string[] protected m_admins: string[] protected m_likers: string[] protected m_parent: string protected m_children: string[] protected m_school: string /** * Ci-dessous les resolvers a proprement parler. */ __typename: string = "SimpleGroup"; /** * @memberof GraphQL.SimpleGroup# * @function members * @summary Renvoie la liste des membres * @return {Promise(User[])} * @rights viewer * @async */ async members(args, context: Context, info): Promise<User[]> { if(context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_members.map(uid => { return new User(uid); }); } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.SimpleGroup# * @function speakers * @summary Renvoie la liste des speakers * @return {Promise(User[])} * @rights viewer * @async */ async speakers(args, context: Context, info): Promise<User[]> { if(context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_speakers.map(uid => { return new User(uid); }); } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.SimpleGroup# * @function admins * @summary Renvoie la liste des admins * @return {Promise(User[])} * @rights viewer * @async */ async admins(args, context: Context, info): Promise<User[]> { if(context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_admins.map(uid => { return new User(uid); }); } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.SimpleGroup# * @function likers * @summary Renvoie la liste des sympathisants * @return {Promise(User[])} * @rights viewer * @async */ async likers(args, context: Context, info): Promise<User[]> { if(context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_likers.map(uid => { return new User(uid); }); } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.SimpleGroup# * @function parent * @summary Renvoie le groupe parent * @return {Promise(SimpleGroup)} * @rights viewer * @async */ async parent(args, context: Context, info): Promise<SimpleGroup> { if(context.models.auth.isViewer(this.gid)) { await this.fetchData(); if(this.m_parent == 'root') { return null; } else { return new SimpleGroup(this.m_parent); } } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.SimpleGroup# * @function children * @summary Renvoie la liste des groupes enfants * @return {Promise(SimpleGroup[])} * @rights viewer * @async */ async children(args, context: Context, info): Promise<SimpleGroup[]> { if(context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_children.map(gid => { return new SimpleGroup(gid); }); } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.SimpleGroup# * @function memberOfMeta * @summary Renvoie la liste des méta-groupes dont ce groupe fait partie * @return {Promise(MetaGroup[])} * @rights viewer * @async */ async memberOfMeta(args, context: Context, info): Promise<MetaGroup[]> { if(context.models.auth.isViewer(this.gid)) { throw "Not implemented"; } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.SimpleGroup# * @function school * @summary Renvoie l'école d'origine * @return {Promise(string)} * @rights viewer * @async */ async school(args, context: Context, info): Promise<string> { if(context.models.auth.isViewer(this.gid)) { await this.fetchData(); return this.m_school; } throw new AuthenticationError("Not a viewer"); } } export class MetaGroup extends Group { /** * @memberof GraphQL * @class MetaGroup * @extends GraphQL.Group * @summary Resolvers des méta-groupes * @classdesc Une classe représentant le type MetaGroup du schéma. * Comme Apollo Server, par défaut, appelle la propriété / fonction avec le nom a résoudre, il n'y a pas besoin d'écrire les resolvers explicitement. * @arg {string} gid - Identifiant du méta-groupe, supposé valide * @rights connectedOrOnplatal */ constructor(gid: string) { super(gid); } /** * @memberof GraphQL.MetaGroup# * @function tryCreate * @summary Fonction qui va essayer de créer le méta-groupe correspondant * @arg {string} gid - Identifiant du méta-groupe, sans hypothese sur la validité. * @returns {Promise(MetaGroup)} - Renvoie le méta-groupe créé, ou null en cas d'erreur. * @rights connectedOrOnplatal * @async * @static */ static async tryCreate(gid: string): Promise<MetaGroup> { let g = new MetaGroup(gid); if(await g.fetchData()) { return g; } return null; } /** * @memberof GraphQL.MetaGroup# * @function fetchData * @summary Fonction qui va chercher toutes les données sur ce méta-groupe dans la BDD, si ce n'est pas déja fait. * @returns {Promise(boolean)} Renvoie true si le chargement est réussi. * @async * @protected */ protected async fetchData(): Promise<boolean> { if (!this.m_dataLoaded) { let data = await knex.select('created_at', 'updated_at', 'name', 'description', //'mail', //'posts_summary', //'front_page', 'website').from('groups').where('gid', this.gid); if(data.length > 0) { let g = data[0]; this.m_createdAt = g.created_at; this.m_updatedAt = g.updated_at; this.m_name = g.name; this.m_description = g.description; //this.m_mail = g.mail; this.m_website = g.website; //this.m_frontPage = g.frontPage; //this.m_postsSummary = g.postsSummary; this.m_dataLoaded = true; return true; } else { return false; } } return true; } /** * Ci-dessous les resolvers a proprement parler. */ __typename: string = "MetaGroup"; /** * @memberof GraphQL.MetaGroup# * @function admins * @summary Renvoie la liste des admins * @return {Promise(User[])} * @rights viewer * @async */ async admins(args, context: Context, info): Promise<User[]> { if (context.models.auth.isViewer(this.gid)) { throw "Not implemented"; } throw new AuthenticationError("Not a viewer"); } /** * @memberof GraphQL.MetaGroup# * @function members * @summary Renvoie la liste des groupes membres * @return {Promise(SimpleGroup[])} * @rights viewer * @async */ async members(args, context: Context, info): Promise<SimpleGroup[]> { if (context.models.auth.isViewer(this.gid)) { throw "Not implemented" /*let member_group_list = await knex.distinct().select().from('groups') .innerJoin('meta_group_membership', 'groups.uid', 'meta_group_membership.member_uid') .where('meta_group_membership.union_uid', '=', group.gid); let members = new Set; for (const memberGroup of member_group_list) { let res = await getGroupMemberUsers(quser, new Group(memberGroup)); for (const member of res.values()) { members.add(member); } } return members;*/ } throw new AuthenticationError("Not a viewer"); } }