diff --git a/js/schema.graphql b/js/schema.graphql index b6010ab90..c9e3930dd 100644 --- a/js/schema.graphql +++ b/js/schema.graphql @@ -44,7 +44,7 @@ type RefreshedToken { } "Represents an application" -type Application { +type Application implements Actor { "Internal ID for this application" id: ID @@ -336,7 +336,7 @@ type PaginatedPostList { } "A comment" -type Comment { +type Comment implements ActionLogObject { "Internal ID for this comment" id: ID @@ -893,7 +893,7 @@ enum EventCommentModeration { } "Represents a person identity" -type Person { +type Person implements ActionLogObject & Actor { "Internal ID for this person" id: ID @@ -1949,7 +1949,7 @@ type RootQueryType { "The limit of events per page" limit: Int - ): [Event] + ): PaginatedEventList "Get an event by uuid" event("The event's UUID" uuid: UUID!): Event @@ -2219,7 +2219,7 @@ input EventOptionsInput { } "A report object" -type Report { +type Report implements ActionLogObject { "The internal ID of the report" id: ID @@ -2285,7 +2285,7 @@ type PaginatedTodoListList { } "An event" -type Event { +type Event implements Interactable & ActionLogObject { "Internal ID for this event" id: ID @@ -2737,7 +2737,7 @@ type Geocoding { } "A report note object" -type ReportNote { +type ReportNote implements ActionLogObject { "The internal ID of the report note" id: ID @@ -3052,7 +3052,7 @@ type Member { } "A local user of Mobilizon" -type User { +type User implements ActionLogObject { "The user's ID" id: ID @@ -3157,7 +3157,7 @@ type User { } "Represents a group of actors" -type Group { +type Group implements Interactable & Actor { "Internal ID for this group" id: ID diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts index 4b0085837..357e28ce9 100644 --- a/js/src/graphql/event.ts +++ b/js/src/graphql/event.ts @@ -196,54 +196,54 @@ export const FETCH_EVENT_BASIC = gql` export const FETCH_EVENTS = gql` query { events { - id, - uuid, - url, - local, - title, - description, - beginsOn, - endsOn, - status, - visibility, - picture { + total + elements { id + uuid url - }, - publishAt, - # online_address, - # phone_address, - physicalAddress { - id, - description, - locality - }, - organizerActor { - id, - avatar { + local + title + description + beginsOn + endsOn + status + visibility + picture { id url - }, - preferredUsername, - domain, - name, - }, -# attributedTo { -# avatar { -# id -# url -# }, -# preferredUsername, -# name, -# }, - category, - participants { - ${participantsQuery} - }, - tags { - slug, - title - }, + } + publishAt + # online_address, + # phone_address, + physicalAddress { + id + description + locality + } + organizerActor { + id + avatar { + id + url + } + preferredUsername + domain + name + } + # attributedTo { + # avatar { + # id + # url + # }, + # preferredUsername, + # name, + # }, + category + tags { + slug + title + } + } } } `; diff --git a/js/src/views/Home.vue b/js/src/views/Home.vue index 4e4c86508..41c975c25 100644 --- a/js/src/views/Home.vue +++ b/js/src/views/Home.vue @@ -220,6 +220,7 @@ diff --git a/lib/graphql/resolvers/admin.ex b/lib/graphql/resolvers/admin.ex index 6aa412882..21698b06c 100644 --- a/lib/graphql/resolvers/admin.ex +++ b/lib/graphql/resolvers/admin.ex @@ -190,7 +190,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Admin do when is_admin(role) do last_public_event_published = case Events.list_events(1, 1, :inserted_at, :desc) do - [event | _] -> event + %Page{elements: [event | _]} -> event _ -> nil end diff --git a/lib/graphql/resolvers/event.ex b/lib/graphql/resolvers/event.ex index 032a9ee20..ef142157e 100644 --- a/lib/graphql/resolvers/event.ex +++ b/lib/graphql/resolvers/event.ex @@ -161,7 +161,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do events = if @number_of_related_events - length(events) > 0 do events - |> Enum.concat(Events.list_events(1, @number_of_related_events, :begins_on, :asc, true)) + |> Enum.concat( + Events.list_events(1, @number_of_related_events, :begins_on, :asc, true).elements + ) |> uniq_events() else events diff --git a/lib/graphql/schema/event.ex b/lib/graphql/schema/event.ex index e3cd9f31a..0b2ae40f3 100644 --- a/lib/graphql/schema/event.ex +++ b/lib/graphql/schema/event.ex @@ -299,7 +299,7 @@ defmodule Mobilizon.GraphQL.Schema.EventType do object :event_queries do @desc "Get all events" - field :events, list_of(:event) do + field :events, :paginated_event_list do 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(&Event.list_events/3) diff --git a/lib/mobilizon/events/events.ex b/lib/mobilizon/events/events.ex index 41ea85b32..89fed4cf4 100644 --- a/lib/mobilizon/events/events.ex +++ b/lib/mobilizon/events/events.ex @@ -357,7 +357,7 @@ defmodule Mobilizon.Events do direction \\ :asc, is_future \\ true ) do - query = from(e in Event, distinct: true, preload: [:organizer_actor, :participants]) + query = from(e in Event, preload: [:organizer_actor, :participants]) query |> sort(sort, direction) @@ -365,8 +365,7 @@ defmodule Mobilizon.Events do |> filter_public_visibility() |> filter_draft() |> filter_local_or_from_followed_instances_events() - |> Page.paginate(page, limit) - |> Repo.all() + |> Page.build_page(page, limit) end @spec stream_events_for_sitemap :: Enum.t() diff --git a/lib/mobilizon/storage/page.ex b/lib/mobilizon/storage/page.ex index af20d4a7b..68fc8aab3 100644 --- a/lib/mobilizon/storage/page.ex +++ b/lib/mobilizon/storage/page.ex @@ -19,12 +19,15 @@ defmodule Mobilizon.Storage.Page do @doc """ Returns a Page struct for a query. + + `field` is use to define the field that will be used for the count aggregate, which should be the same as the field used for order_by + See https://stackoverflow.com/q/12693089/10204399 """ - @spec build_page(Ecto.Query.t(), integer | nil, integer | nil) :: t - def build_page(query, page, limit) do + @spec build_page(Ecto.Query.t(), integer | nil, integer | nil, atom()) :: t + def build_page(query, page, limit, field \\ :id) do [total, elements] = [ - fn -> Repo.aggregate(query, :count, :id) end, + fn -> Repo.aggregate(query, :count, field) end, fn -> Repo.all(paginate(query, page, limit)) end ] |> Enum.map(&Task.async/1) diff --git a/test/graphql/resolvers/event_test.exs b/test/graphql/resolvers/event_test.exs index e974ed068..a83559e06 100644 --- a/test/graphql/resolvers/event_test.exs +++ b/test/graphql/resolvers/event_test.exs @@ -1144,120 +1144,89 @@ defmodule Mobilizon.Web.Resolvers.EventTest do ] end - test "list_events/3 returns events", context do - event = insert(:event) - - query = """ - { - events { - uuid, + @fetch_events_query """ + query Events($page: Int, $limit: Int) { + events(page: $page, limit: $limit) { + total + elements { + uuid } } - """ + } + """ + + test "list_events/3 returns events", %{conn: conn} do + event = insert(:event) res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + conn + |> AbsintheHelpers.graphql_query(query: @fetch_events_query) - assert json_response(res, 200)["data"]["events"] |> Enum.map(& &1["uuid"]) == [event.uuid] + assert res["data"]["events"]["elements"] |> Enum.map(& &1["uuid"]) == [ + event.uuid + ] Enum.each(0..15, fn _ -> insert(:event) end) - query = """ - { - events { - uuid, - } - } - """ + res = + conn + |> AbsintheHelpers.graphql_query(query: @fetch_events_query) + + assert res["data"]["events"]["total"] == 17 + assert res["data"]["events"]["elements"] |> length == 10 res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + conn + |> AbsintheHelpers.graphql_query(query: @fetch_events_query, variables: %{page: 2}) - assert json_response(res, 200)["data"]["events"] |> length == 10 - - query = """ - { - events(page: 2) { - uuid, - } - } - """ + assert res["data"]["events"]["total"] == 17 + assert res["data"]["events"]["elements"] |> length == 7 res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + conn + |> AbsintheHelpers.graphql_query( + query: @fetch_events_query, + variables: %{page: 2, limit: 15} + ) - assert json_response(res, 200)["data"]["events"] |> length == 7 - - query = """ - { - events(page: 2, limit: 15) { - uuid, - } - } - """ + assert res["data"]["events"]["total"] == 17 + assert res["data"]["events"]["elements"] |> length == 2 res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + conn + |> AbsintheHelpers.graphql_query( + query: @fetch_events_query, + variables: %{page: 3, limit: 15} + ) - assert json_response(res, 200)["data"]["events"] |> length == 2 - - query = """ - { - events(page: 3, limit: 15) { - uuid, - } - } - """ - - res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) - - assert json_response(res, 200)["data"]["events"] |> length == 0 + assert res["data"]["events"]["total"] == 17 + assert res["data"]["events"]["elements"] |> length == 0 end - test "list_events/3 doesn't list private events", context do + test "list_events/3 doesn't list private events", %{conn: conn} do insert(:event, visibility: :private) insert(:event, visibility: :unlisted) insert(:event, visibility: :restricted) - query = """ - { - events { - uuid, - } - } - """ - res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + conn + |> AbsintheHelpers.graphql_query(query: @fetch_events_query) - assert json_response(res, 200)["data"]["events"] |> Enum.map(& &1["uuid"]) == [] + assert res["data"]["events"]["total"] == 0 + assert res["data"]["events"]["elements"] |> Enum.map(& &1["uuid"]) == [] end - test "list_events/3 doesn't list draft events", context do + test "list_events/3 doesn't list draft events", %{conn: conn} do insert(:event, visibility: :public, draft: true) - query = """ - { - events { - uuid, - } - } - """ - res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "event")) + conn + |> AbsintheHelpers.graphql_query(query: @fetch_events_query) - assert json_response(res, 200)["data"]["events"] |> Enum.map(& &1["uuid"]) == [] + assert res["data"]["events"]["total"] == 0 + assert res["data"]["events"]["elements"] |> Enum.map(& &1["uuid"]) == [] end test "find_event/3 returns an unlisted event", context do diff --git a/test/mobilizon/events/events_test.exs b/test/mobilizon/events/events_test.exs index a033b8a2c..fbda5b00a 100644 --- a/test/mobilizon/events/events_test.exs +++ b/test/mobilizon/events/events_test.exs @@ -29,12 +29,12 @@ defmodule Mobilizon.EventsTest do end test "list_events/0 returns all events", %{event: event} do - assert event.title == hd(Events.list_events()).title + assert event.title == hd(Events.list_events().elements).title end test "list_events/5 returns events from other instances if we follow them", %{event: _event} do - events = Events.list_events() + events = Events.list_events().elements assert length(events) == 1 %Actor{id: remote_instance_actor_id} = remote_instance_actor = insert(:instance_actor) @@ -46,7 +46,7 @@ defmodule Mobilizon.EventsTest do insert(:follower, target_actor: remote_instance_actor, actor: own_instance_actor) - events = Events.list_events() + events = Events.list_events().elements assert length(events) == 2 assert events |> Enum.any?(fn event -> event.title == "My Remote event" end) end @@ -58,7 +58,7 @@ defmodule Mobilizon.EventsTest do %Event{url: remote_event_url} = insert(:event, local: false, title: "My Remote event") Mobilizon.Share.create(remote_event_url, remote_instance_actor_id, remote_actor_id) - events = Events.list_events() + events = Events.list_events().elements assert length(events) == 1 assert events |> Enum.all?(fn event -> event.title != "My Remote event" end) end