Knd_Lepe

Knd_Lepe

JSON API with Ash requires attributes from wrong action

I’m working on an email API service, Already got the Ash action to send email working in iex.
My send_email action is a custom action that will perfom some non standard CRUD. Got 2 create actions and the calls to api ask for attributes from wrong create action. As for now just trying to get the Ok atom

My resource

defmodule Eppa.Accounts.Servicios do
  use Ash.Resource,
    otp_app: :eppa,
    domain: Eppa.Accounts,
    data_layer: AshPostgres.DataLayer,
    extensions: [AshJsonApi.Resource]

  json_api do
    type "servicios"
  end

  postgres do
    table "servicios"
    repo Eppa.Repo

    references do
      reference :user, index?: true, on_delete: :delete
    end
  end

  actions do
    defaults [:read, :destroy]

    create :create do
      primary? true
      accept [:tipo, :creditos_disponibles, :vigencia]

      change relate_actor(:created_by, allow_nil?: true)
      change relate_actor(:updated_by, allow_nil?: true)
    end

    update :update do
      primary? true
      accept [:creditos_disponibles, :vigencia]

       change relate_actor(:updated_by, allow_nil?: true)
    end

    create :send_email do

      # Definición de argumentos que se esperan recibir vía JSON
      argument :user_email, :ci_string, allow_nil?: false
      argument :destinatario, :ci_string, allow_nil?: false
      argument :cc, {:array, :ci_string}, default: []
      argument :asunto, :string, allow_nil?: false
      argument :contenido_html, :ci_string, allow_nil?: false
      argument :attachments, {:array, :map}, default: []
      argument :plantilla_id, :ci_string, allow_nil?: true

      # Testing - "
      {:ok, "ok"}
    end
  end

  attributes do
    uuid_v7_primary_key :id

    attribute :tipo, :atom do
      constraints one_of: [:correos, :sms]
      allow_nil? false
      public? true
    end

    attribute :creditos_disponibles, :integer do
      allow_nil? false
      default 0
      public? true
    end

    attribute :vigencia, :datetime do
      allow_nil? false
      public? true
    end

    attribute :user_id, :uuid do
    end

    create_timestamp :inserted_at
    update_timestamp :updated_at
  end

  relationships do
    belongs_to :created_by, Eppa.Accounts.User
    belongs_to :updated_by, Eppa.Accounts.User

    belongs_to :user, Eppa.Accounts.User do
      allow_nil? false
    end
  end
end

My domain

defmodule Eppa.Accounts do
  use Ash.Domain, otp_app: :eppa, extensions: [AshJsonApi.Domain]

  json_api do
    routes do
      base_route "/servicios", Eppa.Accounts.Servicios do
        post :send_email
      end
     end
  end

  resources do
    resource Eppa.Accounts.Token

    resource Eppa.Accounts.User do
      define :create_user, action: :register_with_password
      define :update_user, action: :update_user
      define :get_users, action: :read
      define :get_user_by_id, action: :read, get_by: :id
      define :get_user_by_email, action: :read, get_by: :email
      define :get_users_with_services, action: :read
    end

    resource Eppa.Accounts.Servicios do
      define :get_servicios, action: :read
      define :create_servicio, action: :create
      define :send_email, action: :send_email
      define :get_servicio_by_id, action: :read, get_by: :id
    end

    resource Eppa.Accounts.Profile do
      define :create_perfil, action: :create
      define :update_perfil, action: :update
      define :get_profiles, action: :read
      define :get_perfil_by_id, action: :read, get_by: :id
    end
  end

  authorization do
    # disable using the authorize?: false flag when calling actions
    authorize :always
  end
end

Router

