axdc
axdc
AEAsh Elixir
Created by axdc on 9/8/2023 in #support
Adding Postgres NULLS LAST to an Ash prepare build sort
Hi! I spent a little bit trying to figure this out and wanted to share to make it more searchable. Ash's sorts actually support NULLS FIRST and NULLS LAST as different options passed instead of the normal :asc, :desc, etc:
prepare build(sort: [published_at: :desc_nils_last, inserted_at: :desc])
prepare build(sort: [published_at: :desc_nils_last, inserted_at: :desc])
The documentation references nils, rather than Postgres NULLS, so it didn't come up. https://ash-hq.org/docs/module/ash/latest/ash-sort#type-sort_order-0 Thanks!
1 replies
AEAsh Elixir
Created by axdc on 8/31/2023 in #support
st_distance vs <-> in ash_geo for nearest neighbor search/filter (knn)
I'm just digging into ash_geo and attempting to implement a knn filter, as described here: https://postgis.net/workshops/postgis-intro/knn.html
Unlike a distance search, the “nearest neighbour” search doesn’t include any measurement restricting how far away candidate geometries might be, features of any distance away will be accepted, as long as they are the nearest. PostgreSQL solves the nearest neighbor problem by introducing an “order by distance” (<->) operator that induces the database to use an index to speed up a sorted return set. With an “order by distance” operator in place, a nearest neighbor query can return the “N nearest features” just by adding an ordering and limiting the result set to N entries.
I see the st_distance function, in both ash_geo and geo_postgis, but don't yet grok how to use it like the <-> operator, to get an unbounded, sorted result set. I initially dove in with st_distance but then realized I was looking at something ~like:
filter expr(^st_distance(^arg(:search_point), location) < some_integer_distance?) # not quite what we want
filter expr(^st_distance(^arg(:search_point), location) < some_integer_distance?) # not quite what we want
The application idea is a user types in an address, I geocode that and get {long, lat}, and run a knn against that to see the nearest points of interest. Is there a nearest function I'm overlooking or some other way to filter and order geometries? Manually run raw SQL? Thanks! (Sorry if this is the wrong place, I didn't see an ash_geo tag)
32 replies
AEAsh Elixir
Created by axdc on 8/25/2023 in #support
Creating an Identity referencing a key within a :map attribute (postgres-stored jsonb)
I have a resource with these attributes:
attributes do
uuid_primary_key :id

attribute :source, :atom do
constraints one_of: [:source_one, :source_two]
end

attribute :original_json, :map
end
attributes do
uuid_primary_key :id

attribute :source, :atom do
constraints one_of: [:source_one, :source_two]
end

attribute :original_json, :map
end
I'd like to create an identity on the resource that uses a key within the map. Is that possible?
identities do

#something like this?
identity :synthetic_id [:source, :original_json["Key"]]

end
identities do

#something like this?
identity :synthetic_id [:source, :original_json["Key"]]

end
Specifically, I'd like to upsert using this identity. I'm seeing recommendations for Ecto to just do it manually in the database and wondering if there's an Ash Way.
7 replies
AEAsh Elixir
Created by axdc on 7/18/2023 in #support
Bypass not applying - had to pass actor again in load
Hi! I fought with this for an embarrassingly long time so I'm just gonna stick it here for SEO posterity. I thought my superuser/staff bypass was inexplicably failing – it's at the top, what's the deal??? – but it turns out the policy system just wasn't seeing my actor at all where I assumed it was. I had to pass the actor in again on the load! call. not sure if there's a better way to do this type of thing but this is what's working now:
Site.read_all!(actor: socket.assigns.current_user)
|> Panacea.Sites.load!([:domains, :profiles, :configuration, :users],
actor: socket.assigns.current_user # <- had to add this line to fix the issue
)
Site.read_all!(actor: socket.assigns.current_user)
|> Panacea.Sites.load!([:domains, :profiles, :configuration, :users],
actor: socket.assigns.current_user # <- had to add this line to fix the issue
)
To be clear, my system is functioning now, just wanted to leave a note in case anyone else is ever bashing their head into the same thing I was. thanks i love ash
2 replies
AEAsh Elixir
Created by axdc on 5/23/2023 in #support
LiveView uploads with AshPhoenix.Form
Hello, I've got a form that allows the user to select from a few example images, or to upload their own and use that. Right now it's a set of radio buttons plus a live_file_input. I've gotten it ~working but it feels pretty dirty and hacky with some showstopping bugs so I'm looking for prior art and best practices when it comes to the Ash Way to do live uploads.
I've searched here and it doesn't look like there's a specific upload extension or type yet, but in the meantime, is there a better or recommended way to do what I'm trying to do? Specifically this:
defp maybe_put_uploaded_wallpaper_url(socket, form) do
wallpaper_url = consume_uploads(socket) |> List.first()

