moxley
moxley
AEAsh Elixir
Created by moxley on 8/20/2023 in #support
Notifications warning in migration
I'm getting a warning [warning] Missed 1 notifications in action GF.WebComponents.WebSite.update in the logs when running a new migration that calls an :update action on a resource. I know very little about Ash notifications and how they work. I don't want to disable them globally, because they might be useful in the future. How can I prevent this warning from appearing when the migration runs?
12:39:17.050 [warning] Missed 1 notifications in action GF.WebComponents.WebSite.update.

This happens when the resources are in a transaction, and you did not pass
`return_notifications?: true`. If you are in a changeset hook, you can
return the notifications. If not, you can send the notifications using
`Ash.Notifier.notify/1` once your resources are out of a transaction.

(elixir 1.14.3) lib/process.ex:773: Process.info/2
(ash 2.13.2) lib/ash/actions/helpers.ex:239: Ash.Actions.Helpers.warn_missed!/3
(ash 2.13.2) lib/ash/actions/update.ex:176: Ash.Actions.Update.add_notifications/6
(ash 2.13.2) lib/ash/actions/update.ex:38: Ash.Actions.Update.run/4
(ash 2.13.2) lib/ash/api/api.ex:2036: Ash.Api.update!/3
(elixir 1.14.3) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
(ecto_sql 3.10.1) lib/ecto/migration/runner.ex:289: Ecto.Migration.Runner.perform_operation/3
(stdlib 4.3) timer.erl:235: :timer.tc/1


While you should likely leave this setting on, you can ignore these or turn them into errors.

To ignore these in all cases:

config :ash, :missed_notifications, :ignore

To turn this into raised errors:

config :ash, :missed_notifications, :raise
12:39:17.050 [warning] Missed 1 notifications in action GF.WebComponents.WebSite.update.

This happens when the resources are in a transaction, and you did not pass
`return_notifications?: true`. If you are in a changeset hook, you can
return the notifications. If not, you can send the notifications using
`Ash.Notifier.notify/1` once your resources are out of a transaction.

(elixir 1.14.3) lib/process.ex:773: Process.info/2
(ash 2.13.2) lib/ash/actions/helpers.ex:239: Ash.Actions.Helpers.warn_missed!/3
(ash 2.13.2) lib/ash/actions/update.ex:176: Ash.Actions.Update.add_notifications/6
(ash 2.13.2) lib/ash/actions/update.ex:38: Ash.Actions.Update.run/4
(ash 2.13.2) lib/ash/api/api.ex:2036: Ash.Api.update!/3
(elixir 1.14.3) lib/enum.ex:975: Enum."-each/2-lists^foreach/1-0-"/2
(ecto_sql 3.10.1) lib/ecto/migration/runner.ex:289: Ecto.Migration.Runner.perform_operation/3
(stdlib 4.3) timer.erl:235: :timer.tc/1


While you should likely leave this setting on, you can ignore these or turn them into errors.

To ignore these in all cases:

config :ash, :missed_notifications, :ignore

To turn this into raised errors:

