Skip to content

Commit

Permalink
feat: add Relay ID translation in mutation and queries
Browse files Browse the repository at this point in the history
Adds a new option for queries and mutations that defines which arguments or
attributes will use a global Relay ID and their type. This allows automatically
decoding them before hitting their action.
  • Loading branch information
rbino committed Jan 29, 2024
1 parent afa4ae6 commit b4e8bcc
Show file tree
Hide file tree
Showing 9 changed files with 502 additions and 3 deletions.
1 change: 1 addition & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ spark_locals_without_parens = [
read_one: 3,
relationships: 1,
relay?: 1,
relay_id_translations: 1,
root_level_errors?: 1,
show_metadata: 1,
show_raised_errors?: 1,
Expand Down
210 changes: 210 additions & 0 deletions documentation/dsls/DSL:-AshGraphql.Resource.cheatmd
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,29 @@ get :get_post, :read
</td>
</tr>

<tr>
<td style="text-align: left">
<a id="graphql-queries-get-relay_id_translations" href="#graphql-queries-get-relay_id_translations">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
relay_id_translations
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">Keyword.t</code>
</td>
<td style="text-align: left">
<code class="inline">[]</code>
</td>
<td style="text-align: left" colspan=2>
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.

The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.

</td>
</tr>

</tbody>
</table>

Expand Down Expand Up @@ -856,6 +879,29 @@ read_one :current_user, :current_user
</td>
</tr>

<tr>
<td style="text-align: left">
<a id="graphql-queries-read_one-relay_id_translations" href="#graphql-queries-read_one-relay_id_translations">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
relay_id_translations
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">Keyword.t</code>
</td>
<td style="text-align: left">
<code class="inline">[]</code>
</td>
<td style="text-align: left" colspan=2>
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.

The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.

</td>
</tr>

</tbody>
</table>

Expand Down Expand Up @@ -1078,6 +1124,29 @@ list :list_posts_paginated, :read, relay?: true
</td>
</tr>

<tr>
<td style="text-align: left">
<a id="graphql-queries-list-relay_id_translations" href="#graphql-queries-list-relay_id_translations">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
relay_id_translations
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">Keyword.t</code>
</td>
<td style="text-align: left">
<code class="inline">[]</code>
</td>
<td style="text-align: left" colspan=2>
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.

The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.

</td>
</tr>

</tbody>
</table>

Expand Down Expand Up @@ -1161,7 +1230,43 @@ action :check_status, :check_status

</tbody>
</table>
### Options

<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Default</th>
<th colspan=2>Docs</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">
<a id="graphql-queries-action-relay_id_translations" href="#graphql-queries-action-relay_id_translations">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
relay_id_translations
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">Keyword.t</code>
</td>
<td style="text-align: left">
<code class="inline">[]</code>
</td>
<td style="text-align: left" colspan=2>
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.

The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.

</td>
</tr>

</tbody>
</table>



Expand Down Expand Up @@ -1341,6 +1446,29 @@ create :create_post, :create
</td>
</tr>

<tr>
<td style="text-align: left">
<a id="graphql-mutations-create-relay_id_translations" href="#graphql-mutations-create-relay_id_translations">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
relay_id_translations
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">Keyword.t</code>
</td>
<td style="text-align: left">
<code class="inline">[]</code>
</td>
<td style="text-align: left" colspan=2>
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.

The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.

</td>
</tr>

</tbody>
</table>

Expand Down Expand Up @@ -1477,6 +1605,29 @@ update :update_post, :update
</td>
</tr>

<tr>
<td style="text-align: left">
<a id="graphql-mutations-update-relay_id_translations" href="#graphql-mutations-update-relay_id_translations">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
relay_id_translations
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">Keyword.t</code>
</td>
<td style="text-align: left">
<code class="inline">[]</code>
</td>
<td style="text-align: left" colspan=2>
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.

The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.

</td>
</tr>

</tbody>
</table>

Expand Down Expand Up @@ -1613,6 +1764,29 @@ destroy :destroy_post, :destroy
</td>
</tr>

<tr>
<td style="text-align: left">
<a id="graphql-mutations-destroy-relay_id_translations" href="#graphql-mutations-destroy-relay_id_translations">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
relay_id_translations
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">Keyword.t</code>
</td>
<td style="text-align: left">
<code class="inline">[]</code>
</td>
<td style="text-align: left" colspan=2>
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.

The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.

</td>
</tr>

</tbody>
</table>

Expand Down Expand Up @@ -1696,7 +1870,43 @@ action :check_status, :check_status

</tbody>
</table>
### Options

<table>
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Default</th>
<th colspan=2>Docs</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">
<a id="graphql-mutations-action-relay_id_translations" href="#graphql-mutations-action-relay_id_translations">
<span style="font-family: Inconsolata, Menlo, Courier, monospace;">
relay_id_translations
</span>
</a>

</td>
<td style="text-align: left">
<code class="inline">Keyword.t</code>
</td>
<td style="text-align: left">
<code class="inline">[]</code>
</td>
<td style="text-align: left" colspan=2>
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.

The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.

</td>
</tr>

</tbody>
</table>



Expand Down
43 changes: 43 additions & 0 deletions lib/graphql/id_translator.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
defmodule AshGraphql.Graphql.IdTranslator do
def translate_relay_ids(%{state: :unresolved} = resolution, relay_id_translations) do
arguments =
Enum.reduce(relay_id_translations, resolution.arguments, &process/2)

%{resolution | arguments: arguments}
end

def translate_relay_ids(resolution, _relay_id_translations) do
resolution
end

defp process({field, nested_translations}, args) when is_list(nested_translations) do
case Map.get(args, field) do
subtree when is_map(subtree) ->
new_subtree = Enum.reduce(nested_translations, subtree, &process/2)
Map.put(args, field, new_subtree)

_ ->
args
end
end

defp process({field, type}, args) when is_atom(type) do
case Map.get(args, field) do
id when is_binary(id) ->
{:ok, %{type: ^type, id: decoded_id}} = AshGraphql.Resource.decode_relay_id(id)
Map.put(args, field, decoded_id)

[id | _] = ids when is_binary(id) ->
decoded_ids =
Enum.map(ids, fn id ->
{:ok, %{type: ^type, id: decoded_id}} = AshGraphql.Resource.decode_relay_id(id)
decoded_id
end)

Map.put(args, field, decoded_ids)

_ ->
args
end
end
end
30 changes: 29 additions & 1 deletion lib/resource/mutation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ defmodule AshGraphql.Resource.Mutation do
:read_action,
:upsert?,
:upsert_identity,
:modify_resolution
:modify_resolution,
:relay_id_translations
]

