Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions documentation/dsls/DSL-AshJsonApi.Resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ end
| [`paginated_includes`](#json_api-paginated_includes){: #json_api-paginated_includes } | `list(atom \| list(atom))` | `[]` | A list of relationship paths that can be paginated when included via the `included_page` query parameter. Each entry can be either an atom (for top-level relationships) or a list of atoms (for nested paths). |
| [`include_nil_values?`](#json_api-include_nil_values?){: #json_api-include_nil_values? } | `any` | | Whether or not to include properties for values that are nil in the JSON output |
| [`default_fields`](#json_api-default_fields){: #json_api-default_fields } | `list(atom)` | | The fields to include in the object if the `fields` query parameter does not specify. Defaults to all public |
| [`hide_fields`](#json_api-hide_fields){: #json_api-hide_fields } | `list(atom)` | `[]` | A list of fields to hide from the generated OpenAPI specification. Applies to attributes, relationships, calculations, and aggregates. |
| [`show_fields`](#json_api-show_fields){: #json_api-show_fields } | `list(atom)` | | A list of fields to show in the generated OpenAPI specification. If not specified, all public fields are shown except those in `hide_fields`. |
| [`hide_fields`](#json_api-hide_fields){: #json_api-hide_fields } | `list(atom)` | `[]` | A list of fields to hide from generated API specifications and JSON:API responses. Applies to attributes, relationships, calculations, and aggregates. |
| [`show_fields`](#json_api-show_fields){: #json_api-show_fields } | `list(atom)` | | A list of fields to show in generated API specifications and JSON:API responses. If not specified, all public fields are shown except those in `hide_fields`. |
| [`derive_sort?`](#json_api-derive_sort?){: #json_api-derive_sort? } | `boolean` | `true` | Whether or not to derive a sort parameter based on the sortable fields of the resource |
| [`derive_filter?`](#json_api-derive_filter?){: #json_api-derive_filter? } | `boolean` | `true` | Whether or not to derive a filter parameter based on the sortable fields of the resource |
| [`relationship_meta_in`](#json_api-relationship_meta_in){: #json_api-relationship_meta_in } | `keyword` | `[]` | Configures how incoming JSON:API `meta` keys on relationship resource identifiers map to join resource attributes for many_to_many relationship writes. Use together with `relationship_meta_out` for reads. Each relationship you want to support must declare both mappings explicitly. |
Expand Down
11 changes: 10 additions & 1 deletion lib/ash_json_api/domain/persisters/define_router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ defmodule AshJsonApi.Domain.Persisters.DefineRouter do
alias Spark.Dsl.Transformer

def transform(dsl) do
routes = AshJsonApi.Domain.Info.routes(dsl)
routes =
dsl
|> AshJsonApi.Domain.Info.routes()
|> Enum.filter(&route_visible?/1)

route_matchers =
routes
Expand Down Expand Up @@ -45,6 +48,12 @@ defmodule AshJsonApi.Domain.Persisters.DefineRouter do
)}
end

defp route_visible?(%{relationship: nil}), do: true

defp route_visible?(%{resource: resource, relationship: relationship}) do
AshJsonApi.Resource.Info.show_field?(resource, relationship)
end

# sobelow_skip ["DOS.StringToAtom"]
def route_match(route) do
split_route = String.split(route.route, "/", trim: true)
Expand Down
39 changes: 39 additions & 0 deletions lib/ash_json_api/includes/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,48 @@ defmodule AshJsonApi.Includes.Parser do
defp allowed_preloads(resource) do
resource
|> AshJsonApi.Resource.Info.includes()
|> filter_hidden_includes(resource)
|> to_nested_map()
end

defp filter_hidden_includes(includes, resource) when is_list(includes) do
includes
|> Enum.flat_map(fn include ->
case filter_hidden_include(include, resource) do
nil -> []
include -> [include]
end
end)
end

defp filter_hidden_include({include, further}, resource) do
case public_relationship(resource, include) do
%{destination: destination} ->
{include, filter_hidden_includes(List.wrap(further), destination)}

nil ->
nil
end
end

defp filter_hidden_include(include, resource) do
if public_relationship(resource, include) do
include
end
end

defp public_relationship(resource, relationship_name) do
case Ash.Resource.Info.public_relationship(resource, relationship_name) do
%{name: name} = relationship ->
if AshJsonApi.Resource.Info.show_field?(resource, name) do
relationship
end

nil ->
nil
end
end

defp to_nested_map(list) when is_list(list) do
list
|> Enum.map(fn
Expand Down
27 changes: 26 additions & 1 deletion lib/ash_json_api/json_schema/json_schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ defmodule AshJsonApi.JsonSchema do
{new_refs, route_schemas} =
resource
|> AshJsonApi.Resource.Info.routes(domains)
|> Enum.filter(&route_visible?(resource, &1))
|> Enum.reduce({[], []}, fn route, {refs, route_schemas} ->
{new_refs, new_route_schema} =
route_schema(route, domain, resource, opts)
Expand Down Expand Up @@ -278,6 +279,7 @@ defmodule AshJsonApi.JsonSchema do
defp required_attributes(resource) do
resource
|> Ash.Resource.Info.public_attributes()
|> filter_shown_fields(resource)
|> Enum.reject(&(&1.allow_nil? || AshJsonApi.Resource.only_primary_key?(resource, &1.name)))
|> Enum.map(&AshJsonApi.Resource.Info.field_to_json_key(resource, &1.name))
end
Expand All @@ -290,6 +292,7 @@ defmodule AshJsonApi.JsonSchema do
Ash.Resource.Info.public_aggregates(resource)
|> set_aggregate_constraints(resource)
)
|> filter_shown_fields(resource)
|> Enum.reject(&AshJsonApi.Resource.only_primary_key?(resource, &1.name))
|> Enum.reduce(%{}, fn attr, acc ->
Map.put(
Expand Down Expand Up @@ -332,6 +335,7 @@ defmodule AshJsonApi.JsonSchema do
defp resource_relationships(resource) do
resource
|> Ash.Resource.Info.public_relationships()
|> filter_shown_fields(resource)
|> Enum.filter(fn relationship ->
AshJsonApi.Resource.Info.type(relationship.destination)
end)
Expand Down Expand Up @@ -719,6 +723,24 @@ defmodule AshJsonApi.JsonSchema do
action && action.type == :read
end

defp show_field?(resource, %{name: name}) do
AshJsonApi.Resource.Info.show_field?(resource, name)
end

defp show_field?(resource, field) do
AshJsonApi.Resource.Info.show_field?(resource, field)
end

defp filter_shown_fields(fields, resource) do
Enum.filter(fields, &show_field?(resource, &1))
end

defp route_visible?(_resource, %{relationship: nil}), do: true

defp route_visible?(resource, %{relationship: relationship}) do
show_field?(resource, relationship)
end

defp add_route_properties(keys, resource, properties) do
Enum.reduce(properties, keys, fn property, keys ->
spec =
Expand Down Expand Up @@ -760,7 +782,7 @@ defmodule AshJsonApi.JsonSchema do
end

defp sort_format(resource) do
sorts = sortable_fields(resource)
sorts = resource |> sortable_fields() |> filter_shown_fields(resource)

"(#{Enum.map_join(sorts, "|", &AshJsonApi.Resource.Info.field_to_json_key(resource, &1.name))}),*"
end
Expand Down Expand Up @@ -985,6 +1007,7 @@ defmodule AshJsonApi.JsonSchema do
resource
|> Ash.Resource.Info.attributes()
|> Enum.filter(&(&1.name in action.accept && &1.writable?))
|> filter_shown_fields(resource)
|> Enum.reduce(%{}, fn attribute, acc ->
Map.put(
acc,
Expand Down Expand Up @@ -1023,13 +1046,15 @@ defmodule AshJsonApi.JsonSchema do
defp required_relationship_attributes(resource, relationship_arguments, action) do
action.arguments
|> Enum.filter(&has_relationship_argument?(relationship_arguments, &1.name))
|> filter_shown_fields(resource)
|> Enum.reject(& &1.allow_nil?)
|> Enum.map(&AshJsonApi.Resource.Info.argument_to_json_key(resource, action.name, &1.name))
end

defp write_relationships(resource, relationship_arguments, action) do
action.arguments
|> Enum.filter(&has_relationship_argument?(relationship_arguments, &1.name))
|> filter_shown_fields(resource)
|> Enum.reduce(%{}, fn argument, acc ->
data = resource_relationship_field_data(resource, argument)

Expand Down
Loading
Loading