From c58e54d5b9182652aa201a1516d25740e12f58f8 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 27 Apr 2021 16:51:28 +0200 Subject: [PATCH 1/3] Add Group as a possible ActionLog object Signed-off-by: Thomas Citharel --- js/src/graphql/report.ts | 6 ++++++ lib/graphql/schema/actors/group.ex | 2 +- lib/graphql/schema/admin.ex | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/js/src/graphql/report.ts b/js/src/graphql/report.ts index 6ab8ebfcf..3766bc938 100644 --- a/js/src/graphql/report.ts +++ b/js/src/graphql/report.ts @@ -190,6 +190,12 @@ export const LOGS = gql` domain name } + ... on Group { + id + preferredUsername + domain + name + } ... on User { id email diff --git a/lib/graphql/schema/actors/group.ex b/lib/graphql/schema/actors/group.ex index 59d4690f1..58b1c5be9 100644 --- a/lib/graphql/schema/actors/group.ex +++ b/lib/graphql/schema/actors/group.ex @@ -29,7 +29,7 @@ defmodule Mobilizon.GraphQL.Schema.Actors.GroupType do Represents a group of actors """ object :group do - interfaces([:actor, :interactable, :activity_object]) + interfaces([:actor, :interactable, :activity_object, :action_log_object]) field(:id, :id, description: "Internal ID for this group") field(:url, :string, description: "The ActivityPub actor's URL") diff --git a/lib/graphql/schema/admin.ex b/lib/graphql/schema/admin.ex index 22689f322..ad1aaa135 100644 --- a/lib/graphql/schema/admin.ex +++ b/lib/graphql/schema/admin.ex @@ -62,6 +62,9 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do %User{}, _ -> :user + %Actor{type: "Group"}, _ -> + :group + _, _ -> nil end) From 6646391558a898d511ae8a5e9a8a1d7ce912c93c Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 27 Apr 2021 16:51:58 +0200 Subject: [PATCH 2/3] Cleanup fragmentTypes Signed-off-by: Thomas Citharel --- js/fragmentTypes.json | 47 +++++++++++++++++++++++++++++++++++++++++- js/src/apollo/utils.ts | 22 ++------------------ 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/js/fragmentTypes.json b/js/fragmentTypes.json index daad1c6b3..649592581 100644 --- a/js/fragmentTypes.json +++ b/js/fragmentTypes.json @@ -5,17 +5,50 @@ "kind": "INTERFACE", "name": "ActionLogObject", "possibleTypes": [ + { + "name": "Comment" + }, { "name": "Event" }, { - "name": "Comment" + "name": "Person" }, { "name": "Report" }, { "name": "ReportNote" + }, + { + "name": "User" + } + ] + }, + { + "kind": "INTERFACE", + "name": "ActivityObject", + "possibleTypes": [ + { + "name": "Comment" + }, + { + "name": "Discussion" + }, + { + "name": "Event" + }, + { + "name": "Group" + }, + { + "name": "Member" + }, + { + "name": "Post" + }, + { + "name": "Resource" } ] }, @@ -33,6 +66,18 @@ "name": "Application" } ] + }, + { + "kind": "INTERFACE", + "name": "Interactable", + "possibleTypes": [ + { + "name": "Event" + }, + { + "name": "Group" + } + ] } ] } diff --git a/js/src/apollo/utils.ts b/js/src/apollo/utils.ts index c94e5bd08..92cdcd585 100644 --- a/js/src/apollo/utils.ts +++ b/js/src/apollo/utils.ts @@ -6,28 +6,10 @@ import { AUTH_ACCESS_TOKEN, AUTH_REFRESH_TOKEN } from "@/constants"; import { REFRESH_TOKEN } from "@/graphql/auth"; import { saveTokenData } from "@/utils/auth"; import { ApolloClient } from "apollo-client"; +import introspectionQueryResultData from "../../fragmentTypes.json"; export const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData: { - __schema: { - types: [ - { - kind: "UNION", - name: "SearchResult", - possibleTypes: [ - { name: "Event" }, - { name: "Person" }, - { name: "Group" }, - ], - }, - { - kind: "INTERFACE", - name: "Actor", - possibleTypes: [{ name: "Person" }, { name: "Group" }], - }, - ], - }, - }, + introspectionQueryResultData, }); export async function refreshAccessToken( From 495fbda3309134dfa35beaa125e55dcf85d727b6 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Tue, 27 Apr 2021 17:53:11 +0200 Subject: [PATCH 3/3] Add pagination to moderation logs Signed-off-by: Thomas Citharel --- js/src/graphql/report.ts | 79 +++++++++-------- js/src/i18n/en_US.json | 4 +- js/src/i18n/fr_FR.json | 4 +- js/src/views/Moderation/Logs.vue | 113 +++++++++++++++++++++--- lib/graphql/resolvers/admin.ex | 5 +- lib/graphql/schema/admin.ex | 10 ++- lib/mobilizon/admin/admin.ex | 5 +- test/graphql/resolvers/admin_test.exs | 28 +++--- test/graphql/resolvers/comment_test.exs | 41 +++++---- test/graphql/resolvers/event_test.exs | 33 +++---- test/graphql/resolvers/person_test.exs | 19 ++-- 11 files changed, 231 insertions(+), 110 deletions(-) diff --git a/js/src/graphql/report.ts b/js/src/graphql/report.ts index 3766bc938..495ec00fb 100644 --- a/js/src/graphql/report.ts +++ b/js/src/graphql/report.ts @@ -158,51 +158,54 @@ export const CREATE_REPORT_NOTE = gql` `; export const LOGS = gql` - query { - actionLogs { - id - action - actor { + query ActionLogs($page: Int, $limit: Int) { + actionLogs(page: $page, limit: $limit) { + elements { id - preferredUsername - domain - avatar { + action + actor { id - url - } - } - object { - ... on Report { - id - } - ... on ReportNote { - report { + preferredUsername + domain + avatar { id + url } } - ... on Event { - id - title - } - ... on Person { - id - preferredUsername - domain - name - } - ... on Group { - id - preferredUsername - domain - name - } - ... on User { - id - email - confirmedAt + object { + ... on Report { + id + } + ... on ReportNote { + report { + id + } + } + ... on Event { + id + title + } + ... on Person { + id + preferredUsername + domain + name + } + ... on Group { + id + preferredUsername + domain + name + } + ... on User { + id + email + confirmedAt + } } + insertedAt } - insertedAt + total } } `; diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index cffa26a7d..e407330b7 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -985,5 +985,7 @@ "Unable to update the profile. The avatar picture may be too heavy.": "Unable to update the profile. The avatar picture may be too heavy.", "Unable to create the profile. The avatar picture may be too heavy.": "Unable to create the profile. The avatar picture may be too heavy.", "Error while loading the preview": "Error while loading the preview", - "Instance feeds": "Instance feeds" + "Instance feeds": "Instance feeds", + "{moderator} suspended group {profile}": "{moderator} suspended group {profile}", + "{moderator} has unsuspended group {profile}": "{moderator} has unsuspended group {profile}" } diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index 1866b0f3a..65300b43a 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -1079,5 +1079,7 @@ "Unable to update the profile. The avatar picture may be too heavy.": "Impossible de mettre à jour le profil. L'image d'avatar est probablement trop lourde.", "Unable to create the profile. The avatar picture may be too heavy.": "Impossible de créer le profil. L'image d'avatar est probablement trop lourde.", "Error while loading the preview": "Erreur lors du chargement de l'aperçu", - "Instance feeds": "Flux de l'instance" + "Instance feeds": "Flux de l'instance", + "{moderator} suspended group {profile}": "{moderator} a suspendu le groupe {profile}", + "{moderator} has unsuspended group {profile}": "{moderator} a annulé la suspension du groupe {profile}" } diff --git a/js/src/views/Moderation/Logs.vue b/js/src/views/Moderation/Logs.vue index fde4b4ffc..a515257e0 100644 --- a/js/src/views/Moderation/Logs.vue +++ b/js/src/views/Moderation/Logs.vue @@ -14,9 +14,9 @@ -
-
    -
  • +
    +
      +
    • {{ log.object.title }} @@ -169,7 +172,10 @@ @@ -190,6 +196,56 @@ >{{ displayNameAndUsername(log.object) }} + + @{{ log.actor.preferredUsername }} + {{ displayNameAndUsername(log.object) }} + + + + @{{ log.actor.preferredUsername }} + {{ displayNameAndUsername(log.object) }} + +
    -
    - {{ $t("No moderation logs yet") }} -
    + +
    +
    + {{ $t("No moderation logs yet") }} +
    diff --git a/lib/graphql/resolvers/admin.ex b/lib/graphql/resolvers/admin.ex index 9bdbe4901..42d3cd061 100644 --- a/lib/graphql/resolvers/admin.ex +++ b/lib/graphql/resolvers/admin.ex @@ -27,7 +27,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do %{context: %{current_user: %User{role: role}}} ) when is_moderator(role) do - with action_logs <- Mobilizon.Admin.list_action_logs(page, limit) do + with %Page{elements: action_logs, total: total} <- + Mobilizon.Admin.list_action_logs(page, limit) do action_logs = action_logs |> Enum.map(fn %ActionLog{ @@ -44,7 +45,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do end) |> Enum.filter(& &1) - {:ok, action_logs} + {:ok, %Page{elements: action_logs, total: total}} end end diff --git a/lib/graphql/schema/admin.ex b/lib/graphql/schema/admin.ex index ad1aaa135..057548d84 100644 --- a/lib/graphql/schema/admin.ex +++ b/lib/graphql/schema/admin.ex @@ -22,6 +22,14 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do field(:inserted_at, :datetime, description: "The time when the action was performed") end + @desc """ + A paginated list of action logs + """ + object :paginated_action_log_list do + field(:elements, list_of(:action_log), description: "A list of action logs") + field(:total, :integer, description: "The total number of action logs in the list") + end + @desc """ The different types of action log actions """ @@ -147,7 +155,7 @@ defmodule Mobilizon.GraphQL.Schema.AdminType do object :admin_queries do @desc "Get the list of action logs" - field :action_logs, type: list_of(:action_log) do + field :action_logs, type: :paginated_action_log_list do arg(:page, :integer, default_value: 1) arg(:limit, :integer, default_value: 10) resolve(&Admin.list_action_logs/3) diff --git a/lib/mobilizon/admin/admin.ex b/lib/mobilizon/admin/admin.ex index b307ef6af..21cb7cf1a 100644 --- a/lib/mobilizon/admin/admin.ex +++ b/lib/mobilizon/admin/admin.ex @@ -36,11 +36,10 @@ defmodule Mobilizon.Admin do @doc """ Returns the list of action logs. """ - @spec list_action_logs(integer | nil, integer | nil) :: [ActionLog.t()] + @spec list_action_logs(integer | nil, integer | nil) :: Page.t() def list_action_logs(page \\ nil, limit \\ nil) do list_action_logs_query() - |> Page.paginate(page, limit) - |> Repo.all() + |> Page.build_page(page, limit) end @doc """ diff --git a/test/graphql/resolvers/admin_test.exs b/test/graphql/resolvers/admin_test.exs index d079568e7..0812caef7 100644 --- a/test/graphql/resolvers/admin_test.exs +++ b/test/graphql/resolvers/admin_test.exs @@ -31,17 +31,20 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do query = """ { actionLogs { - action, - actor { - preferredUsername - }, - object { - ... on Report { - id, - status + total + elements { + action, + actor { + preferredUsername }, - ... on ReportNote { - content + object { + ... on Report { + id, + status + }, + ... on ReportNote { + content + } } } } @@ -62,9 +65,10 @@ defmodule Mobilizon.GraphQL.Resolvers.AdminTest do assert json_response(res, 200)["errors"] == nil - assert json_response(res, 200)["data"]["actionLogs"] |> length == 3 + assert json_response(res, 200)["data"]["actionLogs"]["total"] == 3 + assert json_response(res, 200)["data"]["actionLogs"]["elements"] |> length == 3 - assert json_response(res, 200)["data"]["actionLogs"] == [ + assert json_response(res, 200)["data"]["actionLogs"]["elements"] == [ %{ "action" => "NOTE_DELETION", "actor" => %{"preferredUsername" => moderator_2.preferred_username}, diff --git a/test/graphql/resolvers/comment_test.exs b/test/graphql/resolvers/comment_test.exs index 395d1ff42..4ded1f48e 100644 --- a/test/graphql/resolvers/comment_test.exs +++ b/test/graphql/resolvers/comment_test.exs @@ -228,25 +228,28 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do query = """ { actionLogs { - action, - actor { - preferredUsername - }, - object { - ... on Report { - id, - status + total + elements { + action, + actor { + preferredUsername }, - ... on ReportNote { - content - } - ... on Event { - id, - title - }, - ... on Comment { - id, - text + object { + ... on Report { + id, + status + }, + ... on ReportNote { + content + } + ... on Event { + id, + title + }, + ... on Comment { + id, + text + } } } } @@ -260,7 +263,7 @@ defmodule Mobilizon.GraphQL.Resolvers.CommentTest do refute json_response(res, 200)["errors"] - assert hd(json_response(res, 200)["data"]["actionLogs"]) == %{ + assert hd(json_response(res, 200)["data"]["actionLogs"]["elements"]) == %{ "action" => "COMMENT_DELETION", "actor" => %{"preferredUsername" => actor_moderator.preferred_username}, "object" => %{"text" => comment.text, "id" => to_string(comment.id)} diff --git a/test/graphql/resolvers/event_test.exs b/test/graphql/resolvers/event_test.exs index 021347ad7..94db0cd68 100644 --- a/test/graphql/resolvers/event_test.exs +++ b/test/graphql/resolvers/event_test.exs @@ -1368,21 +1368,24 @@ defmodule Mobilizon.Web.Resolvers.EventTest do query = """ { actionLogs { - action, - actor { - preferredUsername - }, - object { - ... on Report { - id, - status + total + elements { + action, + actor { + preferredUsername }, - ... on ReportNote { - content - } - ... on Event { - id, - title + object { + ... on Report { + id, + status + }, + ... on ReportNote { + content + } + ... on Event { + id, + title + } } } } @@ -1394,7 +1397,7 @@ defmodule Mobilizon.Web.Resolvers.EventTest do |> auth_conn(user_moderator) |> get("/api", AbsintheHelpers.query_skeleton(query, "actionLogs")) - assert hd(json_response(res, 200)["data"]["actionLogs"]) == %{ + assert hd(json_response(res, 200)["data"]["actionLogs"]["elements"]) == %{ "action" => "EVENT_DELETION", "actor" => %{"preferredUsername" => actor_moderator.preferred_username}, "object" => %{"title" => event.title, "id" => to_string(event.id)} diff --git a/test/graphql/resolvers/person_test.exs b/test/graphql/resolvers/person_test.exs index 8f1bb27b8..a4c2f237f 100644 --- a/test/graphql/resolvers/person_test.exs +++ b/test/graphql/resolvers/person_test.exs @@ -685,15 +685,18 @@ defmodule Mobilizon.GraphQL.Resolvers.PersonTest do @moderation_logs_query """ { actionLogs { - action, - actor { - id, - preferredUsername - }, - object { - ...on Person { + total + elements { + action, + actor { id, preferredUsername + }, + object { + ...on Person { + id, + preferredUsername + } } } } @@ -733,7 +736,7 @@ defmodule Mobilizon.GraphQL.Resolvers.PersonTest do |> auth_conn(modo) |> AbsintheHelpers.graphql_query(query: @moderation_logs_query) - actionlog = hd(res["data"]["actionLogs"]) + actionlog = hd(res["data"]["actionLogs"]["elements"]) refute is_nil(actionlog) assert actionlog["action"] == "ACTOR_SUSPENSION" assert actionlog["actor"]["id"] == to_string(modo_actor_id)