Blibs
Blibs
Explore posts from servers
AEAsh Elixir
Created by Blibs on 9/19/2023 in #support
AshPhoenix.Form fails to submit with Ash.Error.Query.InvalidFilterValue error
Hey there, I'm not sure exactly what is happenig, but I have a embedded resource that was inserted into my resource, when I try to update it via AshPhoenix, by first calling AshPhoenix.Form.remove_form and then AshPhoenix.Form.submit, I get this error:
[warning] Unhandled error in form submission for Marketplace.Markets.Property.update

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

** (Ash.Error.Query.InvalidFilterValue) Invalid filter value `nil` supplied in `#Ecto.Changeset<action: nil, changes: %{updated_at: ~U[2023-09-19 00:18:43.738483Z], off_market: nil, processing_status: :running}, errors: [], data: #Marketplace.Markets.Property<>, valid?: true>`
[warning] Unhandled error in form submission for Marketplace.Markets.Property.update

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

** (Ash.Error.Query.InvalidFilterValue) Invalid filter value `nil` supplied in `#Ecto.Changeset<action: nil, changes: %{updated_at: ~U[2023-09-19 00:18:43.738483Z], off_market: nil, processing_status: :running}, errors: [], data: #Marketplace.Markets.Property<>, valid?: true>`
Any idea on what can be the issue here? I'm trying to create a small example that triggers this but without too much success.
13 replies
AEAsh Elixir
Created by Blibs on 9/15/2023 in #support
AshPhoenix.Form fails in submit without errors with embedded resources
I have a embedded resource like this:
defmodule Marketplace.Markets.Property.OffMarket do
@moduledoc false

use Ash.Resource,
data_layer: :embedded,
extensions: [AshGraphql.Resource]

code_interface do
define_for Marketplace.Markets

define :new
end

attributes do
alias Marketplace.Ash.Types.PhoneNumber

attribute :agent_name, :string
attribute :agent_phone_number, PhoneNumber

attribute :company_name, :string, allow_nil?: false
attribute :company_phone_number, PhoneNumber
end

graphql do
type :off_market
end

actions do
defaults [:update, :read, :destroy]

create :new, primary?: true
end
end
defmodule Marketplace.Markets.Property.OffMarket do
@moduledoc false

use Ash.Resource,
data_layer: :embedded,
extensions: [AshGraphql.Resource]

code_interface do
define_for Marketplace.Markets

define :new
end

attributes do
alias Marketplace.Ash.Types.PhoneNumber

attribute :agent_name, :string
attribute :agent_phone_number, PhoneNumber

attribute :company_name, :string, allow_nil?: false
attribute :company_phone_number, PhoneNumber
end

graphql do
type :off_market
end

actions do
defaults [:update, :read, :destroy]

