subcog/storage/group/traits.rs
1//! Group storage trait definitions.
2//!
3//! Defines the interface for group storage backends, supporting CRUD operations
4//! for groups, members, and invites.
5
6use crate::Result;
7use crate::models::group::{Group, GroupId, GroupInvite, GroupMember, GroupMembership, GroupRole};
8
9/// Trait for group storage backends.
10///
11/// Provides storage operations for groups, members, and invites within an organization.
12/// Implementations must be thread-safe (`Send + Sync`).
13pub trait GroupBackend: Send + Sync {
14 // =========================================================================
15 // Group Operations
16 // =========================================================================
17
18 /// Creates a new group.
19 ///
20 /// # Arguments
21 ///
22 /// * `org_id` - Organization identifier
23 /// * `name` - Group name (must be unique within org)
24 /// * `description` - Optional description
25 /// * `created_by` - Email of the creator
26 ///
27 /// # Errors
28 ///
29 /// Returns an error if:
30 /// - A group with the same name already exists in the org
31 /// - Storage cannot be accessed
32 fn create_group(
33 &self,
34 org_id: &str,
35 name: &str,
36 description: &str,
37 created_by: &str,
38 ) -> Result<Group>;
39
40 /// Gets a group by ID.
41 ///
42 /// # Arguments
43 ///
44 /// * `group_id` - The group identifier
45 ///
46 /// # Returns
47 ///
48 /// The group if found, None otherwise.
49 ///
50 /// # Errors
51 ///
52 /// Returns an error if storage cannot be accessed.
53 fn get_group(&self, group_id: &GroupId) -> Result<Option<Group>>;
54
55 /// Gets a group by name within an organization.
56 ///
57 /// # Arguments
58 ///
59 /// * `org_id` - Organization identifier
60 /// * `name` - Group name
61 ///
62 /// # Returns
63 ///
64 /// The group if found, None otherwise.
65 ///
66 /// # Errors
67 ///
68 /// Returns an error if storage cannot be accessed.
69 fn get_group_by_name(&self, org_id: &str, name: &str) -> Result<Option<Group>>;
70
71 /// Lists all groups in an organization.
72 ///
73 /// # Arguments
74 ///
75 /// * `org_id` - Organization identifier
76 ///
77 /// # Returns
78 ///
79 /// List of groups in the organization.
80 ///
81 /// # Errors
82 ///
83 /// Returns an error if storage cannot be accessed.
84 fn list_groups(&self, org_id: &str) -> Result<Vec<Group>>;
85
86 /// Deletes a group and all its members and invites.
87 ///
88 /// # Arguments
89 ///
90 /// * `group_id` - The group to delete
91 ///
92 /// # Returns
93 ///
94 /// True if the group was deleted, false if it didn't exist.
95 ///
96 /// # Errors
97 ///
98 /// Returns an error if storage cannot be accessed.
99 fn delete_group(&self, group_id: &GroupId) -> Result<bool>;
100
101 // =========================================================================
102 // Member Operations
103 // =========================================================================
104
105 /// Adds a member to a group.
106 ///
107 /// If the member already exists, updates their role.
108 ///
109 /// # Arguments
110 ///
111 /// * `group_id` - The group to add to
112 /// * `email` - Email of the new member
113 /// * `role` - Role to assign
114 /// * `added_by` - Email of the user adding this member
115 ///
116 /// # Errors
117 ///
118 /// Returns an error if:
119 /// - The group doesn't exist
120 /// - Storage cannot be accessed
121 fn add_member(
122 &self,
123 group_id: &GroupId,
124 email: &str,
125 role: GroupRole,
126 added_by: &str,
127 ) -> Result<GroupMember>;
128
129 /// Gets a member's record in a group.
130 ///
131 /// # Arguments
132 ///
133 /// * `group_id` - The group
134 /// * `email` - The member's email
135 ///
136 /// # Returns
137 ///
138 /// The member record if found, None otherwise.
139 ///
140 /// # Errors
141 ///
142 /// Returns an error if storage cannot be accessed.
143 fn get_member(&self, group_id: &GroupId, email: &str) -> Result<Option<GroupMember>>;
144
145 /// Updates a member's role in a group.
146 ///
147 /// # Arguments
148 ///
149 /// * `group_id` - The group
150 /// * `email` - The member's email
151 /// * `new_role` - The new role to assign
152 ///
153 /// # Returns
154 ///
155 /// True if the member was updated, false if not found.
156 ///
157 /// # Errors
158 ///
159 /// Returns an error if storage cannot be accessed.
160 fn update_member_role(
161 &self,
162 group_id: &GroupId,
163 email: &str,
164 new_role: GroupRole,
165 ) -> Result<bool>;
166
167 /// Removes a member from a group.
168 ///
169 /// # Arguments
170 ///
171 /// * `group_id` - The group
172 /// * `email` - The member's email
173 ///
174 /// # Returns
175 ///
176 /// True if the member was removed, false if not found.
177 ///
178 /// # Errors
179 ///
180 /// Returns an error if storage cannot be accessed.
181 fn remove_member(&self, group_id: &GroupId, email: &str) -> Result<bool>;
182
183 /// Lists all members of a group.
184 ///
185 /// # Arguments
186 ///
187 /// * `group_id` - The group
188 ///
189 /// # Returns
190 ///
191 /// List of members with their roles.
192 ///
193 /// # Errors
194 ///
195 /// Returns an error if storage cannot be accessed.
196 fn list_members(&self, group_id: &GroupId) -> Result<Vec<GroupMember>>;
197
198 /// Gets all groups a user is a member of.
199 ///
200 /// # Arguments
201 ///
202 /// * `org_id` - Organization to search within
203 /// * `email` - The user's email
204 ///
205 /// # Returns
206 ///
207 /// List of group memberships for the user.
208 ///
209 /// # Errors
210 ///
211 /// Returns an error if storage cannot be accessed.
212 fn get_user_groups(&self, org_id: &str, email: &str) -> Result<Vec<GroupMembership>>;
213
214 /// Counts the number of admins in a group.
215 ///
216 /// Used to prevent removing the last admin.
217 ///
218 /// # Arguments
219 ///
220 /// * `group_id` - The group
221 ///
222 /// # Returns
223 ///
224 /// Number of members with admin role.
225 ///
226 /// # Errors
227 ///
228 /// Returns an error if storage cannot be accessed.
229 fn count_admins(&self, group_id: &GroupId) -> Result<u32>;
230
231 // =========================================================================
232 // Invite Operations
233 // =========================================================================
234
235 /// Creates an invite for a group.
236 ///
237 /// # Arguments
238 ///
239 /// * `group_id` - The group to invite to
240 /// * `role` - Role to assign when joined
241 /// * `created_by` - Email of the admin creating the invite
242 /// * `expires_in_secs` - How long until expiration
243 /// * `max_uses` - Maximum number of uses
244 ///
245 /// # Returns
246 ///
247 /// A tuple of (invite, `plaintext_token`). The token should be shared
248 /// with invitees and never stored.
249 ///
250 /// # Errors
251 ///
252 /// Returns an error if:
253 /// - The group doesn't exist
254 /// - Storage cannot be accessed
255 fn create_invite(
256 &self,
257 group_id: &GroupId,
258 role: GroupRole,
259 created_by: &str,
260 expires_in_secs: Option<u64>,
261 max_uses: Option<u32>,
262 ) -> Result<(GroupInvite, String)>;
263
264 /// Gets an invite by its token hash.
265 ///
266 /// # Arguments
267 ///
268 /// * `token_hash` - SHA256 hash of the token
269 ///
270 /// # Returns
271 ///
272 /// The invite if found and valid, None otherwise.
273 ///
274 /// # Errors
275 ///
276 /// Returns an error if storage cannot be accessed.
277 fn get_invite_by_token_hash(&self, token_hash: &str) -> Result<Option<GroupInvite>>;
278
279 /// Gets an invite by ID.
280 ///
281 /// # Arguments
282 ///
283 /// * `invite_id` - The invite identifier
284 ///
285 /// # Returns
286 ///
287 /// The invite if found, None otherwise.
288 ///
289 /// # Errors
290 ///
291 /// Returns an error if storage cannot be accessed.
292 fn get_invite(&self, invite_id: &str) -> Result<Option<GroupInvite>>;
293
294 /// Lists all invites for a group.
295 ///
296 /// # Arguments
297 ///
298 /// * `group_id` - The group
299 /// * `include_expired` - Whether to include expired/revoked invites
300 ///
301 /// # Returns
302 ///
303 /// List of invites.
304 ///
305 /// # Errors
306 ///
307 /// Returns an error if storage cannot be accessed.
308 fn list_invites(&self, group_id: &GroupId, include_expired: bool) -> Result<Vec<GroupInvite>>;
309
310 /// Increments the use count of an invite.
311 ///
312 /// Called when a user successfully joins using the invite.
313 ///
314 /// # Arguments
315 ///
316 /// * `invite_id` - The invite to update
317 ///
318 /// # Errors
319 ///
320 /// Returns an error if storage cannot be accessed.
321 fn increment_invite_uses(&self, invite_id: &str) -> Result<()>;
322
323 /// Revokes an invite.
324 ///
325 /// # Arguments
326 ///
327 /// * `invite_id` - The invite to revoke
328 ///
329 /// # Returns
330 ///
331 /// True if the invite was revoked, false if not found.
332 ///
333 /// # Errors
334 ///
335 /// Returns an error if storage cannot be accessed.
336 fn revoke_invite(&self, invite_id: &str) -> Result<bool>;
337
338 /// Deletes expired invites.
339 ///
340 /// # Returns
341 ///
342 /// Number of invites deleted.
343 ///
344 /// # Errors
345 ///
346 /// Returns an error if storage cannot be accessed.
347 fn cleanup_expired_invites(&self) -> Result<u64>;
348}