grafbase_hooks/hooks/authorization/
edge_post_execution_arguments.rs

1/// Arguments passed to the `authorize_edge_post_execution` hook.
2pub struct EdgePostExecutionArguments {
3    definition: crate::wit::EdgeDefinition,
4    edges: Vec<(String, Vec<String>)>,
5    metadata: String,
6}
7
8impl EdgePostExecutionArguments {
9    pub(crate) fn new(
10        definition: crate::wit::EdgeDefinition,
11        edges: Vec<(String, Vec<String>)>,
12        metadata: String,
13    ) -> Self {
14        Self {
15            definition,
16            edges,
17            metadata,
18        }
19    }
20
21    /// The name of the parent type of the edge.
22    ///
23    /// For the following GraphQL schema:
24    ///
25    /// ```graphql
26    /// type Address {
27    ///     street: String!
28    /// }
29    ///
30    /// type User {
31    ///     id: Int!
32    ///     addresses: [Address!]! @authorized(fields: "id", node: "street")
33    /// }
34    ///
35    /// type Query {
36    ///     users: [User!]!
37    /// }
38    /// ```
39    ///
40    /// And the query:
41    ///
42    /// ```graphql
43    /// query {
44    ///     users { addresses { street } }
45    /// }
46    /// ```
47    ///
48    /// The parent type name is `User`.
49    pub fn parent_type_name(&self) -> &str {
50        &self.definition.parent_type_name
51    }
52
53    /// The name of the authorized edge.
54    ///
55    /// For the following GraphQL schema:
56    ///
57    /// ```graphql
58    /// type Address {
59    ///     street: String!
60    /// }
61    ///
62    /// type User {
63    ///     id: Int!
64    ///     addresses: [Address!]! @authorized(fields: "id", node: "street")
65    /// }
66    ///
67    /// type Query {
68    ///     users: [User!]!
69    /// }
70    /// ```
71    ///
72    /// And the query:
73    ///
74    /// ```graphql
75    /// query {
76    ///     users { addresses { street } }
77    /// }
78    /// ```
79    ///
80    /// The field name is `addresses`.
81    pub fn field_name(&self) -> &str {
82        &self.definition.field_name
83    }
84
85    /// The returned edges, serialized as a JSON objects. The first item of the tuple
86    /// is a serialization of the fields defined in the `fields` argument and the second
87    /// one is a serialization of the fields defined in the `node` argument.
88    ///
89    /// This method will deserialize the parent nodes into either `serde_json::Value` or a custom struct.
90    ///
91    /// For the following GraphQL schema:
92    ///
93    /// ```graphql
94    /// type Address {
95    ///     street: String!
96    /// }
97    ///
98    /// type User {
99    ///     id: Int!
100    ///     addresses: [Address!]! @authorized(fields: "id", node: "street")
101    /// }
102    ///
103    /// type Query {
104    ///     users: [User!]!
105    /// }
106    /// ```
107    ///
108    /// And the query:
109    ///
110    /// ```graphql
111    /// query {
112    ///     users { addresses { street } }
113    /// }
114    /// ```
115    ///
116    /// The query returns two entities:
117    ///
118    /// ```json
119    /// [
120    ///   {
121    ///     "id": 1,
122    ///     "addresses": [{ "street": "Elm Street" }]
123    ///   },
124    ///   {
125    ///     "id": 2,
126    ///     "addresses": [{ "street": "Maple Street" }]
127    ///   }
128    /// ]
129    /// ```
130    ///
131    /// The arguments can be deserialized into a custom struct like:
132    ///
133    /// ```rust
134    /// #[derive(serde::Deserialize)]
135    /// struct Parent {
136    ///    id: u64,
137    /// }
138    ///
139    /// #[derive(serde::Deserialize)]
140    /// struct Node {
141    ///    street: String,
142    /// }
143    ///
144    /// # fn foo(arguments: grafbase_hooks::EdgePostExecutionArguments) -> Result<(), serde_json::Error> {
145    /// let edges: Vec<(Parent, Vec<Node>)> = arguments.edges()?;
146    /// # Ok(())
147    /// # }
148    /// ```
149    ///
150    /// The directive defines the `fields` argument as `id` and `node` argument as
151    /// `street`, so the hook gets a vector of tuples where the first item is the
152    /// parent fields with the fields defined in `fields` and the second one is
153    /// the nodes with the fields defined in `node`.
154    pub fn edges<'a, T, K>(&'a self) -> Result<Vec<(T, Vec<K>)>, serde_json::Error>
155    where
156        T: serde::Deserialize<'a>,
157        K: serde::Deserialize<'a>,
158    {
159        self.edges
160            .iter()
161            .map(|(parent, nodes)| {
162                let parent: T = serde_json::from_str(parent)?;
163
164                let nodes: Vec<K> = nodes
165                    .iter()
166                    .map(|node| serde_json::from_str(node))
167                    .collect::<Result<_, _>>()?;
168
169                Ok((parent, nodes))
170            })
171            .collect()
172    }
173
174    /// The metadata passed to the `@authorized` directive. The metadata is
175    /// serialized as a JSON object. This method will deserialize the metadata
176    /// into either `serde_json::Value` or a custom struct.
177    ///
178    /// For the following GraphQL schema:
179    ///
180    /// ```graphql
181    /// type Address {
182    ///     street: String!
183    /// }
184    ///
185    /// type User {
186    ///     id: Int!
187    ///     addresses: [Address!]! @authorized(
188    ///         fields: "id",
189    ///         node: "street",
190    ///         metadata: { role: "admin" }
191    ///     )
192    /// }
193    ///
194    /// type Query {
195    ///     users: [User!]!
196    /// }
197    /// ```
198    ///
199    /// When executing a query like:
200    ///
201    /// ```graphql
202    /// query {
203    ///   users { addresses { street } }
204    /// }
205    /// ```
206    ///
207    /// The metadata is `{"role": "admin"}`.
208    ///
209    /// The metadata can be deserialized into a custom struct like:
210    ///
211    /// ```rust
212    /// #[derive(serde::Deserialize)]
213    /// #[serde(untagged, rename = "snake_case")]
214    /// enum Role {
215    ///    Admin,
216    ///    User,
217    /// }
218    ///
219    /// #[derive(serde::Deserialize)]
220    /// struct Metadata {
221    ///    role: Role,
222    /// }
223    ///
224    /// # fn foo(arguments: grafbase_hooks::EdgePostExecutionArguments) -> Result<(), serde_json::Error> {
225    /// let arguments: Metadata = arguments.metadata()?;
226    /// # Ok(())
227    /// # }
228    /// ```
229    pub fn metadata<'a, T>(&'a self) -> Result<T, serde_json::Error>
230    where
231        T: serde::Deserialize<'a>,
232    {
233        serde_json::from_str(&self.metadata)
234    }
235}