create :new, primary?: true
end
end
This embedded resource is used as an attribute inside my Property resource like so: attribute :off_market, OffMarket, allow_nil?: true I'm having problems when trying to submit an update to it using AshPhoenix.Form.submit, when I run this, it will fail with a {:error, form} reply but there will be no error inside the form. This is how I initialize the form:
AshPhoenix.Form.for_update(property, :update,
api: Markets,
actor: user,
forms: [auto?: true],
prepare_source: fn changeset ->
changeset
|> Ash.Changeset.set_argument(:uploaded_images, [])
|> Ash.Changeset.set_argument(:removed_images, [])
end
)
AshPhoenix.Form.for_update(property, :update,
api: Markets,
actor: user,
forms: [auto?: true],
prepare_source: fn changeset ->
changeset
|> Ash.Changeset.set_argument(:uploaded_images, [])
|> Ash.Changeset.set_argument(:removed_images, [])
end
)
After this, I just run the submit function. An I doing something wrong here?
49 replies
AEAsh Elixir
Created by Blibs on 9/10/2023 in #support
AshAuthentication missing notifications when running Expunger
I noticed that, for time to time, AshAuthentication Expunger is running and it will generate a warning because of a missing nofitication for removing tokens. Here is one of these warnings with the full stacktrace:
8 replies
AEAsh Elixir
Created by Blibs on 8/29/2023 in #support
GraphQL API fails if custom type is not allowed via a field_policy
I noticed that if I ask my graphQL API to return a field that is forbidden, if the field is built-in, it will just return the field as null, all the other fields normally (assuming they are not forbidden as-well) and an error in the errors list telling that this field is forbidden. But, if I try the same with a custom type, then I just get the full result as null:
{
"data": {
"listValidProperties": {
"results": null
}
},
"errors": [
{
"code": "forbidden_field",
"message": "forbidden field",
"path": [
"listValidProperties",
"results",
0,
"price"
],
"fields": [],
"vars": {},
"locations": [
{
"line": 5,
"column": 4
}
],
"short_message": "forbidden field"
}
]
}
{
"data": {
"listValidProperties": {
"results": null
}
},
"errors": [
{
"code": "forbidden_field",
"message": "forbidden field",
"path": [
"listValidProperties",
"results",
0,
"price"
],
"fields": [],
"vars": {},
"locations": [
{
"line": 5,
"column": 4
}
],
"short_message": "forbidden field"
}
]
}
8 replies
AEAsh Elixir
Created by Blibs on 8/27/2023 in #support
How to use fragments and not built-in predicates in Ash.Filter keyword list syntax?
The documentation only shows how to use the Ash.Filter keyword list syntax with built-in predicates, how can I use it with fragments or non-built-in predicates? For example, this filter:
Ash.Query.filter(query, expr(fragment("(? <% ?)", :name, "bla")))
Ash.Query.filter(query, expr(fragment("(? <% ?)", :name, "bla")))
How can I write an equivalent using the keyword list syntax?
7 replies
AEAsh Elixir
Created by Blibs on 8/27/2023 in #support
parse filter with fields from another resource relationship
I'm using Ash.Filter.parse/2 to generate filters using the list syntax, for example:
Ash.Filter.parse(Template, [
or: [
[male_content: [not_equals: "hue"]],
[male_content: [equals: "hua"]]
]
])
Ash.Filter.parse(Template, [
or: [
[male_content: [not_equals: "hue"]],
[male_content: [equals: "hua"]]
]
])
Now, let's say that my Template resource has relationship with another resource School and I want to do a filter expression like this: school.name == "bla" I'm not sure how to express that I'm accessing the name field inside the school relationship field. I tried something like this:
Ash.Filter.parse(Template, [{"school.name", "bla"}])
Ash.Filter.parse(Template, [{"school.name", "bla"}])
But this doesn't work, and at the same time I can't find examples with relationships in the documentation.
2 replies
AEAsh Elixir
Created by Blibs on 8/18/2023 in #support
Create action commits changes even if return fails due to field policy
Hey @Zach Daniel and @barnabasj j, I noticed a behavior that I'm not sure if it is a rbac bug, an Ash bug or if it is not a bug at all and is by design. I have a resource that I forgot to add the fields field policy inside my rbac rules. Because of that, when I run the create action from that resource, it will fail with this error when returning the value:
[error] FeedbackCupcake.SmartSentences.SmartSentence.read
Policy Breakdown for selecting or loading fields fields: [:category, :sentiment, :male_content, :female_content, :teacher_id]
Bypass: Policy | 🔎:
authorize if: Checks if the actor has the role :admin for field :roles | ? | 🔎
Policy | ⛔:
forbid if: always true | ✓ | ⛔
[error] FeedbackCupcake.SmartSentences.SmartSentence.read
Policy Breakdown for selecting or loading fields fields: [:category, :sentiment, :male_content, :female_content, :teacher_id]
Bypass: Policy | 🔎:
authorize if: Checks if the actor has the role :admin for field :roles | ? | 🔎
Policy | ⛔:
forbid if: always true | ✓ | ⛔
This is expected since I don't have a policy for the fields, and adding something like fields [:*] fixes it. The problem is that even if my policy fails as shown above, the create function actually ran correctly, and it did create the row inside the DB. For me this seems odd since I would expect the create and read actions to be inside the same transaction, meaning that if there is a policy error when returning the value, the whole transaction should fail (rollback).
9 replies
AEAsh Elixir
Created by Blibs on 8/16/2023 in #support
use embedded fields inside expr
I have a resource that contains as one of its attributes another resource that is an embedded one.
defmodule Pacman.Markets.Property.Contact do
@moduledoc false

use Ash.Resource,
data_layer: :embedded

attributes do
attribute :full_address, :string do
constraints max_length: 255
end
end
end

defmodule Pacman.Markets.Property do
@moduledoc false

use Ash.Resource,
data_layer: AshPostgres.DataLayer

attributes do
alias Pacman.Markets.Property.Contact

attribute :contact, Contact

...
end
end
defmodule Pacman.Markets.Property.Contact do
@moduledoc false

use Ash.Resource,
data_layer: :embedded

