Ben RMX
Ben RMX
AEAsh Elixir
Created by Ben RMX on 9/27/2023 in #support
Breaking action chain in a before_action?
Problem Statement: My webhook receives at-least-once messaging from an external producer, and I want to persist these Events -- ids generated by the producer-- as an Ash Resource, such that only one instance is saved in the database (postgres), and an Oban job is only enqueued once. I've created a custom :add action with the upsert? feature, and that works great for the Event persistence -- idempotent no-ops on subsequent calls. Issue: However, it doesn't look like there is any information in the Ash.Changeset.after_action record to determine if it was a fresh record or an existing record, and thus, I don't see a way to conditionally enqueue an Oban job. Possible solutions? 1. Add a nonce to Event, have a before action populate the changeset with a random number; followed by an after action that compares the returned record vs the changeset nonce... and enqueue Oban job if they match... This would be a probabilistic approach 2. Or, probably better, is if there a way for a before_action to load a possibly already existing Event with that given id-producer pair; if exists then shortcut stop the action, returning that Event as the result of the action? Or is this an around_action? -- knowing that these steps all live in the same db transaction...
7 replies
AEAsh Elixir
Created by Ben RMX on 9/26/2023 in #support
Does attribute and action order matter? and Upsert fields?
Given Declaration
defmodule MyApp.MyContext.Event do
use Ash.Resource, data_layer: AshPostgres.DataLayer

postgres do
table "event"
repo MyApp.MyContext.Repo
end

multitenancy do
strategy :attribute
attribute :organization_id
end

attributes do
# Tenancy, Identity and Immutability
# * Primary Key - [:organization_id, :id]
# * need to manually declare id so we do not autogenerate UUID
attribute :organization_id, :uuid, allow_nil?: false, primary_key?: true
attribute :id, :uuid, allow_nil?: false, primary_key?: true

attribute :data, :map
attribute :dataschema, :string

attribute :partitionkey, :string
attribute :sequence, :integer
attribute :source, :string, allow_nil?: false
attribute :subject, :string
attribute :time, :utc_datetime_usec
attribute :type, :string, allow_nil?: false

create_timestamp :created_at
end

identities do
identity :id, [:id]
end

actions do
defaults [:read]
create :add do
upsert? true
upsert_identity :id
upsert_fields([:id])
end
end
end
defmodule MyApp.MyContext.Event do
use Ash.Resource, data_layer: AshPostgres.DataLayer

postgres do
table "event"
repo MyApp.MyContext.Repo
end

multitenancy do
strategy :attribute
attribute :organization_id
end

attributes do
# Tenancy, Identity and Immutability
# * Primary Key - [:organization_id, :id]
# * need to manually declare id so we do not autogenerate UUID
attribute :organization_id, :uuid, allow_nil?: false, primary_key?: true
attribute :id, :uuid, allow_nil?: false, primary_key?: true

attribute :data, :map
attribute :dataschema, :string

attribute :partitionkey, :string
attribute :sequence, :integer
attribute :source, :string, allow_nil?: false
attribute :subject, :string
attribute :time, :utc_datetime_usec
attribute :type, :string, allow_nil?: false

create_timestamp :created_at
end

identities do
identity :id, [:id]
end

actions do
defaults [:read]
create :add do
upsert? true
upsert_identity :id
upsert_fields([:id])
end
end
end
The above gives me the warning:
warning: the Inspect protocol has already been consolidated, an implementation for Btwb.Koohbew.Event has no effect. If you want to implement protocols after compilation or during tests, check the "Consolidation" section in the Protocol module documentation
But if I move identities and actions to above the attributes, then no warning... Given:
MyApp.MyContext.Event |>
Ash.Changeset.for_create(
:add,
%{
organization_id: "1c8345b3-f484-47a7-ad21-da17fa511966",
id: "a325ac4a-5c7b-11ee-8c99-0242ac120002",
type: "namtest2",
source: "MyEventSource.34293942394234"
})
|> Ash.Changeset.set_tenant("1c8345b3-f484-47a7-ad21-da17fa511966")
|> MyApp.MyContext.create()
MyApp.MyContext.Event |>
Ash.Changeset.for_create(
:add,
%{
organization_id: "1c8345b3-f484-47a7-ad21-da17fa511966",
id: "a325ac4a-5c7b-11ee-8c99-0242ac120002",
type: "namtest2",
source: "MyEventSource.34293942394234"
})
|> Ash.Changeset.set_tenant("1c8345b3-f484-47a7-ad21-da17fa511966")
|> MyApp.MyContext.create()
The above gives a SQL query with DO UPDATE SET on all fields Am I doing something wrong? Do actions have to be above attributes?
10 replies
AEAsh Elixir
Created by Ben RMX on 9/23/2023 in #support
GraphQL :type, prefix based on Context?
In the example docs, we have something like the following:
defmodule Helpdesk.Support.Ticket do
...omitted...
graphql do
type :ticket
end
end
defmodule Helpdesk.Support.Ticket do
...omitted...
graphql do
type :ticket
end
end
So, the following introspection query would return Ticket as a type.
{
__schema {
types {
name
}
}
}
{
__schema {
types {
name
}
}
}
So, given a totally made up hypothetical, if we had another Ash API with a resource Amusement.Parks.Ticket , included in the same Ash GraphQL Schema... wouldn't the two Ticket graphql names clobber each other? So, is there a way to automatically prefix the resource with a context/api based name? To get graphql like HelpdeskSupportTicket and AmusementParksTicket ? Or is it up to the application developer to explicitly define type :helpdesk_support_ticket and type :amusement_parks_ticket ?
8 replies