Prefixed base62 UUIDv7 primary key implementation with Ash

Hi all, I'm trying to implement a UUIDv7 based primary key for all our Ash resources (and relationships), storing that as UUID type column on PostgreSQL and presenting that outside as a prefixed base62 string (0188aadc-f449-7818-8862-5eff12733f64 will be shown as acct_02tRrww6GFm4urcMhyQpAS), like what is described here for Ecto (https://danschultzer.com/posts/prefixed-base62-uuidv7-object-ids-with-ecto). I'll be happy to contribute with the result of this as a code sample or better as an elixir package as soon as I get it all working properly. What I'd like to know, maybe from @Zach Daniel, is what is the better way with Ash to add a Resource's DSL helper like uuid_primary_key? With older version of Ash and Spark I've successfully patched the Resource's DSL, but with newer version it seems that here https://github.com/ash-project/ash/blob/main/lib/ash/resource/dsl.ex#L127 on Ash side is missing the patchable?: true option that's is defined here https://github.com/ash-project/spark/blob/f997db91f74e772cd19656148092f439743f9cd8/lib/spark/dsl/section.ex#L42 on Spark side. It seems a philosophical decision on the framework side to explicitly disallow DSL patching, so I'm wondering what is the correct way to do it. It would be great to be able to contribute to ash via an external package that will add to the Resource's DSL a simple helper like prefix_uuidv7_primary_key. Another question is how to know in the Ash type module definition the Ash Resource in which it is included as an attribute, in order to generate or obtain the right prefix (maybe with Resource metadata or through a new DSL specific section). Unfortunately I think this is not possible right now so I'm trying to get around the problem by using the constraints like constraints: [prefix: "acct"] because that it's the only flexible parameter I found on the Ash types. And of course any advice or tip is welcome πŸ˜…
17 Replies
Terris
Terrisβ€’2y ago
I don't know how to extend Ash, so I'm useless. However, I want this solution, so I cheerlead thou. I built something that uses calculations but just as a spike. I know that's not what I or anyone else wants - for one, it doesn't work with Ash JSONApi or Ash GraphQL. https://gist.github.com/dev-guy/51a4c12983424d3480cd1201263b50c9
ZachDaniel
ZachDanielβ€’2y ago
I think you could do this without an extension actually.
defmodule prefix_uuidv7_primary_key(name, opts) do
quote do
opts =
unquote(opts)
|> Keyword.merge(type: YourType, more: :options, ...)
|> Keyword.update(:constraints, [resource: __MODULE__], &Keyword.put(&1, :resource, __MODULE__))

attribute unquote(name), opts
end
end
defmodule prefix_uuidv7_primary_key(name, opts) do
quote do
opts =
unquote(opts)
|> Keyword.merge(type: YourType, more: :options, ...)
|> Keyword.update(:constraints, [resource: __MODULE__], &Keyword.put(&1, :resource, __MODULE__))