attributes do
attribute :full_address, :string do
constraints max_length: 255
end
end
end

defmodule Pacman.Markets.Property do
@moduledoc false

use Ash.Resource,
data_layer: AshPostgres.DataLayer

attributes do
alias Pacman.Markets.Property.Contact

attribute :contact, Contact

...
end
end
When I try to access one of the embedded resource fields, ash will fail when I run the read action:
read :my_action do
filter expr(full_address == contact.full_address)
end
read :my_action do
filter expr(full_address == contact.full_address)
end
The error message will be "filter: Invalid reference contact.full_address at relationship_path [:contact]"
3 replies
AEAsh Elixir
Created by Blibs on 8/8/2023 in #support
Ash.Query.filter creates filter with OR condition instead of AND
Ash.Query.filter function documentation explicitly says that a filter will be added with the AND condition, but it seems that this is not the case when fragments is used. For example, if I do this:
import Ash.Query

FeedbackCupcake.Feedbacks.Template
|> Ash.Query.new()
|> Ash.Query.for_read(:read, %{})
|> Ash.Query.filter(expr(name == "a"))
|> Ash.Query.filter(expr(fragment("? %> ?", "a", "a")))
import Ash.Query

FeedbackCupcake.Feedbacks.Template
|> Ash.Query.new()
|> Ash.Query.for_read(:read, %{})
|> Ash.Query.filter(expr(name == "a"))
|> Ash.Query.filter(expr(fragment("? %> ?", "a", "a")))
This will correctly generate the following query:
#Ash.Query<
resource: FeedbackCupcake.Feedbacks.Template,
filter: #Ash.Filter<name == "a" and fragment(
{:raw, ""},
{:expr, "a"},
{:raw, " %> "},
{:expr, "a"},
{:raw, ""}
)>
>
#Ash.Query<
resource: FeedbackCupcake.Feedbacks.Template,
filter: #Ash.Filter<name == "a" and fragment(
{:raw, ""},
{:expr, "a"},
{:raw, " %> "},
{:expr, "a"},
{:raw, ""}
)>
>
But, if I add another fragment to the condition, like this:
import Ash.Query

FeedbackCupcake.Feedbacks.Template
|> Ash.Query.new()
|> Ash.Query.for_read(:read, %{})
|> Ash.Query.filter(expr(name == "a"))
|> Ash.Query.filter(expr(fragment("? %> ?", "a", "a")))
|> Ash.Query.filter(expr(fragment("? %> ?", "b", "b")))
import Ash.Query

FeedbackCupcake.Feedbacks.Template
|> Ash.Query.new()
|> Ash.Query.for_read(:read, %{})
|> Ash.Query.filter(expr(name == "a"))
|> Ash.Query.filter(expr(fragment("? %> ?", "a", "a")))
|> Ash.Query.filter(expr(fragment("? %> ?", "b", "b")))
Now it concatenates the last fragment with a OR:
#Ash.Query<
resource: FeedbackCupcake.Feedbacks.Template,
filter: #Ash.Filter<(name == "a" and fragment(
{:raw, ""},
{:expr, "a"},
{:raw, " %> "},
{:expr, "a"},
{:raw, ""}
)) or fragment(
{:raw, ""},
{:expr, "b"},
{:raw, " %> "},
{:expr, "b"},
{:raw, ""}
)>
>
#Ash.Query<
resource: FeedbackCupcake.Feedbacks.Template,
filter: #Ash.Filter<(name == "a" and fragment(
{:raw, ""},
{:expr, "a"},
{:raw, " %> "},
{:expr, "a"},
{:raw, ""}
)) or fragment(
{:raw, ""},
{:expr, "b"},
{:raw, " %> "},
{:expr, "b"},
{:raw, ""}
)>
>
From what I tested, I only was able to reproduce this when adding more than one fragment to the query.
11 replies
AEAsh Elixir
Created by Blibs on 8/8/2023 in #support
Generated filter expression is incorrect - BUG
Hello, I think I found a bug in Ash's query builder. I have the following filter expression in my resource action:
filter expr(
shared? == :everyone or
(shared? == :organization and organization_id == ^arg(:organization_id)) or
(shared? == :school and school_id == ^arg(:school_id))
)
filter expr(
shared? == :everyone or
(shared? == :organization and organization_id == ^arg(:organization_id)) or
(shared? == :school and school_id == ^arg(:school_id))
)
As you can see, this expression basically says "If shared? is everyone, return it, or, if shared? is organization and organization_id matches, return it, or, if shared? is school and school_id matches, return it". I was expecting the SQL generated from it being something like this:
WHERE (
"shared?" = 'everyone'
OR ("shared?" = 'organization' AND organization_id = null)
OR ("shared?" = 'school' AND school_id = null)
)
WHERE (
"shared?" = 'everyone'
OR ("shared?" = 'organization' AND organization_id = null)
OR ("shared?" = 'school' AND school_id = null)
)
But what Ash.Query generated was:
WHERE (
(organization_id = null AND "shared?" IN ('organization','everyone'))
OR ("shared?" = 'school' AND school_id = null)
)
WHERE (
(organization_id = null AND "shared?" IN ('organization','everyone'))
OR ("shared?" = 'school' AND school_id = null)
)
Note: I passed nil as arg(:organization_id) and arg(:school_id) in this case. Which is incorrect since when shared? is everyone, it will only return it if organization_id matches. What I believe happened here is that Ash.Query ignored the parenthesis in this part of the expression (shared? == :organization and organization_id == ^arg(:organization_id)) since if you remove it, then the generated where expression makes sense.
21 replies
AEAsh Elixir
Created by Blibs on 8/7/2023 in #support
Keyset pagination doesn't seem to use default_limit value
I have this action:
read :list_by_address do
argument :address, :string, allow_nil?: false

