Giterated Stack `ObjectValue` and `Setting` refactor.
This refactor adds value and setting update events, as well as value getters. Additionally, the stack is now the owner of the ability to write settings into storage. This is accomplished with the `MetadataProvider` trait. This sets up the ground work for push federation, cache, and basically everything else. commit 7befc583cb3e0c6719506c550ed66ac76293413c Author: Amber <[email protected]> Date: Fri Sep 29 15:46:48 2023 -0500 Finish value and settings refactor in the stack. commit 3ac09994a0caafd1a0b95d9a781c7f202f20e75b Author: Amber <[email protected]> Date: Fri Sep 29 09:46:32 2023 -0500 Add set_setting handling back in commit 84fd31e3eae85d98fa68a28b333dbb32cde3bdb8 Author: Amber <[email protected]> Date: Wed Sep 27 06:36:31 2023 -0500 Remove some allocations from meta types commit 16c310ce3680c4a14ed35083b6a230aaecd43152 Author: Amber <[email protected]> Date: Wed Sep 27 05:35:03 2023 -0500 Add cargo metadata commit eb2520a20001bac7b21c6c3d34f62db32f0ada80 Author: Amber <[email protected]> Date: Wed Sep 27 05:26:27 2023 -0500 Refactor setting and value management to use the unified stack. Allows for tight management, inspection, and eventing of setting and value management. commit 901fe103da0fce4b40f33b0a8b64404049ae03cf Author: Amber <[email protected]> Date: Wed Sep 27 02:38:33 2023 -0500 Set up ground work for value / settings refactor
parent: tbd commit: c377e4d
Showing 19 changed files with 832 insertions and 391 deletions
Cargo.lock
@@ -746,7 +746,7 @@ dependencies = [ | ||
746 | 746 | |
747 | 747 | [[package]] |
748 | 748 | name = "giterated-daemon" |
749 | version = "0.0.6" | |
749 | version = "0.1.0" | |
750 | 750 | dependencies = [ |
751 | 751 | "aes-gcm", |
752 | 752 | "anyhow", |
giterated-daemon/Cargo.toml
@@ -1,7 +1,14 @@ | ||
1 | 1 | [package] |
2 | 2 | name = "giterated-daemon" |
3 | version = "0.0.6" | |
3 | version = "0.1.0" | |
4 | authors = ["Amber Kowalski"] | |
4 | 5 | edition = "2021" |
6 | rust-version = "1.70.0" | |
7 | description = "Giterated's Data Models" | |
8 | homepage = "https://giterated.dev/ambee/giterated" | |
9 | repository = "https://giterated.dev/ambee/giterated" | |
10 | license = "MIT OR Apache-2.0" | |
11 | keywords = ["giterated"] | |
5 | 12 | |
6 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
7 | 14 |
giterated-daemon/src/backend/git.rs
@@ -5,21 +5,21 @@ use git2::BranchType; | ||
5 | 5 | use giterated_models::instance::{Instance, RepositoryCreateRequest}; |
6 | 6 | |
7 | 7 | use giterated_models::repository::{ |
8 | AccessList, Commit, DefaultBranch, Description, IssueLabel, LatestCommit, Repository, | |
9 | RepositoryBranch, RepositoryBranchesRequest, RepositoryChunkLine, | |
10 | RepositoryCommitBeforeRequest, RepositoryCommitFromIdRequest, RepositoryDiff, | |
11 | RepositoryDiffFile, RepositoryDiffFileChunk, RepositoryDiffFileInfo, RepositoryDiffFileStatus, | |
12 | RepositoryDiffPatchRequest, RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, | |
8 | AccessList, Commit, DefaultBranch, Description, IssueLabel, Repository, RepositoryBranch, | |
9 | RepositoryBranchesRequest, RepositoryChunkLine, RepositoryCommitBeforeRequest, | |
10 | RepositoryCommitFromIdRequest, RepositoryDiff, RepositoryDiffFile, RepositoryDiffFileChunk, | |
11 | RepositoryDiffFileInfo, RepositoryDiffFileStatus, RepositoryDiffPatchRequest, | |
12 | RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, | |
13 | 13 | RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryIssue, |
14 | 14 | RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, |
15 | 15 | RepositoryLastCommitOfFileRequest, RepositoryObjectType, RepositoryStatistics, |
16 | 16 | RepositoryStatisticsRequest, RepositoryTreeEntry, RepositoryVisibility, Visibility, |
17 | 17 | }; |
18 | use giterated_models::settings::{AnySetting, Setting}; | |
19 | use giterated_models::user::{User, UserParseError}; | |
20 | use giterated_models::value::{AnyValue, GiteratedObjectValue}; | |
21 | use giterated_stack::AuthenticatedUser; | |
22 | use serde_json::Value; | |
18 | ||
19 | use giterated_models::user::User; | |
20 | ||
21 | use giterated_stack::{AuthenticatedUser, GiteratedStack}; | |
22 | ||
23 | 23 | use sqlx::PgPool; |
24 | 24 | use std::ops::Deref; |
25 | 25 | use std::{ |
@@ -27,9 +27,9 @@ use std::{ | ||
27 | 27 | sync::Arc, |
28 | 28 | }; |
29 | 29 | use thiserror::Error; |
30 | use tokio::sync::Mutex; | |
30 | use tokio::sync::OnceCell; | |
31 | 31 | |
32 | use super::{IssuesBackend, MetadataBackend, RepositoryBackend}; | |
32 | use super::{IssuesBackend, RepositoryBackend}; | |
33 | 33 | |
34 | 34 | // TODO: Handle this |
35 | 35 | //region database structures |
@@ -52,7 +52,7 @@ impl GitRepository { | ||
52 | 52 | &self, |
53 | 53 | our_instance: &Instance, |
54 | 54 | user: &Option<AuthenticatedUser>, |
55 | settings: &Arc<Mutex<dyn MetadataBackend + Send>>, | |
55 | stack: &GiteratedStack, | |
56 | 56 | ) -> bool { |
57 | 57 | if matches!(self.visibility, RepositoryVisibility::Public) { |
58 | 58 | return true; |
@@ -70,32 +70,20 @@ impl GitRepository { | ||
70 | 70 | } |
71 | 71 | |
72 | 72 | if matches!(self.visibility, RepositoryVisibility::Private) { |
73 | // Check if the user can view | |
74 | let mut settings = settings.lock().await; | |
75 | ||
76 | let access_list = settings | |
77 | .repository_get( | |
78 | &Repository { | |
79 | owner: self.owner_user.clone(), | |
80 | name: self.name.clone(), | |
81 | instance: our_instance.clone(), | |
82 | }, | |
83 | AccessList::name(), | |
84 | ) | |
85 | .await; | |
86 | ||
87 | let access_list: AccessList = match access_list { | |
88 | Ok(list) => serde_json::from_value(list.0).unwrap(), | |
89 | Err(_) => { | |
90 | return false; | |
91 | } | |
92 | }; | |
73 | // Check if the user can view\ | |
74 | let access_list = stack | |
75 | .new_get_setting::<_, AccessList>(&Repository { | |
76 | owner: self.owner_user.clone(), | |
77 | name: self.name.clone(), | |
78 | instance: our_instance.clone(), | |
79 | }) | |
80 | .await | |
81 | .unwrap(); | |
93 | 82 | |
94 | 83 | access_list |
95 | 84 | .0 |
96 | 85 | .iter() |
97 | .find(|access_list_user| *access_list_user == user.deref()) | |
98 | .is_some() | |
86 | .any(|access_list_user| access_list_user == user.deref()) | |
99 | 87 | } else { |
100 | 88 | false |
101 | 89 | } |
@@ -165,7 +153,7 @@ pub struct GitBackend { | ||
165 | 153 | pub pg_pool: PgPool, |
166 | 154 | pub repository_folder: String, |
167 | 155 | pub instance: Instance, |
168 | pub settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>, | |
156 | pub stack: Arc<OnceCell<Arc<GiteratedStack>>>, | |
169 | 157 | } |
170 | 158 | |
171 | 159 | impl GitBackend { |
@@ -173,13 +161,15 @@ impl GitBackend { | ||
173 | 161 | pg_pool: &PgPool, |
174 | 162 | repository_folder: &str, |
175 | 163 | instance: impl ToOwned<Owned = Instance>, |
176 | settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>, | |
164 | stack: Arc<OnceCell<Arc<GiteratedStack>>>, | |
177 | 165 | ) -> Self { |
166 | let instance = instance.to_owned(); | |
167 | ||
178 | 168 | Self { |
179 | 169 | pg_pool: pg_pool.clone(), |
180 | 170 | repository_folder: repository_folder.to_string(), |
181 | instance: instance.to_owned(), | |
182 | settings_provider, | |
171 | instance, | |
172 | stack, | |
183 | 173 | } |
184 | 174 | } |
185 | 175 | |
@@ -256,7 +246,7 @@ impl GitBackend { | ||
256 | 246 | .can_user_view_repository( |
257 | 247 | &self.instance, |
258 | 248 | &Some(requester.clone()), |
259 | &self.settings_provider, | |
249 | self.stack.get().unwrap(), | |
260 | 250 | ) |
261 | 251 | .await |
262 | 252 | { |
@@ -276,7 +266,7 @@ impl GitBackend { | ||
276 | 266 | |
277 | 267 | match repository.open_git2_repository(&self.repository_folder) { |
278 | 268 | Ok(git) => Ok(git), |
279 | Err(err) => return Err(err), | |
269 | Err(err) => Err(err), | |
280 | 270 | } |
281 | 271 | } |
282 | 272 | |
@@ -368,10 +358,9 @@ impl GitBackend { | ||
368 | 358 | match git.find_branch(rev.as_ref().unwrap(), BranchType::Local) { |
369 | 359 | Ok(branch) => tree_id = branch.get().target(), |
370 | 360 | Err(_) => { |
371 | return Err(Box::new(GitBackendError::RefNotFound( | |
372 | rev.unwrap().to_string(), | |
373 | )) | |
374 | .into()) | |
361 | return Err( | |
362 | Box::new(GitBackendError::RefNotFound(rev.unwrap().to_string())).into(), | |
363 | ) | |
375 | 364 | } |
376 | 365 | } |
377 | 366 | } |
@@ -381,10 +370,7 @@ impl GitBackend { | ||
381 | 370 | } |
382 | 371 | |
383 | 372 | /// Gets the last commit in a rev |
384 | pub fn get_last_commit_in_rev( | |
385 | git: &git2::Repository, | |
386 | rev: &str, | |
387 | ) -> anyhow::Result<Commit> { | |
373 | pub fn get_last_commit_in_rev(git: &git2::Repository, rev: &str) -> anyhow::Result<Commit> { | |
388 | 374 | let oid = Self::get_oid_from_reference(git, Some(rev))?; |
389 | 375 | |
390 | 376 | // Walk through the repository commit graph starting at our rev |
@@ -392,11 +378,12 @@ impl GitBackend { | ||
392 | 378 | revwalk.set_sorting(git2::Sort::TIME)?; |
393 | 379 | revwalk.push(oid)?; |
394 | 380 | |
395 | if let Some(commit_oid) = revwalk.next() { | |
396 | if let Ok(commit_oid) = commit_oid { | |
397 | if let Ok(commit) = git.find_commit(commit_oid).map_err(|_| GitBackendError::CommitNotFound(commit_oid.to_string())) { | |
398 | return Ok(Commit::from(commit)); | |
399 | } | |
381 | if let Some(Ok(commit_oid)) = revwalk.next() { | |
382 | if let Ok(commit) = git | |
383 | .find_commit(commit_oid) | |
384 | .map_err(|_| GitBackendError::CommitNotFound(commit_oid.to_string())) | |
385 | { | |
386 | return Ok(Commit::from(commit)); | |
400 | 387 | } |
401 | 388 | } |
402 | 389 | |
@@ -416,7 +403,7 @@ impl RepositoryBackend for GitBackend { | ||
416 | 403 | .await |
417 | 404 | { |
418 | 405 | Ok(repository |
419 | .can_user_view_repository(&self.instance, requester, &self.settings_provider) | |
406 | .can_user_view_repository(&self.instance, requester, self.stack.get().unwrap()) | |
420 | 407 | .await) |
421 | 408 | } else { |
422 | 409 | Ok(false) |
@@ -468,45 +455,29 @@ impl RepositoryBackend for GitBackend { | ||
468 | 455 | request.owner.instance, request.owner.username, request.name |
469 | 456 | ); |
470 | 457 | |
458 | let stack = self.stack.get().unwrap(); | |
459 | ||
471 | 460 | let repository = Repository { |
472 | 461 | owner: request.owner.clone(), |
473 | 462 | name: request.name.clone(), |
474 | 463 | instance: request.instance.as_ref().unwrap_or(&self.instance).clone(), |
475 | 464 | }; |
476 | 465 | |
477 | let mut settings_backend = self.settings_provider.lock().await; | |
478 | settings_backend | |
479 | .repository_write( | |
466 | stack | |
467 | .write_setting( | |
480 | 468 | &repository, |
481 | Description::name(), | |
482 | AnySetting( | |
483 | serde_json::to_value(Description( | |
484 | request.description.clone().unwrap_or_default(), | |
485 | )) | |
486 | .unwrap(), | |
487 | ), | |
469 | Description(request.description.clone().unwrap_or_default()), | |
488 | 470 | ) |
489 | 471 | .await |
490 | 472 | .unwrap(); |
491 | settings_backend | |
492 | .repository_write( | |
493 | &repository, | |
494 | Visibility::name(), | |
495 | AnySetting( | |
496 | serde_json::to_value(Visibility(request.visibility.clone())).unwrap(), | |
497 | ), | |
498 | ) | |
473 | ||
474 | stack | |
475 | .write_setting(&repository, Visibility(request.visibility.clone())) | |
499 | 476 | .await |
500 | 477 | .unwrap(); |
501 | settings_backend | |
502 | .repository_write( | |
503 | &repository, | |
504 | DefaultBranch::name(), | |
505 | AnySetting( | |
506 | serde_json::to_value(DefaultBranch(request.default_branch.clone())) | |
507 | .unwrap(), | |
508 | ), | |
509 | ) | |
478 | ||
479 | stack | |
480 | .write_setting(&repository, DefaultBranch(request.default_branch.clone())) | |
510 | 481 | .await |
511 | 482 | .unwrap(); |
512 | 483 | |
@@ -526,49 +497,6 @@ impl RepositoryBackend for GitBackend { | ||
526 | 497 | } |
527 | 498 | } |
528 | 499 | |
529 | async fn get_value( | |
530 | &mut self, | |
531 | repository: &Repository, | |
532 | name: &str, | |
533 | ) -> Result<AnyValue<Repository>, Error> { | |
534 | Ok(unsafe { | |
535 | if name == Description::value_name() { | |
536 | AnyValue::from_raw(self.get_setting(repository, Description::name()).await?.0) | |
537 | } else if name == Visibility::value_name() { | |
538 | AnyValue::from_raw(self.get_setting(repository, Visibility::name()).await?.0) | |
539 | } else if name == DefaultBranch::value_name() { | |
540 | AnyValue::from_raw(self.get_setting(repository, DefaultBranch::name()).await?.0) | |
541 | } else if name == LatestCommit::value_name() { | |
542 | AnyValue::from_raw(serde_json::to_value(LatestCommit(None)).unwrap()) | |
543 | } else { | |
544 | return Err(UserParseError.into()); | |
545 | } | |
546 | }) | |
547 | } | |
548 | ||
549 | async fn get_setting( | |
550 | &mut self, | |
551 | repository: &Repository, | |
552 | name: &str, | |
553 | ) -> Result<AnySetting, Error> { | |
554 | let mut provider = self.settings_provider.lock().await; | |
555 | ||
556 | Ok(provider.repository_get(repository, name).await?) | |
557 | } | |
558 | ||
559 | async fn write_setting( | |
560 | &mut self, | |
561 | repository: &Repository, | |
562 | name: &str, | |
563 | setting: &Value, | |
564 | ) -> Result<(), Error> { | |
565 | let mut provider = self.settings_provider.lock().await; | |
566 | ||
567 | provider | |
568 | .repository_write(repository, name, AnySetting(setting.clone())) | |
569 | .await | |
570 | } | |
571 | ||
572 | 500 | async fn repository_file_inspect( |
573 | 501 | &mut self, |
574 | 502 | requester: &Option<AuthenticatedUser>, |
@@ -579,7 +507,7 @@ impl RepositoryBackend for GitBackend { | ||
579 | 507 | .open_repository_and_check_permissions(&repository.owner, &repository.name, requester) |
580 | 508 | .await?; |
581 | 509 | |
582 | let tree_id = Self::get_oid_from_reference(&git, request.rev.as_ref().map(|s| s.as_str()))?; | |
510 | let tree_id = Self::get_oid_from_reference(&git, request.rev.as_deref())?; | |
583 | 511 | |
584 | 512 | // unwrap might be dangerous? |
585 | 513 | // Get the commit from the oid |
@@ -701,7 +629,7 @@ impl RepositoryBackend for GitBackend { | ||
701 | 629 | .open_repository_and_check_permissions(&repository.owner, &repository.name, requester) |
702 | 630 | .await?; |
703 | 631 | |
704 | let tree_id = Self::get_oid_from_reference(&git, request.rev.as_ref().map(|s| s.as_str()))?; | |
632 | let tree_id = Self::get_oid_from_reference(&git, request.rev.as_deref())?; | |
705 | 633 | |
706 | 634 | // unwrap might be dangerous? |
707 | 635 | // Get the commit from the oid |
@@ -796,7 +724,7 @@ impl RepositoryBackend for GitBackend { | ||
796 | 724 | .open_repository_and_check_permissions(&repository.owner, &repository.name, requester) |
797 | 725 | .await?; |
798 | 726 | |
799 | let tree_id = Self::get_oid_from_reference(&git, request.rev.as_ref().map(|s| s.as_str()))?; | |
727 | let tree_id = Self::get_oid_from_reference(&git, request.rev.as_deref())?; | |
800 | 728 | |
801 | 729 | // unwrap might be dangerous? |
802 | 730 | // Get the commit from the oid |
@@ -842,12 +770,17 @@ impl RepositoryBackend for GitBackend { | ||
842 | 770 | }; |
843 | 771 | |
844 | 772 | // TODO: Non UTF-8? |
845 | let commit = GitBackend::get_last_commit_in_rev(&git, branch.0.get().name().unwrap()).ok(); | |
773 | let commit = | |
774 | GitBackend::get_last_commit_in_rev(&git, branch.0.get().name().unwrap()).ok(); | |
846 | 775 | |
847 | 776 | // TODO: Implement stale with configurable age |
848 | let mut stale = false; | |
849 | ||
850 | branches.push(RepositoryBranch {name:name.to_string(), stale, last_commit: commit }) | |
777 | let stale = false; | |
778 | ||
779 | branches.push(RepositoryBranch { | |
780 | name: name.to_string(), | |
781 | stale, | |
782 | last_commit: commit, | |
783 | }) | |
851 | 784 | } |
852 | 785 | |
853 | 786 | Ok(branches) |
@@ -1072,12 +1005,10 @@ impl RepositoryBackend for GitBackend { | ||
1072 | 1005 | revwalk.set_sorting(git2::Sort::TIME)?; |
1073 | 1006 | revwalk.push(commit.id())?; |
1074 | 1007 | |
1075 | if let Some(next) = revwalk.next() { | |
1076 | if let Ok(before_commit_oid) = next { | |
1077 | // Find the commit using the parsed oid | |
1078 | if let Ok(before_commit) = git.find_commit(before_commit_oid) { | |
1079 | return Ok(Commit::from(before_commit)); | |
1080 | } | |
1008 | if let Some(Ok(before_commit_oid)) = revwalk.next() { | |
1009 | // Find the commit using the parsed oid | |
1010 | if let Ok(before_commit) = git.find_commit(before_commit_oid) { | |
1011 | return Ok(Commit::from(before_commit)); | |
1081 | 1012 | } |
1082 | 1013 | } |
1083 | 1014 |
giterated-daemon/src/backend/mod.rs
@@ -96,18 +96,6 @@ pub trait RepositoryBackend { | ||
96 | 96 | repository: &Repository, |
97 | 97 | request: &RepositoryBranchesRequest, |
98 | 98 | ) -> Result<Vec<RepositoryBranch>, Error>; |
99 | async fn get_value( | |
100 | &mut self, | |
101 | user: &Repository, | |
102 | name: &str, | |
103 | ) -> Result<AnyValue<Repository>, Error>; | |
104 | async fn get_setting(&mut self, user: &Repository, name: &str) -> Result<AnySetting, Error>; | |
105 | async fn write_setting( | |
106 | &mut self, | |
107 | repository: &Repository, | |
108 | name: &str, | |
109 | setting: &Value, | |
110 | ) -> Result<(), Error>; | |
111 | 99 | async fn exists( |
112 | 100 | &mut self, |
113 | 101 | requester: &Option<AuthenticatedUser>, |
giterated-daemon/src/backend/settings.rs
@@ -1,15 +1,20 @@ | ||
1 | use std::sync::Arc; | |
2 | ||
1 | 3 | use anyhow::Error; |
2 | 4 | |
3 | 5 | use giterated_models::repository::Repository; |
4 | 6 | use giterated_models::settings::AnySetting; |
5 | 7 | use giterated_models::user::User; |
6 | 8 | |
9 | use giterated_stack::GiteratedStack; | |
7 | 10 | use sqlx::PgPool; |
11 | use tokio::sync::OnceCell; | |
8 | 12 | |
9 | 13 | use super::MetadataBackend; |
10 | 14 | |
11 | 15 | pub struct DatabaseSettings { |
12 | 16 | pub pg_pool: PgPool, |
17 | pub stack: Arc<OnceCell<Arc<GiteratedStack>>>, | |
13 | 18 | } |
14 | 19 | |
15 | 20 | #[async_trait::async_trait] |
@@ -75,7 +80,7 @@ impl MetadataBackend for DatabaseSettings { | ||
75 | 80 | |
76 | 81 | #[allow(unused)] |
77 | 82 | #[derive(Debug, sqlx::FromRow)] |
78 | struct UserSettingRow { | |
83 | pub struct UserSettingRow { | |
79 | 84 | pub username: String, |
80 | 85 | pub name: String, |
81 | 86 | pub value: String, |
@@ -83,7 +88,7 @@ struct UserSettingRow { | ||
83 | 88 | |
84 | 89 | #[allow(unused)] |
85 | 90 | #[derive(Debug, sqlx::FromRow)] |
86 | struct RepositorySettingRow { | |
91 | pub struct RepositorySettingRow { | |
87 | 92 | pub repository: String, |
88 | 93 | pub name: String, |
89 | 94 | pub value: String, |
giterated-daemon/src/backend/user.rs
@@ -240,7 +240,7 @@ impl AuthBackend for UserAuth { | ||
240 | 240 | username: user.username, |
241 | 241 | instance: self.this_instance.clone(), |
242 | 242 | }, |
243 | &source, | |
243 | source, | |
244 | 244 | ) |
245 | 245 | .await; |
246 | 246 |
giterated-daemon/src/connection/wrapper.rs
@@ -102,7 +102,7 @@ pub async fn connection_wrapper( | ||
102 | 102 | signature, |
103 | 103 | } = source |
104 | 104 | { |
105 | let public_key = key_cache.get(&instance).await.unwrap(); | |
105 | let public_key = key_cache.get(instance).await.unwrap(); | |
106 | 106 | let public_key = RsaPublicKey::from_pkcs1_pem(&public_key).unwrap(); |
107 | 107 | let verifying_key = VerifyingKey::<Sha256>::new(public_key); |
108 | 108 | |
@@ -130,7 +130,7 @@ pub async fn connection_wrapper( | ||
130 | 130 | for source in &message.source { |
131 | 131 | if let AuthenticationSource::User { user, token } = source { |
132 | 132 | // Get token |
133 | let public_key = key_cache.get(&verified_instance).await.unwrap(); | |
133 | let public_key = key_cache.get(verified_instance).await.unwrap(); | |
134 | 134 | |
135 | 135 | let token: TokenData<UserTokenMetadata> = jsonwebtoken::decode( |
136 | 136 | token.as_ref(), |
giterated-daemon/src/database_backend/handler.rs
@@ -444,44 +444,6 @@ pub fn repository_commit_before( | ||
444 | 444 | .boxed_local() |
445 | 445 | } |
446 | 446 | |
447 | pub fn repository_get_value( | |
448 | object: &Repository, | |
449 | operation: GetValueTyped<AnyValue<Repository>>, | |
450 | state: DatabaseBackend, | |
451 | ) -> LocalBoxFuture<'static, Result<AnyValue<Repository>, OperationError<GetValueError>>> { | |
452 | let object = object.clone(); | |
453 | ||
454 | async move { | |
455 | let mut repository_backend = state.repository_backend.lock().await; | |
456 | let value = repository_backend | |
457 | .get_value(&object, &operation.value_name) | |
458 | .await | |
459 | .as_internal_error()?; | |
460 | ||
461 | Ok(value) | |
462 | } | |
463 | .boxed_local() | |
464 | } | |
465 | ||
466 | pub fn repository_get_setting( | |
467 | object: &Repository, | |
468 | operation: GetSetting, | |
469 | state: DatabaseBackend, | |
470 | ) -> LocalBoxFuture<'static, Result<Value, OperationError<GetSettingError>>> { | |
471 | let object = object.clone(); | |
472 | ||
473 | async move { | |
474 | let mut repository_backend = state.repository_backend.lock().await; | |
475 | let value = repository_backend | |
476 | .get_setting(&object, &operation.setting_name) | |
477 | .await | |
478 | .as_internal_error()?; | |
479 | ||
480 | Ok(value.0) | |
481 | } | |
482 | .boxed_local() | |
483 | } | |
484 | ||
485 | 447 | pub fn instance_authentication_request( |
486 | 448 | object: &Instance, |
487 | 449 | operation: AuthenticationTokenRequest, |
@@ -534,121 +496,106 @@ pub fn instance_create_repository_request( | ||
534 | 496 | |
535 | 497 | pub fn user_get_value_display_name( |
536 | 498 | object: &User, |
537 | operation: GetValueTyped<DisplayName>, | |
538 | state: DatabaseBackend, | |
539 | // _requester: AuthorizedUser, | |
499 | _operation: GetValueTyped<DisplayName>, | |
500 | _state: DatabaseBackend, | |
501 | stack: Arc<GiteratedStack>, // _requester: AuthorizedUser, | |
540 | 502 | ) -> LocalBoxFuture<'static, Result<DisplayName, OperationError<GetValueError>>> { |
541 | 503 | let object = object.clone(); |
542 | 504 | |
543 | 505 | async move { |
544 | let mut backend = state.user_backend.lock().await; | |
545 | ||
546 | let raw_value = backend | |
547 | .get_value(&object, &operation.value_name) | |
506 | stack | |
507 | .new_get_setting::<_, DisplayName>(&object) | |
548 | 508 | .await |
549 | .as_internal_error()?; | |
550 | ||
551 | Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) | |
509 | .as_internal_error() | |
552 | 510 | } |
553 | 511 | .boxed_local() |
554 | 512 | } |
555 | 513 | |
556 | 514 | pub fn user_get_value_bio( |
557 | 515 | object: &User, |
558 | operation: GetValueTyped<Bio>, | |
559 | state: DatabaseBackend, | |
516 | _operation: GetValueTyped<Bio>, | |
517 | _state: DatabaseBackend, | |
518 | stack: Arc<GiteratedStack>, | |
560 | 519 | ) -> LocalBoxFuture<'static, Result<Bio, OperationError<GetValueError>>> { |
561 | 520 | let object = object.clone(); |
562 | 521 | |
563 | 522 | async move { |
564 | let mut backend = state.user_backend.lock().await; | |
565 | ||
566 | let raw_value = backend | |
567 | .get_value(&object, &operation.value_name) | |
523 | stack | |
524 | .new_get_setting::<_, Bio>(&object) | |
568 | 525 | .await |
569 | .as_internal_error()?; | |
570 | ||
571 | Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) | |
526 | .as_internal_error() | |
572 | 527 | } |
573 | 528 | .boxed_local() |
574 | 529 | } |
575 | 530 | |
576 | 531 | pub fn repository_get_value_description( |
577 | 532 | object: &Repository, |
578 | operation: GetValueTyped<Description>, | |
579 | state: DatabaseBackend, | |
533 | _operation: GetValueTyped<Description>, | |
534 | _state: DatabaseBackend, | |
535 | stack: Arc<GiteratedStack>, | |
580 | 536 | ) -> LocalBoxFuture<'static, Result<Description, OperationError<GetValueError>>> { |
581 | 537 | let object = object.clone(); |
582 | 538 | |
583 | 539 | async move { |
584 | let mut backend = state.repository_backend.lock().await; | |
585 | ||
586 | let raw_value = backend | |
587 | .get_value(&object, &operation.value_name) | |
540 | stack | |
541 | .new_get_setting::<_, Description>(&object) | |
588 | 542 | .await |
589 | .as_internal_error()?; | |
590 | ||
591 | Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) | |
543 | .as_internal_error() | |
592 | 544 | } |
593 | 545 | .boxed_local() |
594 | 546 | } |
595 | 547 | |
596 | 548 | pub fn repository_get_value_visibility( |
597 | 549 | object: &Repository, |
598 | operation: GetValueTyped<Visibility>, | |
599 | state: DatabaseBackend, | |
550 | _operation: GetValueTyped<Visibility>, | |
551 | _state: DatabaseBackend, | |
552 | stack: Arc<GiteratedStack>, | |
600 | 553 | ) -> LocalBoxFuture<'static, Result<Visibility, OperationError<GetValueError>>> { |
601 | 554 | let object = object.clone(); |
602 | 555 | |
603 | 556 | async move { |
604 | let mut backend = state.repository_backend.lock().await; | |
605 | ||
606 | let raw_value = backend | |
607 | .get_value(&object, &operation.value_name) | |
557 | stack | |
558 | .new_get_setting::<_, Visibility>(&object) | |
608 | 559 | .await |
609 | .as_internal_error()?; | |
610 | ||
611 | Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) | |
560 | .as_internal_error() | |
612 | 561 | } |
613 | 562 | .boxed_local() |
614 | 563 | } |
615 | 564 | |
616 | 565 | pub fn repository_get_default_branch( |
617 | 566 | object: &Repository, |
618 | operation: GetValueTyped<DefaultBranch>, | |
619 | state: DatabaseBackend, | |
567 | _operation: GetValueTyped<DefaultBranch>, | |
568 | _state: DatabaseBackend, | |
569 | stack: Arc<GiteratedStack>, | |
620 | 570 | ) -> LocalBoxFuture<'static, Result<DefaultBranch, OperationError<GetValueError>>> { |
621 | 571 | let object = object.clone(); |
622 | 572 | |
623 | 573 | async move { |
624 | let mut backend = state.repository_backend.lock().await; | |
625 | ||
626 | let raw_value = backend | |
627 | .get_value(&object, &operation.value_name) | |
574 | stack | |
575 | .new_get_setting::<_, DefaultBranch>(&object) | |
628 | 576 | .await |
629 | .as_internal_error()?; | |
630 | ||
631 | Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) | |
577 | .as_internal_error() | |
632 | 578 | } |
633 | 579 | .boxed_local() |
634 | 580 | } |
635 | 581 | |
636 | 582 | pub fn repository_get_latest_commit( |
637 | 583 | object: &Repository, |
638 | operation: GetValueTyped<LatestCommit>, | |
584 | _operation: GetValueTyped<LatestCommit>, | |
639 | 585 | state: DatabaseBackend, |
586 | _stack: Arc<GiteratedStack>, | |
640 | 587 | ) -> LocalBoxFuture<'static, Result<LatestCommit, OperationError<GetValueError>>> { |
641 | let object = object.clone(); | |
588 | let _object = object.clone(); | |
642 | 589 | |
643 | 590 | async move { |
644 | let mut backend = state.repository_backend.lock().await; | |
591 | let _backend = state.repository_backend.lock().await; | |
645 | 592 | |
646 | let raw_value = backend | |
647 | .get_value(&object, &operation.value_name) | |
648 | .await | |
649 | .as_internal_error()?; | |
593 | // stack | |
594 | // .new_get_setting::<_, LatestCommit>(&*object) | |
595 | // .await | |
596 | // .as_internal_error() | |
650 | 597 | |
651 | Ok(serde_json::from_value(raw_value.into_inner()).as_internal_error()?) | |
598 | todo!() | |
652 | 599 | } |
653 | 600 | .boxed_local() |
654 | 601 | } |
giterated-daemon/src/database_backend/mod.rs
@@ -1,8 +1,10 @@ | ||
1 | 1 | pub mod handler; |
2 | 2 | pub mod updates; |
3 | 3 | |
4 | use std::any::Any; | |
4 | 5 | use std::sync::Arc; |
5 | 6 | |
7 | use anyhow::Context; | |
6 | 8 | use giterated_models::error::OperationError; |
7 | 9 | use giterated_models::instance::Instance; |
8 | 10 | use giterated_models::object::{GiteratedObject, Object, ObjectRequestError}; |
@@ -10,11 +12,15 @@ use giterated_models::object_backend::ObjectBackend; | ||
10 | 12 | use giterated_models::operation::GiteratedOperation; |
11 | 13 | use giterated_models::repository::{DefaultBranch, Description, Repository, Visibility}; |
12 | 14 | use giterated_models::user::{Bio, DisplayName, User}; |
13 | use giterated_stack::StackOperationState; | |
14 | use giterated_stack::SubstackBuilder; | |
15 | use giterated_stack::provider::MetadataProvider; | |
16 | use giterated_stack::{GiteratedStack, ObjectMeta, SubstackBuilder}; | |
17 | use giterated_stack::{SettingMeta, StackOperationState}; | |
18 | use serde_json::Value; | |
19 | use sqlx::PgPool; | |
15 | 20 | use std::fmt::Debug; |
16 | use tokio::sync::Mutex; | |
21 | use tokio::sync::{Mutex, OnceCell}; | |
17 | 22 | |
23 | use crate::backend::settings::{RepositorySettingRow, UserSettingRow}; | |
18 | 24 | use crate::backend::{RepositoryBackend, UserBackend}; |
19 | 25 | |
20 | 26 | use self::handler::{ |
@@ -22,9 +28,9 @@ use self::handler::{ | ||
22 | 28 | instance_registration_request, repository_commit_before, repository_commit_by_id, |
23 | 29 | repository_diff, repository_diff_patch, repository_file_from_id, repository_file_from_path, |
24 | 30 | repository_get_branches, repository_get_default_branch, repository_get_latest_commit, |
25 | repository_get_setting, repository_get_statistics, repository_get_value_description, | |
26 | repository_get_value_visibility, repository_info, repository_last_commit_of_file, | |
27 | user_get_repositories, user_get_setting, user_get_value_bio, user_get_value_display_name, | |
31 | repository_get_statistics, repository_get_value_description, repository_get_value_visibility, | |
32 | repository_info, repository_last_commit_of_file, user_get_repositories, user_get_value_bio, | |
33 | user_get_value_display_name, | |
28 | 34 | }; |
29 | 35 | |
30 | 36 | #[derive(Clone, Debug)] |
@@ -57,8 +63,10 @@ impl ObjectBackend<StackOperationState> for Foobackend { | ||
57 | 63 | #[allow(unused)] |
58 | 64 | pub struct DatabaseBackend { |
59 | 65 | pub(self) our_instance: Instance, |
66 | pub(self) pool: PgPool, | |
60 | 67 | pub(self) user_backend: Arc<Mutex<dyn UserBackend + Send>>, |
61 | 68 | pub(self) repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>, |
69 | pub stack: Arc<OnceCell<Arc<GiteratedStack>>>, | |
62 | 70 | } |
63 | 71 | |
64 | 72 | impl DatabaseBackend { |
@@ -66,30 +74,36 @@ impl DatabaseBackend { | ||
66 | 74 | instance: Instance, |
67 | 75 | user_backend: Arc<Mutex<dyn UserBackend + Send>>, |
68 | 76 | repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>, |
77 | pool: PgPool, | |
78 | stack: Arc<OnceCell<Arc<GiteratedStack>>>, | |
69 | 79 | ) -> Self { |
70 | 80 | Self { |
71 | 81 | our_instance: instance, |
72 | 82 | user_backend, |
73 | 83 | repository_backend, |
84 | pool, | |
85 | stack, | |
74 | 86 | } |
75 | 87 | } |
76 | 88 | |
77 | 89 | pub fn into_substack(self) -> SubstackBuilder<Self> { |
78 | let mut builder = SubstackBuilder::<Self>::new(self); | |
90 | let mut builder = SubstackBuilder::<Self>::new(self.clone()); | |
91 | ||
92 | builder.object_metadata_provider(Box::new(self)); | |
79 | 93 | |
80 | 94 | builder |
81 | 95 | .object::<Repository>() |
82 | .object_settings(repository_get_setting) | |
83 | 96 | .object::<User>() |
84 | .object_settings(user_get_setting) | |
85 | 97 | .object::<Instance>(); |
86 | 98 | |
99 | // Register value settings, which are settings that directly correspond to | |
100 | // value types. | |
87 | 101 | builder |
88 | .setting::<DisplayName>() | |
89 | .setting::<Bio>() | |
90 | .setting::<Description>() | |
91 | .setting::<Visibility>() | |
92 | .setting::<DefaultBranch>(); | |
102 | .value_setting::<User, DisplayName>() | |
103 | .value_setting::<User, Bio>() | |
104 | .value_setting::<Repository, Description>() | |
105 | .value_setting::<Repository, Visibility>() | |
106 | .value_setting::<Repository, DefaultBranch>(); | |
93 | 107 | |
94 | 108 | builder |
95 | 109 | .value(user_get_value_bio) |
@@ -124,3 +138,74 @@ impl Debug for DatabaseBackend { | ||
124 | 138 | f.debug_struct("DatabaseBackend").finish() |
125 | 139 | } |
126 | 140 | } |
141 | ||
142 | #[async_trait::async_trait] | |
143 | impl MetadataProvider for DatabaseBackend { | |
144 | fn provides_for(&self, object: &dyn Any) -> bool { | |
145 | object.is::<Instance>() || object.is::<Repository>() || object.is::<User>() | |
146 | } | |
147 | ||
148 | async fn write( | |
149 | &self, | |
150 | object: &(dyn Any + Send + Sync), | |
151 | _object_meta: &ObjectMeta, | |
152 | setting: &(dyn Any + Send + Sync), | |
153 | setting_meta: &SettingMeta, | |
154 | ) -> Result<(), anyhow::Error> { | |
155 | if let Some(repository) = object.downcast_ref::<Repository>() { | |
156 | sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3", | |
157 | repository.to_string(), setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?) | |
158 | .execute(&self.pool).await?; | |
159 | ||
160 | Ok(()) | |
161 | } else if let Some(user) = object.downcast_ref::<User>() { | |
162 | sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3", | |
163 | user.username, setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?) | |
164 | .execute(&self.pool).await?; | |
165 | ||
166 | Ok(()) | |
167 | } else { | |
168 | unreachable!() | |
169 | } | |
170 | } | |
171 | ||
172 | async fn read( | |
173 | &self, | |
174 | object: &(dyn Any + Send + Sync), | |
175 | _object_meta: &ObjectMeta, | |
176 | setting_meta: &SettingMeta, | |
177 | ) -> Result<Value, anyhow::Error> { | |
178 | if let Some(repository) = object.downcast_ref::<Repository>() { | |
179 | let row = sqlx::query_as!( | |
180 | RepositorySettingRow, | |
181 | "SELECT * FROM repository_settings WHERE repository = $1 AND name = $2", | |
182 | repository.to_string(), | |
183 | setting_meta.name | |
184 | ) | |
185 | .fetch_one(&self.pool) | |
186 | .await?; | |
187 | ||
188 | let setting = | |
189 | serde_json::from_str(&row.value).context("deserializing setting from database")?; | |
190 | ||
191 | Ok(setting) | |
192 | } else if let Some(user) = object.downcast_ref::<User>() { | |
193 | info!("User for {}", setting_meta.name); | |
194 | let row = sqlx::query_as!( | |
195 | UserSettingRow, | |
196 | "SELECT * FROM user_settings WHERE username = $1 AND name = $2", | |
197 | user.username, | |
198 | setting_meta.name | |
199 | ) | |
200 | .fetch_one(&self.pool) | |
201 | .await?; | |
202 | ||
203 | let setting = | |
204 | serde_json::from_str(&row.value).context("deserializing setting from database")?; | |
205 | ||
206 | Ok(setting) | |
207 | } else { | |
208 | unreachable!() | |
209 | } | |
210 | } | |
211 | } |
giterated-daemon/src/database_backend/updates.rs
@@ -1,15 +1,12 @@ | ||
1 | 1 | use futures_util::{future::BoxFuture, FutureExt}; |
2 | 2 | use giterated_models::{ |
3 | repository::{DefaultBranch, Description, Repository}, | |
4 | settings::{AnySetting, Setting}, | |
5 | update::ValueUpdate, | |
3 | repository::{Description, Repository}, | |
4 | settings::AnySetting, | |
6 | 5 | user::User, |
7 | value::{AnyValue, GiteratedObjectValue}, | |
6 | value::AnyValue, | |
8 | 7 | }; |
9 | 8 | use giterated_stack::{AuthorizedUser, StackOperationState}; |
10 | 9 | |
11 | use super::DatabaseBackend; | |
12 | ||
13 | 10 | pub fn user_set_value( |
14 | 11 | _object: User, |
15 | 12 | _value_name: String, |
@@ -54,34 +51,26 @@ pub fn repository_set_description( | ||
54 | 51 | async { Ok(()) }.boxed() |
55 | 52 | } |
56 | 53 | |
57 | pub fn repository_set_default_branch( | |
58 | object: Repository, | |
59 | default_branch: DefaultBranch, | |
60 | // Ensure user is authorized for this request | |
61 | _user: AuthorizedUser, | |
62 | backend: DatabaseBackend, | |
63 | ) -> BoxFuture<'static, Result<(), ()>> { | |
64 | async move { | |
65 | let mut repository_backend = backend.repository_backend.lock().await; | |
54 | // pub fn repository_set_default_branch( | |
55 | // object: Repository, | |
56 | // default_branch: DefaultBranch, | |
57 | // // Ensure user is authorized for this request | |
58 | // _user: AuthorizedUser, | |
59 | // backend: DatabaseBackend, | |
60 | // stack: Arc<GiteratedStack>, | |
61 | // ) -> BoxFuture<'static, Result<(), ()>> { | |
62 | // async move { | |
63 | // stack.write_setting(&object, &default_branch).await.unwrap(); | |
66 | 64 | |
67 | repository_backend | |
68 | .write_setting( | |
69 | &object, | |
70 | DefaultBranch::name(), | |
71 | &serde_json::to_value(default_branch.clone()).unwrap(), | |
72 | ) | |
73 | .await | |
74 | .unwrap(); | |
65 | // let _set_value = ValueUpdate { | |
66 | // object: object.to_string(), | |
67 | // value_name: DefaultBranch::value_name().to_owned(), | |
68 | // value: unsafe { AnyValue::from_raw(serde_json::to_value(default_branch).unwrap()) }, | |
69 | // }; | |
75 | 70 | |
76 | let _set_value = ValueUpdate { | |
77 | object: object.to_string(), | |
78 | value_name: DefaultBranch::value_name().to_owned(), | |
79 | value: unsafe { AnyValue::from_raw(serde_json::to_value(default_branch).unwrap()) }, | |
80 | }; | |
81 | ||
82 | // Submit value update back to the daemon | |
83 | // state.value_update(set_value); | |
84 | Ok(()) | |
85 | } | |
86 | .boxed() | |
87 | } | |
71 | // // Submit value update back to the daemon | |
72 | // // state.value_update(set_value); | |
73 | // Ok(()) | |
74 | // } | |
75 | // .boxed() | |
76 | // } |
giterated-daemon/src/keys.rs
@@ -12,7 +12,7 @@ pub struct PublicKeyCache { | ||
12 | 12 | impl PublicKeyCache { |
13 | 13 | pub async fn get(&mut self, instance: &Instance) -> Result<String, Error> { |
14 | 14 | if let Some(key) = self.keys.get(instance) { |
15 | return Ok(key.clone()); | |
15 | Ok(key.clone()) | |
16 | 16 | } else { |
17 | 17 | let key = reqwest::get(format!("https://{}/.giterated/pubkey.pem", instance)) |
18 | 18 | .await? |
giterated-daemon/src/main.rs
@@ -19,7 +19,7 @@ use tokio::{ | ||
19 | 19 | fs::File, |
20 | 20 | io::{AsyncRead, AsyncReadExt, AsyncWrite}, |
21 | 21 | net::{TcpListener, TcpStream}, |
22 | sync::Mutex, | |
22 | sync::{Mutex, OnceCell}, | |
23 | 23 | }; |
24 | 24 | use tokio_tungstenite::{accept_async, WebSocketStream}; |
25 | 25 | use tokio_util::task::LocalPoolHandle; |
@@ -53,8 +53,11 @@ async fn main() -> Result<(), Error> { | ||
53 | 53 | sqlx::migrate!().run(&db_pool).await?; |
54 | 54 | info!("Connected"); |
55 | 55 | |
56 | let stack_cell = Arc::new(OnceCell::default()); | |
57 | ||
56 | 58 | let settings = Arc::new(Mutex::new(DatabaseSettings { |
57 | 59 | pg_pool: db_pool.clone(), |
60 | stack: stack_cell.clone(), | |
58 | 61 | })); |
59 | 62 | |
60 | 63 | let repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>> = |
@@ -67,7 +70,7 @@ async fn main() -> Result<(), Error> { | ||
67 | 70 | ), |
68 | 71 | instance: Instance::from_str(config["giterated"]["instance"].as_str().unwrap()) |
69 | 72 | .unwrap(), |
70 | settings_provider: settings.clone(), | |
73 | stack: stack_cell.clone(), | |
71 | 74 | })); |
72 | 75 | |
73 | 76 | let token_granter = Arc::new(Mutex::new(AuthenticationTokenGranter { |
@@ -88,6 +91,8 @@ async fn main() -> Result<(), Error> { | ||
88 | 91 | Instance::from_str(config["giterated"]["instance"].as_str().unwrap()).unwrap(), |
89 | 92 | user_backend.clone(), |
90 | 93 | repository_backend.clone(), |
94 | db_pool.clone(), | |
95 | stack_cell.clone(), | |
91 | 96 | ); |
92 | 97 | |
93 | 98 | let mut runtime = GiteratedStack::default(); |
@@ -97,6 +102,10 @@ async fn main() -> Result<(), Error> { | ||
97 | 102 | |
98 | 103 | let runtime = Arc::new(runtime); |
99 | 104 | |
105 | stack_cell | |
106 | .set(runtime.clone()) | |
107 | .expect("failed to store global daemon stack"); | |
108 | ||
100 | 109 | let operation_state = { |
101 | 110 | StackOperationState { |
102 | 111 | our_instance: Instance::from_str(config["giterated"]["instance"].as_str().unwrap()) |
giterated-models/Cargo.toml
@@ -1,7 +1,17 @@ | ||
1 | 1 | [package] |
2 | 2 | name = "giterated-models" |
3 | 3 | version = "0.1.0" |
4 | authors = ["Amber Kowalski"] | |
4 | 5 | edition = "2021" |
6 | rust-version = "1.70.0" | |
7 | description = "Giterated's Data Models" | |
8 | homepage = "https://giterated.dev/ambee/giterated" | |
9 | repository = "https://giterated.dev/ambee/giterated" | |
10 | license = "MIT OR Apache-2.0" | |
11 | keywords = ["giterated"] | |
12 | ||
13 | # Leave until MVP | |
14 | publish = false | |
5 | 15 | |
6 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
7 | 17 |
giterated-stack/Cargo.toml
@@ -1,7 +1,17 @@ | ||
1 | 1 | [package] |
2 | 2 | name = "giterated-stack" |
3 | 3 | version = "0.1.0" |
4 | authors = ["Amber Kowalski"] | |
4 | 5 | edition = "2021" |
6 | rust-version = "1.70.0" | |
7 | description = "Giterated's Unified Stack" | |
8 | homepage = "https://giterated.dev/ambee/giterated" | |
9 | repository = "https://giterated.dev/ambee/giterated" | |
10 | license = "MPL-2.0" | |
11 | keywords = ["giterated"] | |
12 | ||
13 | # Leave until MVP | |
14 | publish = false | |
5 | 15 | |
6 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
7 | 17 |
giterated-stack/src/handler.rs
@@ -1,6 +1,5 @@ | ||
1 | 1 | use std::{any::Any, collections::HashMap, sync::Arc}; |
2 | 2 | |
3 | use anyhow::Context; | |
4 | 3 | use futures_util::FutureExt; |
5 | 4 | use giterated_models::{ |
6 | 5 | authenticated::AuthenticatedPayload, |
@@ -16,11 +15,13 @@ use giterated_models::{ | ||
16 | 15 | value::{AnyValue, GetValue, GetValueTyped, GiteratedObjectValue}, |
17 | 16 | }; |
18 | 17 | |
18 | use serde::{Deserialize, Serialize}; | |
19 | 19 | use tracing::trace; |
20 | 20 | |
21 | 21 | use crate::{ |
22 | GiteratedOperationHandler, ObjectMeta, ObjectOperationPair, ObjectValuePair, OperationMeta, | |
23 | OperationWrapper, SettingMeta, StackOperationState, ValueMeta, | |
22 | provider::MetadataProvider, GiteratedOperationHandler, MissingValue, ObjectMeta, | |
23 | ObjectOperationPair, ObjectSettingPair, ObjectValuePair, OperationMeta, OperationWrapper, | |
24 | SettingMeta, SettingUpdate, StackOperationState, ValueMeta, | |
24 | 25 | }; |
25 | 26 | |
26 | 27 | /// Temporary name for the next generation of Giterated stack |
@@ -29,13 +30,25 @@ pub struct GiteratedStack { | ||
29 | 30 | operation_handlers: HashMap<ObjectOperationPair, HandlerTree>, |
30 | 31 | value_getters: HashMap<ObjectValuePair, OperationWrapper>, |
31 | 32 | setting_getters: HashMap<String, OperationWrapper>, |
33 | value_change: HashMap<ObjectValuePair, OperationWrapper>, | |
34 | setting_change: HashMap<ObjectSettingPair, OperationWrapper>, | |
35 | metadata_providers: Vec<Box<dyn MetadataProvider>>, | |
32 | 36 | metadata: RuntimeMetadata, |
33 | 37 | } |
34 | 38 | |
39 | impl Debug for GiteratedStack { | |
40 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | |
41 | f.debug_struct("GiteratedStack").finish() | |
42 | } | |
43 | } | |
44 | ||
45 | #[derive(Clone)] | |
46 | pub struct ValueChangeEvent(Arc<dyn Any + Send + Sync>); | |
47 | ||
35 | 48 | impl GiteratedStack { |
36 | 49 | pub fn merge_builder<S: GiteratedStackState>( |
37 | 50 | &mut self, |
38 | builder: SubstackBuilder<S>, | |
51 | mut builder: SubstackBuilder<S>, | |
39 | 52 | ) -> &mut Self { |
40 | 53 | for (target, handler) in builder.operation_handlers { |
41 | 54 | let tree = self.get_or_create_tree(&target); |
@@ -51,11 +64,162 @@ impl GiteratedStack { | ||
51 | 64 | assert!(self.setting_getters.insert(target, handler).is_none()); |
52 | 65 | } |
53 | 66 | |
67 | for (target, handler) in builder.value_change { | |
68 | self.value_change.insert(target, handler); | |
69 | } | |
70 | ||
71 | for (target, handler) in builder.setting_change { | |
72 | self.setting_change.insert(target, handler); | |
73 | } | |
74 | ||
75 | self.metadata_providers | |
76 | .append(&mut builder.metadata_providers); | |
77 | ||
54 | 78 | self.metadata.append(builder.metadata); |
55 | 79 | |
56 | 80 | self |
57 | 81 | } |
58 | 82 | |
83 | pub async fn value_update<O, V>( | |
84 | &self, | |
85 | object: O, | |
86 | new_value: V, | |
87 | operation_state: &StackOperationState, | |
88 | ) where | |
89 | O: GiteratedObject + 'static, | |
90 | V: GiteratedObjectValue<Object = O> + 'static, | |
91 | { | |
92 | trace!("value updated {}::{}", O::object_name(), V::value_name()); | |
93 | let target = ObjectValuePair::from_types::<O, V>(); | |
94 | ||
95 | if let Some(handler) = self.value_change.get(&target) { | |
96 | // TODO | |
97 | let _ = handler | |
98 | .handle( | |
99 | &(Box::new(object) as _), | |
100 | &(Box::new(ValueChangedShim { new_value }) as _), | |
101 | operation_state, | |
102 | ) | |
103 | .await; | |
104 | } | |
105 | } | |
106 | ||
107 | pub async fn setting_update<O, S>( | |
108 | &self, | |
109 | object: O, | |
110 | new_setting: S, | |
111 | operation_state: &StackOperationState, | |
112 | ) where | |
113 | O: GiteratedObject + 'static, | |
114 | S: Setting + 'static, | |
115 | { | |
116 | trace!("setting updated {}::{}", O::object_name(), S::name()); | |
117 | let target = ObjectSettingPair::from_types::<O, S>(); | |
118 | ||
119 | if let Some(handler) = self.setting_change.get(&target) { | |
120 | let _ = handler | |
121 | .handle( | |
122 | &(Box::new(object) as _), | |
123 | &(Box::new(SettingUpdate(new_setting)) as _), | |
124 | operation_state, | |
125 | ) | |
126 | .await; | |
127 | } | |
128 | } | |
129 | ||
130 | pub async fn new_object<O>(&self, _new_object: &O, _operation_state: &StackOperationState) | |
131 | where | |
132 | O: GiteratedObject, | |
133 | { | |
134 | // TODO | |
135 | } | |
136 | ||
137 | /// Writes a setting for the specified object. | |
138 | pub async fn write_setting<O, S>( | |
139 | &self, | |
140 | object: &O, | |
141 | setting: S, | |
142 | ) -> Result<(), OperationError<()>> | |
143 | where | |
144 | O: GiteratedObject + 'static, | |
145 | S: Setting + 'static + Clone, | |
146 | { | |
147 | for provider in self.metadata_providers.iter() { | |
148 | if provider.provides_for(object as &dyn Any) { | |
149 | let setting_meta = self | |
150 | .metadata | |
151 | .settings | |
152 | .get(&ObjectSettingPair { | |
153 | object_kind: O::object_name().to_string(), | |
154 | setting_name: S::name().to_string(), | |
155 | }) | |
156 | .ok_or_else(|| OperationError::Unhandled)?; | |
157 | ||
158 | let object_meta = self | |
159 | .metadata | |
160 | .objects | |
161 | .get(O::object_name()) | |
162 | .ok_or_else(|| OperationError::Unhandled)?; | |
163 | ||
164 | let result = provider | |
165 | .write(object, object_meta, &setting, setting_meta) | |
166 | .await | |
167 | .as_internal_error_with_context(format!("writing setting {}", S::name())); | |
168 | ||
169 | return result; | |
170 | } | |
171 | } | |
172 | ||
173 | Err(OperationError::Unhandled) | |
174 | } | |
175 | ||
176 | /// Gets a setting for the specified object. | |
177 | pub async fn new_get_setting<O, S>(&self, object: &O) -> Result<S, OperationError<MissingValue>> | |
178 | where | |
179 | O: GiteratedObject + 'static, | |
180 | S: Setting + 'static, | |
181 | { | |
182 | for provider in self.metadata_providers.iter() { | |
183 | if provider.provides_for(object as &dyn Any) { | |
184 | trace!( | |
185 | "Resolving setting {} for object {} from provider.", | |
186 | S::name(), | |
187 | O::object_name() | |
188 | ); | |
189 | ||
190 | let setting_meta = self | |
191 | .metadata | |
192 | .settings | |
193 | .get(&ObjectSettingPair { | |
194 | object_kind: O::object_name().to_string(), | |
195 | setting_name: S::name().to_string(), | |
196 | }) | |
197 | .ok_or_else(|| OperationError::Unhandled)?; | |
198 | ||
199 | let object_meta = self | |
200 | .metadata | |
201 | .objects | |
202 | .get(O::object_name()) | |
203 | .ok_or_else(|| OperationError::Unhandled)?; | |
204 | ||
205 | let value = provider | |
206 | .read(object, object_meta, setting_meta) | |
207 | .await | |
208 | .as_internal_error_with_context(format!("getting setting {}", S::name()))?; | |
209 | ||
210 | return serde_json::from_value(value) | |
211 | .as_internal_error_with_context("deserializing setting"); | |
212 | } | |
213 | } | |
214 | trace!( | |
215 | "No provider registered for setting {} and object {}", | |
216 | S::name(), | |
217 | O::object_name() | |
218 | ); | |
219 | ||
220 | Err(OperationError::Unhandled) | |
221 | } | |
222 | ||
59 | 223 | fn get_or_create_tree(&mut self, target: &ObjectOperationPair) -> &mut HandlerTree { |
60 | 224 | if self.operation_handlers.contains_key(target) { |
61 | 225 | self.operation_handlers.get_mut(target).unwrap() |
@@ -68,16 +232,11 @@ impl GiteratedStack { | ||
68 | 232 | } |
69 | 233 | } |
70 | 234 | |
235 | #[derive(Default)] | |
71 | 236 | pub struct HandlerTree { |
72 | 237 | elements: Vec<OperationWrapper>, |
73 | 238 | } |
74 | 239 | |
75 | impl Default for HandlerTree { | |
76 | fn default() -> Self { | |
77 | Self { elements: vec![] } | |
78 | } | |
79 | } | |
80 | ||
81 | 240 | impl HandlerTree { |
82 | 241 | pub fn push(&mut self, handler: OperationWrapper) { |
83 | 242 | self.elements.push(handler); |
@@ -90,7 +249,7 @@ impl HandlerTree { | ||
90 | 249 | operation_state: &StackOperationState, |
91 | 250 | ) -> Result<Box<dyn Any + 'static>, OperationError<Box<dyn Any + 'static>>> { |
92 | 251 | for handler in self.elements.iter() { |
93 | match handler.handle(object, &operation, operation_state).await { | |
252 | match handler.handle(object, operation, operation_state).await { | |
94 | 253 | Ok(success) => return Ok(success), |
95 | 254 | Err(err) => match err { |
96 | 255 | OperationError::Operation(failure) => { |
@@ -114,7 +273,7 @@ struct RuntimeMetadata { | ||
114 | 273 | objects: HashMap<String, ObjectMeta>, |
115 | 274 | operations: HashMap<ObjectOperationPair, OperationMeta>, |
116 | 275 | values: HashMap<ObjectValuePair, ValueMeta>, |
117 | settings: HashMap<String, SettingMeta>, | |
276 | settings: HashMap<ObjectSettingPair, SettingMeta>, | |
118 | 277 | } |
119 | 278 | |
120 | 279 | /// Defines a type that is a valid Giterated runtime state. |
@@ -130,6 +289,10 @@ pub struct SubstackBuilder<S: GiteratedStackState> { | ||
130 | 289 | value_getters: HashMap<ObjectValuePair, OperationWrapper>, |
131 | 290 | setting_getters: HashMap<String, OperationWrapper>, |
132 | 291 | metadata: RuntimeMetadata, |
292 | value_change: HashMap<ObjectValuePair, OperationWrapper>, | |
293 | metadata_providers: Vec<Box<dyn MetadataProvider>>, | |
294 | setting_change: HashMap<ObjectSettingPair, OperationWrapper>, | |
295 | ||
133 | 296 | state: S, |
134 | 297 | } |
135 | 298 | |
@@ -140,6 +303,9 @@ impl<S: GiteratedStackState + 'static> SubstackBuilder<S> { | ||
140 | 303 | value_getters: Default::default(), |
141 | 304 | setting_getters: Default::default(), |
142 | 305 | metadata: Default::default(), |
306 | value_change: Default::default(), | |
307 | metadata_providers: Default::default(), | |
308 | setting_change: Default::default(), | |
143 | 309 | state, |
144 | 310 | } |
145 | 311 | } |
@@ -210,8 +376,51 @@ impl<S: GiteratedStackState + 'static> SubstackBuilder<S> { | ||
210 | 376 | /// |
211 | 377 | /// # Type Registration |
212 | 378 | /// This will register the provided setting type. |
213 | pub fn setting<T: Setting + 'static>(&mut self) -> &mut Self { | |
214 | self.metadata.register_setting::<T>(); | |
379 | pub fn setting<O: GiteratedObject + 'static, T: Setting + 'static>(&mut self) -> &mut Self { | |
380 | self.metadata.register_setting::<O, T>(); | |
381 | ||
382 | self | |
383 | } | |
384 | ||
385 | /// Register a [`GiteratedObjectValue`] that is also a [`Setting`], which | |
386 | /// allows for automatic value updates. | |
387 | pub fn value_setting< | |
388 | O: GiteratedObject + 'static + Clone, | |
389 | T: GiteratedObjectValue<Object = O> + Setting + 'static + Clone, | |
390 | >( | |
391 | &mut self, | |
392 | ) -> &mut Self { | |
393 | self.metadata.register_setting::<O, T>(); | |
394 | self.metadata.register_value::<O, T>(); | |
395 | ||
396 | self.setting_change.insert( | |
397 | ObjectSettingPair { | |
398 | object_kind: O::object_name().to_string(), | |
399 | setting_name: T::name().to_string(), | |
400 | }, | |
401 | OperationWrapper::new( | |
402 | move |object: &O, | |
403 | setting: SettingUpdate<T>, | |
404 | _state: (), | |
405 | operation_state: StackOperationState, | |
406 | stack: Arc<GiteratedStack>| { | |
407 | trace!( | |
408 | "value setting updated {}::{}", | |
409 | O::object_name(), | |
410 | T::value_name() | |
411 | ); | |
412 | let object = object.clone(); | |
413 | async move { | |
414 | stack | |
415 | .value_update(object, setting.0, &operation_state) | |
416 | .await; | |
417 | Ok(()) | |
418 | } | |
419 | .boxed_local() | |
420 | }, | |
421 | (), | |
422 | ), | |
423 | ); | |
215 | 424 | |
216 | 425 | self |
217 | 426 | } |
@@ -242,7 +451,7 @@ impl<S: GiteratedStackState + 'static> SubstackBuilder<S> { | ||
242 | 451 | _state: S, |
243 | 452 | operation_state: StackOperationState, |
244 | 453 | stack: Arc<GiteratedStack>| { |
245 | let stack = stack.clone(); | |
454 | let stack = stack; | |
246 | 455 | let object_name = handler_object_name; |
247 | 456 | let value_name = handler_value_name; |
248 | 457 | let object = object.clone(); |
@@ -310,6 +519,42 @@ impl<S: GiteratedStackState + 'static> SubstackBuilder<S> { | ||
310 | 519 | |
311 | 520 | self |
312 | 521 | } |
522 | ||
523 | pub fn value_change<O, A, F, V>(&mut self, handler: F) -> &mut Self | |
524 | where | |
525 | O: GiteratedObject + 'static, | |
526 | F: GiteratedOperationHandler<A, O, ValueChangedShim<V>, S> + Clone + 'static + Send + Sync, | |
527 | V: GiteratedObjectValue<Object = O> + Clone + 'static, | |
528 | { | |
529 | let object_name = handler.object_name().to_string(); | |
530 | ||
531 | let wrapped = OperationWrapper::new(handler, self.state.clone()); | |
532 | ||
533 | assert!(self.setting_getters.insert(object_name, wrapped).is_none()); | |
534 | ||
535 | self | |
536 | } | |
537 | ||
538 | pub fn object_metadata_provider(&mut self, provider: Box<dyn MetadataProvider>) -> &mut Self { | |
539 | self.metadata_providers.push(provider); | |
540 | ||
541 | self | |
542 | } | |
543 | } | |
544 | ||
545 | #[derive(Serialize, Deserialize, Clone)] | |
546 | pub struct ValueChangedShim<V> { | |
547 | new_value: V, | |
548 | } | |
549 | ||
550 | impl<O, V> GiteratedOperation<O> for ValueChangedShim<V> | |
551 | where | |
552 | O: GiteratedObject, | |
553 | V: GiteratedObjectValue<Object = O>, | |
554 | { | |
555 | type Success = V; | |
556 | ||
557 | type Failure = MissingValue; | |
313 | 558 | } |
314 | 559 | |
315 | 560 | impl RuntimeMetadata { |
@@ -338,8 +583,8 @@ impl RuntimeMetadata { | ||
338 | 583 | .operations |
339 | 584 | .insert( |
340 | 585 | ObjectOperationPair { |
341 | object_name: object_name.clone(), | |
342 | operation_name: operation_name.clone(), | |
586 | object_name: object_name, | |
587 | operation_name: operation_name, | |
343 | 588 | }, |
344 | 589 | OperationMeta::new::<O, D>(), |
345 | 590 | ) |
@@ -372,8 +617,8 @@ impl RuntimeMetadata { | ||
372 | 617 | .values |
373 | 618 | .insert( |
374 | 619 | ObjectValuePair { |
375 | object_kind: object_name.clone(), | |
376 | value_kind: value_name.clone(), | |
620 | object_kind: object_name, | |
621 | value_kind: value_name, | |
377 | 622 | }, |
378 | 623 | ValueMeta::new::<V>(), |
379 | 624 | ) |
@@ -393,12 +638,16 @@ impl RuntimeMetadata { | ||
393 | 638 | } |
394 | 639 | } |
395 | 640 | |
396 | fn register_setting<S: Setting + 'static>(&mut self) { | |
397 | let setting_name = S::name().to_string(); | |
398 | ||
641 | fn register_setting<O: GiteratedObject + 'static, S: Setting + 'static>(&mut self) { | |
399 | 642 | if self |
400 | 643 | .settings |
401 | .insert(setting_name.clone(), SettingMeta::new::<S>()) | |
644 | .insert( | |
645 | ObjectSettingPair { | |
646 | object_kind: O::object_name().to_string(), | |
647 | setting_name: S::name().to_string(), | |
648 | }, | |
649 | SettingMeta::new::<O, S>(), | |
650 | ) | |
402 | 651 | .is_some() |
403 | 652 | { |
404 | 653 | trace!( |
@@ -479,7 +728,10 @@ impl GiteratedStack { | ||
479 | 728 | let setting_meta = self |
480 | 729 | .metadata |
481 | 730 | .settings |
482 | .get(&operation.setting_name) | |
731 | .get(&ObjectSettingPair { | |
732 | object_kind: object_type.clone(), | |
733 | setting_name: operation.setting_name.clone(), | |
734 | }) | |
483 | 735 | .ok_or_else(|| OperationError::Unhandled)?; |
484 | 736 | let raw_result = self |
485 | 737 | .get_setting(object, object_type.clone(), operation, operation_state) |
@@ -487,7 +739,7 @@ impl GiteratedStack { | ||
487 | 739 | return match raw_result { |
488 | 740 | Ok(success) => { |
489 | 741 | // Success is the setting type, serialize it |
490 | let serialized = (setting_meta.serialize)(success).unwrap(); | |
742 | let serialized = (setting_meta.serialize)(&(*success)).unwrap(); | |
491 | 743 | |
492 | 744 | Ok(serde_json::to_vec(&serialized).unwrap()) |
493 | 745 | } |
@@ -507,6 +759,79 @@ impl GiteratedStack { | ||
507 | 759 | OperationError::Unhandled => OperationError::Unhandled, |
508 | 760 | }), |
509 | 761 | }; |
762 | } else if message.operation == "set_setting" { | |
763 | let operation: SetSetting = serde_json::from_slice(&message.payload.0).unwrap(); | |
764 | ||
765 | trace!( | |
766 | "Handling network {}::set_setting for {}", | |
767 | object_type, | |
768 | operation.setting_name | |
769 | ); | |
770 | ||
771 | let setting_meta = self | |
772 | .metadata | |
773 | .settings | |
774 | .get(&ObjectSettingPair { | |
775 | object_kind: object_type.clone(), | |
776 | setting_name: operation.setting_name.clone(), | |
777 | }) | |
778 | .unwrap(); | |
779 | ||
780 | let setting = (setting_meta.deserialize)(operation.value.0) | |
781 | .as_internal_error_with_context(format!( | |
782 | "deserializing setting {} for object {}", | |
783 | operation.setting_name, object_type | |
784 | ))?; | |
785 | ||
786 | trace!( | |
787 | "Deserialized setting {} for object {}", | |
788 | operation.setting_name, | |
789 | object_type, | |
790 | ); | |
791 | ||
792 | for provider in self.metadata_providers.iter() { | |
793 | if provider.provides_for(object.as_ref()) { | |
794 | trace!( | |
795 | "Resolved setting provider for setting {} for object {}", | |
796 | operation.setting_name, | |
797 | object_type, | |
798 | ); | |
799 | ||
800 | let object_meta = self | |
801 | .metadata | |
802 | .objects | |
803 | .get(&object_type) | |
804 | .ok_or_else(|| OperationError::Unhandled)?; | |
805 | ||
806 | let raw_result = provider | |
807 | .write(&(*object), object_meta, &(*setting), setting_meta) | |
808 | .await; | |
809 | ||
810 | return match raw_result { | |
811 | Ok(_) => { | |
812 | (setting_meta.setting_updated)( | |
813 | object, | |
814 | setting, | |
815 | operation_state.runtime.clone(), | |
816 | operation_state, | |
817 | ) | |
818 | .await; | |
819 | ||
820 | Ok(serde_json::to_vec(&()).unwrap()) | |
821 | } | |
822 | Err(e) => Err(OperationError::Internal(e.context(format!( | |
823 | "writing object {} setting {}", | |
824 | object_type, operation.setting_name | |
825 | )))), | |
826 | }; | |
827 | } | |
828 | ||
829 | trace!( | |
830 | "Failed to resolve setting provider for setting {} for object {}", | |
831 | operation.setting_name, | |
832 | object_type, | |
833 | ); | |
834 | } | |
510 | 835 | } |
511 | 836 | |
512 | 837 | let target = ObjectOperationPair { |
@@ -606,7 +931,7 @@ impl GiteratedStack { | ||
606 | 931 | } |
607 | 932 | |
608 | 933 | return match getter |
609 | .handle(&(object), &((value_meta.typed_get)()), &operation_state) | |
934 | .handle(&(object), &((value_meta.typed_get)()), operation_state) | |
610 | 935 | .await |
611 | 936 | { |
612 | 937 | Ok(success) => { |
@@ -656,10 +981,46 @@ impl GiteratedStack { | ||
656 | 981 | |
657 | 982 | pub async fn network_set_setting( |
658 | 983 | &self, |
659 | _operation: SetSetting, | |
660 | _operation_state: &StackOperationState, | |
984 | object: Box<dyn Any + Send + Sync>, | |
985 | object_kind: String, | |
986 | operation: SetSetting, | |
987 | operation_state: &StackOperationState, | |
661 | 988 | ) -> Result<Vec<u8>, OperationError<Vec<u8>>> { |
662 | todo!() | |
989 | trace!( | |
990 | "Handling network {}::set_setting for {}", | |
991 | object_kind, | |
992 | operation.setting_name | |
993 | ); | |
994 | ||
995 | let target = ObjectSettingPair { | |
996 | object_kind: object_kind.clone(), | |
997 | setting_name: operation.setting_name.clone(), | |
998 | }; | |
999 | ||
1000 | let handler = self.setting_change.get(&target).unwrap(); | |
1001 | ||
1002 | let raw_result = handler | |
1003 | .handle(&object, &(Box::new(operation) as _), operation_state) | |
1004 | .await; | |
1005 | ||
1006 | match raw_result { | |
1007 | Ok(_) => { | |
1008 | // Serialize success, which is the value type itself | |
1009 | let serialized = serde_json::to_vec(&()).as_internal_error()?; | |
1010 | ||
1011 | Ok(serialized) | |
1012 | } | |
1013 | Err(err) => Err(match err { | |
1014 | OperationError::Operation(failure) => { | |
1015 | // Failure is sourced from GetValue operation, but this is hardcoded for now | |
1016 | let failure: GetValueError = *failure.downcast().unwrap(); | |
1017 | ||
1018 | OperationError::Operation(serde_json::to_vec(&failure).as_internal_error()?) | |
1019 | } | |
1020 | OperationError::Internal(internal) => OperationError::Internal(internal), | |
1021 | OperationError::Unhandled => OperationError::Unhandled, | |
1022 | }), | |
1023 | } | |
663 | 1024 | } |
664 | 1025 | } |
665 | 1026 | |
@@ -712,7 +1073,7 @@ impl ObjectBackend<StackOperationState> for Arc<GiteratedStack> { | ||
712 | 1073 | } |
713 | 1074 | |
714 | 1075 | return match getter |
715 | .handle(&(object), &((value_meta.typed_get)()), &operation_state) | |
1076 | .handle(&(object), &((value_meta.typed_get)()), operation_state) | |
716 | 1077 | .await |
717 | 1078 | { |
718 | 1079 | Ok(success) => Ok(*success.downcast().unwrap()), |
giterated-stack/src/lib.rs
@@ -1,7 +1,9 @@ | ||
1 | 1 | mod handler; |
2 | 2 | mod meta; |
3 | pub mod provider; | |
3 | 4 | pub use handler::{GiteratedStack, GiteratedStackState, *}; |
4 | 5 | pub use meta::*; |
6 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; | |
5 | 7 | pub mod state; |
6 | 8 | pub mod update; |
7 | 9 | |
@@ -18,9 +20,9 @@ use giterated_models::{ | ||
18 | 20 | object_backend::ObjectBackend, |
19 | 21 | operation::GiteratedOperation, |
20 | 22 | repository::{AccessList, Repository}, |
21 | settings::{GetSetting, SetSetting}, | |
23 | settings::{GetSetting, SetSetting, Setting}, | |
22 | 24 | user::User, |
23 | value::GetValue, | |
25 | value::{GetValue, GiteratedObjectValue}, | |
24 | 26 | }; |
25 | 27 | |
26 | 28 | #[derive(Clone, Debug, Hash, Eq, PartialEq)] |
@@ -29,12 +31,46 @@ struct ObjectOperationPair { | ||
29 | 31 | pub operation_name: String, |
30 | 32 | } |
31 | 33 | |
34 | impl ObjectOperationPair { | |
35 | #[allow(unused)] | |
36 | pub fn from_types<O: GiteratedObject, D: GiteratedOperation<O>>() -> Self { | |
37 | Self { | |
38 | object_name: O::object_name().to_string(), | |
39 | operation_name: D::operation_name().to_string(), | |
40 | } | |
41 | } | |
42 | } | |
43 | ||
32 | 44 | #[derive(Clone, Debug, Hash, Eq, PartialEq)] |
33 | 45 | pub struct ObjectValuePair { |
34 | 46 | pub object_kind: String, |
35 | 47 | pub value_kind: String, |
36 | 48 | } |
37 | 49 | |
50 | impl ObjectValuePair { | |
51 | pub fn from_types<O: GiteratedObject, D: GiteratedObjectValue<Object = O>>() -> Self { | |
52 | Self { | |
53 | object_kind: O::object_name().to_string(), | |
54 | value_kind: D::value_name().to_string(), | |
55 | } | |
56 | } | |
57 | } | |
58 | ||
59 | #[derive(Clone, Debug, Hash, Eq, PartialEq)] | |
60 | pub struct ObjectSettingPair { | |
61 | pub object_kind: String, | |
62 | pub setting_name: String, | |
63 | } | |
64 | ||
65 | impl ObjectSettingPair { | |
66 | pub fn from_types<O: GiteratedObject, S: Setting>() -> Self { | |
67 | Self { | |
68 | object_kind: O::object_name().to_string(), | |
69 | setting_name: S::name().to_string(), | |
70 | } | |
71 | } | |
72 | } | |
73 | ||
38 | 74 | #[async_trait::async_trait(?Send)] |
39 | 75 | pub trait GiteratedOperationHandler< |
40 | 76 | L, |
@@ -359,7 +395,7 @@ impl<O: GiteratedObject, D: GiteratedOperation<O>> FromOperationState<O, D> | ||
359 | 395 | } |
360 | 396 | } |
361 | 397 | |
362 | #[derive(Debug, thiserror::Error)] | |
398 | #[derive(Debug, thiserror::Error, Serialize, Deserialize)] | |
363 | 399 | #[error("missing value")] |
364 | 400 | pub struct MissingValue; |
365 | 401 | |
@@ -374,10 +410,7 @@ impl<O: GiteratedObject, D: GiteratedOperation<O> + Send + Sync> FromOperationSt | ||
374 | 410 | _operation: &D, |
375 | 411 | state: &StackOperationState, |
376 | 412 | ) -> Result<AuthenticatedUser, ExtractorError<MissingValue>> { |
377 | state | |
378 | .user | |
379 | .clone() | |
380 | .ok_or_else(|| ExtractorError(MissingValue)) | |
413 | state.user.clone().ok_or(ExtractorError(MissingValue)) | |
381 | 414 | } |
382 | 415 | } |
383 | 416 | |
@@ -392,10 +425,7 @@ impl<O: GiteratedObject, D: GiteratedOperation<O> + Send + Sync> FromOperationSt | ||
392 | 425 | _operation: &D, |
393 | 426 | state: &StackOperationState, |
394 | 427 | ) -> Result<AuthenticatedInstance, ExtractorError<MissingValue>> { |
395 | state | |
396 | .instance | |
397 | .clone() | |
398 | .ok_or_else(|| ExtractorError(MissingValue)) | |
428 | state.instance.clone().ok_or(ExtractorError(MissingValue)) | |
399 | 429 | } |
400 | 430 | } |
401 | 431 | |
@@ -460,7 +490,7 @@ impl AuthorizedOperation<User> for SetSetting { | ||
460 | 490 | authorize_for: &User, |
461 | 491 | operation_state: &StackOperationState, |
462 | 492 | ) -> Result<bool, ExtractorError<MissingValue>> { |
463 | let authenticated_user = operation_state.user.as_ref().ok_or_else(|| MissingValue)?; | |
493 | let authenticated_user = operation_state.user.as_ref().ok_or(MissingValue)?; | |
464 | 494 | |
465 | 495 | Ok(authorize_for == authenticated_user.deref()) |
466 | 496 | } |
@@ -475,7 +505,7 @@ impl AuthorizedOperation<User> for GetSetting { | ||
475 | 505 | authorize_for: &User, |
476 | 506 | operation_state: &StackOperationState, |
477 | 507 | ) -> Result<bool, ExtractorError<MissingValue>> { |
478 | let authenticated_user = operation_state.user.as_ref().ok_or_else(|| MissingValue)?; | |
508 | let authenticated_user = operation_state.user.as_ref().ok_or(MissingValue)?; | |
479 | 509 | |
480 | 510 | Ok(authorize_for == authenticated_user.deref()) |
481 | 511 | } |
@@ -499,18 +529,17 @@ impl AuthorizedOperation<Repository> for SetSetting { | ||
499 | 529 | .runtime |
500 | 530 | .get_object::<Repository>(&authorize_for.to_string(), operation_state) |
501 | 531 | .await |
502 | .map_err(|err| anyhow::Error::from(err))?; | |
532 | .map_err(anyhow::Error::from)?; | |
503 | 533 | |
504 | 534 | let access_list = object |
505 | 535 | .get_setting::<AccessList>(operation_state) |
506 | 536 | .await |
507 | .map_err(|err| anyhow::Error::from(err))?; | |
537 | .map_err(anyhow::Error::from)?; | |
508 | 538 | |
509 | 539 | if access_list |
510 | 540 | .0 |
511 | 541 | .iter() |
512 | .find(|user| *user == authenticated_user.deref()) | |
513 | .is_some() | |
542 | .any(|user| user == authenticated_user.deref()) | |
514 | 543 | { |
515 | 544 | Ok(true) |
516 | 545 | } else { |
@@ -537,18 +566,17 @@ impl AuthorizedOperation<Repository> for GetSetting { | ||
537 | 566 | .runtime |
538 | 567 | .get_object::<Repository>(&authorize_for.to_string(), operation_state) |
539 | 568 | .await |
540 | .map_err(|err| anyhow::Error::from(err))?; | |
569 | .map_err(anyhow::Error::from)?; | |
541 | 570 | |
542 | 571 | let access_list = object |
543 | 572 | .get_setting::<AccessList>(operation_state) |
544 | 573 | .await |
545 | .map_err(|err| anyhow::Error::from(err))?; | |
574 | .map_err(anyhow::Error::from)?; | |
546 | 575 | |
547 | 576 | if access_list |
548 | 577 | .0 |
549 | 578 | .iter() |
550 | .find(|user| *user == authenticated_user.deref()) | |
551 | .is_some() | |
579 | .any(|user| user == authenticated_user.deref()) | |
552 | 580 | { |
553 | 581 | Ok(true) |
554 | 582 | } else { |
@@ -710,3 +738,18 @@ impl Deref for AuthenticatedUser { | ||
710 | 738 | &self.0 |
711 | 739 | } |
712 | 740 | } |
741 | ||
742 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
743 | #[serde(transparent)] | |
744 | #[serde(bound(deserialize = "S: DeserializeOwned"))] | |
745 | pub struct SettingUpdate<S: Setting + Serialize + DeserializeOwned>(pub S); | |
746 | ||
747 | impl<O, S> GiteratedOperation<O> for SettingUpdate<S> | |
748 | where | |
749 | O: GiteratedObject, | |
750 | S: Setting + Serialize + DeserializeOwned, | |
751 | { | |
752 | type Success = (); | |
753 | ||
754 | type Failure = (); | |
755 | } |
giterated-stack/src/meta/mod.rs
@@ -1,5 +1,6 @@ | ||
1 | use std::{any::Any, str::FromStr}; | |
1 | use std::{any::Any, str::FromStr, sync::Arc}; | |
2 | 2 | |
3 | use futures_util::{future::LocalBoxFuture, FutureExt}; | |
3 | 4 | use giterated_models::{ |
4 | 5 | object::GiteratedObject, |
5 | 6 | operation::GiteratedOperation, |
@@ -8,13 +9,14 @@ use giterated_models::{ | ||
8 | 9 | }; |
9 | 10 | use serde_json::Value; |
10 | 11 | |
12 | use crate::{GiteratedStack, StackOperationState}; | |
13 | ||
11 | 14 | pub struct ValueMeta { |
12 | 15 | pub name: String, |
13 | pub deserialize: Box<dyn Fn(&[u8]) -> Result<Box<dyn Any>, serde_json::Error> + Send + Sync>, | |
14 | pub serialize: | |
15 | Box<dyn Fn(Box<dyn Any + Send + Sync>) -> Result<Vec<u8>, serde_json::Error> + Send + Sync>, | |
16 | pub typed_get: Box<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>, | |
17 | pub is_get_value_typed: Box<dyn Fn(&Box<dyn Any + Send + Sync>) -> bool + Send + Sync>, | |
16 | pub deserialize: fn(&[u8]) -> Result<Box<dyn Any>, serde_json::Error>, | |
17 | pub serialize: fn(Box<dyn Any + Send + Sync>) -> Result<Vec<u8>, serde_json::Error>, | |
18 | pub typed_get: fn() -> Box<dyn Any + Send + Sync>, | |
19 | pub is_get_value_typed: fn(&Box<dyn Any + Send + Sync>) -> bool, | |
18 | 20 | } |
19 | 21 | |
20 | 22 | pub trait IntoValueMeta { |
@@ -31,13 +33,13 @@ impl<O: GiteratedObject, V: GiteratedObjectValue<Object = O> + 'static> IntoValu | ||
31 | 33 | } |
32 | 34 | |
33 | 35 | fn deserialize(buffer: &[u8]) -> Result<Box<dyn Any>, serde_json::Error> { |
34 | Ok(Box::new(serde_json::from_slice(&buffer)?)) | |
36 | Ok(Box::new(serde_json::from_slice(buffer)?)) | |
35 | 37 | } |
36 | 38 | |
37 | 39 | fn serialize(value: Box<dyn Any + Send + Sync>) -> Result<Vec<u8>, serde_json::Error> { |
38 | 40 | let value = value.downcast::<V>().unwrap(); |
39 | 41 | |
40 | Ok(serde_json::to_vec(&*value)?) | |
42 | serde_json::to_vec(&*value) | |
41 | 43 | } |
42 | 44 | |
43 | 45 | fn typed_get() -> Box<dyn Any + Send + Sync> { |
@@ -56,10 +58,10 @@ impl ValueMeta { | ||
56 | 58 | pub fn new<I: IntoValueMeta + 'static>() -> Self { |
57 | 59 | Self { |
58 | 60 | name: I::name(), |
59 | deserialize: Box::new(I::deserialize) as _, | |
60 | serialize: Box::new(I::serialize) as _, | |
61 | typed_get: Box::new(I::typed_get) as _, | |
62 | is_get_value_typed: Box::new(I::is_get_value_typed) as _, | |
61 | deserialize: I::deserialize, | |
62 | serialize: I::serialize, | |
63 | typed_get: I::typed_get, | |
64 | is_get_value_typed: I::is_get_value_typed, | |
63 | 65 | } |
64 | 66 | } |
65 | 67 | } |
@@ -67,13 +69,10 @@ impl ValueMeta { | ||
67 | 69 | pub struct OperationMeta { |
68 | 70 | pub name: String, |
69 | 71 | pub object_kind: String, |
70 | pub deserialize: | |
71 | Box<dyn Fn(&[u8]) -> Result<Box<dyn Any + Send + Sync>, serde_json::Error> + Send + Sync>, | |
72 | pub any_is_same: Box<dyn Fn(&dyn Any) -> bool + Send + Sync>, | |
73 | pub serialize_success: | |
74 | Box<dyn Fn(Box<dyn Any>) -> Result<Vec<u8>, serde_json::Error> + Send + Sync>, | |
75 | pub serialize_error: | |
76 | Box<dyn Fn(Box<dyn Any>) -> Result<Vec<u8>, serde_json::Error> + Send + Sync>, | |
72 | pub deserialize: fn(&[u8]) -> Result<Box<dyn Any + Send + Sync>, serde_json::Error>, | |
73 | pub any_is_same: fn(&dyn Any) -> bool, | |
74 | pub serialize_success: fn(Box<dyn Any>) -> Result<Vec<u8>, serde_json::Error>, | |
75 | pub serialize_error: fn(Box<dyn Any>) -> Result<Vec<u8>, serde_json::Error>, | |
77 | 76 | } |
78 | 77 | |
79 | 78 | pub trait IntoOperationMeta<O> { |
@@ -118,11 +117,11 @@ impl OperationMeta { | ||
118 | 117 | pub fn new<O: GiteratedObject + 'static, I: IntoOperationMeta<O> + 'static>() -> Self { |
119 | 118 | Self { |
120 | 119 | name: I::name(), |
121 | deserialize: Box::new(I::deserialize) as _, | |
122 | serialize_success: Box::new(I::serialize_success) as _, | |
123 | serialize_error: Box::new(I::serialize_failure) as _, | |
120 | deserialize: I::deserialize, | |
121 | serialize_success: I::serialize_success, | |
122 | serialize_error: I::serialize_failure, | |
124 | 123 | object_kind: O::object_name().to_string(), |
125 | any_is_same: Box::new(I::any_is_same) as _, | |
124 | any_is_same: I::any_is_same, | |
126 | 125 | } |
127 | 126 | } |
128 | 127 | } |
@@ -130,7 +129,7 @@ impl OperationMeta { | ||
130 | 129 | pub struct ObjectMeta { |
131 | 130 | pub name: String, |
132 | 131 | pub from_str: Box<dyn Fn(&str) -> Result<Box<dyn Any + Send + Sync>, ()> + Send + Sync>, |
133 | pub any_is_same: Box<dyn Fn(&dyn Any) -> bool + Send + Sync>, | |
132 | pub any_is_same: fn(&dyn Any) -> bool, | |
134 | 133 | } |
135 | 134 | |
136 | 135 | pub trait IntoObjectMeta: FromStr { |
@@ -157,44 +156,74 @@ impl ObjectMeta { | ||
157 | 156 | |
158 | 157 | Ok(Box::new(object) as Box<dyn Any + Send + Sync>) |
159 | 158 | }), |
160 | any_is_same: Box::new(I::any_is_same) as _, | |
159 | any_is_same: I::any_is_same, | |
161 | 160 | } |
162 | 161 | } |
163 | 162 | } |
164 | 163 | |
165 | 164 | pub struct SettingMeta { |
166 | 165 | pub name: String, |
167 | pub deserialize: Box<dyn Fn(&[u8]) -> Result<Box<dyn Any>, serde_json::Error> + Send + Sync>, | |
168 | pub serialize: | |
169 | Box<dyn Fn(Box<dyn Any + Send + Sync>) -> Result<Value, serde_json::Error> + Send + Sync>, | |
166 | pub deserialize: fn(Value) -> Result<Box<dyn Any + Send + Sync>, serde_json::Error>, | |
167 | pub serialize: fn(&(dyn Any + Send + Sync)) -> Result<Value, serde_json::Error>, | |
168 | pub setting_updated: for<'fut> fn( | |
169 | Box<dyn Any + Send + Sync>, | |
170 | Box<dyn Any + Send + Sync>, | |
171 | Arc<GiteratedStack>, | |
172 | &StackOperationState, | |
173 | ) -> LocalBoxFuture<'_, ()>, | |
170 | 174 | } |
171 | 175 | |
172 | pub trait IntoSettingMeta { | |
176 | pub trait IntoSettingMeta<O> { | |
173 | 177 | fn name() -> String; |
174 | fn deserialize(buffer: &[u8]) -> Result<Box<dyn Any>, serde_json::Error>; | |
175 | fn serialize(setting: Box<dyn Any + Send + Sync>) -> Result<Value, serde_json::Error>; | |
178 | fn deserialize(value: Value) -> Result<Box<dyn Any + Send + Sync>, serde_json::Error>; | |
179 | fn serialize(setting: &(dyn Any + Send + Sync)) -> Result<Value, serde_json::Error>; | |
180 | fn setting_updated( | |
181 | object: Box<dyn Any + Send + Sync>, | |
182 | setting: Box<dyn Any + Send + Sync>, | |
183 | stack: Arc<GiteratedStack>, | |
184 | operation_state: &StackOperationState, | |
185 | ) -> LocalBoxFuture<'_, ()>; | |
176 | 186 | } |
177 | 187 | |
178 | impl<S: Setting> IntoSettingMeta for S { | |
188 | impl<O: GiteratedObject + 'static, S: Setting + 'static> IntoSettingMeta<O> for S { | |
179 | 189 | fn name() -> String { |
180 | 190 | S::name().to_string() |
181 | 191 | } |
182 | 192 | |
183 | fn deserialize(buffer: &[u8]) -> Result<Box<dyn Any>, serde_json::Error> { | |
184 | Ok(Box::new(serde_json::from_slice(buffer)?)) | |
193 | fn deserialize(value: Value) -> Result<Box<dyn Any + Send + Sync>, serde_json::Error> { | |
194 | Ok(Box::new(serde_json::from_value::<S>(value)?)) | |
195 | } | |
196 | ||
197 | fn serialize(setting: &(dyn Any + Send + Sync)) -> Result<Value, serde_json::Error> { | |
198 | serde_json::to_value(setting.downcast_ref::<S>().unwrap()) | |
185 | 199 | } |
186 | 200 | |
187 | fn serialize(setting: Box<dyn Any + Send + Sync>) -> Result<Value, serde_json::Error> { | |
188 | Ok(*setting.downcast::<Value>().unwrap()) | |
201 | fn setting_updated( | |
202 | object: Box<dyn Any + Send + Sync>, | |
203 | setting: Box<dyn Any + Send + Sync>, | |
204 | stack: Arc<GiteratedStack>, | |
205 | operation_state: &StackOperationState, | |
206 | ) -> LocalBoxFuture<'_, ()> { | |
207 | async move { | |
208 | stack | |
209 | .setting_update( | |
210 | *object.downcast::<O>().unwrap(), | |
211 | *setting.downcast::<S>().unwrap(), | |
212 | operation_state, | |
213 | ) | |
214 | .await | |
215 | } | |
216 | .boxed_local() | |
189 | 217 | } |
190 | 218 | } |
191 | 219 | |
192 | 220 | impl SettingMeta { |
193 | pub fn new<I: IntoSettingMeta + 'static>() -> Self { | |
221 | pub fn new<O: GiteratedObject, I: IntoSettingMeta<O> + 'static>() -> Self { | |
194 | 222 | Self { |
195 | 223 | name: I::name(), |
196 | deserialize: Box::new(I::deserialize) as _, | |
197 | serialize: Box::new(I::serialize) as _, | |
224 | deserialize: I::deserialize, | |
225 | serialize: I::serialize, | |
226 | setting_updated: I::setting_updated, | |
198 | 227 | } |
199 | 228 | } |
200 | 229 | } |
giterated-stack/src/provider/metadata.rs
@@ -0,0 +1,25 @@ | ||
1 | use std::any::Any; | |
2 | ||
3 | use anyhow::Error; | |
4 | ||
5 | use serde_json::Value; | |
6 | ||
7 | use crate::{ObjectMeta, SettingMeta}; | |
8 | ||
9 | #[async_trait::async_trait] | |
10 | pub trait MetadataProvider: Send + Sync + 'static { | |
11 | fn provides_for(&self, object: &dyn Any) -> bool; | |
12 | async fn write( | |
13 | &self, | |
14 | object: &(dyn Any + Send + Sync), | |
15 | object_meta: &ObjectMeta, | |
16 | setting: &(dyn Any + Send + Sync), | |
17 | setting_meta: &SettingMeta, | |
18 | ) -> Result<(), Error>; | |
19 | async fn read( | |
20 | &self, | |
21 | object: &(dyn Any + Send + Sync), | |
22 | object_meta: &ObjectMeta, | |
23 | setting_meta: &SettingMeta, | |
24 | ) -> Result<Value, Error>; | |
25 | } |