config :ash, :missed_notifications, :raise
15 replies
AEAsh Elixir
Created by moxley on 7/19/2023 in #support
Using manage_relationship to delete a related record
I have an action that accepts the ID of a related record. It should delete that record. However, when I tried using the type: :remove option, Ash responds with Invalid value provided for notes: changes would create a new related record. Is this the right approach? Or should I manually delete the record?
23 replies
AEAsh Elixir
Created by moxley on 7/8/2023 in #support
Actor not passed to `read` action after `create` action
I have a resource with a default :create action and :read action, and an AshGraphql mutation that calls :create. The resource has a module-based policy that requires the actor have a certain "role". When I test the mutation, the policy allows the actor to perform the :create, but when it checks the :read action that happens at the end of the mutation, the actor passed to the policy is nil. What makes this especially confusing is that I have other resources that are set up the same way, and they don't have this nil actor problem. I'll post my code in the comments below.
12 replies
AEAsh Elixir
Created by moxley on 6/22/2023 in #support
Arbitrary queries, like Ecto.Query can do?
I'm using AshPostgres. I have requirements that require building a database query dynamically, based on data passed to it, and it involves SQL joins and possibly other SQL features. How can I do this with Ash? To start with, how do I do JOINs?
52 replies
AEAsh Elixir
Created by moxley on 6/18/2023 in #support
Building an aggregates-only Resource
I'm building an admin dashboard that will display several aggregate data points sourced from different database tables. These data points should be available from a single AshGraphql query. How would you do this? I created a resource called Dashboard. My first approach was to create a calculation to calculate a data point. That wasn't going anywhere, so I defined an primary read action that loaded the data point from inside the prepare block. That's where I'm at so far. The problem is that calling read() on the API returns nil. Here is an example of the kind of data I want to expose: - Member counts - Signup counts - Event counts - Attendee counts
112 replies
AEAsh Elixir
Created by moxley on 6/16/2023 in #support
Where do I find documentation on `contains()` , for use within an Ash.Query.filter()?
I checked: 1. Ash.Filter: https://ash-hq.org/docs/module/ash/latest/ash-filter 2. Ash.Query: https://ash-hq.org/docs/module/ash/latest/ash-query 2. Ash.Query.filter() (which was not in alphabetical order in the Ash.Query functions list on the right side): https://ash-hq.org/docs/module/ash/latest/ash-query#macro-filter-2 Where can the documentation for contains() be found?
20 replies
AEAsh Elixir
Created by moxley on 6/15/2023 in #support
Primary key as string (not UUID)
I have an existing table with a string key. In Ecto, this was generated in the changeset function. In the Ash documentation, there is only integer_primary_key(), and uuid_primary_key(). If I try leaving either of those out, it returns an error that says I need to specify a primary key. How can this be made to work?
6 replies
AEAsh Elixir
Created by moxley on 6/13/2023 in #support
Return aggregate after GraphQL mutation
I have an aggregate defined like this:
aggregates do
count :events_count, :events
end
aggregates do
count :events_count, :events
end
And GraphQL queries defined like this:
graphql do
type :venue2

queries do
get :get_venue2, :read
list :list_venues2, :read
end

mutations do
create :create_venue2, :create
update :update_venue2, :update
destroy :delete_venue2, :destroy
end
end
graphql do
type :venue2

queries do
get :get_venue2, :read
list :list_venues2, :read
end

mutations do
create :create_venue2, :create
update :update_venue2, :update
destroy :delete_venue2, :destroy
end
end
When I call get_venue2 or list_venues2, and the query requests the aggregate, the aggregate returns successfully. When I call update_venue2, and request the aggregate, it fails with a forbidden error in the response. Removing the aggregate from the query makes the error go away, but now I don't have the aggregate I need. Apparently, whatever automatic passing of the actor to the aggregate that happens in the read queries isn't happening with the mutation. Is there a solution for this?
13 replies
AEAsh Elixir
Created by moxley on 6/12/2023 in #support
Parameterized custom policy checks?
Is there a way to pass a parameter to a SimpleCheck implementation module? Across most resources in my app, several checks need to be performed. Many of these checks are invariable, and can be put into a SimpleCheck module, but but one checks is variable, depending on a case-by-case basis. I would like to define one SimpleCheck module that does all these checks, and pass a parameter to it to handle that one variable case. Otherwise, I have to define repetitive checks in each of the resource modules. Is there a solution for this?
8 replies
AEAsh Elixir
Created by moxley on 6/12/2023 in #support
No Absinthe schemas generated for resource
I've been using AshGraphql just fine, having created several resources that work with it. Then today, I created a new resource, and no Absinthe schemas are created from it.
38 replies
AEAsh Elixir
Created by moxley on 6/6/2023 in #support
Can't get policies to work with AshGraphql
I have an action, update_customer_registration, which requires a Customer actor. My Customer policy looks like this:
policies do
policy action(:update_customer_registration) do
authorize_if actor_attribute_equals(:__struct__, __MODULE__)
end
end
policies do
policy action(:update_customer_registration) do
authorize_if actor_attribute_equals(:__struct__, __MODULE__)
end
end
This works as expected when using the application's Ash API directly:
customer
|> Ash.Changeset.new()
|> Ash.Changeset.for_update(:update_customer_registration, %{contact_name: "Test Name"},
actor: customer
)
|> Corp.Ash.Api.update!()
customer
|> Ash.Changeset.new()
|> Ash.Changeset.for_update(:update_customer_registration, %{contact_name: "Test Name"},
actor: customer
)
|> Corp.Ash.Api.update!()
Setting actor to nil, raises an policy error as expected. However, it doesn't seem to work when going through AshGraphql, when the actor is present:
%{
"data" => %{"updateCustomerRegistration" => nil},
"errors" => [%{"code" => "Forbidden", "fields" => [], "locations" => [%{"column" => 5, "line" => 2}], "message" => "forbidden", "path" => ["updateCustomerRegistration"], "short_message" => "forbidden", "vars" => %{}}]
}
%{
"data" => %{"updateCustomerRegistration" => nil},
"errors" => [%{"code" => "Forbidden", "fields" => [], "locations" => [%{"column" => 5, "line" => 2}], "message" => "forbidden", "path" => ["updateCustomerRegistration"], "short_message" => "forbidden", "vars" => %{}}]
}
I know the actor is being set correctly with Ash.PlugHelpers.set_actor() However, when I change the policy to match on any action (policy always() do), it works. There seems to be something specific with specifying the action in the policy that doesn't work with AshGraphql.
27 replies
AEAsh Elixir
Created by moxley on 5/29/2023 in #support
cannot be explicitlyGetting error when generating migration: "...converted to an Ecto default"
I'm getting this error when trying to generate a migration:
16:56:34.288 [warning] You have specified a default value for a type that cannot be explicitly
converted to an Ecto default:

`[]`

The default value in the migration will be set to `nil` and you can edit
your migration accordingly.

To prevent this warning, implement the `EctoMigrationDefault` protocol
for the appropriate Elixir type in your Ash project, or configure its
default value in `migration_defaults` in the postgres section. Use `\"nil\"`
for no default.
16:56:34.288 [warning] You have specified a default value for a type that cannot be explicitly
converted to an Ecto default:

`[]`

The default value in the migration will be set to `nil` and you can edit
your migration accordingly.

To prevent this warning, implement the `EctoMigrationDefault` protocol
for the appropriate Elixir type in your Ash project, or configure its
default value in `migration_defaults` in the postgres section. Use `\"nil\"`
for no default.
I didn't have any new changes, and the error gave me no information about where in the codebase the problem came from. After some investigating and guessing, I discovered it was from this attribute:
attribute :attrs, {:array, EmbeddedWebComponent}, default: []
attribute :attrs, {:array, EmbeddedWebComponent}, default: []
First, I changed it to this:
attribute :attrs, {:array, EmbeddedWebComponent}
attribute :attrs, {:array, EmbeddedWebComponent}
Then, I wanted to follow the advice from the error, configure its default value in migration_defaults in the postgres section. The documentation wasn't clear on the syntax for the migration_defaults, so I guessed it should look like this:
# Make the default []
migration_defaults attrs: []
# Make the default []
migration_defaults attrs: []
After trying to generate the migration again, a new error appeared:
8 replies
AEAsh Elixir
Created by moxley on 4/17/2023 in #support
Does attribute `default: _` not set the struct's default value?
It appears that when defining an attribute's default: value, it doesn't set the default on the generated struct definition. I would expect it would, like Ecto.Schema does. Is there a reason it doesn't? Or is there a way of accomplishing this that I couldn't find in the documentation?
defmodule GF.Ash.WebComponent do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshGraphql.Resource]

alias GF.Ash.EmbeddedWebComponent

attributes do
attribute :attrs, {:array, EmbeddedWebComponent}, default: []
attribute :usage, :atom,
constraints: [one_of: [:attribute, :replacement]],
default: :attribute
end
end
defmodule GF.Ash.WebComponent do
use Ash.Resource,
data_layer: AshPostgres.DataLayer,
extensions: [AshGraphql.Resource]

alias GF.Ash.EmbeddedWebComponent