pagination keyset?: true, default_limit: 10, required?: true

filter expr(fragment("? %> ?", normalized_full_address, ^arg(:address)) and not deleted?)

prepare build(
sort: [
normalized_full_address_similarity: {:desc, %{address: arg(:address)}},
updated_at: :desc
]
)
end
read :list_by_address do
argument :address, :string, allow_nil?: false

pagination keyset?: true, default_limit: 10, required?: true

filter expr(fragment("? %> ?", normalized_full_address, ^arg(:address)) and not deleted?)

prepare build(
sort: [
normalized_full_address_similarity: {:desc, %{address: arg(:address)}},
updated_at: :desc
]
)
end
I expect that, if I call this action, it would add a LIMIT 10 to the generated SQL query. but it actually adds a LIMIT 251. Seems like the default_limit value is being ignored for some reason. If I run the query with page: [limit: 10] then it works fine.
4 replies
AEAsh Elixir
Created by Blibs on 7/31/2023 in #support
Add conditions to upsert
Is there any way to add conditionals when doing an upsert in a create action? For example, I have this resource:
defmodule Pacman.Markets.Blibs do
use Ash.Resource, data_layer: AshPostgres.DataLayer

code_interface do
define_for Pacman.Markets

define :create
end

attributes do
attribute :id, :uuid do
default &Ash.UUID.generate/0
primary_key? true
allow_nil? false
end

attribute :name, :string do
allow_nil? false

constraints max_length: 255
end

attribute :transaction_time, :integer, allow_nil?: false

attribute :deleted?, :boolean, allow_nil?: false, default: false

timestamps(private?: false)
end

postgres do
table "blibs"

repo Pacman.Repo

migration_types transaction_time: :bigint, name: {:varchar, 255}
end

actions do
defaults [:read, :destroy]

create :create do
primary? true

upsert? true
end
end
end
defmodule Pacman.Markets.Blibs do
use Ash.Resource, data_layer: AshPostgres.DataLayer

code_interface do
define_for Pacman.Markets

define :create
end

attributes do
attribute :id, :uuid do
default &Ash.UUID.generate/0
primary_key? true
allow_nil? false
end

attribute :name, :string do
allow_nil? false

constraints max_length: 255
end

attribute :transaction_time, :integer, allow_nil?: false

attribute :deleted?, :boolean, allow_nil?: false, default: false

timestamps(private?: false)
end

postgres do
table "blibs"

repo Pacman.Repo

migration_types transaction_time: :bigint, name: {:varchar, 255}
end

actions do
defaults [:read, :destroy]

create :create do
primary? true

upsert? true
end
end
end
14 replies
AEAsh Elixir
Created by Blibs on 7/26/2023 in #support
What is the __metadata__ field in new versions of Ash?
I just updated my Ash to the latest version 2.13.2. After that, I noticed that a lot of my unit tests stopped working basically because I was comparing resources. From what I can see, when I create the resource, the __metadata__ field will be a empty map %{}, but when I fetch the same resource with a read action, it will contain a key called selected in that map with the list of fields that were selected. Was this a new addition? Is there some way to disable that in tests so I can compare resources maps instead of having to process then before doing my asserts?
7 replies
AEAsh Elixir
Created by Blibs on 7/2/2023 in #support
How to return custom errors in a after_transaction call?
Let's say I have a change with the following implementation:
def change(changeset, _opts, _context),
do: Ash.Changeset.after_transaction(changeset, &do_change/2)

