keepsake_sqlx/repository/
query.rs1use keepsake::{Keepsake, RelationId, RelationKey, SubjectRef};
2
3use super::{
4 ActiveRelation, ActiveRelationRow, AppliedKeepsakeRow, KeepsakeRepository, MembershipCursor,
5 RelationCache, RepositoryResult, validate_limit,
6};
7
8impl<C> KeepsakeRepository<C>
9where
10 C: RelationCache,
11{
12 pub async fn active_for_subject(
14 &self,
15 subject: &SubjectRef,
16 ) -> RepositoryResult<Vec<Keepsake>> {
17 let rows = sqlx::query_as::<_, AppliedKeepsakeRow>(
18 r"
19 select id, subject_kind, subject_id, relation_id, state, expiry_policy, applied_at,
20 expires_at, fulfilled_at, revoked_at, metadata
21 from keepsakes
22 where subject_kind = $1 and subject_id = $2 and state = 'applied'
23 order by relation_id, id
24 ",
25 )
26 .bind(&subject.kind)
27 .bind(&subject.id)
28 .fetch_all(&self.pool)
29 .await?;
30
31 rows.into_iter()
32 .map(AppliedKeepsakeRow::try_into_keepsake)
33 .collect()
34 }
35
36 pub async fn active_relations_for_subject(
38 &self,
39 subject: &SubjectRef,
40 ) -> RepositoryResult<Vec<ActiveRelation>> {
41 let rows = sqlx::query_as::<_, ActiveRelationRow>(
42 r"
43 select
44 k.id,
45 k.subject_kind,
46 k.subject_id,
47 k.relation_id,
48 k.state,
49 k.expiry_policy,
50 k.applied_at,
51 k.expires_at,
52 k.fulfilled_at,
53 k.revoked_at,
54 k.metadata,
55 r.id as relation_definition_id,
56 r.kind as relation_kind,
57 r.key as relation_key,
58 r.enabled as relation_enabled,
59 r.expiry_policy as relation_expiry_policy
60 from keepsakes k
61 join keepsake_relation_definitions r on r.id = k.relation_id
62 where k.subject_kind = $1 and k.subject_id = $2 and k.state = 'applied'
63 order by k.relation_id, k.id
64 ",
65 )
66 .bind(&subject.kind)
67 .bind(&subject.id)
68 .fetch_all(&self.pool)
69 .await?;
70
71 let mut active = Vec::with_capacity(rows.len());
72 for row in rows {
73 let active_relation = row.try_into_active_relation()?;
74 self.relation_cache.store(&active_relation.relation).await;
75 active.push(active_relation);
76 }
77 Ok(active)
78 }
79
80 pub async fn active_relations_for_subject_by_keys(
87 &self,
88 subject: &SubjectRef,
89 keys: &[RelationKey],
90 ) -> RepositoryResult<Vec<ActiveRelation>> {
91 if keys.is_empty() {
92 return Ok(Vec::new());
93 }
94
95 let kinds = keys
96 .iter()
97 .map(|key| key.kind().to_owned())
98 .collect::<Vec<String>>();
99 let names = keys
100 .iter()
101 .map(|key| key.name().to_owned())
102 .collect::<Vec<String>>();
103
104 let rows = sqlx::query_as::<_, ActiveRelationRow>(
105 r"
106 with requested_relation_keys(kind, key) as (
107 select distinct kind, key
108 from unnest($3::text[], $4::text[]) as requested(kind, key)
109 )
110 select
111 k.id,
112 k.subject_kind,
113 k.subject_id,
114 k.relation_id,
115 k.state,
116 k.expiry_policy,
117 k.applied_at,
118 k.expires_at,
119 k.fulfilled_at,
120 k.revoked_at,
121 k.metadata,
122 r.id as relation_definition_id,
123 r.kind as relation_kind,
124 r.key as relation_key,
125 r.enabled as relation_enabled,
126 r.expiry_policy as relation_expiry_policy
127 from requested_relation_keys requested
128 join keepsake_relation_definitions r
129 on r.kind = requested.kind and r.key = requested.key
130 join keepsakes k
131 on k.relation_id = r.id
132 and k.subject_kind = $1
133 and k.subject_id = $2
134 and k.state = 'applied'
135 order by k.relation_id, k.id
136 ",
137 )
138 .bind(&subject.kind)
139 .bind(&subject.id)
140 .bind(&kinds)
141 .bind(&names)
142 .fetch_all(&self.pool)
143 .await?;
144
145 let mut active = Vec::with_capacity(rows.len());
146 for row in rows {
147 let active_relation = row.try_into_active_relation()?;
148 self.relation_cache.store(&active_relation.relation).await;
149 active.push(active_relation);
150 }
151 Ok(active)
152 }
153
154 pub async fn active_membership_scan(
156 &self,
157 relation_id: RelationId,
158 limit: i64,
159 ) -> RepositoryResult<Vec<Keepsake>> {
160 self.active_membership_scan_after(relation_id, None, limit)
161 .await
162 }
163
164 pub async fn active_membership_scan_after(
166 &self,
167 relation_id: RelationId,
168 after: Option<&MembershipCursor>,
169 limit: i64,
170 ) -> RepositoryResult<Vec<Keepsake>> {
171 let limit = validate_limit(limit)?;
172 let rows = sqlx::query_as::<_, AppliedKeepsakeRow>(
173 r"
174 select id, subject_kind, subject_id, relation_id, state, expiry_policy, applied_at,
175 expires_at, fulfilled_at, revoked_at, metadata
176 from keepsakes
177 where relation_id = $1
178 and state = 'applied'
179 and (
180 $2::text is null
181 or (subject_kind, subject_id, id) > ($2, $3, $4)
182 )
183 order by subject_kind, subject_id, id
184 limit $5
185 ",
186 )
187 .bind(relation_id)
188 .bind(after.map(|cursor| cursor.subject_kind.as_str()))
189 .bind(after.map(|cursor| cursor.subject_id.as_str()))
190 .bind(after.map(|cursor| cursor.keepsake_id))
191 .bind(limit)
192 .fetch_all(&self.pool)
193 .await?;
194
195 rows.into_iter()
196 .map(AppliedKeepsakeRow::try_into_keepsake)
197 .collect()
198 }
199}