improvement: cache action known inputs individually

improvement: cache action required inputs all together
This commit is contained in:
Zach Daniel 2024-08-19 21:49:34 -04:00
parent 29e3a35d4c
commit 5fc5d32508
7 changed files with 55 additions and 29 deletions

View file

@ -15,6 +15,7 @@ spark_locals_without_parens = [
args: 1,
argument: 2,
argument: 3,
async?: 1,
atomic_upgrade?: 1,
atomic_upgrade_with: 1,
attribute: 1,

View file

@ -95,7 +95,7 @@ end
| [`generated?`](#attributes-attribute-generated?){: #attributes-attribute-generated? } | `boolean` | `false` | Whether or not the value may be generated by the data layer. |
| [`writable?`](#attributes-attribute-writable?){: #attributes-attribute-writable? } | `boolean` | `true` | Whether or not the value can be written to. Non-writable attributes can still be written with `Ash.Changeset.force_change_attribute/3`. |
| [`public?`](#attributes-attribute-public?){: #attributes-attribute-public? } | `boolean` | `false` | Whether or not the attribute should be shown over public interfaces. See the [sensitive data guide](/documentation/topics/security/sensitive-data.md) for more. |
| [`default`](#attributes-attribute-default){: #attributes-attribute-default } | `(-> any) \| mfa \| any` | | A value to be set on all creates, unless a value is being provided already. Note: The default value is casted according to the type's Ash.Type.* module, before it is saved. For `:string`, for example, if `constraints: [allow_empty?: _]` is false, the value `""` will be cast to `nil`. See the `:constraints` option, the `:allow_nil?` option, and the relevant `Ash.Type.*` documentation. |
| [`default`](#attributes-attribute-default){: #attributes-attribute-default } | `(-> any) \| mfa \| any` | | A value to be set on all creates, unless a value is being provided already. Note: The default value is casted according to the type's `Ash.Type.*` module, before it is saved. For `:string`, for example, if `constraints: [allow_empty?: _]` is false, the value `""` will be cast to `nil`. See the `:constraints` option, the `:allow_nil?` option, and the relevant `Ash.Type.*` documentation. |
| [`update_default`](#attributes-attribute-update_default){: #attributes-attribute-update_default } | `(-> any) \| mfa \| any` | | A value to be set on all updates, unless a value is being provided already. |
| [`filterable?`](#attributes-attribute-filterable?){: #attributes-attribute-filterable? } | `boolean \| :simple_equality` | `true` | Whether or not the attribute can be referenced in filters. |
| [`sortable?`](#attributes-attribute-sortable?){: #attributes-attribute-sortable? } | `boolean` | `true` | Whether or not the attribute can be referenced in sorts. |
@ -3340,6 +3340,7 @@ calculate :full_name, :string, expr(first_name <> " " <> last_name)
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`async?`](#calculations-calculate-async?){: #calculations-calculate-async? } | `boolean` | `false` | |
| [`constraints`](#calculations-calculate-constraints){: #calculations-calculate-constraints } | `keyword` | `[]` | Constraints to provide to the type. See `Ash.Type` for more. |
| [`description`](#calculations-calculate-description){: #calculations-calculate-description } | `String.t` | | An optional description for the calculation |
| [`public?`](#calculations-calculate-public?){: #calculations-calculate-public? } | `boolean` | `false` | Whether or not the calculation will appear in public interfaces. |

View file

@ -3117,30 +3117,12 @@ defmodule Ash.Changeset do
# Attributes that are private and/or are the source field of a belongs_to relationship
# are typically not set by input, so they aren't required until the actual action
# is run.
defp attributes_to_require(resource, _action, true) do
resource
|> Ash.Resource.Info.attributes()
|> Enum.reject(&(&1.allow_nil? || &1.generated?))
defp attributes_to_require(resource, _action, true = _final?) do
Ash.Resource.Info.attributes_to_require(resource)
end
defp attributes_to_require(resource, action, false) do
accept = action.accept
require_attributes = action.require_attributes
allow_nil_input = action.allow_nil_input
argument_names = action.arguments |> Enum.map(& &1.name)
accepted =
accept
|> Kernel.++(require_attributes)
|> Kernel.--(allow_nil_input)
|> Kernel.--(argument_names)
resource
|> Ash.Resource.Info.attributes()
|> Enum.reject(
&(&1.name not in accepted || !&1.writable? || &1.generated? ||
(&1.allow_nil? && &1.name not in require_attributes))
)
defp attributes_to_require(resource, action, false = _final?) do
Ash.Resource.Info.attributes_to_require(resource, action.name)
end
@doc """

View file

@ -351,6 +351,14 @@ defmodule Ash.Resource.Info do
)
end
def attributes_to_require(resource) do
Extension.get_persisted(resource, :attributes_to_require)
end
def attributes_to_require(resource, action_name) do
Extension.get_persisted(resource, {:attributes_to_require, action_name})
end
@doc "Get a public relationship by name or path"
def public_relationship(resource, [name]) do
public_relationship(resource, name)
@ -617,6 +625,8 @@ defmodule Ash.Resource.Info do
@spec action_input?(Ash.Resource.t(), action :: atom(), input :: atom() | String.t()) ::
boolean()
def action_input?(resource, action, input) do
# Extension.get_persisted(resource, {:action_inputs, action, input}) || false
case Extension.get_persisted(resource, {:action_inputs, action}) do
nil -> false
map_set -> input in map_set

View file

@ -5,18 +5,50 @@ defmodule Ash.Resource.Transformers.CacheActionInputs do
alias Spark.Dsl.Transformer
def transform(dsl_state) do
final_attributes_to_require =
dsl_state
|> Ash.Resource.Info.attributes()
|> Enum.reject(&(&1.allow_nil? || &1.generated?))
dsl_state =
Transformer.persist(dsl_state, :attributes_to_require, final_attributes_to_require)
dsl_state
|> Ash.Resource.Info.actions()
|> Enum.reject(&(&1.type in [:read, :action]))
|> Enum.reduce({:ok, dsl_state}, fn action, {:ok, dsl_state} ->
|> Enum.reduce(dsl_state, fn action, dsl_state ->
inputs =
action.arguments
|> Enum.map(& &1.name)
|> Enum.concat(action.accept)
|> Enum.flat_map(&[&1, to_string(&1)])
|> MapSet.new()
{:ok, Transformer.persist(dsl_state, {:action_inputs, action.name}, inputs)}
argument_names = action.arguments |> Enum.map(& &1.name)
accepted =
action.accept
|> Kernel.++(action.require_attributes)
|> Kernel.--(action.allow_nil_input)
|> Kernel.--(argument_names)
attributes_to_require_for_action =
dsl_state
|> Ash.Resource.Info.attributes()
|> Enum.reject(
&(&1.name not in accepted || !&1.writable? || &1.generated? ||
(&1.allow_nil? && &1.name not in action.require_attributes))
)
inputs
|> Enum.reduce(dsl_state, fn input, dsl_state ->
Transformer.persist(dsl_state, {:action_inputs, action.name, input}, true)
end)
|> Transformer.persist({:action_inputs, action.name}, MapSet.new(inputs))
|> Transformer.persist(
{:attributes_to_require, action.name},
attributes_to_require_for_action
)
end)
|> then(&{:ok, &1})
end
end

View file

@ -336,7 +336,7 @@ defmodule Ash.MixProject do
defp deps do
[
# DSLs
{:spark, "~> 2.1 and >= 2.2.19"},
{:spark, "~> 2.1 and >= 2.2.22"},
# Ash resources are backed by ecto scheams
{:ecto, "~> 3.7"},
# Used by the ETS data layer

View file

@ -18,7 +18,7 @@
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
"git_ops": {:hex, :git_ops, "2.6.1", "cc7799a68c26cf814d6d1a5121415b4f5bf813de200908f930b27a2f1fe9dad5", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "ce62d07e41fe993ec22c35d5edb11cf333a21ddaead6f5d9868fcb607d42039e"},
"glob_ex": {:hex, :glob_ex, "0.1.8", "f7ef872877ca2ae7a792ab1f9ff73d9c16bf46ecb028603a8a3c5283016adc07", [:mix], [], "hexpm", "9e39d01729419a60a937c9260a43981440c43aa4cadd1fa6672fecd58241c464"},
"igniter": {:hex, :igniter, "0.3.19", "dfa4a05e94be72e9b75c9ce73b9ce408c9a5fe8f1f010eb36e711e70da698b46", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:owl, "~> 0.9", [hex: :owl, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}, {:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: false]}], "hexpm", "f9b1ca6d11a7dcd1beb0bc0a1c08b8ce1fb4eb140a763037f3b79d9170cbf958"},
"igniter": {:hex, :igniter, "0.3.20", "2af4b7f20ba21d3f2c304b51b4b337dc9d971cd2325932758db88ddaa7db308a", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:owl, "~> 0.9", [hex: :owl, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}, {:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: false]}], "hexpm", "8066b23e120b676c57cb2b6a1a46bb2a4a188476badeebfe6283faadd58a78c1"},
"inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
"iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
@ -41,7 +41,7 @@
"simple_sat": {:hex, :simple_sat, "0.1.3", "f650fc3c184a5fe741868b5ac56dc77fdbb428468f6dbf1978e14d0334497578", [:mix], [], "hexpm", "a54305066a356b7194dc81db2a89232bacdc0b3edaef68ed9aba28dcbc34887b"},
"sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"},
"sourceror": {:hex, :sourceror, "1.6.0", "9907884e1449a4bd7dbaabe95088ed4d9a09c3c791fb0103964e6316bc9448a7", [:mix], [], "hexpm", "e90aef8c82dacf32c89c8ef83d1416fc343cd3e5556773eeffd2c1e3f991f699"},
"spark": {:hex, :spark, "2.2.21", "b343f3488b5a986ad38d15ac124b9111b8f63f953e3a6ef3fad44fe129b7fad6", [:mix], [{:igniter, ">= 0.2.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "45b05c52a1afe4858e10b5e6b8cd33bff3cf0098afc144146c5ff05002d75a9d"},
"spark": {:hex, :spark, "2.2.22", "abb5ba74ed8b8a69f8d3112fe0d74a1dea261664d9a3bcaf2d0f94f9ee7102f6", [:mix], [{:igniter, ">= 0.2.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "98b6ea8c19fe97b2b7b20be034ae6cf34e98b03ecba8b7d5a4cc2449f60f3f5e"},
"spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"},
"splode": {:hex, :splode, "0.2.4", "71046334c39605095ca4bed5d008372e56454060997da14f9868534c17b84b53", [:mix], [], "hexpm", "ca3b95f0d8d4b482b5357954fec857abd0fa3ea509d623334c1328e7382044c2"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},