Jason
Jason
AEAsh Elixir
Created by Jason on 9/17/2023 in #support
Filter check is returning an error tuple, rather than filtering rows.
I'm trying to use policies to filter out rows whose visibility attribute is not 1. The table contains some rows where visibility == 2, so I expected the query will filter those out.
policy action_type(:read) do
forbid_if Invisible
end
policy action_type(:read) do
forbid_if Invisible
end
But Task |> Ash.Query.for_read(:read) |> Tasks.read() returns an error tuple like this. What am I missing?
{:error,
%Ash.Error.Forbidden{
errors: [
%Ash.Error.Forbidden.Policy{
scenarios: [],
facts: %{
false => false,
true => true,
{Ash.Policy.Check.ActionType, [type: :read, access_type: :filter]} => true
},
filter: nil,
policy_breakdown?: false,
{:error,
%Ash.Error.Forbidden{
errors: [
%Ash.Error.Forbidden.Policy{
scenarios: [],
facts: %{
false => false,
true => true,
{Ash.Policy.Check.ActionType, [type: :read, access_type: :filter]} => true
},
filter: nil,
policy_breakdown?: false,
23 replies
AEAsh Elixir
Created by Jason on 9/13/2023 in #support
Resource MyApp.Accounts.FriendLink is not present in any known Ash.Api module.
I'm getting the above warning even though this module is indeed listed in the registry. What am I missing?
defmodule MyApp.Accounts.Registry do
use Ash.Registry,
extensions: Ash.Registry.ResourceValidations

entries do
entry MyApp.Accounts.User
entry MyApp.Accounts.FriendLink
defmodule MyApp.Accounts.Registry do
use Ash.Registry,
extensions: Ash.Registry.ResourceValidations

entries do
entry MyApp.Accounts.User
entry MyApp.Accounts.FriendLink
config :myapp, ash_apis: [MyApp.Accounts, ]
config :myapp, ash_apis: [MyApp.Accounts, ]
12 replies
AEAsh Elixir
Created by Jason on 9/10/2023 in #support
(Postgrex.Error) ERROR 42P18 (indeterminate_datatype) could not determine data type of parameter $1
Gotting the above error while try this code for a regex search. Ash.Query.filter(fragment("task_id ~* '?\\d+'", ^prefix)) The Ecto doc suggests explicitly defining type withtype(^prefix, :string) but I'm not sure how it's supposed to fit in as an Ash.Query. expression.
17 replies
AEAsh Elixir
Created by Jason on 7/17/2023 in #support
How to determine if the current page is the last or first page in Keyset pagination
I may be missing this from the documentation, but is there a way to find out if the current "page" is the firsrt or the last?
3 replies
AEAsh Elixir
Created by Jason on 6/25/2023 in #support
no route found for POST /admin
With 1 ash_admin("/admin")` in the router.ex, I can open the admin site and see it perform some actions, but for some others, I get "no route found for POST /admin ". These are some simple read actions with a couple of arguments that I'm running, if that matters. Thanks.
73 replies
AEAsh Elixir
Created by Jason on 6/20/2023 in #support
How to assign a default value to existing rows via Ash.postgres generated migration code
When a non-null column is added, is there a way to specify a default value to assign to the new column for existing records? I believe in Ecto, it's done something like this, but I'm curious if this can be done through Ash DSL.
add :new_field, :string, default: "default value"
add :new_field, :string, default: "default value"
If it makes a difference, the new column in my case is a source attribute for a relationship as in something like this. (So manually adding default: "default value" actually causes a foreign_key_violation., which is a different issue on its own.)
belongs_to :theme, Items.Theme do
api Items
allow_nil? false
destination_attribute :destination_new_field
source_attribute :new_field
attribute_type :integer
end
belongs_to :theme, Items.Theme do
api Items
allow_nil? false
destination_attribute :destination_new_field
source_attribute :new_field
attribute_type :integer
end
`
4 replies
AEAsh Elixir
Created by Jason on 6/17/2023 in #support
attribute match constraints error message
When validating email format using regex, is there an easier way to show a more user friendly validation error message such as "invalid email format" rather than the default "must match the pattern ~r/[a-z...."? I believe it can be done inside action definition, but thought I might be missing an easier way to do the same in the attribute definition.
attribute :email, :ci_string do
allow_nil? false
constraints match: ~r/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$/
end
attribute :email, :ci_string do
allow_nil? false
constraints match: ~r/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9-.]+$/
end
4 replies
AEAsh Elixir
Created by Jason on 6/14/2023 in #support
An aggregate field loaded in create action isn't available for pub_sub?
publish :create, ["update", :aggregate_field1] in one of my resources fails with the following error, despite the fact that change load(:aggregate_field1) is in the action definition. If I remove the publish setting from pub_sub, the create action succeeds, with the :aggregate_field1 properly loaded to the changeset. I wonder if something changed about how pub_sub works in Ash lately, because my pub_sub and create action codes used to work fine. I did recently update ash to the latest version. Thanks.
89 replies
AEAsh Elixir
Created by Jason on 6/9/2023 in #support
What happens when :prev page request is used but there is no previous page?
Need help understanding how keyset pagination works. Here are the steps I took. 1. 'search-change' handle_event does the initial search properly. The query output is saved in a variable called page. 2. When the 'prev'' button is clicked triggering 'prev-page' handler, (as expected) nothing happens. 3. When the "next" button is clicked triggering 'next-page' handler, nothing happens. I expected it to return the iintial search results again, or the set after that.
def handle_event("search-change", %{"search" => search}, socket) do

query = Ash.Query.filter(User, ilike(username, "%" <> ^search <> "%"))

page = User.read!(query: query)

searched_users = page.results

{:noreply,
socket
|> assign(:page, page)
|> stream(:searched_users, searched_users, reset: true)}
end

def handle_event("prev-page", params, socket) do
page = MyApp.Accounts.page!(socket.assigns.page, :prev)

{:noreply,
socket
|> assign(:page, page)
|> stream(:searched_users, page.results, at: 0)}
end

def handle_event("search-change", %{"search" => search}, socket) do

query = Ash.Query.filter(User, ilike(username, "%" <> ^search <> "%"))

page = User.read!(query: query)

searched_users = page.results

{:noreply,
socket
|> assign(:page, page)
|> stream(:searched_users, searched_users, reset: true)}
end

def handle_event("prev-page", params, socket) do
page = MyApp.Accounts.page!(socket.assigns.page, :prev)

{:noreply,
socket
|> assign(:page, page)
|> stream(:searched_users, page.results, at: 0)}
end

` Do I need to do something to prevent :prev page request where there is no previous page? If so, how do I do that? I saw this example, but thought page request is a simpler way to do the same.
next_page = Api.read(Resource, page: [limit: 10, after: last_record.__metadata__.keyset])
next_page = Api.read(Resource, page: [limit: 10, after: last_record.__metadata__.keyset])
15 replies
AEAsh Elixir
Created by Jason on 5/31/2023 in #support
Deleting intermediary table record in a many_to_many relationship
In this example (https://hexdocs.pm/ash/2.6.18/managing-relationships.html) that Post and Tag have a many_to_many relationshp through PostTag, how do I make it so that the deletion of Post also deletes the record in PostTag , but not Tag? In post.ex, I have this action
destroy :delete_post do
end
destroy :delete_post do
end
which issues this error.
** (Ash.Error.Invalid) Input Invalid
* Invalid value provided for id: would leave records behind.
nil
** (Ash.Error.Invalid) Input Invalid
* Invalid value provided for id: would leave records behind.
nil
I tried adding manage_relationship with various type options to this but couldn't get it to do what I want.
5 replies
AEAsh Elixir
Created by Jason on 5/14/2023 in #support
authorize_url for Google oauth
Hi @jart I was able to get github oauth to work following the getting started with Github guide, and now trying to get google oauth to work using oauth2. The doc's example for the authorize_url value is this.
authorize_url fn _, _ -> {:ok, "https://exampe.com/authorize"} end
authorize_url fn _, _ -> {:ok, "https://exampe.com/authorize"} end
If the path 'authorize' should be added to the router, I'm not sure what kind of module it should point to. Can you give me some pointers? I tried /auth/user/google/ as the authorize_url but it causes an ERR_TOO_MANY_REDIRECTS browser error, so I'm guessing that's not the right url.
3 replies
AEAsh Elixir
Created by Jason on 5/9/2023 in #support
submit_errors: [id: {"has already been taken", []}],
When a for_create form is submitted, I get this error. Assuming the id it's referring to is "form", I'm not sure why it thinks it has been taken.
api: MyApp.Tweets,
method: "post",
submit_errors: [id: {"has already been taken", []}],
id: "form",
transform_errors: nil,
...
api: MyApp.Tweets,
method: "post",
submit_errors: [id: {"has already been taken", []}],
id: "form",
transform_errors: nil,
...
The form is created using apply_action called from the mount action (similar to the Ash twitter clone example).
defp apply_action(socket, :new, _params) do
current_user = socket.assigns.current_user

form =
AshPhoenix.Form.for_create(MyApp.Tweets.Tweet, :create,
api: MyApp.Tweets,
forms: [
items: [
defp apply_action(socket, :new, _params) do
current_user = socket.assigns.current_user

form =
AshPhoenix.Form.for_create(MyApp.Tweets.Tweet, :create,
api: MyApp.Tweets,
forms: [
items: [
7 replies
AEAsh Elixir
Created by Jason on 5/9/2023 in #support
Could not relate to actor, as no actor was found (and :allow_nil? is false
When I submit a form that triggers :create action which contains change relate_actor(:author), I get this error. What causes no actor to be found despite that there is a logged in user?
submit_errors: [
author: {"Could not relate to actor, as no actor was found (and :allow_nil? is false)",
[]}
],
submit_errors: [
author: {"Could not relate to actor, as no actor was found (and :allow_nil? is false)",
[]}
],
8 replies
AEAsh Elixir
Created by Jason on 5/7/2023 in #support
MyApp.Secrets must implement the 'AshAuthentication.Secret` behaviour.
I followed the example here (https://ash-hq.org/docs/guides/ash_authentication/latest/github-quickstart) but ran into this error. It talks about password_reset which I'm not sure where it's coming from.
** (EXIT from #PID<0.104.0>) an exception was raised:
** (Spark.Error.DslError) [nil]
password_reset:
`MyApp.Secrets` must implement the `AshAuthentication.Secret` behaviour.
lib/MyApp/accounts/resources/user.ex:1: anonymous fn/1 in MyApp.Accounts.User.__verify_ash_dsl__/1
(elixir 1.14.1) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
lib/MyApp/accounts/resources/user.ex:1: MyApp.Accounts.User.__verify_ash_dsl__/1
(elixir 1.14.1) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
(elixir 1.14.1) lib/module/parallel_checker.ex:239: Module.ParallelChecker.check_module/2
(elixir 1.14.1) lib/module/parallel_checker.ex:78: anonymous fn/5 in Module.ParallelChecker.spawn/3
** (EXIT from #PID<0.104.0>) an exception was raised:
** (Spark.Error.DslError) [nil]
password_reset:
`MyApp.Secrets` must implement the `AshAuthentication.Secret` behaviour.
lib/MyApp/accounts/resources/user.ex:1: anonymous fn/1 in MyApp.Accounts.User.__verify_ash_dsl__/1
(elixir 1.14.1) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
lib/MyApp/accounts/resources/user.ex:1: MyApp.Accounts.User.__verify_ash_dsl__/1
(elixir 1.14.1) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
(elixir 1.14.1) lib/module/parallel_checker.ex:239: Module.ParallelChecker.check_module/2
(elixir 1.14.1) lib/module/parallel_checker.ex:78: anonymous fn/5 in Module.ParallelChecker.spawn/3
6 replies
AEAsh Elixir
Created by Jason on 4/30/2023 in #support
Publish :create, ["created", deeply_nested_item_id]
Let's say my resources have this relationship, chat_room has many posts which then has many comments. and I want to have this pub_sub definedin comment.ex.
pub_sub do
...
publish :create, ["created", :chat_room_id]

relationships do
belongs_to :post, MyApp.Chat.Post do
api MyApp.Chat
..

aggregates do
first :chat_room_id, :post, :chat_room_id
pub_sub do
...
publish :create, ["created", :chat_room_id]

relationships do
belongs_to :post, MyApp.Chat.Post do
api MyApp.Chat
..

aggregates do
first :chat_room_id, :post, :chat_room_id
Unfortuantely, this gives me the following error. What's the right way to do this?
** (Protocol.UndefinedError) protocol String.Chars not implemented for #Ash.NotLoaded<:aggregate> of type Ash.NotLoaded (a struct)
** (Protocol.UndefinedError) protocol String.Chars not implemented for #Ash.NotLoaded<:aggregate> of type Ash.NotLoaded (a struct)
My chat_room_load() function that feeds the data to the form is defined like this.
def chat_room_load() do
comments =
Comment
|> Ash.Query.load([:post, :chat_room_id])
posts =
Post
|> Ash.Query.load([
comment: comments
...
])
def chat_room_load() do
comments =
Comment
|> Ash.Query.load([:post, :chat_room_id])
posts =
Post
|> Ash.Query.load([
comment: comments
...
])
16 replies
AEAsh Elixir
Created by Jason on 4/25/2023 in #support
Nested form with filtered subform elements
Let's say there is a nested form with Tweet as the main and Comment as the sub. The requirement is to only show and make editable the comments entered by the current user in the subform. I'm hoping to accomplish this without using CSS tricks of hiding other users' comments. By loading filtered records into the form using the code below, I was able to make the form look like what I want, but when I submit the form, comments previously made by other users (apparently, filtered out from the form) seem to be removed. Is this the expected behavior of a nested form? Is there a relatively easy way to make it save the change without removing other users' comments?
def tweet_load(current_user) do
comments=
Comment
|> Ash.Query.filter(user_id == ^current_user.id)

tweets=
Tweet
|> Ash.Query.sort(row_order: :asc)
|> Ash.Query.load([
comments: comments
])
def tweet_load(current_user) do
comments=
Comment
|> Ash.Query.filter(user_id == ^current_user.id)

tweets=
Tweet
|> Ash.Query.sort(row_order: :asc)
|> Ash.Query.load([
comments: comments
])
14 replies
AEAsh Elixir
Created by Jason on 4/24/2023 in #support
Not getting pub_sub notification
Trying pub_sub the first time using Ash, so I could be missing something very simple. The console shows a debug message like this, but the liveview handle_info isn't triggered. Do I need any other plumbing code than what's shown in the code excerpts below?
[debug] Broadcasting to topics ["items:update:bcb9b300-22b9-474e-8a8a-81473568e438"] via MyAppWeb.Endpoint.broadcast

Notification:

%Ash.Notifier.Notification{resource: ...
[debug] Broadcasting to topics ["items:update:bcb9b300-22b9-474e-8a8a-81473568e438"] via MyAppWeb.Endpoint.broadcast

Notification:

%Ash.Notifier.Notification{resource: ...
Here are the relevant codes currently in place. ``` <Liveview> @impl true def handle_info( %Phoenix.Socket.Broadcast{ topic: "items:update:" <> id }, socket ) do IO.inspect(id, "items:update id") ### This doesn't show up in the console. <Resource> pub_sub do module MyAppWeb.Endpoint prefix "items" broadcast_type :phoenix_broadcast publish :update, ["update", :id] end update :update do accept [ :name ] end ...
3 replies
AEAsh Elixir
Created by Jason on 4/19/2023 in #support
Refresh "calculations" during form.validate
This question is kind of similar to https://discordapp.com/channels/711271361523351632/1071883200378445854/1071883200378445854 However, that example is a simple calculation involving two of the readily available form fields, whereas mine isn't as simple in that one of the inputs needs an aggregation of counts from an embedded array field as shown below. I want to be able to display the remaining count of an item which would decrease as the user increases the usage count.
calculate :item_remaining_count,
:integer,
MyApp.Calculations.ActionCountsHelpers
calculate :item_remaining_count,
:integer,
MyApp.Calculations.ActionCountsHelpers
defmodule MyApp.Calculations.ActionCountsHelpers do
use Ash.Calculation

@impl true
def calculate(records, opts, params) do
Enum.map(records, fn record ->
ActionCountHelpers.item_remaining_count(records) ## << Error happens here.
end)
end
end

def sum_usage_count(%Item{} = item) do
if item.usage_embeds do
Enum.reduce(item.usage_embeds , 0, fn cnt, acc -> acc + cnt.usage_count end)

else
0
end
end

def item_remaining_count(%Item{} = item) do
item.item_count - sum_usage_count(item)
end
defmodule MyApp.Calculations.ActionCountsHelpers do
use Ash.Calculation

@impl true
def calculate(records, opts, params) do
Enum.map(records, fn record ->
ActionCountHelpers.item_remaining_count(records) ## << Error happens here.
end)
end
end

def sum_usage_count(%Item{} = item) do
if item.usage_embeds do
Enum.reduce(item.usage_embeds , 0, fn cnt, acc -> acc + cnt.usage_count end)

else
0
end
end

def item_remaining_count(%Item{} = item) do
item.item_count - sum_usage_count(item)
end
I'm not sure how to initiate the re-calculation, but here is what I tried.
def handle_event("validate", %{"form" => form_params} = params, socket) do

form = AshPhoenix.Form.validate(socket.assigns.form, form_params)
MyApp.Projects.Item.item_remaining_count() # <<<

{:noreply, assign(socket, form: form)}
end
def handle_event("validate", %{"form" => form_params} = params, socket) do

form = AshPhoenix.Form.validate(socket.assigns.form, form_params)
MyApp.Projects.Item.item_remaining_count() # <<<

{:noreply, assign(socket, form: form)}
end
5 replies
AEAsh Elixir
Created by Jason on 4/14/2023 in #support
Re-sort items in a form without submitting the form
Let's say I have a form to input a lit of to-do items, and want to give the user up/down buttons to reorder them based on priority. I can probably change the priority value via phx-click event, but I'm not sure what's the best way to display the items reordered based on the new priority values, without submitting the form. I'm guessing it should be done in the 'validate' handle_event, which currently only includes this.
form = AshPhoenix.Form.validate(socket.assigns.form, form_params)
form = AshPhoenix.Form.validate(socket.assigns.form, form_params)
30 replies
AEAsh Elixir
Created by Jason on 3/12/2023 in #support
Passing validation arguments for the Form's underlying actions
How do I pass arguments in this line? I can't seem to find an example in the docs. AshPhoenix.Form.validate(socket.assigns.form, params) If action :create requires user_id as an argument, is it simply passed by including it in the params? like %{user_id: "12345"}
6 replies