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

ambee/giterated

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

Major refactor to handler traits

Added `IntoGiteratedHandler`, which is the new trait for handler functions. This allows us to finally get rid of the Object and ObjectOperation bounds that resulted in hacks around the newer features of the unified stack. Squashed commit of the following: commit 62e1ecf76ee31cda0bab4602d9d00fa0dc2f9158 Author: Amber <[email protected]> Date: Wed Oct 11 09:31:11 2023 -0500 Update commit dfd2d1b0b5d81ee3bc48f0321c6aceaa677e3b8b Author: Amber <[email protected]> Date: Wed Oct 11 09:31:07 2023 -0500 Major refactor to handler traits Added `IntoGiteratedHandler`, which is the new trait for handler functions. This allows us to finally get rid of the Object and ObjectOperation bounds that resulted in hacks around the newer features of the unified stack. Removed dead and legacy code. I think... commit 57b4b398eff32e69f2f4b9700e42a1277a4d1055 Author: Amber <[email protected]> Date: Sun Oct 1 23:05:10 2023 -0500 New handler trait for giterated stack Refactor the old handler trait so it is more generic and can be used for specific kinds of handlers

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨90c4780

⁨giterated-daemon/src/database_backend/mod.rs⁩ - ⁨6110⁩ bytes
Raw
1 pub mod handler;
2 pub mod updates;
3
4 use std::any::Any;
5 use std::sync::Arc;
6
7 use anyhow::Context;
8
9 use giterated_models::instance::Instance;
10 use giterated_models::repository::{DefaultBranch, Description, Repository, Visibility};
11 use giterated_models::user::{Bio, DisplayName, User};
12 use giterated_stack::provider::MetadataProvider;
13 use giterated_stack::SettingMeta;
14 use giterated_stack::{AnyObject, AnySetting, GiteratedStack, ObjectMeta, SubstackBuilder};
15 use serde_json::Value;
16 use sqlx::PgPool;
17 use std::fmt::Debug;
18 use tokio::sync::{Mutex, OnceCell};
19
20 use crate::backend::settings::{RepositorySettingRow, UserSettingRow};
21 use crate::backend::{RepositoryBackend, UserBackend};
22
23 use self::handler::{
24 instance_authentication_request, instance_create_repository_request,
25 instance_registration_request, repository_commit_before, repository_commit_by_id,
26 repository_diff, repository_diff_patch, repository_file_from_id, repository_file_from_path,
27 repository_get_branches, repository_get_statistics, repository_info,
28 repository_last_commit_of_file, user_get_repositories,
29 };
30
31 /// A backend implementation which attempts to resolve data from the instance's database.
32 #[derive(Clone)]
33 #[allow(unused)]
34 pub struct DatabaseBackend {
35 pub(self) our_instance: Instance,
36 pub(self) pool: PgPool,
37 pub(self) user_backend: Arc<Mutex<dyn UserBackend + Send>>,
38 pub(self) repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
39 pub stack: Arc<OnceCell<Arc<GiteratedStack>>>,
40 }
41
42 impl DatabaseBackend {
43 pub fn new(
44 instance: Instance,
45 user_backend: Arc<Mutex<dyn UserBackend + Send>>,
46 repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
47 pool: PgPool,
48 stack: Arc<OnceCell<Arc<GiteratedStack>>>,
49 ) -> Self {
50 Self {
51 our_instance: instance,
52 user_backend,
53 repository_backend,
54 pool,
55 stack,
56 }
57 }
58
59 pub fn into_substack(self) -> SubstackBuilder<Self> {
60 let mut builder = SubstackBuilder::<Self>::new(self.clone());
61
62 builder.object_metadata_provider(Box::new(self));
63
64 builder
65 .object::<Repository>()
66 .object::<User>()
67 .object::<Instance>();
68
69 // Register value settings, which are settings that directly correspond to
70 // value types.
71 builder
72 .value_setting::<User, DisplayName>()
73 .value_setting::<User, Bio>()
74 .value_setting::<Repository, Description>()
75 .value_setting::<Repository, Visibility>()
76 .value_setting::<Repository, DefaultBranch>();
77
78 builder
79 .operation(user_get_repositories)
80 .operation(instance_registration_request)
81 .operation(instance_authentication_request)
82 .operation(instance_create_repository_request)
83 .operation(repository_info)
84 .operation(repository_get_statistics)
85 .operation(repository_file_from_id)
86 .operation(repository_file_from_path)
87 .operation(repository_last_commit_of_file)
88 .operation(repository_commit_by_id)
89 .operation(repository_diff)
90 .operation(repository_diff_patch)
91 .operation(repository_commit_before)
92 .operation(repository_get_branches);
93
94 builder
95 }
96 }
97
98 impl Debug for DatabaseBackend {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 f.debug_struct("DatabaseBackend").finish()
101 }
102 }
103
104 #[async_trait::async_trait]
105 impl MetadataProvider for DatabaseBackend {
106 fn provides_for(&self, object: &dyn Any) -> bool {
107 object.is::<Instance>() || object.is::<Repository>() || object.is::<User>()
108 }
109
110 async fn write(
111 &self,
112 object: AnyObject,
113 _object_meta: &ObjectMeta,
114 setting: AnySetting,
115 setting_meta: &SettingMeta,
116 ) -> Result<(), anyhow::Error> {
117 if let Some(repository) = object.0.downcast_ref::<Repository>() {
118 sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3",
119 repository.to_string(), setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?)
120 .execute(&self.pool).await?;
121
122 Ok(())
123 } else if let Some(user) = object.0.downcast_ref::<User>() {
124 sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
125 user.username, setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?)
126 .execute(&self.pool).await?;
127
128 Ok(())
129 } else {
130 unreachable!()
131 }
132 }
133
134 async fn read(
135 &self,
136 object: AnyObject,
137 _object_meta: &ObjectMeta,
138 setting_meta: &SettingMeta,
139 ) -> Result<Value, anyhow::Error> {
140 if let Some(repository) = object.0.downcast_ref::<Repository>() {
141 let row = sqlx::query_as!(
142 RepositorySettingRow,
143 "SELECT * FROM repository_settings WHERE repository = $1 AND name = $2",
144 repository.to_string(),
145 setting_meta.name
146 )
147 .fetch_one(&self.pool)
148 .await?;
149
150 let setting =
151 serde_json::from_str(&row.value).context("deserializing setting from database")?;
152
153 Ok(setting)
154 } else if let Some(user) = object.0.downcast_ref::<User>() {
155 info!("User for {}", setting_meta.name);
156 let row = sqlx::query_as!(
157 UserSettingRow,
158 "SELECT * FROM user_settings WHERE username = $1 AND name = $2",
159 user.username,
160 setting_meta.name
161 )
162 .fetch_one(&self.pool)
163 .await?;
164
165 let setting =
166 serde_json::from_str(&row.value).context("deserializing setting from database")?;
167
168 Ok(setting)
169 } else {
170 unreachable!()
171 }
172 }
173 }
174