@create_schema [
Expand Down Expand Up @@ -37,6 +38,15 @@ defmodule AshGraphql.Resource.Mutation do
doc: """
An MFA that will be called with the resolution, the query, and the result of the action as the first three arguments. See the [the guide](/documentation/topics/modifying-the-resolution.html) for more.
"""
],
relay_id_translations: [
type: :keyword_list,
doc: """
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.
The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.
""",
default: []
]
]

Expand All @@ -61,6 +71,15 @@ defmodule AshGraphql.Resource.Mutation do
type: :atom,
doc:
"The read action to use to fetch the record to be updated. Defaults to the primary read action."
],
relay_id_translations: [
type: :keyword_list,
doc: """
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.
The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.
""",
default: []
]
]

Expand All @@ -85,6 +104,15 @@ defmodule AshGraphql.Resource.Mutation do
doc: """
The identity to use to fetch the record to be destroyed. Use `false` if no identity is required.
"""
],
relay_id_translations: [
type: :keyword_list,
doc: """
A keyword list indicating which arguments or attributes will be automatically translated from a global Relay ID to a normal ID.
The keyword list can be arbitrarily nested to match the argument structure. The leaves of the keyword list must have the attribute or argument name as key and its GraphQL type as value.
""",
default: []
]
]

Expand Down
Loading

0 comments on commit b4e8bcc

Please sign in to comment.