# SPDX-FileCopyrightText: 2020 ash_phoenix contributors # # SPDX-License-Identifier: MIT defmodule AshPhoenix do @moduledoc """ An extension to add form builders to the code interface. There is currently no DSL for this extension. This defines a `form_to_` function for each code interface function. Arguments are processed according to any custom input transformations defined on the code interface, while the `params` option remains untouched. The generated function passes all options through to `AshPhoenix.Form.for_action/3` Update and destroy actions take the record being updated/destroyed as the first argument. For example, given this code interface definition on a domain called `MyApp.Accounts`: ```elixir resources do resource MyApp.Accounts.User do define :register_with_password, args: [:email, :password] define :update_user, action: :update, args: [:email, :password] end end ``` Adding the `AshPhoenix` extension would define `form_to_register_with_password/2`. ## Custom Input Transformations If your code interface defines custom inputs with transformations, the form interface will honor those transformations for arguments, but not for params passed via the `params` option: ```elixir # In your domain resource MyApp.Blog.Comment do define :create_with_post do action :create_with_post_id args [:post] custom_input :post, :struct do constraints instance_of: MyApp.Blog.Post transform to: :post_id, using: & &1.id end end end # Usage - the post argument will be transformed form = MyApp.Blog.form_to_create_with_post( %MyApp.Blog.Post{id: "some-id"}, params: %{"text" => "Hello world"} ) # The post struct gets transformed to post_id in the form # The params remain unchanged ``` ## Usage Without options: ```elixir MyApp.Accounts.form_to_register_with_password() #=> %AshPhoenix.Form{} ``` With options: ```elixir MyApp.Accounts.form_to_register_with_password(params: %{"email" => "placeholder@email"}) #=> %AshPhoenix.Form{} ``` For update/destroy actions, the record is required as the first parameter: ```elixir user = MyApp.Accounts.get_user!(id) MyApp.Accounts.form_to_update_user(user) #=> %AshPhoenix.Form{} ``` Update/destroy with options ```elixir user = MyApp.Accounts.get_user!(id) MyApp.Accounts.form_to_update_user(user, params: %{"email" => "placeholder@email"}) #=> %AshPhoenix.Form{} ``` """ defmodule FormDefinition do @moduledoc "A customized form code interface" defstruct [:name, :args, :__spark_metadata__] end @form %Spark.Dsl.Entity{ name: :form, target: FormDefinition, describe: "Customize the definition of a form for a code inteface", examples: [ """ # customize the generated `form_to_create_student` function # args defaults to empty for form definitions form :create_student, args: [:school_id] """ ], args: [:name], schema: [ name: [ type: :atom, doc: "The name of the interface to modify. Must match an existing interface definition." ], args: [ type: {:list, {:or, [:atom, {:tagged_tuple, :optional, :atom}]}}, doc: "Map specific arguments to named inputs. Can provide any argument/attributes that the action allows." ] ] } @forms %Spark.Dsl.Section{ name: :forms, describe: "Customize the definition of forms for code interfaces", examples: [ """ forms do # customize the generated `form_to_create_student` function form :create_student, args: [:school_id] end """ ], entities: [ @form ] } use Spark.Dsl.Extension, verifiers: [AshPhoenix.Verifiers.VerifyFormDefinitions], transformers: [AshPhoenix.Transformers.AddFormCodeInterfaces], sections: [@forms] end