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

ambee/giterated

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

Add CommitBodyType option

erremilia - ⁨2⁩ years ago

parent: tbd commit: ⁨6671085

⁨giterated-daemon/src/database_backend/mod.rs⁩ - ⁨6285⁩ 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, CommitBodyType};
11 use giterated_models::user::{Bio, DisplayName, User};
12 use giterated_stack::provider::MetadataProvider;
13 use giterated_stack::{AnyObject, AnySetting, GiteratedStack, ObjectMeta, SubstackBuilder};
14 use giterated_stack::{SettingMeta, StackOperationState};
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<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<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, StackOperationState> {
60 let mut builder = SubstackBuilder::<Self, StackOperationState>::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 .value_setting::<Repository, CommitBodyType>();
78
79 // builder.value(repository_latest_commit);
80
81 builder
82 .operation(user_get_repositories)
83 .operation(instance_registration_request)
84 .operation(instance_authentication_request)
85 .operation(instance_create_repository_request)
86 .operation(repository_info)
87 .operation(repository_get_statistics)
88 .operation(repository_file_from_id)
89 .operation(repository_file_from_path)
90 .operation(repository_last_commit_of_file)
91 .operation(repository_commit_by_id)
92 .operation(repository_diff)
93 .operation(repository_diff_patch)
94 .operation(repository_commit_before)
95 .operation(repository_get_branches);
96
97 builder
98 }
99 }
100
101 impl Debug for DatabaseBackend {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("DatabaseBackend").finish()
104 }
105 }
106
107 #[async_trait::async_trait]
108 impl MetadataProvider for DatabaseBackend {
109 fn provides_for(&self, object: &dyn Any) -> bool {
110 object.is::<Instance>() || object.is::<Repository>() || object.is::<User>()
111 }
112
113 async fn write(
114 &self,
115 object: AnyObject,
116 _object_meta: &ObjectMeta,
117 setting: AnySetting,
118 setting_meta: &SettingMeta,
119 ) -> Result<(), anyhow::Error> {
120 if let Some(repository) = object.downcast_ref::<Repository>() {
121 sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3",
122 repository.to_string(), setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?)
123 .execute(&self.pool).await?;
124
125 Ok(())
126 } else if let Some(user) = object.downcast_ref::<User>() {
127 sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
128 user.username, setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?)
129 .execute(&self.pool).await?;
130
131 Ok(())
132 } else {
133 unreachable!()
134 }
135 }
136
137 async fn read(
138 &self,
139 object: AnyObject,
140 _object_meta: &ObjectMeta,
141 setting_meta: &SettingMeta,
142 ) -> Result<Value, anyhow::Error> {
143 if let Some(repository) = object.downcast_ref::<Repository>() {
144 let row = sqlx::query_as!(
145 RepositorySettingRow,
146 "SELECT * FROM repository_settings WHERE repository = $1 AND name = $2",
147 repository.to_string(),
148 setting_meta.name
149 )
150 .fetch_one(&self.pool)
151 .await?;
152
153 let setting =
154 serde_json::from_str(&row.value).context("deserializing setting from database")?;
155
156 Ok(setting)
157 } else if let Some(user) = object.downcast_ref::<User>() {
158 info!("User for {}", setting_meta.name);
159 let row = sqlx::query_as!(
160 UserSettingRow,
161 "SELECT * FROM user_settings WHERE username = $1 AND name = $2",
162 user.username,
163 setting_meta.name
164 )
165 .fetch_one(&self.pool)
166 .await?;
167
168 let setting =
169 serde_json::from_str(&row.value).context("deserializing setting from database")?;
170
171 Ok(setting)
172 } else {
173 unreachable!()
174 }
175 }
176 }
177