JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Repository commit before request and handler

Type: Feature

erremilia - ⁨2⁩ years ago

parent: tbd commit: ⁨144b159

⁨giterated-daemon/src/database_backend/handler.rs⁩ - ⁨13487⁩ bytes
Raw
1 use std::{collections::HashMap, error::Error, pin::Pin, sync::Arc};
2
3 use futures_util::{future::BoxFuture, Future, FutureExt};
4 use giterated_models::{
5 error::{GetValueError, OperationError, RepositoryError, UserError},
6 object::{AnyObject, GiteratedObject},
7 object_backend::ObjectBackend,
8 operation::{AnyOperation, GiteratedOperation},
9 repository::{
10 DefaultBranch, Description, LatestCommit, Repository, RepositoryFileInspectRequest,
11 RepositoryInfoRequest, RepositorySummary, RepositoryView, Visibility, RepositoryFile, RepositoryFileFromIdRequest, RepositoryDiff, RepositoryDiffRequest, RepositoryCommitBeforeRequest, Commit,
12 },
13 settings::{AnySetting, GetSetting, GetSettingError, SetSetting, SetSettingError},
14 user::{User, UserRepositoriesRequest},
15 value::{AnyValue, GetValue},
16 };
17
18 use super::DatabaseBackend;
19
20 #[async_trait::async_trait]
21 pub trait GiteratedOperationHandler<
22 O: GiteratedObject,
23 D: GiteratedOperation<O>,
24 S: Send + Sync + Clone,
25 >
26 {
27 fn operation_name(&self) -> &str;
28 fn object_name(&self) -> &str;
29
30 async fn handle(
31 &self,
32 object: &O,
33 operation: D,
34 state: S,
35 ) -> Result<D::Success, OperationError<D::Failure>>;
36 }
37
38 #[async_trait::async_trait]
39 impl<O, D, F, S> GiteratedOperationHandler<O, D, S> for F
40 where
41 F: FnMut(
42 &O,
43 D,
44 S,
45 ) -> Pin<
46 Box<dyn Future<Output = Result<D::Success, OperationError<D::Failure>>> + Send>,
47 > + Send
48 + Sync
49 + Clone,
50 O: GiteratedObject + Send + Sync,
51 D: GiteratedOperation<O> + 'static,
52 <D as GiteratedOperation<O>>::Failure: Send,
53 S: Send + Sync + Clone + 'static,
54 {
55 fn operation_name(&self) -> &str {
56 D::operation_name()
57 }
58
59 fn object_name(&self) -> &str {
60 O::object_name()
61 }
62
63 async fn handle(
64 &self,
65 object: &O,
66 operation: D,
67 state: S,
68 ) -> Result<D::Success, OperationError<D::Failure>> {
69 self.clone()(object, operation, state).await
70 }
71 }
72
73 pub struct OperationWrapper<S: Send + Sync + Clone>(
74 Box<
75 dyn Fn(
76 AnyObject,
77 AnyOperation,
78 S,
79 )
80 -> Pin<Box<dyn Future<Output = Result<Vec<u8>, OperationError<Vec<u8>>>> + Send>>
81 + Send
82 + Sync,
83 >,
84 );
85
86 impl<S: Send + Sync + Clone + 'static> OperationWrapper<S> {
87 pub fn new<
88 O: GiteratedObject + Send + Sync,
89 D: GiteratedOperation<O> + 'static,
90 F: GiteratedOperationHandler<O, D, S> + Send + Sync + 'static + Clone,
91 >(
92 handler: F,
93 ) -> Self {
94 let handler = Arc::new(Box::pin(handler));
95 Self(Box::new(move |any_object, any_operation, state| {
96 let handler = handler.clone();
97 async move {
98 let handler = handler.clone();
99 let object: O = O::from_object_str(&any_object.0).unwrap();
100 let operation: D = serde_json::from_value(any_operation.0.clone()).unwrap();
101
102 let result = handler.handle(&object, operation, state).await;
103 result
104 .map(|success| serde_json::to_vec(&success).unwrap())
105 .map_err(|err| match err {
106 OperationError::Operation(err) => {
107 OperationError::Operation(serde_json::to_vec(&err).unwrap())
108 }
109 OperationError::Internal(internal) => OperationError::Internal(internal),
110 OperationError::Unhandled => OperationError::Unhandled,
111 })
112 }
113 .boxed()
114 }))
115 }
116
117 async fn handle(
118 &mut self,
119 object: AnyObject,
120 operation: AnyOperation,
121 state: S,
122 ) -> Result<Vec<u8>, OperationError<Vec<u8>>> {
123 self.0(object, operation, state).await
124 }
125 }
126
127 pub fn user_get_repositories(
128 object: &User,
129 _operation: UserRepositoriesRequest,
130 state: DatabaseBackend,
131 ) -> BoxFuture<'static, Result<Vec<RepositorySummary>, OperationError<UserError>>> {
132 let object = object.clone();
133
134 async move {
135 let mut user_backend = state.user_backend.lock().await;
136 let repositories = user_backend
137 .repositories_for_user(None, &object)
138 .await
139 .map_err(|e| OperationError::Internal(e.to_string()))?;
140
141 Ok(repositories)
142 }
143 .boxed()
144 }
145
146 pub fn user_get_value(
147 object: &User,
148 operation: GetValue<AnyValue<User>>,
149 state: DatabaseBackend,
150 ) -> BoxFuture<'static, Result<AnyValue<User>, OperationError<GetValueError>>> {
151 let object = object.clone();
152
153 async move {
154 let mut user_backend = state.user_backend.lock().await;
155 let value = user_backend
156 .get_value(&object, &operation.value_name)
157 .await
158 .map_err(|e| OperationError::Internal(e.to_string()))?;
159
160 Ok(value)
161 }
162 .boxed()
163 }
164
165 pub fn user_get_setting(
166 object: &User,
167 operation: GetSetting<AnySetting>,
168 state: DatabaseBackend,
169 ) -> BoxFuture<'static, Result<AnySetting, OperationError<GetSettingError>>> {
170 let object = object.clone();
171
172 async move {
173 let mut user_backend = state.user_backend.lock().await;
174 let value = user_backend
175 .get_setting(&object, &operation.setting_name)
176 .await
177 .map_err(|e| OperationError::Internal(e.to_string()))?;
178
179 Ok(value)
180 }
181 .boxed()
182 }
183
184 pub fn user_set_setting(
185 object: &User,
186 operation: SetSetting<AnySetting>,
187 state: DatabaseBackend,
188 ) -> BoxFuture<'static, Result<(), OperationError<SetSettingError>>> {
189 let object = object.clone();
190
191 async move {
192 let mut user_backend = state.user_backend.lock().await;
193 let value = user_backend
194 .write_setting(&object, &operation.setting_name, &operation.value.0)
195 .await
196 .map_err(|e| OperationError::Internal(e.to_string()))?;
197
198 Ok(value)
199 }
200 .boxed()
201 }
202
203 pub fn repository_info(
204 object: &Repository,
205 operation: RepositoryInfoRequest,
206 state: DatabaseBackend,
207 ) -> BoxFuture<'static, Result<RepositoryView, OperationError<RepositoryError>>> {
208 let object = object.clone();
209
210 async move {
211 let mut object = state
212 .get_object::<Repository>(&object.to_string())
213 .await
214 .unwrap();
215
216 let mut repository_backend = state.repository_backend.lock().await;
217 let tree = repository_backend
218 .repository_file_inspect(
219 None,
220 object.object(),
221 &RepositoryFileInspectRequest {
222 extra_metadata: operation.extra_metadata,
223 path: operation.path,
224 rev: operation.rev.clone(),
225 },
226 )
227 .await
228 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
229 drop(repository_backend);
230
231 let info = RepositoryView {
232 name: object.object().name.clone(),
233 owner: object.object().owner.clone(),
234 description: object.get::<Description>().await.ok(),
235 visibility: object
236 .get::<Visibility>()
237 .await
238 .map_err(|e| OperationError::Internal(format!("{:?}: {}", e.source(), e)))?,
239 default_branch: object
240 .get::<DefaultBranch>()
241 .await
242 .map_err(|e| OperationError::Internal(format!("{:?}: {}", e.source(), e)))?,
243 // TODO: Can't be a simple get function, this needs to be returned alongside the tree as this differs depending on the rev and path.
244 latest_commit: object.get::<LatestCommit>().await.ok(),
245 tree_rev: operation.rev,
246 tree,
247 };
248
249 Ok(info)
250 }
251 .boxed()
252 }
253
254 pub fn repository_file_from_id(
255 object: &Repository,
256 operation: RepositoryFileFromIdRequest,
257 state: DatabaseBackend,
258 ) -> BoxFuture<'static, Result<RepositoryFile, OperationError<RepositoryError>>> {
259 let object = object.clone();
260
261 async move {
262 let object = state
263 .get_object::<Repository>(&object.to_string())
264 .await
265 .unwrap();
266
267 let mut repository_backend = state.repository_backend.lock().await;
268 let file = repository_backend
269 .repository_file_from_id(
270 None,
271 object.object(),
272 &RepositoryFileFromIdRequest(operation.0),
273 )
274 .await
275 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
276 drop(repository_backend);
277
278 Ok(file)
279 }.boxed()
280 }
281
282 pub fn repository_diff(
283 object: &Repository,
284 operation: RepositoryDiffRequest,
285 state: DatabaseBackend,
286 ) -> BoxFuture<'static, Result<RepositoryDiff, OperationError<RepositoryError>>> {
287 let object = object.clone();
288
289 async move {
290 let object = state
291 .get_object::<Repository>(&object.to_string())
292 .await
293 .unwrap();
294
295 let mut repository_backend = state.repository_backend.lock().await;
296 let file = repository_backend
297 .repository_diff(
298 None,
299 object.object(),
300 &operation,
301 )
302 .await
303 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
304 drop(repository_backend);
305
306 Ok(file)
307 }.boxed()
308 }
309
310 pub fn repository_commit_before(
311 object: &Repository,
312 operation: RepositoryCommitBeforeRequest,
313 state: DatabaseBackend,
314 ) -> BoxFuture<'static, Result<Commit, OperationError<RepositoryError>>> {
315 let object = object.clone();
316
317 async move {
318 let object = state
319 .get_object::<Repository>(&object.to_string())
320 .await
321 .unwrap();
322
323 let mut repository_backend = state.repository_backend.lock().await;
324 let file = repository_backend
325 .repository_commit_before(
326 None,
327 object.object(),
328 &operation,
329 )
330 .await
331 .map_err(|err| OperationError::Internal(format!("{:?}", err)))?;
332 drop(repository_backend);
333
334 Ok(file)
335 }.boxed()
336 }
337
338 pub fn repository_get_value(
339 object: &Repository,
340 operation: GetValue<AnyValue<Repository>>,
341 state: DatabaseBackend,
342 ) -> BoxFuture<'static, Result<AnyValue<Repository>, OperationError<GetValueError>>> {
343 let object = object.clone();
344
345 async move {
346 let mut repository_backend = state.repository_backend.lock().await;
347 let value = repository_backend
348 .get_value(&object, &operation.value_name)
349 .await
350 .map_err(|e| {
351 OperationError::Internal(format!("error getting value: {}", e.to_string()))
352 })?;
353
354 Ok(value)
355 }
356 .boxed()
357 }
358
359 pub fn repository_get_setting(
360 object: &Repository,
361 operation: GetSetting<AnySetting>,
362 state: DatabaseBackend,
363 ) -> BoxFuture<'static, Result<AnySetting, OperationError<GetSettingError>>> {
364 let object = object.clone();
365
366 async move {
367 let mut repository_backend = state.repository_backend.lock().await;
368 let value = repository_backend
369 .get_setting(&object, &operation.setting_name)
370 .await
371 .map_err(|e| OperationError::Internal(e.to_string()))?;
372
373 Ok(value)
374 }
375 .boxed()
376 }
377
378 pub fn repository_set_setting(
379 object: &Repository,
380 operation: SetSetting<AnySetting>,
381 state: DatabaseBackend,
382 ) -> BoxFuture<'static, Result<(), OperationError<SetSettingError>>> {
383 let object = object.clone();
384
385 async move {
386 let mut repository_backend = state.repository_backend.lock().await;
387 let value = repository_backend
388 .write_setting(&object, &operation.setting_name, &operation.value.0)
389 .await
390 .map_err(|e| OperationError::Internal(e.to_string()))?;
391
392 Ok(value)
393 }
394 .boxed()
395 }
396
397 pub struct OperationHandlers<S: Send + Sync + Clone> {
398 operations: HashMap<String, OperationWrapper<S>>,
399 }
400
401 impl<S: Send + Sync + Clone> Default for OperationHandlers<S> {
402 fn default() -> Self {
403 Self {
404 operations: HashMap::new(),
405 }
406 }
407 }
408
409 impl<S: Send + Sync + Clone + 'static> OperationHandlers<S> {
410 pub fn insert<
411 O: GiteratedObject + Send + Sync,
412 D: GiteratedOperation<O> + 'static,
413 H: GiteratedOperationHandler<O, D, S> + Send + Sync + 'static + Clone,
414 >(
415 &mut self,
416 handler: H,
417 ) -> &mut Self {
418 let operation_name = handler.operation_name().to_string();
419
420 let wrapped = OperationWrapper::new(handler);
421
422 self.operations.insert(operation_name, wrapped);
423
424 self
425 }
426
427 pub async fn handle<O: GiteratedObject>(
428 &mut self,
429 object: &O,
430 operation_name: &str,
431 operation: AnyOperation,
432 state: S,
433 ) -> Result<Vec<u8>, OperationError<Vec<u8>>> {
434 if let Some(handler) = self.operations.get_mut(operation_name) {
435 handler
436 .handle(AnyObject(object.to_string()), operation, state)
437 .await
438 } else {
439 Err(OperationError::Internal(format!(
440 "unknown operation: {}",
441 operation_name
442 )))
443 }
444 }
445 }
446