attributes do
attribute :attrs, {:array, EmbeddedWebComponent}, default: []
attribute :usage, :atom,
constraints: [one_of: [:attribute, :replacement]],
default: :attribute
end
end
component = %WebComponent{}
dbg(component.attrs) # nil
dbg(component.usage) # nil
component = %WebComponent{}
dbg(component.attrs) # nil
dbg(component.usage) # nil
5 replies
AEAsh Elixir
Created by moxley on 4/8/2023 in #support
Porting `cloak`-managed fields from Ecto schema to Ash
One of the Ecto schemas in my project uses Cloak to encrypt a value to a column on write, then decrypt the column on read and expose that on a virtual field on the schema. For example, the schema has these two fields:
field :encrypted_meetup, GF.Encrypted.Map
field :meetup, :map, virtual: true
field :encrypted_meetup, GF.Encrypted.Map
field :meetup, :map, virtual: true
GF.Encrypted.Map is a very small module tells cloak about the details of encrypting and decrypting the data. cloak will then perform its magic in the background. On database write, encrypted_meetup is expected to be an unencrypted value, and cloak will encrypt that value to the underlying column as a Postgres bytea type. On database read, cloak will decrypt the value, and put it in encrypted_meetup, and it will also cast that value to a struct, and put it in meetup. How should I approach porting this to an Ash resource? Should I also set up two attributes in the Ash resource, just like in the Ecto schema? Should I define a custom Ash type for transparently encrypting and decrypting the value?
61 replies
AEAsh Elixir
Created by moxley on 3/22/2023 in #support
AshGraphql: Updating a managed has_many relationship
I'm getting "In field \"id\": Unknown field." in the GraphQL response error. It's referring to the id provided in the has_many related record .
# This is the updateWebSite input argument. It's a UpdateWebSiteInput type.
input = %{
# The has_many relationship
components: [
%{
# This is the primary key of the associated record (UpdateWebSiteComponentsInput)
id: "38f61258-0e8f-4a99-8bae-0cb892e6ccaa",
value: "new value",
key: "test-component",
}
]
}
# This is the updateWebSite input argument. It's a UpdateWebSiteInput type.
input = %{
# The has_many relationship
components: [
%{
# This is the primary key of the associated record (UpdateWebSiteComponentsInput)
id: "38f61258-0e8f-4a99-8bae-0cb892e6ccaa",
value: "new value",
key: "test-component",
}
]
}
Here are the relevant parts of my WebSite resource:
actions do
# Add a set of simple actions. You'll customize these later.
defaults [:read, :destroy]

update :update do
argument :components, {:array, :map}

# I'm not sure what options to pass here.
# These current options I got from looking at the AshGraphql tests
change manage_relationship(
:components,
type: :direct_control,
on_lookup: :relate,
on_no_match: :create
)
end
end

managed_relationships do
managed_relationship :create, :components

# I'm not sure what options to pass here
managed_relationship :update, :components
# managed_relationship :update, :components, lookup_with_primary_key?: true
end
actions do
# Add a set of simple actions. You'll customize these later.
defaults [:read, :destroy]

update :update do
argument :components, {:array, :map}

# I'm not sure what options to pass here.
# These current options I got from looking at the AshGraphql tests
change manage_relationship(
:components,
type: :direct_control,
on_lookup: :relate,
on_no_match: :create
)
end
end

managed_relationships do
managed_relationship :create, :components

# I'm not sure what options to pass here
managed_relationship :update, :components
# managed_relationship :update, :components, lookup_with_primary_key?: true
end
I tried playing with the options both for change manage_relationship() and managed_relationship(). I got different errors. I'm not quite understanding the documentation yet.
55 replies
AEAsh Elixir
Created by moxley on 3/22/2023 in #support
AshGraphql Manage Relationships with multiple actions
Hello, I'm using the AshGraphql managed_relationships DSL to allow a GraphQL mutation to update relationship data on a resource. I works fine with the :create action, but on adding the similar code for the :update action, it gives errors. Here are the relevant parts of the resource module:
defmodule GF.WebSite do
actions do
# Add a set of simple actions. You'll customize these later.
defaults [:read, :destroy]

create :create do
argument :components, {:array, :map}
change manage_relationship(:components, type: :direct_control)
end

create :update do
argument :components, {:array, :map}
change manage_relationship(:components, type: :direct_control)
end
end

graphql do
type :web_site

mutations do
create :create_web_site, :create
update :update_web_site, :update
end

managed_relationships do
managed_relationship :create, :components
managed_relationship :update, :components
end
end
defmodule GF.WebSite do
actions do
# Add a set of simple actions. You'll customize these later.
defaults [:read, :destroy]

create :create do
argument :components, {:array, :map}
change manage_relationship(:components, type: :direct_control)
end

create :update do
argument :components, {:array, :map}
change manage_relationship(:components, type: :direct_control)
end
end

graphql do
type :web_site

mutations do
create :create_web_site, :create
update :update_web_site, :update
end