defp do_change(_changeset, _return) do
{:error, :something}
end
def change(changeset, _opts, _context),
do: Ash.Changeset.after_transaction(changeset, &do_change/2)

defp do_change(_changeset, _return) do
{:error, :something}
end
As you can see, my change just ignores everything and returns {:error, :something}. In other words, I want to create a change that in some conditions, I want to return an error with some custom data. The problem is that instead of Ash returning {:error, :something} directly, it returns:
{:error,
%Ash.Error.Unknown{
errors: [
%Ash.Error.Unknown.UnknownError{
error: "unknown error: :something",
field: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :unknown
}
],
stacktraces?: true,
changeset: #Ash.Changeset<
api: FeedbackCupcake.Payments,
action_type: :update,
action: :refresh,
attributes: %{},
relationships: %{},
arguments: %{create_payment_transaction?: true},
errors: [
%Ash.Error.Unknown.UnknownError{
error: "unknown error: :something",
field: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :unknown
}
],
data: #FeedbackCupcake.Payments.Customer<
...
>,
context: %{
...
>,
authorize?: false
},
valid?: true
>,
query: nil,
error_context: [nil],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :unknown
}}
{:error,
%Ash.Error.Unknown{
errors: [
%Ash.Error.Unknown.UnknownError{
error: "unknown error: :something",
field: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :unknown
}
],
stacktraces?: true,
changeset: #Ash.Changeset<
api: FeedbackCupcake.Payments,
action_type: :update,
action: :refresh,
attributes: %{},
relationships: %{},
arguments: %{create_payment_transaction?: true},
errors: [
%Ash.Error.Unknown.UnknownError{
error: "unknown error: :something",
field: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :unknown
}
],
data: #FeedbackCupcake.Payments.Customer<
...
>,
context: %{
...
>,
authorize?: false
},
valid?: true
>,
query: nil,
error_context: [nil],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :unknown
}}
So it actually converts :something into a string and returns it as a UnknownError. Is there some way to actually return :something directly or create a custom error that will have it as one of the fields?
11 replies
AEAsh Elixir
Created by Blibs on 7/2/2023 in #support
How to use relationship id in a `get_by` read action
My resource has this relationship:
belongs_to :customer, Customer do
allow_nil? true
attribute_writable? true
end
belongs_to :customer, Customer do
allow_nil? true
attribute_writable? true
end
This means that this resource table will contain a :customer_id field. Now I want to create a read action that will filter it by the :customer_id. I can do that by passing that as an argument and doing the filter manually, but I expected that I could use the get_by shortcut here, like this:
read :get_by_customer_id do
get_by :customer_id
end
read :get_by_customer_id do
get_by :customer_id
end
The problem is that this fails with Ash saying that there is not attribute, calculation or aggregate with that name.
5 replies
AEAsh Elixir
Created by Blibs on 6/25/2023 in #support
Why there is no `Ash.Changeset.around_transaction`?
I was wondering why there is no around_transaction function in Ash.Changeset. We already have a around_action option, but that one runs inside a transaction, meaning that I can't use it if I wan't to add something to the DB regardless if the action itself fails or not. I know that there is a before_transaction and after_transaction, but depending on what I'm doing this would not work. Just to give a more concrete example. I'm planning to port this into a Ash change:
def lock_and_transact(user, type, apply_function, update_function) do
Mutex.under(SubscribeMutex, user.uuid, fn ->
response =
case create_transaction(user, type) do
{:ok, _} ->
{:ok, response} = apply_function.()

response

{:error, _} ->
user = update_function(user)

{:error, :unfinished_transaction, handle_error!(type, user)}
end

delete_transaction!(user, type)

response
end)
end
def lock_and_transact(user, type, apply_function, update_function) do
Mutex.under(SubscribeMutex, user.uuid, fn ->
response =
case create_transaction(user, type) do
{:ok, _} ->
{:ok, response} = apply_function.()

response

{:error, _} ->
user = update_function(user)

{:error, :unfinished_transaction, handle_error!(type, user)}
end

delete_transaction!(user, type)

response
end)
end
The idea in this function is that I will first use Mutex to make sure that I always have only one code path reaching this code block at a time, after it, I create a transaction, which basically means that I have a transaction table and I store a row there for that user and type. I then run the apply_function, if that function doesn't return an error, it will call the delete_transaction! function that will remove the transaction and return the response, otherwise, it will just crash leaving the added transaction in the DB, this means that next time this code is called, it will fail to create a transaction and it will run the update_function instead. If I just wanted to create the transaction row in the DB, I would be able to create two changes, one with after_transaction(to create the row) and one with before_transaction (to delete it), but that doesn't work with the Mutex call, for the mutex I need something like around_action but for transactions.
15 replies
AEAsh Elixir
Created by Blibs on 6/24/2023 in #support
Identities split into multiple resources doesn't seem to work
Not sure if I'm doing something wrong, but I have 2 resources that uses the same table, User and Customer. In my User resource I have these identities:
identities do
identity :unique_email, [:email] do
eager_check_with FeedbackCupcake.Accounts
end

