From 90ad595149ade58e54ceddc7d7b30f15ccca16e7 Mon Sep 17 00:00:00 2001 From: Oliver Facklam <oliver.facklam.lfgeb@gmail.com> Date: Mon, 24 Dec 2018 21:18:46 +0100 Subject: [PATCH] messages.ts --- src/graphql/resolvers/groups.ts | 3 +- src/graphql/resolvers/messages.js | 121 ----- src/graphql/resolvers/messages.ts | 718 +++++++++++++++++++++++++++ src/graphql/resolvers/users.ts | 5 +- src/graphql/typeDefs/objects.graphql | 13 +- 5 files changed, 729 insertions(+), 131 deletions(-) delete mode 100644 src/graphql/resolvers/messages.js create mode 100644 src/graphql/resolvers/messages.ts diff --git a/src/graphql/resolvers/groups.ts b/src/graphql/resolvers/groups.ts index a671135..0e0885c 100644 --- a/src/graphql/resolvers/groups.ts +++ b/src/graphql/resolvers/groups.ts @@ -4,7 +4,8 @@ */ import {Authorisation} from '../connectors/authorisation'; -import {User} from './users' +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'; diff --git a/src/graphql/resolvers/messages.js b/src/graphql/resolvers/messages.js deleted file mode 100644 index 85c9d8f..0000000 --- a/src/graphql/resolvers/messages.js +++ /dev/null @@ -1,121 +0,0 @@ -/** - * @file Resolvers pour tous les types de messages. hawkspar->akka ; bien, mais pas suffisant - * @author akka - */ -import * as connectors from '../connectors/connectors'; - -const MessageResolvers = { - Message: { - __resolveType: function(obj) { - if (obj.location) { - return "Event"; - } - if (obj.views) { - return "Announcement"; - } - if (obj.for_question) { - return "Answer"; - } - if (obj.for_answer) { - return "Question"; - } - return "PrivatePost"; - } - }, - - Announcement: { - forEvent : function(obj, args, context){ - // le champ is_announcement n'existe que sur les Events - // une ligne de la bdd events peut résoudre comme évènement ou comme annonce - if(obj.is_announcement) - return obj; - else - return null; - }, - - authors: async function (obj, args, context){ - return connectors.getMessageGroupAuthors(context.user, obj.id); - }, - - recipients: async function (obj, args, context){ - return connectors.getMessageGroupRecipients(context.user, obj.id); - }, - - }, - - Event: { - - startTime: function (obj) { - return obj.start_time; - }, - - endTime: function (obj) { - return obj.end_time; - }, - - asAnnouncement: function (obj, args, context) { - // le champ is_announcement indique si il existe une annonce qui va avec l'évènement - // une ligne de la bdd events peut résoudre comme évènement ou comme annonce - if (obj.is_announcement) - return obj; - else - return null; - }, - - authors: async function (obj, args, context) { - return connectors.getMessageGroupAuthors(context.user, obj.id); - }, - - recipients: async function (obj, args, context) { - return connectors.getMessageGroupRecipients(context.user, obj.id); - } - }, - - - - PrivatePost : { - - authors: async function(obj, args, context){ - return connectors.getUser(context.user, obj.author_uid, obj.author_db); - - }, - - recipients: async function(obj, args, context){ - return connectors.getGroup(context.user, obj.recipient_uid); - - } - }, - - Question : { - - authors: async function(obj, args, context){ - return connectors.getUser(context.user, obj.author_uid, obj.author_db); - }, - - recipients: async function(obj, args, context){ - return connectors.getGroup(context.user, obj.recipient_uid); - }, - - forAnswer: function(obj, args, context){ - return obj.for_answer; - } - }, - - Answer : { - - authors: async function(obj, args, context){ - return connectors.getGroup(context.user, obj.author_uid); - }, - - recipients: async function(obj, args, context){ - return connectors.getGroup(context.user, obj.recipient_uid); - }, - - forQuestion: function(obj, args, context){ - return obj.for_question; - - }, - } -}; - -export default MessageResolvers; \ No newline at end of file diff --git a/src/graphql/resolvers/messages.ts b/src/graphql/resolvers/messages.ts new file mode 100644 index 0000000..01c81e4 --- /dev/null +++ b/src/graphql/resolvers/messages.ts @@ -0,0 +1,718 @@ +/** + * @file Resolvers pour tous les types de messages + * @author akka, ofacklam + */ + +import {Group, SimpleGroup, MetaGroup} from './groups'; +import {User} from './users'; +import knex from '../../../db/knex_router'; + +export abstract class Message { + + /** + * @memberof GraphQL + * @class Message + * @summary Resolvers des messages + * @classdesc Une classe abstraite représentant l'interface Message 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 {number} mid - Identifiant du message, supposé valide. + * @rights dépend du type de message + * @abstract + */ + constructor(mid: number) { + this.mid = mid; + this.m_dataLoaded = false; + } + + /** + * @memberof GraphQL.Message# + * @function fetchData + * @summary Fonction qui va chercher toutes les données sur ce message dans la BDD, si ce n'est pas déja fait. + * @async + * @protected + * @abstract + */ + protected abstract async fetchData(): Promise<void> + + /** + * Protected properties. + * Ce sont tous les champs triviaux, ie qui peuvent etre récupérés en une seule requete a la BDD, + * 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_title: string + protected m_content: string + + /** + * Ci-dessous les resolvers a proprement parler. + */ + + /** + * @memberof GraphQL.Message# + * @function __resolveType + * @summary Renvoie quel type de Message c'est + * @return {string} + * @rights same as message object + */ + __resolveType(): string { + if (this instanceof Announcement) { + return "Announcement"; + } + else if (this instanceof Event) { + return "Event"; + } + else if (this instanceof Question) { + return "Question"; + } + else if (this instanceof Answer) { + return "Answer"; + } + else if (this instanceof PrivatePost) { + return "PrivatePost"; + } + else { + throw "Mauvais type de message"; + } + } + + /** @rights same as message object */ + mid: number; + + /** + * @memberof GraphQL.Message# + * @function createdAt + * @summary Renvoie la date de création + * @return {Promise(string)} + * @rights same as message object + * @async + */ + async createdAt(args, context, info): Promise<string> { + await this.fetchData(); + return this.m_createdAt; + } + + /** + * @memberof GraphQL.Message# + * @function updatedAt + * @summary Renvoie la date de mise a jour + * @return {Promise(string)} + * @rights same as message object + * @async + */ + async updatedAt(args, context, info): Promise<string> { + await this.fetchData(); + return this.m_updatedAt; + } + + /** + * @memberof GraphQL.Message# + * @function title + * @summary Renvoie le titre du message + * @return {Promise(string)} + * @rights same as message object + * @async + */ + async title(args, context, info): Promise<string> { + await this.fetchData(); + return this.m_title; + } + + /** + * @memberof GraphQL.Message# + * @function content + * @summary Renvoie le contenu du message + * @return {Promise(string)} + * @rights same as message object + * @async + */ + async content(args, context, info): Promise<string> { + await this.fetchData(); + return this.m_content; + } +} + +export class Announcement extends Message { + + /** + * @memberof GraphQL + * @class Announcement + * @extends GraphQL.Message + * @summary Resolvers des announcements + * @classdesc Une classe représentant le type Announcement 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 {number} mid - Identifiant du message, supposé valide. + * @rights membre d'un groupe author ou d'un groupe recipient + */ + constructor(mid: number) { + super(mid); + } + + /** + * @memberof GraphQL.Announcement# + * @function fetchData + * @summary Fonction qui va chercher toutes les données sur cet announcement dans la BDD, si ce n'est pas déja fait. + * @async + * @protected + */ + protected async fetchData(): Promise<void> { + if(!this.m_dataLoaded) { + let data = await knex.select('created_at', + 'updated_at', + 'title', + 'content', + //'importance', + 'views').from('messages_announcements').where('mid', this.mid); + + if (data.length() > 0) { + let m = data[0]; + + this.m_createdAt = m.created_at; + this.m_updatedAt = m.updated_at; + this.m_title = m.title; + this.m_content = m.content; + //this.m_importance = m.importance; + this.m_views = m.views; + + this.m_dataLoaded = true; + } + } + } + + /** + * Protected properties. + * Ce sont tous les champs triviaux, ie qui peuvent etre récupérés en une seule requete a la BDD, + * 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_importance: number + protected m_views : number + + /** + * Ci-dessous les resolvers a proprement parler. + */ + + /** + * @memberof GraphQL.Announcement# + * @function authors + * @summary Renvoie les groupes auteurs + * @return {Promise(Group[])} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async authors(args, context, info): Promise<Group[]> { + throw "Not implemented"; + } + + /** + * @memberof GraphQL.Announcement# + * @function recipients + * @summary Renvoie les groupes destinataires + * @return {Promise(Group[])} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async recipients(args, context, info): Promise<Group[]> { + throw "Not implemented"; + } + + /** + * @memberof GraphQL.Announcement# + * @function importance + * @summary Renvoie l'importance + * @return {Promise(number)} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async importance(args, context, info): Promise<number> { + await this.fetchData(); + return this.m_importance; + } + + /** + * @memberof GraphQL.Announcement# + * @function views + * @summary Renvoie le nombre de vues + * @return {Promise(number)} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async views(args, context, info): Promise<number> { + await this.fetchData(); + return this.m_views; + } + + /** + * @memberof GraphQL.Announcement# + * @function forEvent + * @summary Renvoie l'evenement correspondant, s'il existe + * @return {Promise(Event)} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async forEvent(args, context, info): Promise<Event> { + let data = await knex.select('mid').from('messages_events').where('for_announcement', this.mid); + if(data.length() > 0) { + return new Event(data[0].mid); + } + return null; + } +} + +export class Event extends Message { + + /** + * @memberof GraphQL + * @class Event + * @extends GraphQL.Message + * @summary Resolvers des events + * @classdesc Une classe représentant le type Event 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 {number} mid - Identifiant du message, supposé valide. + * @rights membre d'un groupe author ou d'un groupe recipient + */ + constructor(mid: number) { + super(mid); + } + + /** + * @memberof GraphQL.Event# + * @function fetchData + * @summary Fonction qui va chercher toutes les données sur cet event dans la BDD, si ce n'est pas déja fait. + * @async + * @protected + */ + protected async fetchData(): Promise<void> { + if (!this.m_dataLoaded) { + let data = await knex.select('created_at', + 'updated_at', + 'title', + 'content', + 'location', + 'start_time', + 'end_time', + 'for_announcement').from('messages_events').where('mid', this.mid); + + if (data.length() > 0) { + let m = data[0]; + + this.m_createdAt = m.created_at; + this.m_updatedAt = m.updated_at; + this.m_title = m.title; + this.m_content = m.content; + this.m_location = m.location; + this.m_startTime = m.start_time; + this.m_endTime = m.end_time; + this.m_forAnnouncement = m.for_announcement; + + this.m_dataLoaded = true; + } + } + } + + /** + * Protected properties. + * Ce sont tous les champs triviaux, ie qui peuvent etre récupérés en une seule requete a la BDD, + * 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_location: string + protected m_startTime: string + protected m_endTime: string + protected m_forAnnouncement: number + + /** + * Ci-dessous les resolvers a proprement parler. + */ + + /** + * @memberof GraphQL.Event# + * @function authors + * @summary Renvoie les groupes auteurs + * @return {Promise(Group[])} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async authors(args, context, info): Promise<Group[]> { + throw "Not implemented"; + } + + /** + * @memberof GraphQL.Event# + * @function recipients + * @summary Renvoie les groupes destinataires + * @return {Promise(Group[])} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async recipients(args, context, info): Promise<Group[]> { + throw "Not implemented"; + } + + /** + * @memberof GraphQL.Event# + * @function location + * @summary Renvoie l'endroit + * @return {Promise(string)} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async location(args, context, info): Promise<string> { + await this.fetchData(); + return this.m_location; + } + + /** + * @memberof GraphQL.Event# + * @function startTime + * @summary Renvoie l'horaire de début + * @return {Promise(string)} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async startTime(args, context, info): Promise<string> { + await this.fetchData(); + return this.m_startTime; + } + + /** + * @memberof GraphQL.Event# + * @function endTime + * @summary Renvoie l'horaire de fin + * @return {Promise(string)} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async endTime(args, context, info): Promise<string> { + await this.fetchData(); + return this.m_endTime; + } + + /** + * @memberof GraphQL.Event# + * @function participatingGroups + * @summary Renvoie les groupes qui participent + * @return {Promise(Group[])} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async participatingGroups(args, context, info): Promise<Group[]> { + throw "Not implemented"; + } + + /** + * @memberof GraphQL.Event# + * @function participatingUsers + * @summary Renvoie les users qui participent + * @return {Promise(User[])} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async participatingUsers(args, context, info): Promise<User[]> { + throw "Not implemented"; + } + + /** + * @memberof GraphQL.Event# + * @function forAnnouncement + * @summary Renvoie l'announcement correspondant, s'il existe + * @return {Promise(Announcement)} + * @rights membre d'un groupe author ou d'un groupe recipient + * @async + */ + async forAnnouncement(args, context, info): Promise<Announcement> { + await this.fetchData(); + if (this.m_forAnnouncement == 0) { //what is the default ????? + return null; + } + else { + return new Announcement(this.m_forAnnouncement); + } + } +} + +export class PrivatePost extends Message { + + /** + * @memberof GraphQL + * @class PrivatePost + * @extends GraphQL.Message + * @summary Resolvers des posts privés + * @classdesc Une classe représentant le type PrivatePost 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 {number} mid - Identifiant du message, supposé valide. + * @rights membre du groupe recipient + */ + constructor(mid: number) { + super(mid); + } + + /** + * @memberof GraphQL.PrivatePost# + * @function fetchData + * @summary Fonction qui va chercher toutes les données sur ce post privé dans la BDD, si ce n'est pas déja fait. + * @async + * @protected + */ + protected async fetchData(): Promise<void> { + if (!this.m_dataLoaded) { + let data = await knex.select('created_at', + 'updated_at', + 'title', + 'content', + 'author', + 'recipient').from('messages_private_posts').where('mid', this.mid); + + if (data.length() > 0) { + let m = data[0]; + + this.m_createdAt = m.created_at; + this.m_updatedAt = m.updated_at; + this.m_title = m.title; + this.m_content = m.content; + this.m_author = m.author; + this.m_recipient = m.recipient; + + this.m_dataLoaded = true; + } + } + } + + /** + * Protected properties. + * Ce sont tous les champs triviaux, ie qui peuvent etre récupérés en une seule requete a la BDD, + * 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_author: string + protected m_recipient: string + + /** + * Ci-dessous les resolvers a proprement parler. + */ + + /** + * @memberof GraphQL.Event# + * @function author + * @summary Renvoie le user auteur + * @return {Promise(User)} + * @rights membre du groupe recipient + * @async + */ + async author(args, context, info): Promise<User> { + await this.fetchData(); + return new User(this.m_author); + } + + /** + * @memberof GraphQL.Event# + * @function recipient + * @summary Renvoie le groupe destinataire + * @return {Promise(Group)} + * @rights membre du groupe recipient + * @async + */ + async recipient(args, context, info): Promise<Group> { + await this.fetchData(); + throw "Not implemented"; + } +} + +export class Question extends Message { + + /** + * @memberof GraphQL + * @class Question + * @extends GraphQL.Message + * @summary Resolvers des questions + * @classdesc Une classe représentant le type Question 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 {number} mid - Identifiant du message, supposé valide. + * @rights viewer du groupe recipient + */ + constructor(mid: number) { + super(mid); + } + + /** + * @memberof GraphQL.Question# + * @function fetchData + * @summary Fonction qui va chercher toutes les données sur ce post privé dans la BDD, si ce n'est pas déja fait. + * @async + * @protected + */ + protected async fetchData(): Promise<void> { + if (!this.m_dataLoaded) { + let data = await knex.select('created_at', + 'updated_at', + 'title', + 'content', + 'author', + 'recipient').from('messages_questions').where('mid', this.mid); + + if (data.length() > 0) { + let m = data[0]; + + this.m_createdAt = m.created_at; + this.m_updatedAt = m.updated_at; + this.m_title = m.title; + this.m_content = m.content; + this.m_author = m.author; + this.m_recipient = m.recipient; + + this.m_dataLoaded = true; + } + } + } + + /** + * Protected properties. + * Ce sont tous les champs triviaux, ie qui peuvent etre récupérés en une seule requete a la BDD, + * 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_author: string + protected m_recipient: string + + /** + * Ci-dessous les resolvers a proprement parler. + */ + + /** + * @memberof GraphQL.Question# + * @function author + * @summary Renvoie le user auteur + * @return {Promise(User)} + * @rights viewer du groupe recipient + * @async + */ + async author(args, context, info): Promise<User> { + await this.fetchData(); + return new User(this.m_author); + } + + /** + * @memberof GraphQL.Question# + * @function recipient + * @summary Renvoie le groupe destinataire + * @return {Promise(Group)} + * @rights viewer du groupe recipient + * @async + */ + async recipient(args, context, info): Promise<Group> { + await this.fetchData(); + throw "Not implemented"; + } + + /** + * @memberof GraphQL.Question# + * @function forAnswer + * @summary Renvoie la réponse correspondante, si elle existe + * @return {Promise(Answer)} + * @rights viewer du groupe recipient + * @async + */ + async forAnswer(args, context, info): Promise<Answer> { + let data = await knex.select('mid').from('messages_answers').where('for_question', this.mid); + if (data.length() > 0) { + return new Answer(data[0].mid); + } + return null; + } +} + +export class Answer extends Message { + + /** + * @memberof GraphQL + * @class Answer + * @extends GraphQL.Message + * @summary Resolvers des réponses + * @classdesc Une classe représentant le type Answer 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 {number} mid - Identifiant du message, supposé valide. + * @rights viewer du groupe author + */ + constructor(mid: number) { + super(mid); + } + + /** + * @memberof GraphQL.Answer# + * @function fetchData + * @summary Fonction qui va chercher toutes les données sur ce post privé dans la BDD, si ce n'est pas déja fait. + * @async + * @protected + */ + protected async fetchData(): Promise<void> { + if (!this.m_dataLoaded) { + let data = await knex.select('created_at', + 'updated_at', + 'title', + 'content', + 'recipient', //<--- WTF ???? + 'for_question').from('messages_answers').where('mid', this.mid); + + if (data.length() > 0) { + let m = data[0]; + + this.m_createdAt = m.created_at; + this.m_updatedAt = m.updated_at; + this.m_title = m.title; + this.m_content = m.content; + this.m_author = m.recipient; + this.m_forQuestion = m.for_question; + + this.m_dataLoaded = true; + } + } + } + + /** + * Protected properties. + * Ce sont tous les champs triviaux, ie qui peuvent etre récupérés en une seule requete a la BDD, + * 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_author: string + protected m_forQuestion: number + + /** + * Ci-dessous les resolvers a proprement parler. + */ + + /** + * @memberof GraphQL.Answer# + * @function author + * @summary Renvoie le groupe auteur + * @return {Promise(Group)} + * @rights viewer du groupe author + * @async + */ + async author(args, context, info): Promise<Group> { + await this.fetchData(); + throw "Not implemented"; + } + + /** + * @memberof GraphQL.Answer# + * @function forQuestion + * @summary Renvoie la question correspondante + * @return {Promise(Question)} + * @rights viewer du groupe author + * @async + */ + async forQuestion(args, context, info): Promise<Question> { + await this.fetchData(); + return new Question(this.m_forQuestion); + } +} diff --git a/src/graphql/resolvers/users.ts b/src/graphql/resolvers/users.ts index d1263e7..4a856aa 100644 --- a/src/graphql/resolvers/users.ts +++ b/src/graphql/resolvers/users.ts @@ -4,8 +4,9 @@ */ import {Group, SimpleGroup, MetaGroup} from './groups'; +import {Question} from './messages'; import {User as LDAP_User} from '../../ldap/export/user'; -import {Tools, GroupSet} from '../connectors/tools' +import {Tools, GroupSet} from '../connectors/tools'; export class User { @@ -309,7 +310,7 @@ export class User { * @function questionsFromUser * @summary Renvoie les questions que le user a posées * @return {Promise(Question[])} - * @rights connectedOrOnplatal + * @rights viewer of recipient group * @async */ async questionsFromUser(args, context, info): Promise<Question[]> { diff --git a/src/graphql/typeDefs/objects.graphql b/src/graphql/typeDefs/objects.graphql index d3d83a8..3244955 100644 --- a/src/graphql/typeDefs/objects.graphql +++ b/src/graphql/typeDefs/objects.graphql @@ -174,7 +174,7 @@ interface Message { content: String! #authors: AuthorUnion - recipient: Group + #recipient: Group } # Annonce effectuée par un ou plusieurs groupes. @@ -186,7 +186,7 @@ type Announcement implements Message { content: String! authors: [Group] - recipient: Group + recipients: [Group] importance: Int # importance de cette Announcement, sur une échelle de [??] à [??] (TODO) views: Int # nombre de vues @@ -204,7 +204,7 @@ type Event implements Message { content: String! authors: [Group] # Organisateurs de l'événement - recipient: Group + recipients: [Group] location: String! startTime: String! @@ -226,7 +226,7 @@ type PrivatePost implements Message { title: String! content: String! - authors: User + author: User recipient: Group } @@ -238,7 +238,7 @@ type Question implements Message { title: String! content: String! - authors: User + author: User recipient: Group # Référence la réponse donnée par le groupe à cette Question. Si pas encore répondu, null. @@ -253,8 +253,7 @@ type Answer implements Message { title: String! content: String! - authors: Group - recipient: Group # doit être le même que authors + author: Group # La question à laquelle cette Answer répond. Non-nullable bien sûr forQuestion: Question! -- GitLab