From 1cd680526a9073b9780d8bf2f963f2883df33a7e Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 20 Nov 2020 16:35:48 +0100 Subject: [PATCH] Add backend to remove pictures Signed-off-by: Thomas Citharel --- lib/graphql/resolvers/picture.ex | 30 +++- lib/graphql/schema/picture.ex | 10 +- test/graphql/resolvers/picture_test.exs | 178 +++++++++++++++--------- 3 files changed, 144 insertions(+), 74 deletions(-) diff --git a/lib/graphql/resolvers/picture.ex b/lib/graphql/resolvers/picture.ex index a5b26e13f..9a17e22ef 100644 --- a/lib/graphql/resolvers/picture.ex +++ b/lib/graphql/resolvers/picture.ex @@ -6,6 +6,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do alias Mobilizon.Actors.Actor alias Mobilizon.{Media, Users} alias Mobilizon.Media.Picture + alias Mobilizon.Users.User import Mobilizon.Web.Gettext @doc """ @@ -37,8 +38,8 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do size: file.size }} - _error -> - {:error, dgettext("errors", "Picture with ID %{id} was not found", id: picture_id)} + nil -> + {:error, :not_found} end end @@ -46,7 +47,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do def upload_picture( _parent, %{file: %Plug.Upload{} = file} = args, - %{context: %{current_user: user}} + %{context: %{current_user: %User{} = user}} ) do with %Actor{id: actor_id} <- Users.get_actor_for_user(user), {:ok, %{name: _name, url: url, content_type: content_type, size: size}} <- @@ -75,7 +76,26 @@ defmodule Mobilizon.GraphQL.Resolvers.Picture do end end - def upload_picture(_parent, _args, _resolution) do - {:error, dgettext("errors", "You need to login to upload a picture")} + def upload_picture(_parent, _args, _resolution), do: {:error, :unauthenticated} + + @doc """ + Remove a picture that the user owns + """ + @spec remove_picture(map(), map(), map()) :: + {:ok, Picture.t()} + | {:error, :unauthorized} + | {:error, :unauthenticated} + | {:error, :not_found} + def remove_picture(_parent, %{id: picture_id}, %{context: %{current_user: %User{} = user}}) do + with {:picture, %Picture{actor_id: actor_id} = picture} <- + {:picture, Media.get_picture(picture_id)}, + {:is_owned, %Actor{} = _actor} <- User.owns_actor(user, actor_id) do + Media.delete_picture(picture) + else + {:picture, nil} -> {:error, :not_found} + {:is_owned, _} -> {:error, :unauthorized} + end end + + def remove_picture(_parent, _args, _resolution), do: {:error, :unauthenticated} end diff --git a/lib/graphql/schema/picture.ex b/lib/graphql/schema/picture.ex index 401664b62..02f76a96d 100644 --- a/lib/graphql/schema/picture.ex +++ b/lib/graphql/schema/picture.ex @@ -35,7 +35,7 @@ defmodule Mobilizon.GraphQL.Schema.PictureType do object :picture_queries do @desc "Get a picture" field :picture, :picture do - arg(:id, non_null(:string), description: "The picture ID") + arg(:id, non_null(:id), description: "The picture ID") resolve(&Picture.picture/3) end end @@ -48,5 +48,13 @@ defmodule Mobilizon.GraphQL.Schema.PictureType do arg(:file, non_null(:upload), description: "The picture file") resolve(&Picture.upload_picture/3) end + + @desc """ + Remove a picture + """ + field :remove_picture, :deleted_object do + arg(:id, non_null(:id), description: "The picture's ID") + resolve(&Picture.remove_picture/3) + end end end diff --git a/test/graphql/resolvers/picture_test.exs b/test/graphql/resolvers/picture_test.exs index 6343bb906..620335a45 100644 --- a/test/graphql/resolvers/picture_test.exs +++ b/test/graphql/resolvers/picture_test.exs @@ -17,76 +17,69 @@ defmodule Mobilizon.GraphQL.Resolvers.PictureTest do {:ok, conn: conn, user: user, actor: actor} end + @picture_query """ + query Picture($id: ID!) { + picture(id: $id) { + id + name, + alt, + url, + content_type, + size + } + } + """ + describe "Resolver: Get picture" do - test "picture/3 returns the information on a picture", context do + test "picture/3 returns the information on a picture", %{conn: conn} do %Picture{id: id} = picture = insert(:picture) - query = """ - { - picture(id: "#{id}") { - name, - alt, - url, - content_type, - size - } - } - """ - res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "picture")) + conn + |> AbsintheHelpers.graphql_query(query: @picture_query, variables: %{id: id}) - assert json_response(res, 200)["data"]["picture"]["name"] == picture.file.name + assert res["data"]["picture"]["name"] == picture.file.name - assert json_response(res, 200)["data"]["picture"]["content_type"] == + assert res["data"]["picture"]["content_type"] == picture.file.content_type - assert json_response(res, 200)["data"]["picture"]["size"] == 13_120 + assert res["data"]["picture"]["size"] == 13_120 - assert json_response(res, 200)["data"]["picture"]["url"] =~ Endpoint.url() + assert res["data"]["picture"]["url"] =~ Endpoint.url() end - test "picture/3 returns nothing on a non-existent picture", context do - query = """ - { - picture(id: "3") { - name, - alt, - url - } - } - """ - + test "picture/3 returns nothing on a non-existent picture", %{conn: conn} do res = - context.conn - |> get("/api", AbsintheHelpers.query_skeleton(query, "picture")) + conn + |> AbsintheHelpers.graphql_query(query: @picture_query, variables: %{id: 3}) - assert hd(json_response(res, 200)["errors"])["message"] == - "Picture with ID 3 was not found" + assert hd(res["errors"])["message"] == "Resource not found" + assert hd(res["errors"])["status_code"] == 404 end end describe "Resolver: Upload picture" do + @upload_picture_mutation """ + mutation UploadPicture($name: String!, $alt: String, $file: Upload!) { + uploadPicture( + name: $name + alt: $alt + file: $file + ) { + url + name + content_type + size + } + } + """ + test "upload_picture/3 uploads a new picture", %{conn: conn, user: user} do picture = %{name: "my pic", alt: "represents something", file: "picture.png"} - mutation = """ - mutation { uploadPicture( - name: "#{picture.name}", - alt: "#{picture.alt}", - file: "#{picture.file}" - ) { - url, - name, - content_type, - size - } - } - """ - map = %{ - "query" => mutation, + "query" => @upload_picture_mutation, + "variables" => picture, picture.file => %Plug.Upload{ path: "test/fixtures/picture.png", filename: picture.file @@ -101,30 +94,20 @@ defmodule Mobilizon.GraphQL.Resolvers.PictureTest do "/api", map ) + |> json_response(200) - assert json_response(res, 200)["data"]["uploadPicture"]["name"] == picture.name - assert json_response(res, 200)["data"]["uploadPicture"]["content_type"] == "image/png" - assert json_response(res, 200)["data"]["uploadPicture"]["size"] == 10_097 - assert json_response(res, 200)["data"]["uploadPicture"]["url"] + assert res["data"]["uploadPicture"]["name"] == picture.name + assert res["data"]["uploadPicture"]["content_type"] == "image/png" + assert res["data"]["uploadPicture"]["size"] == 10_097 + assert res["data"]["uploadPicture"]["url"] end test "upload_picture/3 forbids uploading if no auth", %{conn: conn} do picture = %{name: "my pic", alt: "represents something", file: "picture.png"} - mutation = """ - mutation { uploadPicture( - name: "#{picture.name}", - alt: "#{picture.alt}", - file: "#{picture.file}" - ) { - url, - name - } - } - """ - map = %{ - "query" => mutation, + "query" => @upload_picture_mutation, + "variables" => picture, picture.file => %Plug.Upload{ path: "test/fixtures/picture.png", filename: picture.file @@ -138,9 +121,68 @@ defmodule Mobilizon.GraphQL.Resolvers.PictureTest do "/api", map ) + |> json_response(200) - assert hd(json_response(res, 200)["errors"])["message"] == - "You need to login to upload a picture" + assert hd(res["errors"])["message"] == "You need to be logged in" + end + end + + describe "Resolver: Remove picture" do + @remove_picture_mutation """ + mutation RemovePicture($id: ID!) { + removePicture(id: $id) { + id + } + } + """ + + test "Removes a previously uploaded picture", %{conn: conn, user: user, actor: actor} do + %Picture{id: picture_id} = insert(:picture, actor: actor) + + res = + conn + |> auth_conn(user) + |> AbsintheHelpers.graphql_query( + query: @remove_picture_mutation, + variables: %{id: picture_id} + ) + + assert is_nil(res["errors"]) + assert res["data"]["removePicture"]["id"] == to_string(picture_id) + + res = + conn + |> AbsintheHelpers.graphql_query(query: @picture_query, variables: %{id: picture_id}) + + assert hd(res["errors"])["message"] == "Resource not found" + assert hd(res["errors"])["status_code"] == 404 + end + + test "Removes nothing if picture is not found", %{conn: conn, user: user} do + res = + conn + |> auth_conn(user) + |> AbsintheHelpers.graphql_query( + query: @remove_picture_mutation, + variables: %{id: 400} + ) + + assert hd(res["errors"])["message"] == "Resource not found" + assert hd(res["errors"])["status_code"] == 404 + end + + test "Removes nothing if picture if not logged-in", %{conn: conn, actor: actor} do + %Picture{id: picture_id} = insert(:picture, actor: actor) + + res = + conn + |> AbsintheHelpers.graphql_query( + query: @remove_picture_mutation, + variables: %{id: picture_id} + ) + + assert hd(res["errors"])["message"] == "You need to be logged in" + assert hd(res["errors"])["status_code"] == 401 end end end