Query result to changeset: invalid input

At one point I’ll wrap my head around the new changes to resource attributes in Ash, I promise :slight_smile:

Meanwhile here’s an issue I have:

module Quire.Blog.Page do
  actions do
    defaults [
      :read,
      :destroy,
      create: [
        :title,
        :slug,
        :raw,
        :rendered,
        :is_published,
        :published_at,
        :is_tag,
        :toc,
        :settings,
        :is_archived,
        :is_private,
        :show_on_timeline,
        :language
      ]
    ]
    action :duplicate, :struct do
      argument :page_id, :uuid
      transaction? true

      run fn query, _context ->
        id = query.arguments.page_id

        {:ok, page} =
          __MODULE__.get(id)
          |> Ash.load([:media, :tags, :pages, :external_scripts])

        new_page = %{
          page
          | id: nil,
            is_published: false,
            published_at: nil,
            slug: nil,
            is_private: false
        }

        {:ok, new_page} = new_page |> Map.from_struct() |> __MODULE__.create()
        ...
    end
end

This action will fail in create because the input will have quite more fields than just listed attributes: it will have all the calculated fields, and all the aggregations etc.

This worked in Ash 2.x, but now fails in Ash 3.0 with %Ash.Error.Invalid.NoSuchInput for every calculation, aggregate etc. in the provided map.

Is their a fast way to convert this into a proper changeset with inputs. E.g. by filtering only on keys available to the action?

I’ve solved it by

        new_page =
          page
          |> Map.from_struct()
          |> Map.take(@create_attributes)

        new_page = %{
          new_page
          | is_published: false,
            published_at: nil,
            slug: nil,
            is_private: false
        }

        {:ok, new_page} = new_page |> __MODULE__.create()

where @create_attributes contains all the necessary fields

1 Like

You can also tell Ash what keys to ignore in cases that they are missing:

inputs = new_page |> Map.from_struct()
{:ok, new_page} =  __MODULE__.create(inputs, skip_unknown_inputs: Map.keys(inputs))

With that said, being more explicit as you are showing is always better :+1:

1 Like