managed_relationships do
managed_relationship :create, :components
managed_relationship :update, :components
end
end
With the code above, there is a compiler error, Type name "CreateWebSiteComponentsInput" is not unique.. What do I need to change to make this work? I read the documentation about Graphql managed relationships. It's not all clicking yet for me. https://ash-hq.org/docs/dsl/ash-resource#graphql-managed_relationships
19 replies
AEAsh Elixir
Created by moxley on 3/19/2023 in #support
Using AshGraphql to fetch a single item without providing arguments
My app uses attribute-based multitenancy. There is a resource called WebSite, and the tenant would normally have only one of these. I am trying to create a GraphQL query called getWebSite that returns the tenant's WebSite, but the query response has the error, "In argument "id": Expected type "ID!", found null." I don't want the query to take any arguments. Here is the test that reproduces there error:
# This returns an error
query = """
query GetWebSite {
getWebSite {
id
}
}
"""

resp_body =
conn
|> set_authorized_user(user)
|> set_org(org) # tenant
|> put_req_header("content-type", "application/json")
|> post("/api/gql", query: query)
|> json_response(200)

# resp_body:
# %{"errors" => [%{"locations" => [%{"column" => 3, "line" => 2}], "message" => "In argument \"id\": Expected type \"ID!\", found null."}]}
# This returns an error
query = """
query GetWebSite {
getWebSite {
id
}
}
"""

resp_body =
conn
|> set_authorized_user(user)
|> set_org(org) # tenant
|> put_req_header("content-type", "application/json")
|> post("/api/gql", query: query)
|> json_response(200)

# resp_body:
# %{"errors" => [%{"locations" => [%{"column" => 3, "line" => 2}], "message" => "In argument \"id\": Expected type \"ID!\", found null."}]}
Here are the relevant parts of the WebSite resource:
actions do
read :get_web_site do
prepare build(limit: 1)
end
end

graphql do
type :web_site

queries do
get :get_web_site, :get_web_site
end
end
actions do
read :get_web_site do
prepare build(limit: 1)
end
end

graphql do
type :web_site

queries do
get :get_web_site, :get_web_site
end
end
How do I solve this?
8 replies
AEAsh Elixir
Created by moxley on 3/17/2023 in #support
How to manage relationships with AshGraphql
I have a Resource called WebSite that has_many :components, and each is a WebComponent. This test failed:
test "success creating a WebSite", ctx do
input = %{
attrs: [%{title: "Test Site", key: "test_site"}],
components: [%{type: "html", value: "test"}]
}

resp_body = post_gql(ctx, query: @create_web_site, variables: %{input: input})

%{
"data" => %{
"createWebSite" => %{
"result" => result,
# Argument "input" has invalid value $input. In field "components": Expected type "[JsonString!]"
"errors" => []
}
}
} = resp_body
end
test "success creating a WebSite", ctx do
input = %{
attrs: [%{title: "Test Site", key: "test_site"}],
components: [%{type: "html", value: "test"}]
}

resp_body = post_gql(ctx, query: @create_web_site, variables: %{input: input})

%{
"data" => %{
"createWebSite" => %{
"result" => result,
# Argument "input" has invalid value $input. In field "components": Expected type "[JsonString!]"
"errors" => []
}
}
} = resp_body
end
In the list of components in the input, does each component need to be individually JSON-encoded? Apparently it does, because when I encode the component, the test passes. Why would this need to be JSON-encoded? Is this documented somewhere? I didn't see mention of it.
5 replies
AEAsh Elixir
Created by moxley on 3/17/2023 in #support
I'm trying to understand how to use attribute-based multitenancy with AshGraphql
1. The documentation says to set up a multitenancy block in your resource module, and add a strategy and attribute. Check:
multitenancy do
strategy :attribute
attribute :org_id
end
multitenancy do
strategy :attribute
attribute :org_id
end
2. The documentation says to pass the tenant to the conn by calling Ash.PlugHelpers.set_tenant/2. Check:
def call(conn, _opts) do
...
conn
|> Ash.PlugHelpers.set_tenant(org)
|> Ash.PlugHelpers.set_actor(session_resource)
end
def call(conn, _opts) do
...
conn
|> Ash.PlugHelpers.set_tenant(org)
|> Ash.PlugHelpers.set_actor(session_resource)
end
Also, I inspected the above call to ensure it's being called correctly in my test. So now in my test, I call the "create" mutation for my resource, and it returns the error "GF.Ash.WebComponent changesets require a tenant to be specified" What am I missing?
57 replies
AEAsh Elixir
Created by moxley on 3/17/2023 in #support
AshGraphql :create mutation returns empty result
I wrote a test for a GraphQL query that was passing, and the response has a nil result object. Details to follow...
5 replies