Is it possible to configure the default uuid type and postgres function?

Is there any global configuration I change use to change both the default Ash.UUID.generate/0 function and gen_random_uuid() functions for uuid fields?

For example, I want to use uuid_v7 by default.

I know I can at least change the default Ash.UUID.generate/0 by using the default option in each attribute, but I believe in some cases that is not sufficient (ex. with an ash_authentication user_resource)

IIRC ash_authentication will only add the id attribute if you haven’t already. I don’t believe there is a way to do it globally at the moment.

Ah, that’s true. I added a separated field for the id and it worked just fine.

That actually brings me to another question.

Is it possible to configure an Ash.Type to use some specific fragment for when generating migrations?

For example, I have my own Ash.Type for UUIDV7. I want to make it that when i add this attribute to some resource, it will automatically set the default postgres value as uuid_generate_v7() the same way that adding the default uuid primary key field for Ash adds the gen_random_uuid().

Of course, I can add that using the migration_defaults function, but that means that I need to do that for basically every resource in my system (since they all use my UUIDV7 type), it would be great if I could make that be added automatically.

I saw that there is a AshPostgres.Type that adds a callback for a function called value_to_postgres_default, but that function doesn’t seem to be used to generate that default.

Maybe we could add a new callback for AshPostgres.Type called @callback migration_default/0 where the user defines what is the default migration default value for the type that implements it?

Ah, so TBH I don’t think you actually want all usages of that type get a default value. Otherwise belongs_to :something, SomethingElse, type: UUIDV7 would also get a default value, which would fail to insert if that relationship is optional.

What I’d suggest is adding this to your app:

defmodule MyApp.Resource do
  def macro __using__(opts) do
     quote do
       use Ash.Resource, unquote(opts)

       import MyApp.Resource
    end
  end

  def macro uuid_v7_primary_key(name, opts) do
    quote do
      attribute unquote(name), UUIDV7, unquote(Keyword.put(opts, default: &UUIDV7.generate/0))
    end
  end
end

Then you can use MyApp.Resource instead of use Ash.Resource, and have uuid_v7_primary_key available.

With all of that said, the next release of Ash will have uuidv7 built in to core, so maybe you just want to use that?

Yep, that is actually what I used. Here is my implementation:

defmodule UUIDV7.Macros do
  @moduledoc false

  defmacro uuid_v7_primary_key(name, opts \\ []) do
    default =
      quote do
        fn -> UUIDV7.Type.generator([]) end
      end

    field_opts =
      opts
      |> Keyword.put_new(:primary_key?, true)
      |> Keyword.put_new(:allow_nil?, false)
      |> Keyword.put_new(:default, default)
      |> Keyword.put_new(:writable?, false)

    quote do
      attribute unquote(name), UUIDV7.Type, unquote(field_opts)
    end
  end

  defmacro uuid_v7_attribute(name, opts \\ []) do
    default =
      quote do
        fn -> UUIDV7.Type.generator([]) end
      end

    field_opts =
      opts
      |> Keyword.put_new(:primary_key?, false)
      |> Keyword.put_new(:allow_nil?, false)
      |> Keyword.put_new(:default, default)
      |> Keyword.put_new(:writable?, false)

    quote do
      attribute unquote(name), UUIDV7.Type, unquote(field_opts)
    end
  end
end

What bothered me with that is that this would not add default as uuid_generate_v7() in the postgres layer, it will be nil but the uuid_primary_key will add gen_random_uuid() as the default in postgres layer.

Is that built-in uuidv7 implementation already in master? Can it be tested already? that would be super great!

Yes, it is available in ash, main, but the ash_postgres changes are waiting to be merged until ash has been released, unfortunately. But you could try the branch from the PR.
improvement: add support for `:uuid_v7` type by moissela · Pull Request #333 · ash-project/ash_postgres · GitHub

1 Like