attribute unquote(name), opts
end
end
That is one potentially simple way of doing it, by just providing a macro that does that work what you can also do is use transformers with your extension to alter the attribute and add that constraint. It's a bit more complex, but transformers can pretty much do anything they want to a resource.
moissela
moisselaOPβ€’2y ago
Thanks Zach for the tip, I'll try both!
moissela
moisselaOPβ€’2y ago
@Zach Daniel , do You mean through this https://ash-hq.org/docs/guides/ash/latest/tutorials/extending-resources#make-the-extension-configurable right? Can I also auto add the postgres migration_defaults DSL part with the configurable extension?
Ash HQ
Guide: Extending Resources
Read the "Extending Resources" guide on Ash HQ
ZachDaniel
ZachDanielβ€’2y ago
Yes πŸ™‚ You’d need to use Transformer.set_opt to set an updated value.
moissela
moisselaOPβ€’2y ago
Hi @Zach Daniel, another question for you πŸ˜… I'm still working on this post's subject feature that I'm packaging as a hex module named Ash UUIDv7. As promised I'll opensourcing that as soon as ready. For completeness I would add this gist's psql function https://gist.github.com/kjmph/5bd772b2c2df145aa645b837da7eca74 as a migration so that there's also a postgres-side defined default for fields using the AshUUIDv7 type, of course I've implemented the Ash-side autogenerator yet. I've seen how you've implemented a similar requirement for the ash-functions extension here https://github.com/ash-project/ash_postgres/blob/3d9a0cfb4f6748174bfa7396d4677b71f2556610/lib/migration_generator/migration_generator.ex#L276. So my question: can I define a function in the project Repo, that people will inject using my module, so that for having that psql migration auto-generated the only requirements will be add use AshUUIDv7.Postgres and add something like ashuuidv7-functions to the installed_extensions/0 list? I think that if we permit having a mix of string and function captures in the installed_extensions/0 list, it will be simple to modify how this is working right now in a retro-compatible way and easily add the feature that module like mine needed. Of course I'm happy to try to contribute myself to the AshPostgres repo if you think that is a good idea πŸ™‚
ZachDaniel
ZachDanielβ€’2y ago
There is not a way to have your own thing like the ash-functions extension currently That would need to be added, happy for PRs in that directiona
Korbin
Korbinβ€’2y ago
@moissela I too was trying to figure out how to use UUIDv7. Made a support post about it as well. I was thinking yesterday that maybe it could be added to something like the uniq package. They already define extentions for Ecto here: https://github.com/bitwalker/uniq/blob/f229d462c939ec655dd8ac8abbfe7325f2e83e6e/lib/uuid.ex#L951. Someone could define extensions for Ash similarly.
moissela
moisselaOPβ€’2y ago
Hi @Zach Daniel, PR ready here https://github.com/ash-project/ash_postgres/pull/162 Hi @Korbin , the ash extension is almost ready. It uses Uniq for uuid generation with support for version 4 and 7, with outside prefixed and encoded strings, or only encoded, or straight raw uuid. I'll post here as soon as releasing πŸ˜‰
Korbin
Korbinβ€’2y ago
Are you still intending to release a completely new package or are you contributing that extension to Uniq?
moissela
moisselaOPβ€’2y ago
actually a completely new package with Uniq as a dependency
Korbin
Korbinβ€’2y ago
Idk if Uniq’s maintainers would except an extension contribution in the first place, but why a new package vs contributing to existing package? I’m asking out curiosity as I don’t really know the advantages/disadvantages of either approach.
moissela
moisselaOPβ€’2y ago
Simply because we've start working on the extension based on our company's projects needs and at that time we not used Uniq for uuids generation. Right now we've switched to Uniq for releasing the extension as opensource but Uniq represents only a dependency for us and not all the extension. In the future we could switch to a faster or better Uniq alternative, who knows Hi @Zach Daniel, the AshUUID extension is ready here https://github.com/zoonect-oss/ash_uuid. Unfortunately we can't release it on hex until this https://github.com/ash-project/ash_postgres/pull/162 will be merged: as you can see here https://github.com/zoonect-oss/ash_uuid/actions/runs/5796129613/job/15709244164#step:3:309:
Dependencies excluded from the package (only Hex packages can be dependencies): ash_postgres
Error: Process completed with exit code 1.
Dependencies excluded from the package (only Hex packages can be dependencies): ash_postgres
Error: Process completed with exit code 1.
Manfred Wuits
Manfred Wuitsβ€’2y ago
how lucky: I needed this for a new project today, and it got published 4 hrs ago ! thanks @moissela!
ZachDaniel
ZachDanielβ€’2y ago
@moissela I've merged your PR, but will not be at a computer until tonight to publish a new version of the package Just released a new version of ash_postgres
moissela
moisselaOPβ€’2y ago
Thank you @Zach Daniel: I'm sorry to have disturbed you on vacation πŸ™ˆ I'll post AshUUID to hex tomorrow πŸ‘πŸ»
ZachDaniel
ZachDanielβ€’12mo ago
@moissela submitted a fix PR to AshUUID: https://github.com/zoonect-oss/ash_uuid/pull/7
GitHub
install takes the old version, not the version being installed by...
We fixed a bug in ash_postgres where it was doing the wrong thing here, so it makes sense that this broke, but the solution is to fix downstream extensions.

Did you find this page helpful?