if(wallpaper_url) do
# a wallpaper was uploaded
Logger.info("handling uploaded wallpaper")
form = form |> AshPhoenix.Form.set_data(%{form.data | wallpaper_url: wallpaper_url})
form = %{form | params: %{form.params | "wallpaper_url" => wallpaper_url}}

form = %{
form
| source: %{
form.source
| params: %{form.source.params | "wallpaper_url" => wallpaper_url}
}
}

IO.inspect(form)
else
# pass through form unchanged
form
end
end
defp maybe_put_uploaded_wallpaper_url(socket, form) do
wallpaper_url = consume_uploads(socket) |> List.first()

if(wallpaper_url) do
# a wallpaper was uploaded
Logger.info("handling uploaded wallpaper")
form = form |> AshPhoenix.Form.set_data(%{form.data | wallpaper_url: wallpaper_url})
form = %{form | params: %{form.params | "wallpaper_url" => wallpaper_url}}

form = %{
form
| source: %{
form.source
| params: %{form.source.params | "wallpaper_url" => wallpaper_url}
}
}

IO.inspect(form)
else
# pass through form unchanged
form
end
end
This feels like sin to me. The liveview file upload examples have you patching params but I don't have params, I have an AshPhoenix.Form. One bug example: If I change the image, save, and then change it back to what it originally was, it looks like it persists but actually doesn't. I suspect this may have something to do with how I'm setting the checked attribute? my index.ex: https://gitlab.com/avoh-labs/panacea/-/blob/main/lib/panacea_web/live/rosegarden/configuration_live/index.ex my index.html.heex: https://gitlab.com/avoh-labs/panacea/-/blob/main/lib/panacea_web/live/rosegarden/configuration_live/index.html.heex#L597 Thank you. Posting here is always clarifying. chasing down some more threads now. prepare_source maybe
27 replies
AEAsh Elixir
Created by axdc on 5/9/2023 in #support
Identity not creating unique index
I have a resource with an identity:
attributes do
uuid_primary_key :id

attribute :title, :string do
allow_nil? false
end

attribute :slug, :string do
allow_nil? false
end

attribute :content, :string do
allow_nil? false
end

create_timestamp(:inserted_at)
update_timestamp(:updated_at)
end

relationships do
belongs_to :site, MyApp.Sites.Site
end

identities do
identity :slug_site, [:slug, :site]
end
attributes do
uuid_primary_key :id

attribute :title, :string do
allow_nil? false
end

attribute :slug, :string do
allow_nil? false
end

attribute :content, :string do
allow_nil? false
end

create_timestamp(:inserted_at)
update_timestamp(:updated_at)
end

relationships do
belongs_to :site, MyApp.Sites.Site
end

