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}