actions do
# The public?: true MUST be an option to the read macro
read :get_tenant, public?: true do
get? true
primary? true
argument :id, :uuid, allow_nil?: false
filter expr(id == ^arg(:id))
end
# ... other actions
end
get_teant wont compile. Any help will be appreciated
In general you’d need to provide more information than this (like what the error message you’re getting is). However, what you’re asking for is not supported. There is no public?: true option on actions. It hasn’t been added: Allow marking actions as internal to prevent exposing them via APIs · Issue #1271 · ash-project/ash · GitHub
Thanks Zack for your time.
below is the error == Compilation error in file lib/eloni/accounts/tenant.ex ==
** (CompileError) lib/eloni/accounts/tenant.ex: cannot compile module Eloni.Accounts.Tenant (errors have been logged)
(ash 3.15.0) expanding macro: Ash.Resource.Dsl.actions/1
and heres my tenant resource:
defmodule Eloni.Accounts.Tenant do
use Ash.Resource,
otp_app: :eloni,
domain: Eloni.Accounts,
data_layer: AshPostgres.DataLayer,
authorizers: [Ash.Policy.Authorizer]
postgres do
table “tenants”
repo Eloni.Repo
end
attributes do
uuid_primary_key :id, default: &Ash.UUID.generate/0
attribute :name, :string, allow_nil?: false
attribute :national_identity, :string, allow_nil?: false
attribute :nationality, :string, allow_nil?: false, default: “Nigerian”
attribute :tenant_type, :atom do
allow_nil? false
constraints one_of: [:seller, :wholesale_market, :cluster_manager]
default :seller
end
attribute :currency_code, :atom do
allow_nil? false
default :NGN
end
attribute :slug, :string, allow_nil?: false
timestamps()
end
relationships do
belongs_to :owner, Eloni.Accounts.User
belongs_to :lga, Eloni.Locations.Lga
has_one :tenant_profile, Eloni.Accounts.TenantProfile
has_many :memberships, Eloni.Accounts.UserTenant
many_to_many :users, Eloni.Accounts.User do
through Eloni.Accounts.UserTenant
source_attribute_on_join_resource :tenant_id
destination_attribute_on_join_resource :user_id
end
# Sellers can belong to a Cluster
belongs_to :parent_cluster, Eloni.Accounts.Tenant do
domain Eloni.Accounts
attribute_type :uuid
end
# Cluster Managers can have many Sellers
has_many :managed_sellers, Eloni.Accounts.Tenant do
destination_attribute :parent_cluster_id
end
end
identities do
identity :unique_tenant, [:name, :national_identity]
end
actions do
defaults [:read, :destroy, update: :*]
# Internal primary read (no arguments, no filters)
read :read do
primary? true
end
# Your public interface action
read :get_tenant, public?: true do
get? true
argument :id, :uuid, allow_nil?: false
filter expr(id == ^arg(:id))
end
create :register do
accept [:name, :national_identity, :nationality, :tenant_type, :slug]
argument :owner_id, :uuid, allow_nil?: false
# This triggers the ‘UserTenant’ primary create action
change manage_relationship(:owner_id, :users, type: :append)
end
update :invite_user do
argument :user_id, :uuid, allow_nil?: false
# manage_relationship handles the creation of the UserTenant row
change manage_relationship(:user_id, :users, type: :append)
end
update :add_user do
# Accept a map like %{user_id: “…”, role: “admin”}
argument :user_data, :map
require_atomic? false
# join_keys tells Ash which fields to set on the join table
change manage_relationship(:user_data, :users,
type: :append,
join_keys: [:role, :joined_at]
)
end
update :add_seller do
# Accepts the UUID of the seller tenant to be managed
argument :seller_id, :uuid, allow_nil?: false
# Logic: Find the seller by ID and set its parent_cluster_id to THIS cluster
change manage_relationship(:seller_id, :managed_sellers, type: :append, on_lookup: :relate)
end
end
policies do
# 1. ALLOW the Plug/Cache to fetch basic tenant info (No actor required)
# Lock this to ONLY the :get_tenant action for security
policy action(:get_tenant) do
authorize_if always()
end
# 2. REQUIRE membership for other general reads
policy action_type(:read) do
authorize_if relates_to_actor_via(:users)
end
# 3. Super Admin bypass
bypass actor_attribute_equals(:system_role, :super_admin), do: authorize_if always()
end
end
i want an interface to get_tenant by other operations in my project. heres my domain if that helps
defmodule Eloni.Accounts do
use Ash.Domain,
otp_app: :eloni
resources do
resource Eloni.Accounts.User
resource Eloni.Accounts.Token
resource Eloni.Accounts.Tenant do
# # This creates the Eloni.Accounts.get_tenant!(id, opts) function
define :get_tenant, action: :get_tenant, args: [:id]
end
resource Eloni.Accounts.TenantProfile
resource Eloni.Accounts.UserTenant
end
end
Thanks once more
It’s right there. Also please format your stuff with tripple backticks. ```
Thanks but cant figure it still.
i came by this option, because i need to extract tenant currency….heres my plug
defmodule EloniWeb.SetAshTenantContext do
@moduledoc “”"
Sets the Ash actor, tenant, and custom currency context for the request.
“”"
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
user = conn.assigns[:current_user]
tenant_id = get_session(conn, :current_tenant_id) || conn.assigns[:current_tenant_id]
# 1. Set the Actor (Essential for Policies)
if user, do: Ash.PlugHelpers.set_actor(conn, user)
# 2. Set the Tenant (Essential for Data Isolation)
if tenant_id do
Ash.PlugHelpers.set_tenant(conn, tenant_id)
# 3. Set Custom Context for your Currency Calculations
# This allows your ‘ConvertCurrency’ calculation to access the tenant’s default
currency = Eloni.Cache.get_tenant_currency(tenant_id) || :NGN
Ash.PlugHelpers.set_context(conn, %{tenant_currency: currency})
end
conn
end
end
Maybe my design is the challenge.
i want to add the tenant currency to the tenant context. the tenant must be in that context for it to work.
I think basic elixir understanding is your challenge. You need to know elixir at a basic level before you can dive into ash.
Check out exercism for elixir exercises.
Really? Thanks for your time. Will lookup exercism






















