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

ambee/giterated

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

Repository diff request and handler

Type: Feature

erremilia - ⁨2⁩ years ago

parent: tbd commit: ⁨502a11c

Showing ⁨⁨6⁩ changed files⁩ with ⁨⁨182⁩ insertions⁩ and ⁨⁨6⁩ deletions⁩

giterated-daemon/src/backend/git.rs

View file
@@ -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

View file
@@ -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

View file
@@ -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

View file
@@ -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

View file
@@ -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

View file
@@ -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