Showing 6 changed files with 182 insertions and 6 deletions
giterated-daemon/src/backend/git.rs
@@ -8,7 +8,7 @@ use giterated_models::repository::{ | ||
8 | 8 | Commit, DefaultBranch, Description, IssueLabel, LatestCommit, Repository, |
9 | 9 | RepositoryFileInspectRequest, RepositoryIssue, RepositoryIssueLabelsRequest, |
10 | 10 | RepositoryIssuesCountRequest, RepositoryIssuesRequest, RepositoryObjectType, |
11 | RepositoryTreeEntry, RepositoryVisibility, Visibility, RepositoryFile, RepositoryFileFromIdRequest, | |
11 | RepositoryTreeEntry, RepositoryVisibility, Visibility, RepositoryFile, RepositoryFileFromIdRequest, RepositoryDiffRequest, RepositoryDiff, | |
12 | 12 | }; |
13 | 13 | use giterated_models::settings::{AnySetting, Setting}; |
14 | 14 | use giterated_models::user::{User, UserParseError}; |
@@ -95,6 +95,10 @@ pub enum GitBackendError { | ||
95 | 95 | InvalidObjectId(String), |
96 | 96 | #[error("Blob with ID `{0}` not found")] |
97 | 97 | BlobNotFound(String), |
98 | #[error("Tree with ID `{0}` not found")] | |
99 | TreeNotFound(String), | |
100 | #[error("Failed diffing tree with ID `{0}` to tree with ID `{1}`")] | |
101 | FailedDiffing(String, String), | |
98 | 102 | } |
99 | 103 | |
100 | 104 | pub struct GitBackend { |
@@ -694,6 +698,95 @@ impl RepositoryBackend for GitBackend { | ||
694 | 698 | |
695 | 699 | Ok(file) |
696 | 700 | } |
701 | ||
702 | async fn repository_diff( | |
703 | &mut self, | |
704 | requester: Option<&User>, | |
705 | repository: &Repository, | |
706 | request: &RepositoryDiffRequest, | |
707 | ) -> Result<RepositoryDiff, Error> { | |
708 | // TODO: Get rid of all this duplicate code | |
709 | let repository = match self | |
710 | .find_by_owner_user_name( | |
711 | // &request.owner.instance.url, | |
712 | &repository.owner, | |
713 | &repository.name, | |
714 | ) | |
715 | .await | |
716 | { | |
717 | Ok(repository) => repository, | |
718 | Err(err) => return Err(Box::new(err).into()), | |
719 | }; | |
720 | ||
721 | if let Some(requester) = requester { | |
722 | if !repository.can_user_view_repository(Some(requester)) { | |
723 | return Err(Box::new(GitBackendError::RepositoryNotFound { | |
724 | owner_user: repository.owner_user.to_string(), | |
725 | name: repository.name.clone(), | |
726 | }) | |
727 | .into()); | |
728 | } | |
729 | } else if matches!(repository.visibility, RepositoryVisibility::Private) { | |
730 | info!("Unauthenticated"); | |
731 | // Unauthenticated users can never view private repositories | |
732 | ||
733 | return Err(Box::new(GitBackendError::RepositoryNotFound { | |
734 | owner_user: repository.owner_user.to_string(), | |
735 | name: repository.name.clone(), | |
736 | }) | |
737 | .into()); | |
738 | } | |
739 | ||
740 | let git = match repository.open_git2_repository(&self.repository_folder) { | |
741 | Ok(git) => git, | |
742 | Err(err) => return Err(Box::new(err).into()), | |
743 | }; | |
744 | ||
745 | // Parse the passed object ids | |
746 | let oid_old = match git2::Oid::from_str(request.old_id.as_str()) { | |
747 | Ok(oid) => oid, | |
748 | Err(_) => return Err(Box::new(GitBackendError::InvalidObjectId(request.old_id.clone())).into()), | |
749 | }; | |
750 | let oid_new = match git2::Oid::from_str(request.new_id.as_str()) { | |
751 | Ok(oid) => oid, | |
752 | Err(_) => return Err(Box::new(GitBackendError::InvalidObjectId(request.new_id.clone())).into()), | |
753 | }; | |
754 | ||
755 | // Get the trees of those object ids | |
756 | let tree_old = match git.find_tree(oid_old) { | |
757 | Ok(tree) => tree, | |
758 | Err(_) => return Err(Box::new(GitBackendError::TreeNotFound(oid_old.to_string())).into()), | |
759 | }; | |
760 | let tree_new = match git.find_tree(oid_new) { | |
761 | Ok(tree) => tree, | |
762 | Err(_) => return Err(Box::new(GitBackendError::TreeNotFound(oid_new.to_string())).into()), | |
763 | }; | |
764 | ||
765 | // Diff the two trees against each other | |
766 | let diff = match git.diff_tree_to_tree(Some(&tree_old), Some(&tree_new), None) { | |
767 | Ok(diff) => diff, | |
768 | Err(_) => return Err(Box::new(GitBackendError::FailedDiffing(oid_old.to_string(), oid_new.to_string())).into()), | |
769 | }; | |
770 | ||
771 | // Should be safe to unwrap? | |
772 | let stats = diff.stats().unwrap(); | |
773 | ||
774 | // Honestly not quite sure what is going on here, could not find documentation. | |
775 | // Print the entire patch | |
776 | let mut patch = String::new(); | |
777 | ||
778 | diff.print(git2::DiffFormat::Patch, |_, _, line| { | |
779 | patch.push_str(std::str::from_utf8(line.content()).unwrap()); | |
780 | true | |
781 | }).unwrap(); | |
782 | ||
783 | Ok(RepositoryDiff { | |
784 | files_changed: stats.files_changed(), | |
785 | insertions: stats.insertions(), | |
786 | deletions: stats.deletions(), | |
787 | patch, | |
788 | }) | |
789 | } | |
697 | 790 | } |
698 | 791 | |
699 | 792 | impl IssuesBackend for GitBackend { |
giterated-daemon/src/backend/mod.rs
@@ -18,7 +18,7 @@ use giterated_models::instance::{ | ||
18 | 18 | use giterated_models::repository::{ |
19 | 19 | IssueLabel, Repository, RepositoryFileInspectRequest, RepositoryIssue, |
20 | 20 | RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, |
21 | RepositorySummary, RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile, | |
21 | RepositorySummary, RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile, RepositoryDiffRequest, RepositoryDiff, | |
22 | 22 | }; |
23 | 23 | use giterated_models::settings::AnySetting; |
24 | 24 | use giterated_models::user::User; |
@@ -43,6 +43,12 @@ pub trait RepositoryBackend { | ||
43 | 43 | repository: &Repository, |
44 | 44 | request: &RepositoryFileFromIdRequest, |
45 | 45 | ) -> Result<RepositoryFile, Error>; |
46 | async fn repository_diff( | |
47 | &mut self, | |
48 | requester: Option<&User>, | |
49 | repository: &Repository, | |
50 | request: &RepositoryDiffRequest, | |
51 | ) -> Result<RepositoryDiff, Error>; | |
46 | 52 | async fn get_value( |
47 | 53 | &mut self, |
48 | 54 | user: &Repository, |
giterated-daemon/src/database_backend/handler.rs
@@ -8,7 +8,7 @@ use giterated_models::{ | ||
8 | 8 | operation::{AnyOperation, GiteratedOperation}, |
9 | 9 | repository::{ |
10 | 10 | DefaultBranch, Description, LatestCommit, Repository, RepositoryFileInspectRequest, |
11 | RepositoryInfoRequest, RepositorySummary, RepositoryView, Visibility, RepositoryFile, RepositoryFileFromIdRequest, | |
11 | RepositoryInfoRequest, RepositorySummary, RepositoryView, Visibility, RepositoryFile, RepositoryFileFromIdRequest, RepositoryDiff, RepositoryDiffRequest, | |
12 | 12 | }, |
13 | 13 | settings::{AnySetting, GetSetting, GetSettingError, SetSetting, SetSettingError}, |
14 | 14 | user::{User, UserRepositoriesRequest}, |
@@ -279,6 +279,34 @@ pub fn repository_file_from_id( | ||
279 | 279 | }.boxed() |
280 | 280 | } |
281 | 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 | ||
282 | 310 | pub fn repository_get_value( |
283 | 311 | object: &Repository, |
284 | 312 | operation: GetValue<AnyValue<Repository>>, |
giterated-daemon/src/database_backend/mod.rs
@@ -18,7 +18,7 @@ use crate::backend::{RepositoryBackend, UserBackend}; | ||
18 | 18 | |
19 | 19 | use self::handler::{ |
20 | 20 | repository_get_setting, repository_get_value, repository_info, repository_set_setting, |
21 | user_get_repositories, user_get_setting, user_get_value, user_set_setting, OperationHandlers, repository_file_from_id, | |
21 | user_get_repositories, user_get_setting, user_get_value, user_set_setting, OperationHandlers, repository_file_from_id, repository_diff, | |
22 | 22 | }; |
23 | 23 | |
24 | 24 | #[derive(Clone, Debug)] |
@@ -118,6 +118,7 @@ impl ObjectBackend for DatabaseBackend { | ||
118 | 118 | handler |
119 | 119 | .insert(repository_info) |
120 | 120 | .insert(repository_file_from_id) |
121 | .insert(repository_diff) | |
121 | 122 | .insert(repository_get_value) |
122 | 123 | .insert(repository_get_setting) |
123 | 124 | .insert(repository_set_setting); |
@@ -256,7 +257,7 @@ mod test { | ||
256 | 257 | |
257 | 258 | use giterated_models::repository::{ |
258 | 259 | Description, Repository, RepositoryFileInspectRequest, RepositorySummary, |
259 | RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile | |
260 | RepositoryTreeEntry, RepositoryFileFromIdRequest, RepositoryFile, RepositoryDiff, RepositoryDiffRequest | |
260 | 261 | }; |
261 | 262 | use giterated_models::settings::AnySetting; |
262 | 263 | use giterated_models::user::{DisplayName, User}; |
@@ -350,6 +351,14 @@ mod test { | ||
350 | 351 | ) -> Result<RepositoryFile, Error> { |
351 | 352 | todo!() |
352 | 353 | } |
354 | async fn repository_diff( | |
355 | &mut self, | |
356 | requester: Option<&User>, | |
357 | repository: &Repository, | |
358 | request: &RepositoryDiffRequest, | |
359 | ) -> Result<RepositoryDiff, Error> { | |
360 | todo!() | |
361 | } | |
353 | 362 | async fn get_value( |
354 | 363 | &mut self, |
355 | 364 | _repository: &Repository, |
giterated-models/src/repository/mod.rs
@@ -147,6 +147,18 @@ pub struct RepositoryFile { | ||
147 | 147 | pub size: usize, |
148 | 148 | } |
149 | 149 | |
150 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
151 | pub struct RepositoryDiff { | |
152 | /// Total number of files changed | |
153 | pub files_changed: usize, | |
154 | /// Total number of insertions | |
155 | pub insertions: usize, | |
156 | /// Total number of deletions | |
157 | pub deletions: usize, | |
158 | /// Preferably unified patch, probably a git patch. | |
159 | pub patch: String, | |
160 | } | |
161 | ||
150 | 162 | #[derive(Debug, Clone, Serialize, Deserialize)] |
151 | 163 | pub enum RepositoryObjectType { |
152 | 164 | Tree, |
giterated-models/src/repository/operations.rs
@@ -7,7 +7,7 @@ use crate::{ | ||
7 | 7 | operation::GiteratedOperation, |
8 | 8 | }; |
9 | 9 | |
10 | use super::{IssueLabel, Repository, RepositoryIssue, RepositoryTreeEntry, RepositoryView, RepositoryFile}; | |
10 | use super::{IssueLabel, Repository, RepositoryIssue, RepositoryTreeEntry, RepositoryView, RepositoryFile, RepositoryDiff}; | |
11 | 11 | |
12 | 12 | /// A request to get a repository's information. |
13 | 13 | /// |
@@ -47,6 +47,26 @@ impl GiteratedOperation<Repository> for RepositoryFileFromIdRequest { | ||
47 | 47 | type Failure = RepositoryError; |
48 | 48 | } |
49 | 49 | |
50 | /// A request to get the difference between two repository trees. | |
51 | /// | |
52 | /// # Authentication | |
53 | /// - Instance Authentication | |
54 | /// - Validate request against the `issued_for` public key | |
55 | /// - Validate User token against the user's instance's public key | |
56 | /// # Authorization | |
57 | /// - User Authorization | |
58 | /// - Potential User permissions checks | |
59 | #[derive(Clone, Debug, Serialize, Deserialize)] | |
60 | pub struct RepositoryDiffRequest { | |
61 | pub old_id: String, | |
62 | pub new_id: String, | |
63 | } | |
64 | ||
65 | impl GiteratedOperation<Repository> for RepositoryDiffRequest { | |
66 | type Success = RepositoryDiff; | |
67 | type Failure = RepositoryError; | |
68 | } | |
69 | ||
50 | 70 | #[derive(Clone, Debug, Serialize, Deserialize)] |
51 | 71 | pub struct RepositoryIssuesCountRequest; |
52 | 72 | |
@@ -135,6 +155,14 @@ impl<B: ObjectBackend + std::fmt::Debug> Object<'_, Repository, B> { | ||
135 | 155 | self.request::<RepositoryFileFromIdRequest>(RepositoryFileFromIdRequest(id)).await |
136 | 156 | } |
137 | 157 | |
158 | pub async fn diff( | |
159 | &mut self, | |
160 | old_id: String, | |
161 | new_id: String, | |
162 | ) -> Result<RepositoryDiff, OperationError<RepositoryError>> { | |
163 | self.request::<RepositoryDiffRequest>(RepositoryDiffRequest { old_id, new_id }).await | |
164 | } | |
165 | ||
138 | 166 | // pub async fn issues_count(&mut self) -> Result<u64, OperationError<RepositoryError>> { |
139 | 167 | // self.request::<RepositoryIssuesCountRequest>(RepositoryIssuesCountRequest) |
140 | 168 | // .await |