Skip to content

Commit 0fef638

Browse files
committed
refactor(ceres): split MonoApiService into domain modules under api_service/mono
Replace the monolithic mono_api_service.rs with focused modules for CL, edit, buck, admin, sync, tag, and shared logic. Move admin/group/bot ops into mono/admin, extract tag_ops for import repo APIs, and add mono ref upsert helpers for sync and merge flows. Update call sites across ceres, mono, and jupiter, and extract web sync path utilities with tests.
1 parent 341918d commit 0fef638

44 files changed

Lines changed: 5735 additions & 5059 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Generated by Cargo
22
# will have compiled files and executables
33
/target
4+
vendor/**
45

56
# These are backup files generated by rustfmt
67
**/*.rs.bk
@@ -16,7 +17,6 @@
1617
.DS_Store
1718
**/*.dylib
1819

19-
.cursor
2020
.md
2121

2222
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

ceres/src/api_service/import_api_service.rs

Lines changed: 50 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,16 @@ use git_internal::{
2424
use jupiter::{storage::Storage, utils::converter::FromGitModel};
2525

2626
use crate::{
27-
api_service::{ApiHandler, cache::GitObjectCache, history},
27+
api_service::{
28+
ApiHandler,
29+
cache::GitObjectCache,
30+
history,
31+
mono::MonoServiceLogic,
32+
tag_ops::{
33+
self, build_git_internal_tag, db_error, format_tagger_info, is_annotated_tag,
34+
lightweight_commit_tag, merge_paginated_tags, tag_already_exists, tags_full_ref,
35+
},
36+
},
2837
model::{
2938
git::{CreateEntryInfo, CreateEntryResult, EditFilePayload, EditFileResult},
3039
tag::TagInfo,
@@ -167,51 +176,35 @@ impl ApiHandler for ImportApiService {
167176
message: Option<String>,
168177
) -> Result<TagInfo, GitError> {
169178
let git_storage = self.storage.git_db_storage();
170-
let is_annotated = message.as_ref().map(|s| !s.is_empty()).unwrap_or(false);
171-
let tagger_info = match (tagger_name, tagger_email) {
172-
(Some(n), Some(e)) => format!("{} <{}>", n, e),
173-
(Some(n), None) => n,
174-
(None, Some(e)) => e,
175-
(None, None) => "unknown".to_string(),
176-
};
179+
let tagger_info = format_tagger_info(tagger_name, tagger_email);
177180

178-
// validate target commit if provided
179181
self.validate_target_commit(target.as_ref()).await?;
180182

181-
let full_ref = format!("refs/tags/{}", name.clone());
182-
// Prevent duplicate tag/ref creation: check annotated table and refs first.
183+
let full_ref = tags_full_ref(&name);
183184
match git_storage
184185
.get_tag_by_repo_and_name(self.repo.repo_id, &name)
185186
.await
186187
{
187-
Ok(Some(_)) => {
188-
return Err(GitError::CustomError(format!(
189-
"[code:400] Tag '{}' already exists",
190-
name
191-
)));
192-
}
188+
Ok(Some(_)) => return Err(tag_already_exists(&name)),
193189
Ok(None) => {}
194190
Err(e) => {
195191
tracing::error!("DB error while checking git_tag existence: {}", e);
196-
return Err(GitError::CustomError("[code:500] DB error".to_string()));
192+
return Err(db_error());
197193
}
198194
}
199195

200196
if let Ok(refs) = git_storage.get_ref(self.repo.repo_id).await
201197
&& refs.iter().any(|r| r.ref_name == full_ref)
202198
{
203-
return Err(GitError::CustomError(format!(
204-
"[code:400] Tag '{}' already exists",
205-
name
206-
)));
199+
return Err(tag_already_exists(&name));
207200
}
208-
if is_annotated {
201+
202+
if is_annotated_tag(&message) {
209203
return self
210204
.create_annotated_tag(&git_storage, full_ref, name, target, tagger_info, message)
211205
.await;
212206
}
213207

214-
// lightweight
215208
self.create_lightweight_tag(&git_storage, full_ref, name, target, tagger_info)
216209
.await
217210
}
@@ -234,8 +227,7 @@ impl ApiHandler for ImportApiService {
234227
}
235228
};
236229

237-
// map annotated page into TagInfo
238-
let mut result: Vec<TagInfo> = annotated_tags_page
230+
let result: Vec<TagInfo> = annotated_tags_page
239231
.into_iter()
240232
.map(|t| TagInfo {
241233
name: t.tag_name,
@@ -248,48 +240,30 @@ impl ApiHandler for ImportApiService {
248240
})
249241
.collect();
250242

251-
// lightweight refs
252243
let mut lightweight_refs: Vec<TagInfo> = vec![];
253244
if let Ok(refs) = git_storage.get_ref(self.repo.repo_id).await {
254245
for r in refs {
255246
if r.ref_name.starts_with("refs/tags/") {
256247
let tag_name = r.ref_name.trim_start_matches("refs/tags/").to_string();
257-
// skip if annotated exists (anywhere)
258-
// Note: we only have the annotated page in memory; to avoid duplicate names we check by tag_name against annotated page and will accept duplicates only if not present.
259248
if result.iter().any(|t| t.name == tag_name) {
260249
continue;
261250
}
262-
let created_at = r.created_at.and_utc().to_rfc3339();
263-
lightweight_refs.push(TagInfo {
264-
name: tag_name.clone(),
265-
tag_id: r.ref_git_id.clone(),
266-
object_id: r.ref_git_id.clone(),
267-
object_type: "commit".to_string(),
268-
tagger: "".to_string(),
269-
message: "".to_string(),
270-
created_at,
271-
});
251+
lightweight_refs.push(lightweight_commit_tag(
252+
tag_name,
253+
r.ref_git_id.clone(),
254+
"",
255+
r.created_at.and_utc().to_rfc3339(),
256+
));
272257
}
273258
}
274259
}
275260

276-
// total is annotated_total + lightweight_refs.len()
277-
let total = annotated_total + lightweight_refs.len() as u64;
278-
279-
// fill page: annotated page items come first, then lightweight refs to make up page size
280-
let per_page = if pagination.per_page == 0 {
281-
20
282-
} else {
283-
pagination.per_page
284-
} as usize;
285-
if result.len() < per_page {
286-
let need = per_page - result.len();
287-
for r in lightweight_refs.into_iter().take(need) {
288-
result.push(r);
289-
}
290-
}
291-
292-
Ok((result, total))
261+
Ok(merge_paginated_tags(
262+
result,
263+
lightweight_refs,
264+
annotated_total,
265+
pagination.per_page,
266+
))
293267
}
294268

295269
async fn get_tag(
@@ -320,21 +294,16 @@ impl ApiHandler for ImportApiService {
320294
return Err(GitError::CustomError("[code:500] DB error".to_string()));
321295
}
322296
}
323-
// check import_refs for lightweight
324-
let full_ref = format!("refs/tags/{}", name.clone());
297+
let full_ref = tags_full_ref(&name);
325298
if let Ok(refs) = git_storage.get_ref(self.repo.repo_id).await {
326299
for r in refs {
327300
if r.ref_name == full_ref {
328-
let created_at = r.created_at.and_utc().to_rfc3339();
329-
return Ok(Some(TagInfo {
330-
name: name.clone(),
331-
tag_id: r.ref_git_id.clone(),
332-
object_id: r.ref_git_id.clone(),
333-
object_type: "commit".to_string(),
334-
tagger: "".to_string(),
335-
message: "".to_string(),
336-
created_at,
337-
}));
301+
return Ok(Some(lightweight_commit_tag(
302+
name,
303+
r.ref_git_id.clone(),
304+
"",
305+
r.created_at.and_utc().to_rfc3339(),
306+
)));
338307
}
339308
}
340309
}
@@ -349,8 +318,7 @@ impl ApiHandler for ImportApiService {
349318
.await
350319
{
351320
Ok(Some(_tag)) => {
352-
// remove import ref if exists
353-
let full_ref = format!("refs/tags/{}", name.clone());
321+
let full_ref = tags_full_ref(&name);
354322
git_storage
355323
.remove_ref(self.repo.repo_id, &full_ref)
356324
.await
@@ -371,8 +339,7 @@ impl ApiHandler for ImportApiService {
371339
Ok(())
372340
}
373341
Ok(None) => {
374-
// remove lightweight ref if exists
375-
let full_ref = format!("refs/tags/{}", name.clone());
342+
let full_ref = tags_full_ref(&name);
376343
git_storage
377344
.remove_ref(self.repo.repo_id, &full_ref)
378345
.await
@@ -418,7 +385,7 @@ impl ApiHandler for ImportApiService {
418385
// Create new blob and rebuild tree up to root
419386
let new_blob = Blob::from_content(&payload.content);
420387
let (updated_trees, new_root_id) =
421-
self.build_updated_trees(path.clone(), update_chain, new_blob.id)?;
388+
MonoServiceLogic::propagate_tree_chain(path.clone(), update_chain, new_blob.id)?;
422389

423390
// Save commit and objects under import repo tables
424391
let git_storage = self.storage.git_db_storage();
@@ -488,12 +455,8 @@ impl ImportApiService {
488455
message: Option<String>,
489456
) -> Result<TagInfo, GitError> {
490457
// build git_internal tag and models
491-
let (tag_id_hex, object_id) = self.build_git_internal_tag(
492-
name.clone(),
493-
target.clone(),
494-
tagger_info.clone(),
495-
message.clone(),
496-
)?;
458+
let (tag_id_hex, object_id) =
459+
build_git_internal_tag(name.clone(), target, tagger_info.clone(), message.clone())?;
497460

498461
let new_model = self.build_git_tag_model(
499462
tag_id_hex.clone(),
@@ -559,66 +522,32 @@ impl ImportApiService {
559522
tracing::error!("Failed to write import ref for lightweight tag: {}", e);
560523
GitError::CustomError("[code:500] Failed to write import ref".to_string())
561524
})?;
562-
Ok(TagInfo {
563-
name: name.clone(),
564-
tag_id: object_id.clone(),
565-
object_id: object_id.clone(),
566-
object_type: "commit".to_string(),
567-
tagger: tagger_info.clone(),
568-
message: String::new(),
525+
Ok(lightweight_commit_tag(
526+
name,
527+
object_id,
528+
tagger_info,
569529
created_at,
570-
})
530+
))
571531
}
572532

573533
async fn validate_target_commit(&self, target: Option<&String>) -> Result<(), GitError> {
574-
if let Some(ref t) = target {
534+
if let Some(t) = target {
575535
let git_storage = self.storage.git_db_storage();
576536
match git_storage.get_commit_by_hash(self.repo.repo_id, t).await {
577537
Ok(c) => {
578538
if c.is_none() {
579-
return Err(GitError::CustomError(format!(
580-
"[code:404] Target commit '{}' not found",
581-
t
582-
)));
539+
return Err(tag_ops::commit_not_found(t));
583540
}
584541
}
585542
Err(e) => {
586543
tracing::error!("DB error while fetching commit by hash: {}", e);
587-
return Err(GitError::CustomError("[code:500] DB error".to_string()));
544+
return Err(db_error());
588545
}
589546
}
590547
}
591548
Ok(())
592549
}
593550

594-
fn build_git_internal_tag(
595-
&self,
596-
name: String,
597-
target: Option<String>,
598-
tagger_info: String,
599-
message: Option<String>,
600-
) -> Result<(String, String), GitError> {
601-
let tag_target = target
602-
.as_ref()
603-
.ok_or(GitError::InvalidCommitObject)
604-
.and_then(|t| ObjectHash::from_str(t).map_err(|_| GitError::InvalidCommitObject))?;
605-
let git_internal_tag = git_internal::internal::object::tag::Tag::new(
606-
tag_target,
607-
git_internal::internal::object::types::ObjectType::Commit,
608-
name.clone(),
609-
git_internal::internal::object::signature::Signature::new(
610-
git_internal::internal::object::signature::SignatureType::Tagger,
611-
tagger_info.clone(),
612-
String::new(),
613-
),
614-
message.clone().unwrap_or_default(),
615-
);
616-
Ok((
617-
git_internal_tag.id.to_string(),
618-
target.unwrap_or_else(|| "HEAD".to_string()),
619-
))
620-
}
621-
622551
fn build_git_tag_model(
623552
&self,
624553
tag_id_hex: String,
@@ -675,43 +604,4 @@ impl ImportApiService {
675604
}
676605
Ok(())
677606
}
678-
679-
fn update_tree_hash(
680-
&self,
681-
tree: Arc<Tree>,
682-
name: &str,
683-
target_hash: ObjectHash,
684-
) -> Result<Tree, GitError> {
685-
let index = tree
686-
.tree_items
687-
.iter()
688-
.position(|item| item.name == name)
689-
.ok_or_else(|| GitError::CustomError(format!("Tree item '{}' not found", name)))?;
690-
let mut items = tree.tree_items.clone();
691-
items[index].id = target_hash;
692-
Tree::from_tree_items(items).map_err(|_| GitError::CustomError("Invalid tree".to_string()))
693-
}
694-
695-
/// Build updated trees chain and return (updated_trees, new_root_tree_id)
696-
fn build_updated_trees(
697-
&self,
698-
mut path: PathBuf,
699-
mut update_chain: Vec<Arc<Tree>>,
700-
mut updated_tree_hash: ObjectHash,
701-
) -> Result<(Vec<Tree>, ObjectHash), GitError> {
702-
let mut updated_trees = Vec::new();
703-
while let Some(tree) = update_chain.pop() {
704-
let cloned_path = path.clone();
705-
let name = cloned_path
706-
.file_name()
707-
.and_then(|n| n.to_str())
708-
.ok_or_else(|| GitError::CustomError("Invalid path".into()))?;
709-
path.pop();
710-
711-
let new_tree = self.update_tree_hash(tree, name, updated_tree_hash)?;
712-
updated_tree_hash = new_tree.id;
713-
updated_trees.push(new_tree);
714-
}
715-
Ok((updated_trees, updated_tree_hash))
716-
}
717607
}

ceres/src/api_service/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,23 @@ use crate::{
3030
},
3131
};
3232

33-
pub mod admin_ops;
3433
pub mod blame_ops;
3534
pub mod blob_ops;
36-
pub mod bot_ops;
3735
pub mod buck_tree_builder;
3836
pub mod cache;
3937
pub mod commit_ops;
40-
pub mod group_ops;
4138
pub mod history;
4239
pub mod import_api_service;
43-
pub mod mono_api_service;
40+
pub mod mono;
4441
pub mod state;
42+
pub mod tag_ops;
4543
pub mod tree_ops;
4644

45+
pub use mono::{
46+
ADMIN_FILE, EffectiveResourcePermission, MonoApiService, MonoServiceLogic, RefUpdate,
47+
TreeUpdateResult, cl_merge,
48+
};
49+
4750
#[async_trait]
4851
pub trait ApiHandler: Send + Sync {
4952
fn get_context(&self) -> Storage;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use callisto::sea_orm_active_enums::{
33
};
44
use common::errors::MegaError;
55

6-
use crate::api_service::mono_api_service::MonoApiService;
6+
use crate::api_service::mono::MonoApiService;
77

88
impl MonoApiService {
99
/// Check whether a bot has sufficient permission on a given resource.

0 commit comments

Comments
 (0)