identity :unique_referral_code, [:referral_code]
end
identities do
identity :unique_email, [:email] do
eager_check_with FeedbackCupcake.Accounts
end

identity :unique_referral_code, [:referral_code]
end
And in my Customer resource, I have these identities:
identities do
identity :unique_customer_id, [:customer_id]
end
identities do
identity :unique_customer_id, [:customer_id]
end
For some reason, Ash just adds the indexes for the User resource in my PostgreSQL table, the one in Customer is simply ignored and doesn't show up in the migrations at all.
13 replies
AEAsh Elixir
Created by Blibs on 6/24/2023 in #support
Create SQL queries for migrations not related to a resource
Basically I want to be able to run something similar to a custom_statements but outside any resource, probably in a custom type or repo level. To be more specific, I'm porting this type https://github.com/elixirmoney/money/blob/v1.12.2/lib/money/ecto/composite_type.ex into a Ash.Type. For that type to work, I need to run execute "CREATE TYPE public.money_with_currency AS (amount integer, currency varchar(3))" in a migration file to actually create the custom type inside postgres. Of course, I can just create a migration file and add that line there manually, that would just work. But, if possible, I would like to do this thing in a more automatic way since that would prevent me from forgetting to add that migration when I copy that custom type to other projects. Is there any way to do that today? Or creating the migration manually is the only option?
4 replies
AEAsh Elixir
Created by Blibs on 6/24/2023 in #support
Atom fields will save upcase atoms as downcase
I noticed that if I add an attribute like this:
attribute :currency, :atom do
allow_nil? false

constraints one_of: [:USD, :BRL, ...]
end
attribute :currency, :atom do
allow_nil? false

constraints one_of: [:USD, :BRL, ...]
end
And then save a :USD or :BRL value to it, when I retrieve the record, the value will be retrieved in lower case (ex. :BRL will be returned as :brl). Is this by design? In my case I want to store the currencies supported by the Money library, but they are all in upper case, meaning that when i get them back, they are considered invalid by the Money library.
5 replies
AEAsh Elixir
Created by Blibs on 6/21/2023 in #support
What's the correct approach to update an actor inside an action
In my system, I have an Accounts.User resource that is normally sent to actions as the actor (this is also the resource used in AshAuthentication, etc). I also have another API called Payments and inside of it, it has a resource Payments.Customer. Both Accounts.User and Payments.User uses the same table, the just have some unique fields to it (mostly Accounts.User has info about authentication like password, email, etc, and Payments.User info about Stripe customer id, payments methods, etc). I also have another resources inside Payments, for example, Payments.Subscription. When an action from Payments.Subscription is called, it will be called with the Accounts.User as its resource. The thing is, besides some checks from Accounts.User (ex. if the user email is confirmed), these actions from the Payments API also need to check some things related to info only contained in the Payments.Customer resource (ex. if the user already have an Stripe's customer id set). The way that I was thinking about handling this is to pass Accounts.User as the actor and have policies that checks its fields (again, checking if the email is confirmed, etc), and then, inside the action, having, as the first change, a change that will get the current actor, fetch the Payments.Customer from it, and storing it as the new actor, and then doing the rest of checks (like if the user has a customer id) in subsequent changes. My first question in this case is: Is this the best approach for that scenario? Would you do that in some other way? My second question is: If that is the best approach, how can I update the action actor inside a change? I thought about using Ash.Change.set_context to set the new actor, but I'm not sure if that works (I can't test it right now).
11 replies