Improve GraphQL documentation and cleanup API
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
e8a3b6aa94
commit
3eacbb2ca3
@ -1,8 +0,0 @@
|
||||
projects:
|
||||
Mobilizon:
|
||||
schemaPath: schema.graphql
|
||||
extensions:
|
||||
endpoints:
|
||||
dev:
|
||||
url: 'http://localhost:4000/api'
|
||||
introspect: true
|
3231
js/schema.graphql
Normal file
3231
js/schema.graphql
Normal file
File diff suppressed because it is too large
Load Diff
@ -288,7 +288,6 @@ export default class Comment extends Vue {
|
||||
mutation: CREATE_REPORT,
|
||||
variables: {
|
||||
eventId: this.event.id,
|
||||
reporterId: this.currentActor.id,
|
||||
reportedId: this.comment.actor.id,
|
||||
commentsIds: [this.comment.id],
|
||||
content,
|
||||
|
@ -112,7 +112,6 @@ export default class CommentTree extends Vue {
|
||||
mutation: CREATE_COMMENT_FROM_EVENT,
|
||||
variables: {
|
||||
eventId: this.event.id,
|
||||
actorId: comment.actor.id,
|
||||
text: comment.text,
|
||||
inReplyToCommentId: comment.inReplyToComment ? comment.inReplyToComment.id : null,
|
||||
},
|
||||
@ -204,7 +203,6 @@ export default class CommentTree extends Vue {
|
||||
mutation: DELETE_COMMENT,
|
||||
variables: {
|
||||
commentId: comment.id,
|
||||
actorId: this.currentActor.id,
|
||||
},
|
||||
update: (store, { data }) => {
|
||||
if (data == null) return;
|
||||
|
@ -530,7 +530,6 @@ export default class EditorComponent extends Vue {
|
||||
variables: {
|
||||
file: image,
|
||||
name: image.name,
|
||||
actorId: this.currentActor.id,
|
||||
},
|
||||
});
|
||||
if (data.uploadPicture && data.uploadPicture.url) {
|
||||
|
@ -88,15 +88,12 @@ export default class Image extends Node {
|
||||
});
|
||||
if (!coordinates) return false;
|
||||
const client = apolloProvider.defaultClient as ApolloClient<NormalizedCacheObject>;
|
||||
const editorElem = document.getElementById("tiptab-editor");
|
||||
const actorId = editorElem && editorElem.dataset.actorId;
|
||||
|
||||
try {
|
||||
images.forEach(async (image) => {
|
||||
const { data } = await client.mutate({
|
||||
mutation: UPLOAD_PICTURE,
|
||||
variables: {
|
||||
actorId,
|
||||
file: image,
|
||||
name: image.name,
|
||||
},
|
||||
|
@ -246,7 +246,7 @@ export default class EventListCard extends mixins(ActorMixin, EventMixin) {
|
||||
async openDeleteEventModalWrapper(): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
await this.openDeleteEventModal(this.participation.event, this.currentActor);
|
||||
await this.openDeleteEventModal(this.participation.event);
|
||||
}
|
||||
|
||||
async gotToWithCheck(participation: IParticipant, route: RawLocation): Promise<Route> {
|
||||
|
@ -127,7 +127,6 @@ export default class ParticipationWithoutAccount extends Vue {
|
||||
mutation: JOIN_EVENT,
|
||||
variables: {
|
||||
eventId: this.event.id,
|
||||
actorId: this.config.anonymous.actorId,
|
||||
email: this.anonymousParticipation.email,
|
||||
message: this.anonymousParticipation.message,
|
||||
locale: this.$i18n.locale,
|
||||
|
@ -68,18 +68,8 @@ export const COMMENTS_THREADS = gql`
|
||||
`;
|
||||
|
||||
export const CREATE_COMMENT_FROM_EVENT = gql`
|
||||
mutation CreateCommentFromEvent(
|
||||
$eventId: ID!
|
||||
$actorId: ID!
|
||||
$text: String!
|
||||
$inReplyToCommentId: ID
|
||||
) {
|
||||
createComment(
|
||||
eventId: $eventId
|
||||
actorId: $actorId
|
||||
text: $text
|
||||
inReplyToCommentId: $inReplyToCommentId
|
||||
) {
|
||||
mutation CreateCommentFromEvent($eventId: ID!, $text: String!, $inReplyToCommentId: ID) {
|
||||
createComment(eventId: $eventId, text: $text, inReplyToCommentId: $inReplyToCommentId) {
|
||||
...CommentRecursive
|
||||
}
|
||||
}
|
||||
|
@ -84,8 +84,8 @@ export const DISCUSSION_FIELDS_FRAGMENT = gql`
|
||||
`;
|
||||
|
||||
export const CREATE_DISCUSSION = gql`
|
||||
mutation createDiscussion($title: String!, $creatorId: ID!, $actorId: ID!, $text: String!) {
|
||||
createDiscussion(title: $title, text: $text, creatorId: $creatorId, actorId: $actorId) {
|
||||
mutation createDiscussion($title: String!, $actorId: ID!, $text: String!) {
|
||||
createDiscussion(title: $title, text: $text, actorId: $actorId) {
|
||||
...DiscussionFields
|
||||
}
|
||||
}
|
||||
|
@ -498,8 +498,8 @@ export const CONFIRM_PARTICIPATION = gql`
|
||||
`;
|
||||
|
||||
export const UPDATE_PARTICIPANT = gql`
|
||||
mutation AcceptParticipant($id: ID!, $moderatorActorId: ID!, $role: ParticipantRoleEnum!) {
|
||||
updateParticipation(id: $id, moderatorActorId: $moderatorActorId, role: $role) {
|
||||
mutation UpdateParticipant($id: ID!, $role: ParticipantRoleEnum!) {
|
||||
updateParticipation(id: $id, role: $role) {
|
||||
role
|
||||
id
|
||||
}
|
||||
@ -507,20 +507,20 @@ export const UPDATE_PARTICIPANT = gql`
|
||||
`;
|
||||
|
||||
export const DELETE_EVENT = gql`
|
||||
mutation DeleteEvent($eventId: ID!, $actorId: ID!) {
|
||||
deleteEvent(eventId: $eventId, actorId: $actorId) {
|
||||
mutation DeleteEvent($eventId: ID!) {
|
||||
deleteEvent(eventId: $eventId) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const PARTICIPANTS = gql`
|
||||
query($uuid: UUID!, $page: Int, $limit: Int, $roles: String, $actorId: ID!) {
|
||||
query($uuid: UUID!, $page: Int, $limit: Int, $roles: String) {
|
||||
event(uuid: $uuid) {
|
||||
id,
|
||||
uuid,
|
||||
title,
|
||||
participants(page: $page, limit: $limit, roles: $roles, actorId: $actorId) {
|
||||
participants(page: $page, limit: $limit, roles: $roles) {
|
||||
${participantsQuery}
|
||||
},
|
||||
participantStats {
|
||||
|
@ -223,7 +223,6 @@ export const GET_GROUP = gql`
|
||||
|
||||
export const CREATE_GROUP = gql`
|
||||
mutation CreateGroup(
|
||||
$creatorActorId: ID!
|
||||
$preferredUsername: String!
|
||||
$name: String!
|
||||
$summary: String
|
||||
@ -231,7 +230,6 @@ export const CREATE_GROUP = gql`
|
||||
$banner: PictureInput
|
||||
) {
|
||||
createGroup(
|
||||
creatorActorId: $creatorActorId
|
||||
preferredUsername: $preferredUsername
|
||||
name: $name
|
||||
summary: $summary
|
||||
|
@ -121,7 +121,6 @@ export const REPORT = gql`
|
||||
export const CREATE_REPORT = gql`
|
||||
mutation CreateReport(
|
||||
$eventId: ID
|
||||
$reporterId: ID!
|
||||
$reportedId: ID!
|
||||
$content: String
|
||||
$commentsIds: [ID]
|
||||
@ -129,7 +128,6 @@ export const CREATE_REPORT = gql`
|
||||
) {
|
||||
createReport(
|
||||
eventId: $eventId
|
||||
reporterId: $reporterId
|
||||
reportedId: $reportedId
|
||||
content: $content
|
||||
commentsIds: $commentsIds
|
||||
@ -141,8 +139,8 @@ export const CREATE_REPORT = gql`
|
||||
`;
|
||||
|
||||
export const UPDATE_REPORT = gql`
|
||||
mutation UpdateReport($reportId: ID!, $moderatorId: ID!, $status: ReportStatus!) {
|
||||
updateReportStatus(reportId: $reportId, moderatorId: $moderatorId, status: $status) {
|
||||
mutation UpdateReport($reportId: ID!, $status: ReportStatus!) {
|
||||
updateReportStatus(reportId: $reportId, status: $status) {
|
||||
...ReportFragment
|
||||
}
|
||||
}
|
||||
@ -150,8 +148,8 @@ export const UPDATE_REPORT = gql`
|
||||
`;
|
||||
|
||||
export const CREATE_REPORT_NOTE = gql`
|
||||
mutation CreateReportNote($reportId: ID!, $moderatorId: ID!, $content: String!) {
|
||||
createReportNote(reportId: $reportId, moderatorId: $moderatorId, content: $content) {
|
||||
mutation CreateReportNote($reportId: ID!, $content: String!) {
|
||||
createReportNote(reportId: $reportId, content: $content) {
|
||||
id
|
||||
content
|
||||
insertedAt
|
||||
|
@ -2,8 +2,8 @@ import gql from "graphql-tag";
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export const UPLOAD_PICTURE = gql`
|
||||
mutation UploadPicture($file: Upload!, $alt: String, $name: String!, $actorId: ID!) {
|
||||
uploadPicture(file: $file, alt: $alt, name: $name, actorId: $actorId) {
|
||||
mutation UploadPicture($file: Upload!, $alt: String, $name: String!) {
|
||||
uploadPicture(file: $file, alt: $alt, name: $name) {
|
||||
url
|
||||
id
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ export default class EventMixin extends mixins(Vue) {
|
||||
this.$notifier.success(this.$t("You have cancelled your participation") as string);
|
||||
}
|
||||
|
||||
protected async openDeleteEventModal(event: IEvent, currentActor: IPerson): Promise<void> {
|
||||
protected async openDeleteEventModal(event: IEvent): Promise<void> {
|
||||
function escapeRegExp(string: string) {
|
||||
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
|
||||
}
|
||||
@ -115,11 +115,11 @@ export default class EventMixin extends mixins(Vue) {
|
||||
placeholder: event.title,
|
||||
pattern: escapeRegExp(event.title),
|
||||
},
|
||||
onConfirm: () => this.deleteEvent(event, currentActor),
|
||||
onConfirm: () => this.deleteEvent(event),
|
||||
});
|
||||
}
|
||||
|
||||
private async deleteEvent(event: IEvent, currentActor: IPerson) {
|
||||
private async deleteEvent(event: IEvent) {
|
||||
const eventTitle = event.title;
|
||||
|
||||
try {
|
||||
@ -127,7 +127,6 @@ export default class EventMixin extends mixins(Vue) {
|
||||
mutation: DELETE_EVENT,
|
||||
variables: {
|
||||
eventId: event.id,
|
||||
actorId: currentActor.id,
|
||||
},
|
||||
});
|
||||
/**
|
||||
|
@ -70,7 +70,6 @@ export default class CreateDiscussion extends Vue {
|
||||
title: this.discussion.title,
|
||||
text: this.discussion.text,
|
||||
actorId: parseInt(this.group.id, 10),
|
||||
creatorId: parseInt(this.currentActor.id, 10),
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -761,7 +761,7 @@ export default class Event extends EventMixin {
|
||||
* Delete the event, then redirect to home.
|
||||
*/
|
||||
async openDeleteEventModalWrapper(): Promise<void> {
|
||||
await this.openDeleteEventModal(this.event, this.currentActor);
|
||||
await this.openDeleteEventModal(this.event);
|
||||
}
|
||||
|
||||
async reportEvent(content: string, forward: boolean): Promise<void> {
|
||||
@ -771,19 +771,12 @@ export default class Event extends EventMixin {
|
||||
this.$refs.reportModal.close();
|
||||
if (!this.event.organizerActor) return;
|
||||
const eventTitle = this.event.title;
|
||||
let reporterId = null;
|
||||
if (this.currentActor.id) {
|
||||
reporterId = this.currentActor.id;
|
||||
} else if (this.config.anonymous.reports.allowed) {
|
||||
reporterId = this.config.anonymous.actorId;
|
||||
}
|
||||
if (!reporterId) return;
|
||||
|
||||
try {
|
||||
await this.$apollo.mutate<IReport>({
|
||||
mutation: CREATE_REPORT,
|
||||
variables: {
|
||||
eventId: this.event.id,
|
||||
reporterId,
|
||||
reportedId: this.actorForReport ? this.actorForReport.id : null,
|
||||
content,
|
||||
forward,
|
||||
@ -808,7 +801,6 @@ export default class Event extends EventMixin {
|
||||
mutation: JOIN_EVENT,
|
||||
variables: {
|
||||
eventId: this.event.id,
|
||||
actorId: identity.id,
|
||||
message,
|
||||
},
|
||||
update: (store, { data }) => {
|
||||
|
@ -214,7 +214,6 @@ const MESSAGE_ELLIPSIS_LENGTH = 130;
|
||||
page: 1,
|
||||
limit: PARTICIPANTS_PER_PAGE,
|
||||
roles: this.roles,
|
||||
actorId: this.currentActor.id,
|
||||
};
|
||||
},
|
||||
skip() {
|
||||
@ -298,7 +297,6 @@ export default class Participants extends Vue {
|
||||
mutation: UPDATE_PARTICIPANT,
|
||||
variables: {
|
||||
id: participant.id,
|
||||
moderatorActorId: this.currentActor.id,
|
||||
role: ParticipantRole.PARTICIPANT,
|
||||
},
|
||||
});
|
||||
@ -313,7 +311,6 @@ export default class Participants extends Vue {
|
||||
mutation: UPDATE_PARTICIPANT,
|
||||
variables: {
|
||||
id: participant.id,
|
||||
moderatorActorId: this.currentActor.id,
|
||||
role: ParticipantRole.REJECTED,
|
||||
},
|
||||
});
|
||||
|
@ -178,15 +178,10 @@ export default class CreateGroup extends mixins(IdentityEditionMixin) {
|
||||
};
|
||||
}
|
||||
|
||||
const currentActor = {
|
||||
creatorActorId: this.currentActor.id,
|
||||
};
|
||||
|
||||
return {
|
||||
...this.group,
|
||||
...avatarObj,
|
||||
...bannerObj,
|
||||
...currentActor,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -481,18 +481,10 @@ export default class Group extends mixins(GroupMixin) {
|
||||
// @ts-ignore
|
||||
this.$refs.reportModal.close();
|
||||
const groupTitle = this.group.name || usernameWithDomain(this.group);
|
||||
let reporterId = null;
|
||||
if (this.currentActor.id) {
|
||||
reporterId = this.currentActor.id;
|
||||
} else if (this.config.anonymous.reports.allowed) {
|
||||
reporterId = this.config.anonymous.actorId;
|
||||
}
|
||||
if (!reporterId) return;
|
||||
try {
|
||||
await this.$apollo.mutate<IReport>({
|
||||
mutation: CREATE_REPORT,
|
||||
variables: {
|
||||
reporterId,
|
||||
reportedId: this.group.id,
|
||||
content,
|
||||
forward,
|
||||
|
@ -303,7 +303,6 @@ export default class Report extends Vue {
|
||||
mutation: CREATE_REPORT_NOTE,
|
||||
variables: {
|
||||
reportId: this.report.id,
|
||||
moderatorId: this.currentActor.id,
|
||||
content: this.noteContent,
|
||||
},
|
||||
update: (store, { data }) => {
|
||||
@ -372,7 +371,6 @@ export default class Report extends Vue {
|
||||
mutation: DELETE_EVENT,
|
||||
variables: {
|
||||
eventId: this.report.event.id.toString(),
|
||||
actorId: this.currentActor.id,
|
||||
},
|
||||
});
|
||||
|
||||
@ -395,7 +393,6 @@ export default class Report extends Vue {
|
||||
mutation: DELETE_COMMENT,
|
||||
variables: {
|
||||
commentId: comment.id,
|
||||
actorId: this.currentActor.id,
|
||||
},
|
||||
});
|
||||
this.$notifier.success(this.$t("Comment deleted") as string);
|
||||
@ -410,7 +407,6 @@ export default class Report extends Vue {
|
||||
mutation: UPDATE_REPORT,
|
||||
variables: {
|
||||
reportId: this.report.id,
|
||||
moderatorId: this.currentActor.id,
|
||||
status,
|
||||
},
|
||||
update: (store, { data }) => {
|
||||
|
@ -30,11 +30,11 @@ export default class Validate extends Vue {
|
||||
|
||||
failed = false;
|
||||
|
||||
async created() {
|
||||
async created(): Promise<void> {
|
||||
await this.validateAction();
|
||||
}
|
||||
|
||||
async validateAction() {
|
||||
async validateAction(): Promise<void> {
|
||||
try {
|
||||
await this.$apollo.mutate<{ validateEmail: ICurrentUser }>({
|
||||
mutation: VALIDATE_EMAIL,
|
||||
@ -43,11 +43,10 @@ export default class Validate extends Vue {
|
||||
},
|
||||
});
|
||||
this.loading = false;
|
||||
return await this.$router.push({ name: RouteName.HOME });
|
||||
await this.$router.push({ name: RouteName.HOME });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.failed = true;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": ["webpack-env", "mocha", "chai"],
|
||||
"types": ["webpack-env"],
|
||||
"typeRoots": ["./@types", "./node_modules/@types"],
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
|
@ -26,9 +26,6 @@ defmodule Mobilizon.GraphQL.API.Groups do
|
||||
else
|
||||
{:existing_group, _} ->
|
||||
{:error, "A group with this name already exists"}
|
||||
|
||||
{:is_owned, nil} ->
|
||||
{:error, "Profile is not owned by authenticated user"}
|
||||
end
|
||||
end
|
||||
|
||||
@ -42,9 +39,6 @@ defmodule Mobilizon.GraphQL.API.Groups do
|
||||
else
|
||||
{:existing_group, _} ->
|
||||
{:error, "A group with this name already exists"}
|
||||
|
||||
{:is_owned, nil} ->
|
||||
{:error, "Profile is not owned by authenticated user"}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3,11 +3,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
|
||||
Handles the comment-related GraphQL calls.
|
||||
"""
|
||||
|
||||
alias Mobilizon.{Actors, Admin, Discussions, Events}
|
||||
alias Mobilizon.{Actors, Admin, Discussions, Events, Users}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Discussions.Comment, as: CommentModel
|
||||
alias Mobilizon.Events.{Event, EventOptions}
|
||||
alias Mobilizon.Users
|
||||
alias Mobilizon.Users.User
|
||||
import Mobilizon.Web.Gettext
|
||||
|
||||
@ -21,14 +20,14 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
|
||||
|
||||
def create_comment(
|
||||
_parent,
|
||||
%{actor_id: actor_id, event_id: event_id} = args,
|
||||
%{event_id: event_id} = args,
|
||||
%{
|
||||
context: %{
|
||||
current_user: %User{} = user
|
||||
}
|
||||
}
|
||||
) do
|
||||
with {:is_owned, %Actor{} = _organizer_actor} <- User.owns_actor(user, actor_id),
|
||||
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
|
||||
{:find_event,
|
||||
{:ok,
|
||||
%Event{
|
||||
@ -36,18 +35,15 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
|
||||
organizer_actor_id: organizer_actor_id
|
||||
}}} <-
|
||||
{:find_event, Events.get_event(event_id)},
|
||||
{actor_id, ""} <- Integer.parse(actor_id),
|
||||
{:allowed, true} <-
|
||||
{:allowed, comment_moderation != :closed || actor_id == organizer_actor_id},
|
||||
args <- Map.put(args, :actor_id, actor_id),
|
||||
{:ok, _, %CommentModel{} = comment} <-
|
||||
Comments.create_comment(args) do
|
||||
{:ok, comment}
|
||||
else
|
||||
{:allowed, false} ->
|
||||
{:error, :unauthorized}
|
||||
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||
end
|
||||
end
|
||||
|
||||
@ -107,9 +103,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Comment do
|
||||
else
|
||||
%CommentModel{deleted_at: deleted_at} when not is_nil(deleted_at) ->
|
||||
{:error, dgettext("errors", "Comment is already deleted")}
|
||||
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -62,6 +62,15 @@ defmodule Mobilizon.GraphQL.Resolvers.Discussion do
|
||||
end
|
||||
end
|
||||
|
||||
def get_discussion(_parent, _args, %{
|
||||
context: %{
|
||||
current_user: %User{} = _user
|
||||
}
|
||||
}),
|
||||
do:
|
||||
{:error,
|
||||
dgettext("errors", "You must provide either an ID or a slug to access a discussion")}
|
||||
|
||||
def get_discussion(_parent, _args, _resolution),
|
||||
do: {:error, dgettext("errors", "You need to be logged-in to access discussions")}
|
||||
|
||||
|
@ -3,7 +3,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||
Handles the event-related GraphQL calls.
|
||||
"""
|
||||
|
||||
alias Mobilizon.{Actors, Admin, Events}
|
||||
alias Mobilizon.{Actors, Admin, Events, Users}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Config
|
||||
alias Mobilizon.Events.{Event, EventParticipantStats}
|
||||
@ -74,10 +74,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||
"""
|
||||
def list_participants_for_event(
|
||||
%Event{id: event_id},
|
||||
%{page: page, limit: limit, roles: roles, actor_id: actor_id},
|
||||
%{page: page, limit: limit, roles: roles},
|
||||
%{context: %{current_user: %User{} = user}} = _resolution
|
||||
) do
|
||||
with {:is_owned, %Actor{} = _actor} <- User.owns_actor(user, actor_id),
|
||||
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
|
||||
# Check that moderator has right
|
||||
{:actor_approve_permission, true} <-
|
||||
{:actor_approve_permission, Events.moderator_for_event?(event_id, actor_id)} do
|
||||
@ -96,9 +96,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||
participants = Events.list_participants_for_event(event_id, roles, page, limit)
|
||||
{:ok, participants}
|
||||
else
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Moderator profile is not owned by authenticated user")}
|
||||
|
||||
{:actor_approve_permission, _} ->
|
||||
{:error,
|
||||
dgettext("errors", "Provided moderator profile doesn't have permission on this event")}
|
||||
@ -191,8 +188,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||
%{context: %{current_user: user}} = _resolution
|
||||
) do
|
||||
# See https://github.com/absinthe-graphql/absinthe/issues/490
|
||||
with args <- Map.put(args, :options, args[:options] || %{}),
|
||||
{:is_owned, %Actor{} = organizer_actor} <- User.owns_actor(user, organizer_actor_id),
|
||||
with {:is_owned, %Actor{} = organizer_actor} <- User.owns_actor(user, organizer_actor_id),
|
||||
args <- Map.put(args, :options, args[:options] || %{}),
|
||||
args_with_organizer <- Map.put(args, :organizer_actor, organizer_actor),
|
||||
{:ok, %Activity{data: %{"object" => %{"type" => "Event"}}}, %Event{} = event} <-
|
||||
API.Events.create_event(args_with_organizer) do
|
||||
@ -257,12 +254,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||
"""
|
||||
def delete_event(
|
||||
_parent,
|
||||
%{event_id: event_id, actor_id: actor_id},
|
||||
%{event_id: event_id},
|
||||
%{context: %{current_user: %User{role: role} = user}}
|
||||
) do
|
||||
with {:ok, %Event{local: is_local} = event} <- Events.get_event_with_preload(event_id),
|
||||
{actor_id, ""} <- Integer.parse(actor_id),
|
||||
{:is_owned, %Actor{} = actor} <- User.owns_actor(user, actor_id) do
|
||||
%Actor{id: actor_id} = actor <- Users.get_actor_for_user(user) do
|
||||
cond do
|
||||
{:event_can_be_managed, true} == Event.can_be_managed_by(event, actor_id) ->
|
||||
do_delete_event(event, actor)
|
||||
@ -281,9 +277,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||
else
|
||||
{:error, :event_not_found} ->
|
||||
{:error, dgettext("errors", "Event not found")}
|
||||
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -121,10 +121,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
|
||||
}
|
||||
}
|
||||
) do
|
||||
with creator_actor_id <- Map.get(args, :creator_actor_id),
|
||||
{:is_owned, %Actor{} = creator_actor} <- User.owns_actor(user, creator_actor_id),
|
||||
with %Actor{id: creator_actor_id} = creator_actor <- Users.get_actor_for_user(user),
|
||||
args <- Map.update(args, :preferred_username, "", &String.downcase/1),
|
||||
args <- Map.put(args, :creator_actor, creator_actor),
|
||||
args <- Map.put(args, :creator_actor_id, creator_actor_id),
|
||||
args <- save_attached_pictures(args),
|
||||
{:ok, _activity, %Actor{type: :Group} = group} <-
|
||||
API.Groups.create_group(args) do
|
||||
@ -132,9 +132,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
|
||||
else
|
||||
{:error, err} when is_binary(err) ->
|
||||
{:error, err}
|
||||
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Creator profile is not owned by the current user")}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -76,9 +76,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Member do
|
||||
{:ok, _activity, %Member{} = member} <- ActivityPub.invite(group, actor, target_actor) do
|
||||
{:ok, member}
|
||||
else
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||
|
||||
{:error, :group_not_found} ->
|
||||
{:error, dgettext("errors", "Group not found")}
|
||||
|
||||
|
@ -2,7 +2,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
|
||||
@moduledoc """
|
||||
Handles the participation-related GraphQL calls.
|
||||
"""
|
||||
alias Mobilizon.{Actors, Config, Crypto, Events}
|
||||
alias Mobilizon.{Actors, Config, Crypto, Events, Users}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events.{Event, Participant}
|
||||
alias Mobilizon.GraphQL.API.Participations
|
||||
@ -206,7 +206,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
|
||||
|
||||
def update_participation(
|
||||
_parent,
|
||||
%{id: participation_id, moderator_actor_id: moderator_actor_id, role: new_role},
|
||||
%{id: participation_id, role: new_role},
|
||||
%{
|
||||
context: %{
|
||||
current_user: user
|
||||
@ -214,7 +214,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
|
||||
}
|
||||
) do
|
||||
# Check that moderator provided is rightly authenticated
|
||||
with {:is_owned, moderator_actor} <- User.owns_actor(user, moderator_actor_id),
|
||||
with %Actor{id: moderator_actor_id} = moderator_actor <- Users.get_actor_for_user(user),
|
||||
# Check that participation already exists
|
||||
{:has_participation, %Participant{role: old_role} = participation} <-
|
||||
{:has_participation, Events.get_participant(participation_id)},
|
||||
@ -227,9 +227,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Participant do
|
||||
Participations.update(participation, moderator_actor, new_role) do
|
||||
{:ok, participation}
|
||||
else
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Moderator profile is not owned by authenticated user")}
|
||||
|
||||
{:has_participation, nil} ->
|
||||
{:error, dgettext("errors", "Participant not found")}
|
||||
|
||||
|
@ -5,12 +5,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Person do
|
||||
|
||||
import Mobilizon.Users.Guards
|
||||
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.{Actors, Events, Users}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events
|
||||
alias Mobilizon.Events.Participant
|
||||
alias Mobilizon.Storage.Page
|
||||
alias Mobilizon.Users
|
||||
alias Mobilizon.Users.User
|
||||
import Mobilizon.Web.Gettext
|
||||
|
||||
|
@ -4,9 +4,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do
|
||||
"""
|
||||
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Media
|
||||
alias Mobilizon.{Media, Users}
|
||||
alias Mobilizon.Media.Picture
|
||||
alias Mobilizon.Users.User
|
||||
import Mobilizon.Web.Gettext
|
||||
|
||||
@doc """
|
||||
@ -46,10 +45,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do
|
||||
@spec upload_picture(map, map, map) :: {:ok, Picture.t()} | {:error, any}
|
||||
def upload_picture(
|
||||
_parent,
|
||||
%{file: %Plug.Upload{} = file, actor_id: actor_id} = args,
|
||||
%{file: %Plug.Upload{} = file} = args,
|
||||
%{context: %{current_user: user}}
|
||||
) do
|
||||
with {:is_owned, %Actor{}} <- User.owns_actor(user, actor_id),
|
||||
with %Actor{id: actor_id} <- Users.get_actor_for_user(user),
|
||||
{:ok, %{name: _name, url: url, content_type: content_type, size: size}} <-
|
||||
Mobilizon.Web.Upload.store(file),
|
||||
args <-
|
||||
@ -68,9 +67,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do
|
||||
size: picture.file.size
|
||||
}}
|
||||
else
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||
|
||||
{:error, :mime_type_not_allowed} ->
|
||||
{:error, dgettext("errors", "File doesn't have an allowed MIME type.")}
|
||||
|
||||
|
@ -5,10 +5,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do
|
||||
|
||||
import Mobilizon.Users.Guards
|
||||
|
||||
alias Mobilizon.Actors
|
||||
alias Mobilizon.{Actors, Config, Reports, Users}
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Config
|
||||
alias Mobilizon.Reports
|
||||
alias Mobilizon.Reports.{Note, Report}
|
||||
alias Mobilizon.Users.User
|
||||
import Mobilizon.Web.Gettext
|
||||
@ -48,16 +46,14 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do
|
||||
"""
|
||||
def create_report(
|
||||
_parent,
|
||||
%{reporter_id: reporter_id} = args,
|
||||
args,
|
||||
%{context: %{current_user: %User{} = user}} = _resolution
|
||||
) do
|
||||
with {:is_owned, %Actor{}} <- User.owns_actor(user, reporter_id),
|
||||
{:ok, _, %Report{} = report} <- API.Reports.report(args) do
|
||||
with %Actor{id: reporter_id} <- Users.get_actor_for_user(user),
|
||||
{:ok, _, %Report{} = report} <-
|
||||
args |> Map.put(:reporter_id, reporter_id) |> API.Reports.report() do
|
||||
{:ok, report}
|
||||
else
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Reporter profile is not owned by authenticated user")}
|
||||
|
||||
_error ->
|
||||
{:error, dgettext("errors", "Error while saving report")}
|
||||
end
|
||||
@ -65,47 +61,37 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do
|
||||
|
||||
def create_report(
|
||||
_parent,
|
||||
%{reporter_id: reporter_id} = args,
|
||||
args,
|
||||
_resolution
|
||||
) do
|
||||
with {:anonymous_reporting_allowed, true} <-
|
||||
{:anonymous_reporting_allowed, Config.anonymous_reporting?()},
|
||||
{:wrong_id, true} <- {:wrong_id, reporter_id == to_string(Config.anonymous_actor_id())},
|
||||
{:ok, _, %Report{} = report} <- API.Reports.report(args) do
|
||||
{:ok, _, %Report{} = report} <-
|
||||
args |> Map.put(:reporter_id, Config.anonymous_actor_id()) |> API.Reports.report() do
|
||||
{:ok, report}
|
||||
else
|
||||
{:anonymous_reporting_allowed, _} ->
|
||||
{:error, dgettext("errors", "You need to be logged-in to create reports")}
|
||||
|
||||
{:wrong_id, _} ->
|
||||
{:error, dgettext("errors", "Reporter ID does not match the anonymous profile id")}
|
||||
|
||||
_error ->
|
||||
{:error, dgettext("errors", "Error while saving report")}
|
||||
end
|
||||
end
|
||||
|
||||
def create_report(_parent, _args, _resolution) do
|
||||
{:error, dgettext("errors", "You need to be logged-in to create reports")}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Update a report's status
|
||||
"""
|
||||
def update_report(
|
||||
_parent,
|
||||
%{report_id: report_id, moderator_id: moderator_id, status: status},
|
||||
%{report_id: report_id, status: status},
|
||||
%{context: %{current_user: %User{role: role} = user}}
|
||||
)
|
||||
when is_moderator(role) do
|
||||
with {:is_owned, %Actor{} = actor} <- User.owns_actor(user, moderator_id),
|
||||
with %Actor{} = actor <- Users.get_actor_for_user(user),
|
||||
%Report{} = report <- Mobilizon.Reports.get_report(report_id),
|
||||
{:ok, %Report{} = report} <- API.Reports.update_report_status(actor, report, status) do
|
||||
{:ok, report}
|
||||
else
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||
|
||||
_error ->
|
||||
{:error, dgettext("errors", "Error while updating report")}
|
||||
end
|
||||
@ -117,11 +103,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do
|
||||
|
||||
def create_report_note(
|
||||
_parent,
|
||||
%{report_id: report_id, moderator_id: moderator_id, content: content},
|
||||
%{report_id: report_id, content: content},
|
||||
%{context: %{current_user: %User{role: role} = user}}
|
||||
)
|
||||
when is_moderator(role) do
|
||||
with {:is_owned, %Actor{}} <- User.owns_actor(user, moderator_id),
|
||||
with %Actor{id: moderator_id} <- Users.get_actor_for_user(user),
|
||||
%Report{} = report <- Reports.get_report(report_id),
|
||||
%Actor{} = moderator <- Actors.get_local_actor_with_preload(moderator_id),
|
||||
{:ok, %Note{} = note} <- API.Reports.create_report_note(report, moderator, content) do
|
||||
@ -131,11 +117,11 @@ defmodule Mobilizon.GraphQL.Resolvers.Report do
|
||||
|
||||
def delete_report_note(
|
||||
_parent,
|
||||
%{note_id: note_id, moderator_id: moderator_id},
|
||||
%{note_id: note_id},
|
||||
%{context: %{current_user: %User{role: role} = user}}
|
||||
)
|
||||
when is_moderator(role) do
|
||||
with {:is_owned, %Actor{}} <- User.owns_actor(user, moderator_id),
|
||||
with %Actor{id: moderator_id} <- Users.get_actor_for_user(user),
|
||||
%Note{} = note <- Reports.get_note(note_id),
|
||||
%Actor{} = moderator <- Actors.get_local_actor_with_preload(moderator_id),
|
||||
{:ok, %Note{} = note} <- API.Reports.delete_report_note(note, moderator) do
|
||||
|
@ -53,9 +53,6 @@ defmodule Mobilizon.GraphQL.Resolvers.Todos do
|
||||
%Page{} = page <- Todos.get_todos_for_todo_list(todo_list) do
|
||||
{:ok, page}
|
||||
else
|
||||
{:is_owned, nil} ->
|
||||
{:error, dgettext("errors", "Profile is not owned by authenticated user")}
|
||||
|
||||
{:member, _} ->
|
||||
{:error, dgettext("errors", "Profile is not member of group")}
|
||||
end
|
||||
|
@ -62,18 +62,21 @@ defmodule Mobilizon.GraphQL.Schema.ActorInterface do
|
||||
end
|
||||
|
||||
object :actor_mutations do
|
||||
@desc "Suspend an actor"
|
||||
field :suspend_profile, :deleted_object do
|
||||
arg(:id, non_null(:id), description: "The profile ID to suspend")
|
||||
arg(:id, non_null(:id), description: "The remote profile ID to suspend")
|
||||
resolve(&ActorResolver.suspend_profile/3)
|
||||
end
|
||||
|
||||
@desc "Unsuspend an actor"
|
||||
field :unsuspend_profile, :actor do
|
||||
arg(:id, non_null(:id), description: "The profile ID to unsuspend")
|
||||
arg(:id, non_null(:id), description: "The remote profile ID to unsuspend")
|
||||
resolve(&ActorResolver.unsuspend_profile/3)
|
||||
end
|
||||
|
||||
@desc "Refresh a profile"
|
||||
field :refresh_profile, :actor do
|
||||
arg(:id, non_null(:id))
|
||||
arg(:id, non_null(:id), description: "The remote profile ID to refresh")
|
||||
resolve(&ActorResolver.refresh_profile/3)
|
||||
end
|
||||
end
|
||||
|
@ -19,6 +19,9 @@ defmodule Mobilizon.GraphQL.Schema.Actors.FollowerType do
|
||||
field(:updated_at, :datetime, description: "When the follow was updated")
|
||||
end
|
||||
|
||||
@desc """
|
||||
A paginated list of follower objects
|
||||
"""
|
||||
object :paginated_follower_list do
|
||||
field(:elements, list_of(:follower), description: "A list of followers")
|
||||
field(:total, :integer, description: "The total number of elements in the list")
|
||||
|
@ -54,10 +54,18 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||
|
||||
# This one should have a privacy setting
|
||||
field :organized_events, :paginated_event_list do
|
||||
arg(:after_datetime, :datetime, default_value: nil)
|
||||
arg(:before_datetime, :datetime, default_value: nil)
|
||||
arg(:page, :integer, default_value: 1)
|
||||
arg(:limit, :integer, default_value: 10)
|
||||
arg(:after_datetime, :datetime,
|
||||
default_value: nil,
|
||||
description: "Filter events that begin after this datetime"
|
||||
)
|
||||
|
||||
arg(:before_datetime, :datetime,
|
||||
default_value: nil,
|
||||
description: "Filter events that begin before this datetime"
|
||||
)
|
||||
|
||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated event list")
|
||||
arg(:limit, :integer, default_value: 10, description: "The limit of events per page")
|
||||
resolve(&Group.find_events_for_group/3)
|
||||
description("A list of the events this actor has organized")
|
||||
end
|
||||
@ -74,23 +82,27 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||
)
|
||||
|
||||
field :members, :paginated_member_list do
|
||||
arg(:page, :integer, default_value: 1)
|
||||
arg(:limit, :integer, default_value: 10)
|
||||
arg(:roles, :string, default_value: "")
|
||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated member list")
|
||||
arg(:limit, :integer, default_value: 10, description: "The limit of members per page")
|
||||
arg(:roles, :string, default_value: "", description: "Filter members by their role")
|
||||
resolve(&Member.find_members_for_group/3)
|
||||
description("A paginated list of group members")
|
||||
end
|
||||
|
||||
field :resources, :paginated_resource_list do
|
||||
arg(:page, :integer, default_value: 1)
|
||||
arg(:limit, :integer, default_value: 10)
|
||||
arg(:page, :integer,
|
||||
default_value: 1,
|
||||
description: "The page in the paginated resource list"
|
||||
)
|
||||
|
||||
arg(:limit, :integer, default_value: 10, description: "The limit of resources per page")
|
||||
resolve(&Resource.find_resources_for_group/3)
|
||||
description("A paginated list of the resources this group has")
|
||||
end
|
||||
|
||||
field :posts, :paginated_post_list do
|
||||
arg(:page, :integer, default_value: 1)
|
||||
arg(:limit, :integer, default_value: 10)
|
||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated post list")
|
||||
arg(:limit, :integer, default_value: 10, description: "The limit of posts per page")
|
||||
resolve(&Post.find_posts_for_group/3)
|
||||
description("A paginated list of the posts this group has")
|
||||
end
|
||||
@ -120,9 +132,12 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||
value(:open, description: "The actor is open to followings")
|
||||
end
|
||||
|
||||
@desc """
|
||||
A paginated list of groups
|
||||
"""
|
||||
object :paginated_group_list do
|
||||
field(:elements, list_of(:group), description: "A list of groups")
|
||||
field(:total, :integer, description: "The total number of elements in the list")
|
||||
field(:total, :integer, description: "The total number of groups in the list")
|
||||
end
|
||||
|
||||
@desc "The list of visibility options for a group"
|
||||
@ -134,25 +149,33 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||
object :group_queries do
|
||||
@desc "Get all groups"
|
||||
field :groups, :paginated_group_list do
|
||||
arg(:preferred_username, :string, default_value: "")
|
||||
arg(:name, :string, default_value: "")
|
||||
arg(:domain, :string, default_value: "")
|
||||
arg(:local, :boolean, default_value: true)
|
||||
arg(:suspended, :boolean, default_value: false)
|
||||
arg(:page, :integer, default_value: 1)
|
||||
arg(:limit, :integer, default_value: 10)
|
||||
arg(:preferred_username, :string, default_value: "", description: "Filter by username")
|
||||
arg(:name, :string, default_value: "", description: "Filter by name")
|
||||
arg(:domain, :string, default_value: "", description: "Filter by domain")
|
||||
|
||||
arg(:local, :boolean,
|
||||
default_value: true,
|
||||
description: "Filter whether group is local or not"
|
||||
)
|
||||
|
||||
arg(:suspended, :boolean, default_value: false, description: "Filter by suspended status")
|
||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated group list")
|
||||
arg(:limit, :integer, default_value: 10, description: "The limit of groups per page")
|
||||
resolve(&Group.list_groups/3)
|
||||
end
|
||||
|
||||
@desc "Get a group by its ID"
|
||||
field :get_group, :group do
|
||||
arg(:id, non_null(:id))
|
||||
arg(:id, non_null(:id), description: "The group ID")
|
||||
resolve(&Group.get_group/3)
|
||||
end
|
||||
|
||||
@desc "Get a group by its preferred username"
|
||||
field :group, :group do
|
||||
arg(:preferred_username, non_null(:string))
|
||||
arg(:preferred_username, non_null(:string),
|
||||
description: "The group preferred_username, eventually containing their domain if remote"
|
||||
)
|
||||
|
||||
resolve(&Group.find_group/3)
|
||||
end
|
||||
end
|
||||
@ -162,8 +185,6 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||
field :create_group, :group do
|
||||
arg(:preferred_username, non_null(:string), description: "The name for the group")
|
||||
|
||||
arg(:creator_actor_id, non_null(:id), description: "The identity that creates the group")
|
||||
|
||||
arg(:name, :string, description: "The displayed name for the group")
|
||||
arg(:summary, :string, description: "The summary for the group", default_value: "")
|
||||
|
||||
@ -182,7 +203,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||
"The banner for the group, either as an object or directly the ID of an existing Picture"
|
||||
)
|
||||
|
||||
arg(:physical_address, :address_input)
|
||||
arg(:physical_address, :address_input, description: "The physical address for the group")
|
||||
|
||||
resolve(&Group.create_group/3)
|
||||
end
|
||||
@ -210,14 +231,14 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do
|
||||
"The banner for the group, either as an object or directly the ID of an existing Picture"
|
||||
)
|
||||
|
||||
arg(:physical_address, :address_input)
|
||||
arg(:physical_address, :address_input, description: "The physical address for the group")
|
||||
|
||||
resolve(&Group.update_group/3)
|
||||
end
|
||||
|
||||
@desc "Delete a group"
|
||||
field :delete_group, :deleted_object do
|
||||
arg(:group_id, non_null(:id))
|
||||
arg(:group_id, non_null(:id), description: "The group ID")
|
||||
|
||||
resolve(&Group.delete_group/3)
|
||||
end
|
||||
|
@ -19,16 +19,22 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||
field(:updated_at, :naive_datetime, description: "When was this member updated")
|
||||
end
|
||||
|
||||
@desc """
|
||||
Values for a member role
|
||||
"""
|
||||
enum :member_role_enum do
|
||||
value(:not_approved)
|
||||
value(:invited)
|
||||
value(:member)
|
||||
value(:moderator)
|
||||
value(:administrator)
|
||||
value(:creator)
|
||||
value(:rejected)
|
||||
value(:not_approved, description: "The member needs to be approved by the group admins")
|
||||
value(:invited, description: "The member has been invited")
|
||||
value(:member, description: "Regular member")
|
||||
value(:moderator, description: "The member is a moderator")
|
||||
value(:administrator, description: "The member is an administrator")
|
||||
value(:creator, description: "The member was the creator of the group. Shouldn't be used.")
|
||||
value(:rejected, description: "The member has been rejected or excluded from the group")
|
||||
end
|
||||
|
||||
@desc """
|
||||
A paginated list of members
|
||||
"""
|
||||
object :paginated_member_list do
|
||||
field(:elements, list_of(:member), description: "A list of members")
|
||||
field(:total, :integer, description: "The total number of elements in the list")
|
||||
@ -37,51 +43,54 @@ defmodule Mobilizon.GraphQL.Schema.Actors.MemberType do
|
||||
object :member_mutations do
|
||||
@desc "Join a group"
|
||||
field :join_group, :member do
|
||||