如何使用Elixir和GraphQL从数据库中删除照片

时间:2018-02-02 01:32:51

标签: elixir phoenix-framework graphql absinthe

我一直在关注EQuimper Instagram克隆的教程。我一直试图通过后端进一步扩展应用程序,因为前端目前并不受关注。我正在尝试实现删除照片功能,但它似乎不起作用,我无法弄清楚原因。

def photos(_,_,_) do
    {:ok, Instagram.Posts.list_photos}
end

def photo(_, %{id: id}, _) do
    {:ok, Instagram.Posts.get_photo!(id)}
end

def photo_delete(_, %{id: id}, _) do
    photo_from_db = {:ok, Instagram.Posts.get_photo!(id)}
    {:ok, Instagram.Posts.delete_photo(photo_from_db)}
end

我的schema.ex在突变部分下包含此代码。

def delete_photo(%Photo{} = photo) do
Repo.delete(photo)
end

这是resolver.posts文件中的代码,用于返回列表或单张照片。

object :photo do
    field :id, non_null(:id)
    field :image_url, non_null(:string)
    field :caption, :string

    field :inserted_at, non_null(:string)
    field :update_at, non_null(:string)
end

这是执行突变以从数据库中删除照片的代码,该照片接收照片结构并删除它。

schema "photos" do
field :caption, :string
field :image_url, :string

timestamps()

end

这是定义照片架构的代码。

 mutation {
  photo_delete(id: 1){
    id
 }
}

@doc false   def changeset(%Photo {} = photo,attrs)do     照片     |> cast(attrs,[:image_url,:caption])     |> validate_required([:IMAGE_URL])   端

此代码位于处理架构的photo.ex文件中(我认为)

"no function clause matching in Instagram.Posts.delete_photo/1"

这是我从数据库中删除查询的突变。它返回错误说

NodeJS Plugin

从终端返回。 我做错了什么?我对这个例子中的函数流程有什么不了解。 链接到视频系列:remaining contents以进一步说明。

3 个答案:

答案 0 :(得分:0)

我认为最可能的罪魁祸首是schema.ex中函数调用正上方的行:

photo_from_db = {:ok, Instagram.Posts.get_photo!(id)}

你可能想要的是:

{:ok, photo_from_db} = Instagram.Posts.get_photo!(id)

通过这种方式,您可以将函数传递给它所期望的照片结构,而不是{:ok, %Photo{}}

答案 1 :(得分:0)

找到答案

field :delete_user, :user do
        arg :id, non_null(:id)
        resolve &Graphical.UserResolver.delete/2
    end

这可以在你的schema.ex文件

中找到
 def delete(%{id: id}, _info) do
    Accounts.get_user!(id)
    |> Accounts.delete_user
end

然后从模式文件中调用它,该文件将在该id处找到记录,然后将其传递给delete方法,在该方法中将从数据库中删除

答案 2 :(得分:0)

def photo_delete(_, %{id: id}, _) do
    photo_from_db = {:ok, Instagram.Posts.get_photo!(id)}
    {:ok, Instagram.Posts.delete_photo(photo_from_db)}
end

应该类似于

with {:ok, photo_from_db} <- Instagram.Posts.get_photo!(id) do
  Instagram.Posts.delete_photo(photo_from_db)
else 
  {:error, error} ->
    {:error, error}
end

或类似的东西。

此外,我现在编写所有上下文函数以返回:ok/:error元组,因此苦艾酒一切都很好。一般而言,这似乎也是一种好习惯。

通过硬编码:ok条件,您无法正确处理失败的案例,只会引发异常,而不是返回有用的错误。

您可以使用这种中间件来处理错误:

defmodule ApiWeb.Middleware.ErrorMiddleware do
  require Logger
  alias Api.Error.AbsintheError

  def add_error_handling(spec) do
    fn res, config ->
      spec
      |> to_fun(res, config)
      |> exec_safely(res)
    end
  end

  defp to_fun({{module, function}, config}, res, _config) do
    fn -> apply(module, function, [res, config]) end
  end

  defp to_fun({module, config}, res, _config) do
    fn -> apply(module, :call, [res, config]) end
  end

  defp to_fun(module, res, config) when is_atom(module) do
    fn -> apply(module, :call, [res, config]) end
  end

  defp to_fun(fun, res, config) when is_function(fun, 2) do
    fn -> fun.(res, config) end
  end

  defp exec_safely(fun, res) do
    fun.()
    |> Map.update!(:errors, &Enum.map(&1, fn e -> AbsintheError.serialize(e) end))
  rescue
    err ->
      # TODO: https://authkit.atlassian.net/projects/AUT/issues/AUT-9
      Logger.error(Exception.format(:error, err, __STACKTRACE__))

      Absinthe.Resolution.put_result(
        res,
        {:error, %{code: :internal_server_error, message: "An internal server error has occured"}}
      )
  end
end

然后构建这样的错误结构

defmodule Api.Error.NotFoundError do
  @type error_source ::
          :internal | :network

  @type t :: %__MODULE__{
          source: error_source,
          code: :not_found,
          message: String.t()
        }

  @enforce_keys [:source, :code, :message]
  defstruct [:source, :code, :message]

  @spec new(Keyword.t()) :: t
  def new(fields) do
    struct!(__MODULE__, fields)
  end
end

并像这样实现

defprotocol Api.Error.AbsintheError do
  def serialize(err)
end

defimpl Api.Error.AbsintheError, for: Api.Error.NotFoundError do
  @doc """
  `serialize` takes our standard `%NotFoundError{}` struct and converts it
  into a regular map in order to make it play nice with `Absinthe`.  We then
  use `Absinthe` `middleware` to force the execution of serialize every time
  it gets passed a `%NotFoundError{}`.
  """
  def serialize(err) do
    %{source: err.source, code: err.code, message: err.message}
  end
end