keepsake_sqlx/repository/
query.rs1use keepsake::{
2 ActiveRelation, ActiveRelationSource, Keepsake, RelationId, RelationKey, SubjectRef,
3};
4
5use super::{
6 ActiveRelationRow, AppliedKeepsakeRow, KeepsakeRepository, MembershipCursor, RelationCache,
7 RepositoryError, RepositoryResult, validate_limit,
8};
9
10impl<C> KeepsakeRepository<C>
11where
12 C: RelationCache,
13{
14 pub async fn active_for_subject(
16 &self,
17 subject: &SubjectRef,
18 ) -> RepositoryResult<Vec<Keepsake>> {
19 let rows = sqlx::query_as::<_, AppliedKeepsakeRow>(
20 r"
21 select id, subject_kind, subject_id, relation_id, state, expiry_policy, applied_at,
22 expires_at, fulfilled_at, revoked_at, metadata
23 from keepsakes
24 where subject_kind = $1 and subject_id = $2 and state = 'applied'
25 order by relation_id, id
26 ",
27 )
28 .bind(&subject.kind)
29 .bind(&subject.id)
30 .fetch_all(&self.pool)
31 .await?;
32
33 rows.into_iter()
34 .map(AppliedKeepsakeRow::try_into_keepsake)
35 .collect()
36 }
37
38 pub async fn active_relations_for_subject(
40 &self,
41 subject: &SubjectRef,
42 ) -> RepositoryResult<Vec<ActiveRelation>> {
43 let rows = sqlx::query_as::<_, ActiveRelationRow>(
44 r"
45 select
46 k.id,
47 k.subject_kind,
48 k.subject_id,
49 k.relation_id,
50 k.state,
51 k.expiry_policy,
52 k.applied_at,
53 k.expires_at,
54 k.fulfilled_at,
55 k.revoked_at,
56 k.metadata,
57 r.id as relation_definition_id,
58 r.kind as relation_kind,
59 r.key as relation_key,
60 r.enabled as relation_enabled,
61 r.expiry_policy as relation_expiry_policy
62 from keepsakes k
63 join keepsake_relation_definitions r on r.id = k.relation_id
64 where k.subject_kind = $1 and k.subject_id = $2 and k.state = 'applied'
65 order by k.relation_id, k.id
66 ",
67 )
68 .bind(&subject.kind)
69 .bind(&subject.id)
70 .fetch_all(&self.pool)
71 .await?;
72
73 let mut active = Vec::with_capacity(rows.len());
74 for row in rows {
75 let active_relation = row.try_into_active_relation()?;
76 self.relation_cache.store(active_relation.relation()).await;
77 active.push(active_relation);
78 }
79 Ok(active)
80 }
81
82 pub async fn active_relations_for_subject_by_ids(
90 &self,
91 subject: &SubjectRef,
92 relation_ids: &[RelationId],
93 ) -> RepositoryResult<Vec<ActiveRelation>> {
94 if relation_ids.is_empty() {
95 return Ok(Vec::new());
96 }
97
98 let requested_relation_ids = relation_ids.to_vec();
99 let rows = sqlx::query_as::<_, ActiveRelationRow>(
100 r"
101 with requested_relation_ids(id) as (
102 select distinct id
103 from unnest($3::uuid[]) as requested(id)
104 )
105 select
106 k.id,
107 k.subject_kind,
108 k.subject_id,
109 k.relation_id,
110 k.state,
111 k.expiry_policy,
112 k.applied_at,
113 k.expires_at,
114 k.fulfilled_at,
115 k.revoked_at,
116 k.metadata,
117 r.id as relation_definition_id,
118 r.kind as relation_kind,
119 r.key as relation_key,
120 r.enabled as relation_enabled,
121 r.expiry_policy as relation_expiry_policy
122 from requested_relation_ids requested
123 join keepsake_relation_definitions r
124 on r.id = requested.id
125 join keepsakes k
126 on k.relation_id = r.id
127 and k.subject_kind = $1
128 and k.subject_id = $2
129 and k.state = 'applied'
130 order by k.relation_id, k.id
131 ",
132 )
133 .bind(&subject.kind)
134 .bind(&subject.id)
135 .bind(&requested_relation_ids)
136 .fetch_all(&self.pool)
137 .await?;
138
139 let mut active = Vec::with_capacity(rows.len());
140 for row in rows {
141 let active_relation = row.try_into_active_relation()?;
142 self.relation_cache.store(active_relation.relation()).await;
143 active.push(active_relation);
144 }
145 Ok(active)
146 }
147
148 pub async fn active_relations_for_subject_by_keys(
155 &self,
156 subject: &SubjectRef,
157 keys: &[RelationKey],
158 ) -> RepositoryResult<Vec<ActiveRelation>> {
159 if keys.is_empty() {
160 return Ok(Vec::new());
161 }
162
163 let kinds = keys
164 .iter()
165 .map(|key| key.kind().to_owned())
166 .collect::<Vec<String>>();
167 let names = keys
168 .iter()
169 .map(|key| key.name().to_owned())
170 .collect::<Vec<String>>();
171
172 let rows = sqlx::query_as::<_, ActiveRelationRow>(
173 r"
174 with requested_relation_keys(kind, key) as (
175 select distinct kind, key
176 from unnest($3::text[], $4::text[]) as requested(kind, key)
177 )
178 select
179 k.id,
180 k.subject_kind,
181 k.subject_id,
182 k.relation_id,
183 k.state,
184 k.expiry_policy,
185 k.applied_at,
186 k.expires_at,
187 k.fulfilled_at,
188 k.revoked_at,
189 k.metadata,
190 r.id as relation_definition_id,
191 r.kind as relation_kind,
192 r.key as relation_key,
193 r.enabled as relation_enabled,
194 r.expiry_policy as relation_expiry_policy
195 from requested_relation_keys requested
196 join keepsake_relation_definitions r
197 on r.kind = requested.kind and r.key = requested.key
198 join keepsakes k
199 on k.relation_id = r.id
200 and k.subject_kind = $1
201 and k.subject_id = $2
202 and k.state = 'applied'
203 order by k.relation_id, k.id
204 ",
205 )
206 .bind(&subject.kind)
207 .bind(&subject.id)
208 .bind(&kinds)
209 .bind(&names)
210 .fetch_all(&self.pool)
211 .await?;
212
213 let mut active = Vec::with_capacity(rows.len());
214 for row in rows {
215 let active_relation = row.try_into_active_relation()?;
216 self.relation_cache.store(active_relation.relation()).await;
217 active.push(active_relation);
218 }
219 Ok(active)
220 }
221
222 pub async fn active_membership_scan(
224 &self,
225 relation_id: RelationId,
226 limit: i64,
227 ) -> RepositoryResult<Vec<Keepsake>> {
228 self.active_membership_scan_after(relation_id, None, limit)
229 .await
230 }
231
232 pub async fn active_membership_scan_after(
234 &self,
235 relation_id: RelationId,
236 after: Option<&MembershipCursor>,
237 limit: i64,
238 ) -> RepositoryResult<Vec<Keepsake>> {
239 let limit = validate_limit(limit)?;
240 let rows = sqlx::query_as::<_, AppliedKeepsakeRow>(
241 r"
242 select id, subject_kind, subject_id, relation_id, state, expiry_policy, applied_at,
243 expires_at, fulfilled_at, revoked_at, metadata
244 from keepsakes
245 where relation_id = $1
246 and state = 'applied'
247 and (
248 $2::text is null
249 or (subject_kind, subject_id, id) > ($2, $3, $4)
250 )
251 order by subject_kind, subject_id, id
252 limit $5
253 ",
254 )
255 .bind(relation_id)
256 .bind(after.map(|cursor| cursor.subject_kind.as_str()))
257 .bind(after.map(|cursor| cursor.subject_id.as_str()))
258 .bind(after.map(|cursor| cursor.keepsake_id))
259 .bind(limit)
260 .fetch_all(&self.pool)
261 .await?;
262
263 rows.into_iter()
264 .map(AppliedKeepsakeRow::try_into_keepsake)
265 .collect()
266 }
267}
268
269impl<C> ActiveRelationSource for KeepsakeRepository<C>
270where
271 C: RelationCache,
272{
273 type Error = RepositoryError;
274
275 async fn active_relations_for_subject<'a>(
276 &'a self,
277 subject: &'a SubjectRef,
278 ) -> RepositoryResult<Vec<ActiveRelation>> {
279 self.active_relations_for_subject(subject).await
280 }
281
282 async fn active_relations_for_subject_by_ids<'a>(
283 &'a self,
284 subject: &'a SubjectRef,
285 relation_ids: &'a [RelationId],
286 ) -> RepositoryResult<Vec<ActiveRelation>> {
287 self.active_relations_for_subject_by_ids(subject, relation_ids)
288 .await
289 }
290
291 async fn active_relations_for_subject_by_keys<'a>(
292 &'a self,
293 subject: &'a SubjectRef,
294 keys: &'a [RelationKey],
295 ) -> RepositoryResult<Vec<ActiveRelation>> {
296 self.active_relations_for_subject_by_keys(subject, keys)
297 .await
298 }
299}