Autogenerated unique short IDs
HarryET:
What is the best way in Ash to generate short IDs e.g. invite codes that are unique and will keep generating till it gets a unique one? Actions?
ZachDaniel:
Yep! You can do it in a before action hook and check until you get a unique one 🙂
HarryET:
Check manually or does ash have checking built in?
ZachDaniel:
There is nothing that will repeatedly check and retry built in
HarryET:
👌🏼 So i write an action that generates then tryies to query for it, if it finds it -> generate a new one, else return
ZachDaniel:
Well, you’d just include that as a change on a create action.
HarryET:
yeah
ZachDaniel:
create :create do
# assuming short_id is allow_nil: false, you'd use this to not require it for this action
allow_nil_input [:short_id]
change fn changeset, _ ->
Ash.Changeset.before_action(changeset, fn changeset ->
Ash.Changeset.force_change_attribute(changeset, :short_id, generate_short_id(...))
end)
end
end
HarryET:
🔥 thanks yeah thats what I was plannin
HarryET:
iex(2)> Project.Resources.Invites.generate_code
[debug] QUERY OK source="invites" db=22.0ms idle=213.1ms
SELECT c0."id", c0."code", c0."sent_to", c0."max_uses", c0."created_at", c0."updated_at", c0."company_id" FROM "invites" AS c0 WHERE (c0."code"::text = $1::text) ["ExpDCJLb"]
↳ AshPostgres.DataLayer.run_query/2, at: lib/data_layer.ex:599
"ExpDCJLb"
iex(3)>
HarryET:
@alphabet Enum.concat([?0..?9, ?A..?Z, ?a..?z])
@max_tries 15
def generate_code(attempts \\ 1, len \\ 8) do
code = for _ <- 1..len, into: "", do: <>
with {:ok, []} <-
Invite
|> Ash.Query.for_read(:read)
|> Ash.Query.filter(code == ^code)
|> Project.Api.read() do
code
else
_ ->
if attempts > @max_tries do
throw("failed to generate code")
else
generate_code(attempts + 1, len)
end
end
end
HarryET:
Just putting the code here for anyone else <a:magic_sparkles:989799235274817646>
ZachDaniel:
🥳
HarryET:
An optimisation would be checking for count rather than an actual query but idk if you can do that
ZachDaniel:
Not quite yet, but I’m working on
Api.aggregate(query, :count, ...)
to do exactly that
Terris:
This is a tangent but you might find this interesting. https://morioh.com/p/52d5efc4cba3
HarryET:
They are a bit long for what I need xD thanks for sending though
Terris:
I know. The js ulid library I use lets you control the length and other aspects of the format. This elixir one is a bit deficient.