identities do
identity :slug_site, [:slug, :site]
end
When creating these resources, the uniqueness that I think I'm specifying is not being enforced, and I don't see a unique index in postgres reflecting it. I've dropped and recreated the database, deleting all migrations and snapshots to be sure. Do Identities not work with relationships? Is there a recommended way to communicate to Ash what I'm looking for? The intended behavior is that slugs should be unique within a site.
9 replies
AEAsh Elixir
Created by axdc on 5/2/2023 in #support
Multiple checkbox group with Ash Forms
Is there a recommended guide to implementing something similar to the checkboxgroup component featured in https://fly.io/phoenix-files/making-a-checkboxgroup-input/ but compatible with Ash's form helpers? I've been working through that guide but it doesn't seem to play nice with the resources and forms I have. Been trying to munge my data into the right shape and now I'm wondering if there's an existing component system for this I'm overlooking (I've heard about Surface but I'm under the impression it may have been superseded by heex?)
22 replies
AEAsh Elixir
Created by axdc on 5/1/2023 in #support
No Such Relationship
I have a many_to_many relationship between Plan and Post. Plan is the source, the relationship is defined there. Post is the destination. PlansPosts is the join resource. It's all in the same api this time. I'm making a form with, and calling manage_relationship from, Post, and that's resulting in a Spark error:
** (EXIT from #PID<0.95.0>) an exception was raised:
** (Spark.Error.DslError) [Panacea.Sites.Post]
actions -> create -> commander_create -> change -> manage_relationship -> relate_plans -> plans:
No such relationship plans exists.
(ash 2.7.1) lib/ash/resource/transformers/validate_manage_relationship_opts.ex:40: anonymous fn/3 in Ash.Resource.Transformers.ValidateManagedRelationshipOpts.transform/1
** (EXIT from #PID<0.95.0>) an exception was raised:
** (Spark.Error.DslError) [Panacea.Sites.Post]
actions -> create -> commander_create -> change -> manage_relationship -> relate_plans -> plans:
No such relationship plans exists.
(ash 2.7.1) lib/ash/resource/transformers/validate_manage_relationship_opts.ex:40: anonymous fn/3 in Ash.Resource.Transformers.ValidateManagedRelationshipOpts.transform/1
I based this relationship on my existing, working, cross-api many_to_many SitesUsers, and I've run through the relationship guide a few more times to check if I'm missing anything from recent updates. Nothing is jumping out at me yet. Am I meant to be able to use relationships in actions on destination resources and not just source resources? That's the only difference that immediately comes to mind compared to the SitesUsers relationship that works. This error persists after mix clean --all, mix deps.update ash (still using main), etc
23 replies
AEAsh Elixir
Created by axdc on 4/23/2023 in #support
Nested form error - using create when I (think I) want update?
I have a nested form that has been working wonderfully to edit a Site and its related resources, and now I'm attempting to manage the Users who will be permissioned to use the Site as well. When I click to do my add_form for Users, I get a crash with:
[error] GenServer #PID<0.25850.0> terminating
** (AshPhoenix.Form.NoActionConfigured) Attempted to add a form at path: [:edit_users], but no `create_action` was configured.
[error] GenServer #PID<0.25850.0> terminating
** (AshPhoenix.Form.NoActionConfigured) Attempted to add a form at path: [:edit_users], but no `create_action` was configured.
The relationship is many_to_many:
many_to_many :users, MyApp.Accounts.User do
api MyApp.Accounts
through MyApp.Sites.SitesUsers
source_attribute :id
source_attribute_on_join_resource :site_id
destination_attribute :id
destination_attribute_on_join_resource :user_id
end
many_to_many :users, MyApp.Accounts.User do
api MyApp.Accounts
through MyApp.Sites.SitesUsers
source_attribute :id
source_attribute_on_join_resource :site_id
destination_attribute :id
destination_attribute_on_join_resource :user_id
end
In my action I have:
change manage_relationship(:edit_users, :users,
on_lookup: :relate,
on_no_match: :ignore,
on_missing: :unrelate,
on_match: :ignore
)
change manage_relationship(:edit_users, :users,
on_lookup: :relate,
on_no_match: :ignore,
on_missing: :unrelate,
on_match: :ignore
)
Because you should only be able to select from the existing users in a select widget and attach them to the Site, not create new users from this form (they require passwords and all that, they have their own separate form for creating). Similar to the tags example I found here, except without creating new ones. But it looks (based on my current understanding of the error) like the form helper is expecting there to be a create action involved. Am I misunderstanding some component of the system? The form is made like this:
form =
AshPhoenix.Form.for_update(site, :update_existing_site,
api: MyApp.Sites,
forms: [auto?: true],
actor: socket.assigns.current_user
)
|> to_form
form =
AshPhoenix.Form.for_update(site, :update_existing_site,
api: MyApp.Sites,
forms: [auto?: true],
actor: socket.assigns.current_user
)
|> to_form
Perhaps forms: auto? is getting confused somehow? Have I incompletely specified my resources so as to guide it?
94 replies
AEAsh Elixir
Created by axdc on 4/14/2023 in #support
Destroying related resources
I have some related resources with existing actions utilizing manage_relationship to create and update them. For destroying, is it pretty much the same process, where the form has all the nested subforms and then I take those in through arguments and manage_relationship them and destroy them? I haven't seen anything explicitly on this topic in the documentation or here, pardon me if I've missed it. I want to make sure I don't leave any orphaned resources, which the loaded form on my edit page should take care of, right, but I'm wondering if there's a way you're supposed to do it directly in the action to be sure.
12 replies
AEAsh Elixir
Created by axdc on 4/14/2023 in #support
Not seeing policy authorization errors
I'm adding policies to my app, feeling my way to the correct modeling of my domain, and when submitting an action all I see in iex is:
[warning] Unhandled error in form submission for Panacea.Accounts.User.add_user

This error was unhandled because it did not implement the `AshPhoenix.FormData.Error` protocol.

** (Ash.Error.Forbidden.Policy) forbidden:

Panacea.Accounts.User.add_user
[warning] Unhandled error in form submission for Panacea.Accounts.User.add_user

This error was unhandled because it did not implement the `AshPhoenix.FormData.Error` protocol.

** (Ash.Error.Forbidden.Policy) forbidden:

Panacea.Accounts.User.add_user
rather than Ash's nice verbose breakdown. I have the config from the documentation set up:
config :ash, :policies, show_policy_breakdowns?: true
config :ash, :policies, show_policy_breakdowns?: true
I've tried it at the top of the file, at the bottom of the file, etc, mix clean && mix compile just to be safe. Trying to figure out what I'm missing.
17 replies
AEAsh Elixir
Created by axdc on 4/11/2023 in #support
Custom validation error not appearing in form
I'm using the .input and .inputs_for components from Phoenix, and I see the normal is required errors in the html when I leave fields blank, but while my custom error shows up in the changeset from IO.inspect I don't see it in the html. The validation:
defmodule Panacea.Sites.Validations.ValidateHostname do
use Ash.Resource.Validation
alias Ash.Error.Changes.InvalidAttribute

alias Ash.Changeset

@impl true
def init(opts), do: {:ok, opts}

@impl true
@spec validate(Changeset.t(), Keyword.t()) :: :ok | {:error, term()}
def validate(changeset, keyword) do
IO.inspect(changeset)
IO.inspect(keyword)

# always return an error because i am trying to see it
{:error,
InvalidAttribute.exception(
message: "hello i am an error if you cannot see me that is a bigger error"
)}
end
end
defmodule Panacea.Sites.Validations.ValidateHostname do
use Ash.Resource.Validation
alias Ash.Error.Changes.InvalidAttribute

alias Ash.Changeset

@impl true
def init(opts), do: {:ok, opts}

@impl true
@spec validate(Changeset.t(), Keyword.t()) :: :ok | {:error, term()}
def validate(changeset, keyword) do
IO.inspect(changeset)
IO.inspect(keyword)

# always return an error because i am trying to see it
{:error,
InvalidAttribute.exception(
message: "hello i am an error if you cannot see me that is a bigger error"
)}
end
end
The resource:
...
validations do
validate Panacea.Sites.Validations.ValidateHostname
end
...
...
validations do
validate Panacea.Sites.Validations.ValidateHostname
end
...
Is there something I'm missing in implementing a validation or could it be something in the form?
8 replies
AEAsh Elixir
Created by axdc on 3/30/2023 in #support
Is there a policy bypass for manipulating resources from iex?
Didn't see anything about this already posted. Or should I be identifying as an actor from iex somehow?
6 replies
AEAsh Elixir
Created by axdc on 3/29/2023 in #support
AshAuthentication redirecting on errors
I'm using AshAuthentication with the email and password strategy. When I enter incorrect info, whether that's a wrong password or for a nonexistent user, I get redirected to another page with the error. Is this intentional design or have I configured something incorrectly? I was expecting to get errors in the (to me) "normal" form validation red text sort of way. Didn't see anything about this in the documentation, just curious if anyone else has experienced this. Thanks so much :thinkies: 🙇
9 replies
AEAsh Elixir
Created by axdc on 3/29/2023 in #support
LiveView interface for {:array, :string} type
I'm making a LiveView interface to edit a resource with an {:array, :string} Ash type attribute and I'm looking for up-to-date, Ash-aware best practices. I've cobbled a few prototypes together but I feel like I'm missing something fundamental and getting hacky. Is there any documentation on the idiomatic way to accomplish this?
14 replies
AEAsh Elixir
Created by axdc on 3/24/2023 in #support
passing actor when using AshPhoenix.Form.submit
I've been reading the policy guide and docs and I've got them up and running for all my read actions, but when submitting a create or update using AshPhoenix.Form I'm not sure where exactly to provide the actor. Is there a different way this is meant to be done?
7 replies