defmodule EppaWeb.Router do
  use EppaWeb, :router

  use AshAuthentication.Phoenix.Router

  import AshAuthentication.Plug.Helpers

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {EppaWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
    plug :load_from_session
  end

  pipeline :api do
    plug :accepts, ["json"]
    plug :load_from_bearer
    plug :set_actor, :user
  end



  scope "/api/json" do
    pipe_through [:api]

    forward "/swaggerui", OpenApiSpex.Plug.SwaggerUI,
      path: "/api/json/open_api",
      default_model_expand_depth: 4

    forward "/", EppaWeb.AshJsonApiRouter
  end

  scope "/", EppaWeb do
    pipe_through :browser

    ash_authentication_live_session :authenticated_routes do
      live "/dashboard", DashboardLive, :index

      live "/users", UserLive.Index, :index
      live "/users/new", UserLive.Index, :new
      live "/users/:id/edit", UserLive.Index, :edit

      live "/users/:id", UserLive.Show, :show
      live "/users/:id/show/edit", UserLive.Show, :edit
      live "/users/:id/assign_profile", UserLive.Index, :assign_profile
      live "/users/:id/servicios", EppaWeb.UserLive.Servicios, :index

      live "/profiles", ProfileLive.Index, :index
      live "/profiles/new", ProfileLive.Index, :new
      live "/profiles/:id/edit", ProfileLive.Index, :edit

      live "/profiles/:id", ProfileLive.Show, :show
      live "/profiles/:id/show/edit", ProfileLive.Show, :edit

      # live "/services", ServiceAssignmentLive.Index, :index
      # live "/services/new", ServiceAssignmentLive.Index, :new
      live "/services", ServicesLive.Index, :index
      live "/services/:id/creditos", ServiceLive.Creditos, :creditos

      # in each liveview, add one of the following at the top of the module:
      #
      # If an authenticated user must be present:
      # on_mount {EppaWeb.LiveUserAuth, :live_user_required}
      #
      # If an authenticated user *may* be present:
      # on_mount {EppaWeb.LiveUserAuth, :live_user_optional}
      #
      # If an authenticated user must *not* be present:
      # on_mount {EppaWeb.LiveUserAuth, :live_no_user}
    end
  end

  scope "/", EppaWeb do
    pipe_through :browser
    # live "/sign-in", SignInLive, :index
    get "/", RedirectController, :to_sign_in
    # get "/", PageController, :home
    auth_routes AuthController, Eppa.Accounts.User, path: "/auth"
    sign_out_route AuthController

    # Remove these if you'd like to use your own authentication views
    sign_in_route register_path: "/register",
                  reset_path: "/reset",
                  auth_routes_prefix: "/auth",
                  on_mount: [{EppaWeb.LiveUserAuth, :live_no_user}],
                  overrides: [EppaWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default]

    # Remove this if you do not want to use the reset password feature
    reset_route auth_routes_prefix: "/auth",
                overrides: [EppaWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default]
  end

  # Other scopes may use custom stacks.
  # scope "/api", EppaWeb do
  #   pipe_through :api
  # end

  # Enable LiveDashboard and Swoosh mailbox preview in development
  if Application.compile_env(:eppa, :dev_routes) do
    # If you want to use the LiveDashboard in production, you should put
    # it behind authentication and allow only admins to access it.
    # If your application does not have an admins-only section yet,
    # you can use Plug.BasicAuth to set up some basic authentication
    # as long as you are also using SSL (which you should anyway).
    import Phoenix.LiveDashboard.Router

    scope "/dev" do
      pipe_through :browser

      live_dashboard "/dashboard", metrics: EppaWeb.Telemetry
      forward "/mailbox", Plug.Swoosh.MailboxPreview
    end
  end
end

API CALL

{
	"data":{
		"type":"servicios",
			"attributes": {
				"user_email": "knd_rt@hotmail.com",
				"destinatario": "blitzlepe@gmail.com" ,
				"cc":[
					"cande.lepe@agustindeiturbide.com"
					],
				"asunto":"Prueba desde Insomnia",
				"contenido_html":"Su pinche madre, si jalo !!!",
				"attachments":[]
				
			}
	}
}

Response

{
	"errors": [
		{
			"code": "required",
			"id": "c5e78bdc-fc81-421e-a9b9-cf71c85256bb",
			"meta": {},
			"status": "400",
			"title": "Required",
			"source": {
				"pointer": "/data/attributes/tipo"
			},
			"detail": "is required"
		},
		{
			"code": "required",
			"id": "c6c953b2-0925-4ada-9c8f-fd682c248b99",
			"meta": {},
			"status": "400",
			"title": "Required",
			"source": {
				"pointer": "/data/attributes/vigencia"
			},
			"detail": "is required"
		}
	],
	"jsonapi": {
		"version": "1.0"
	}
}```

Marked As Solved

zachdaniel

zachdaniel

Creator of Ash

The function in after_action takes two arguments, Ash.Changeset.after_action(changeset, fn changeset, result -> end)

Where Next?

Popular in Questions Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
LegitStack
I’m trying to make a websocket server in Phoenix or raw Elixir. I heard about gun, I think I could use cowboy, but since I’m not that sma...
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53690 245
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

We're in Beta

About us Mission Statement