1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use serde_json::{Value, json};
6use winterbaume_core::{
7 BackendState, DEFAULT_ACCOUNT_ID, MockRequest, MockResponse, MockService, StateChangeNotifier,
8 json_error_response,
9};
10
11use crate::state::{CodeCommitError, CodeCommitState};
12use crate::views::CodeCommitStateView;
13use crate::wire;
14
15pub struct CodeCommitService {
16 pub(crate) state: Arc<BackendState<CodeCommitState>>,
17 pub(crate) notifier: StateChangeNotifier<CodeCommitStateView>,
18}
19
20impl CodeCommitService {
21 pub fn new() -> Self {
22 Self {
23 state: Arc::new(BackendState::new()),
24 notifier: StateChangeNotifier::new(),
25 }
26 }
27}
28
29impl Default for CodeCommitService {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35impl MockService for CodeCommitService {
36 fn service_name(&self) -> &str {
37 "codecommit"
38 }
39
40 fn url_patterns(&self) -> Vec<&str> {
41 vec![
42 r"https?://codecommit\..*\.amazonaws\.com",
43 r"https?://codecommit\.amazonaws\.com",
44 ]
45 }
46
47 fn handle(
48 &self,
49 request: MockRequest,
50 ) -> Pin<Box<dyn Future<Output = MockResponse> + Send + '_>> {
51 Box::pin(async move { self.dispatch(request).await })
52 }
53}
54
55impl CodeCommitService {
56 async fn dispatch(&self, request: MockRequest) -> MockResponse {
57 let region = winterbaume_core::auth::extract_region_from_uri(&request.uri);
58 let account_id = DEFAULT_ACCOUNT_ID;
59
60 let action = request
63 .headers
64 .get("x-amz-target")
65 .and_then(|v| v.to_str().ok())
66 .and_then(|v| v.split('.').next_back())
67 .map(|s| s.to_string());
68
69 let action = match action {
70 Some(a) => a,
71 None => {
72 return json_error_response(400, "MissingAction", "Missing X-Amz-Target header");
73 }
74 };
75
76 if serde_json::from_slice::<Value>(&request.body).is_err() {
79 return json_error_response(400, "SerializationException", "Invalid JSON body");
80 }
81 let body_bytes: &[u8] = &request.body;
82
83 let state = self.state.get(account_id, ®ion);
84
85 match action.as_str() {
86 "CreateRepository" => {
87 self.handle_create_repository(&state, body_bytes, account_id, ®ion)
88 .await
89 }
90 "GetRepository" => self.handle_get_repository(&state, body_bytes).await,
91 "DeleteRepository" => self.handle_delete_repository(&state, body_bytes).await,
92 "ListRepositories" => self.handle_list_repositories(&state, body_bytes).await,
93 "UpdateRepositoryDescription" => {
94 self.handle_update_repository_description(&state, body_bytes)
95 .await
96 }
97 "UpdateRepositoryName" => {
98 self.handle_update_repository_name(&state, body_bytes, account_id, ®ion)
99 .await
100 }
101 "CreateBranch" => self.handle_create_branch(&state, body_bytes).await,
103 "GetBranch" => self.handle_get_branch(&state, body_bytes).await,
104 "ListBranches" => self.handle_list_branches(&state, body_bytes).await,
105 "DeleteBranch" => self.handle_delete_branch(&state, body_bytes).await,
106 "UpdateDefaultBranch" => self.handle_update_default_branch(&state, body_bytes).await,
107 "CreateCommit" => self.handle_create_commit(&state, body_bytes).await,
109 "GetCommit" => self.handle_get_commit(&state, body_bytes).await,
110 "GetDifferences" => self.handle_get_differences(&state, body_bytes).await,
111 "GetFile" => self.handle_get_file(&state, body_bytes).await,
112 "GetFolder" => self.handle_get_folder(&state, body_bytes).await,
113 "PutFile" => self.handle_put_file(&state, body_bytes).await,
114 "DeleteFile" => self.handle_delete_file(&state, body_bytes).await,
115 "CreatePullRequest" => self.handle_create_pull_request(&state, body_bytes).await,
117 "GetPullRequest" => self.handle_get_pull_request(&state, body_bytes).await,
118 "ListPullRequests" => self.handle_list_pull_requests(&state, body_bytes).await,
119 "UpdatePullRequestStatus" => {
120 self.handle_update_pull_request_status(&state, body_bytes)
121 .await
122 }
123 "TagResource" => self.handle_tag_resource(&state, body_bytes).await,
125 "UntagResource" => self.handle_untag_resource(&state, body_bytes).await,
126 "ListTagsForResource" => self.handle_list_tags_for_resource(&state, body_bytes).await,
127 "AssociateApprovalRuleTemplateWithRepository" => json_error_response(
129 501,
130 "NotImplementedError",
131 "AssociateApprovalRuleTemplateWithRepository is not yet implemented in winterbaume-codecommit",
132 ),
133 "BatchAssociateApprovalRuleTemplateWithRepositories" => json_error_response(
134 501,
135 "NotImplementedError",
136 "BatchAssociateApprovalRuleTemplateWithRepositories is not yet implemented in winterbaume-codecommit",
137 ),
138 "BatchDescribeMergeConflicts" => json_error_response(
139 501,
140 "NotImplementedError",
141 "BatchDescribeMergeConflicts is not yet implemented in winterbaume-codecommit",
142 ),
143 "BatchDisassociateApprovalRuleTemplateFromRepositories" => json_error_response(
144 501,
145 "NotImplementedError",
146 "BatchDisassociateApprovalRuleTemplateFromRepositories is not yet implemented in winterbaume-codecommit",
147 ),
148 "BatchGetCommits" => json_error_response(
149 501,
150 "NotImplementedError",
151 "BatchGetCommits is not yet implemented in winterbaume-codecommit",
152 ),
153 "BatchGetRepositories" => json_error_response(
154 501,
155 "NotImplementedError",
156 "BatchGetRepositories is not yet implemented in winterbaume-codecommit",
157 ),
158 "CreateApprovalRuleTemplate" => json_error_response(
159 501,
160 "NotImplementedError",
161 "CreateApprovalRuleTemplate is not yet implemented in winterbaume-codecommit",
162 ),
163 "CreatePullRequestApprovalRule" => json_error_response(
164 501,
165 "NotImplementedError",
166 "CreatePullRequestApprovalRule is not yet implemented in winterbaume-codecommit",
167 ),
168 "CreateUnreferencedMergeCommit" => json_error_response(
169 501,
170 "NotImplementedError",
171 "CreateUnreferencedMergeCommit is not yet implemented in winterbaume-codecommit",
172 ),
173 "DeleteApprovalRuleTemplate" => json_error_response(
174 501,
175 "NotImplementedError",
176 "DeleteApprovalRuleTemplate is not yet implemented in winterbaume-codecommit",
177 ),
178 "DeleteCommentContent" => json_error_response(
179 501,
180 "NotImplementedError",
181 "DeleteCommentContent is not yet implemented in winterbaume-codecommit",
182 ),
183 "DeletePullRequestApprovalRule" => json_error_response(
184 501,
185 "NotImplementedError",
186 "DeletePullRequestApprovalRule is not yet implemented in winterbaume-codecommit",
187 ),
188 "DescribeMergeConflicts" => json_error_response(
189 501,
190 "NotImplementedError",
191 "DescribeMergeConflicts is not yet implemented in winterbaume-codecommit",
192 ),
193 "DescribePullRequestEvents" => json_error_response(
194 501,
195 "NotImplementedError",
196 "DescribePullRequestEvents is not yet implemented in winterbaume-codecommit",
197 ),
198 "DisassociateApprovalRuleTemplateFromRepository" => json_error_response(
199 501,
200 "NotImplementedError",
201 "DisassociateApprovalRuleTemplateFromRepository is not yet implemented in winterbaume-codecommit",
202 ),
203 "EvaluatePullRequestApprovalRules" => json_error_response(
204 501,
205 "NotImplementedError",
206 "EvaluatePullRequestApprovalRules is not yet implemented in winterbaume-codecommit",
207 ),
208 "GetApprovalRuleTemplate" => json_error_response(
209 501,
210 "NotImplementedError",
211 "GetApprovalRuleTemplate is not yet implemented in winterbaume-codecommit",
212 ),
213 "GetBlob" => json_error_response(
214 501,
215 "NotImplementedError",
216 "GetBlob is not yet implemented in winterbaume-codecommit",
217 ),
218 "GetComment" => json_error_response(
219 501,
220 "NotImplementedError",
221 "GetComment is not yet implemented in winterbaume-codecommit",
222 ),
223 "GetCommentReactions" => json_error_response(
224 501,
225 "NotImplementedError",
226 "GetCommentReactions is not yet implemented in winterbaume-codecommit",
227 ),
228 "GetCommentsForComparedCommit" => json_error_response(
229 501,
230 "NotImplementedError",
231 "GetCommentsForComparedCommit is not yet implemented in winterbaume-codecommit",
232 ),
233 "GetCommentsForPullRequest" => json_error_response(
234 501,
235 "NotImplementedError",
236 "GetCommentsForPullRequest is not yet implemented in winterbaume-codecommit",
237 ),
238 "GetMergeCommit" => json_error_response(
239 501,
240 "NotImplementedError",
241 "GetMergeCommit is not yet implemented in winterbaume-codecommit",
242 ),
243 "GetMergeConflicts" => json_error_response(
244 501,
245 "NotImplementedError",
246 "GetMergeConflicts is not yet implemented in winterbaume-codecommit",
247 ),
248 "GetMergeOptions" => json_error_response(
249 501,
250 "NotImplementedError",
251 "GetMergeOptions is not yet implemented in winterbaume-codecommit",
252 ),
253 "GetPullRequestApprovalStates" => json_error_response(
254 501,
255 "NotImplementedError",
256 "GetPullRequestApprovalStates is not yet implemented in winterbaume-codecommit",
257 ),
258 "GetPullRequestOverrideState" => json_error_response(
259 501,
260 "NotImplementedError",
261 "GetPullRequestOverrideState is not yet implemented in winterbaume-codecommit",
262 ),
263 "GetRepositoryTriggers" => json_error_response(
264 501,
265 "NotImplementedError",
266 "GetRepositoryTriggers is not yet implemented in winterbaume-codecommit",
267 ),
268 "ListApprovalRuleTemplates" => json_error_response(
269 501,
270 "NotImplementedError",
271 "ListApprovalRuleTemplates is not yet implemented in winterbaume-codecommit",
272 ),
273 "ListAssociatedApprovalRuleTemplatesForRepository" => json_error_response(
274 501,
275 "NotImplementedError",
276 "ListAssociatedApprovalRuleTemplatesForRepository is not yet implemented in winterbaume-codecommit",
277 ),
278 "ListFileCommitHistory" => json_error_response(
279 501,
280 "NotImplementedError",
281 "ListFileCommitHistory is not yet implemented in winterbaume-codecommit",
282 ),
283 "ListRepositoriesForApprovalRuleTemplate" => json_error_response(
284 501,
285 "NotImplementedError",
286 "ListRepositoriesForApprovalRuleTemplate is not yet implemented in winterbaume-codecommit",
287 ),
288 "MergeBranchesByFastForward" => json_error_response(
289 501,
290 "NotImplementedError",
291 "MergeBranchesByFastForward is not yet implemented in winterbaume-codecommit",
292 ),
293 "MergeBranchesBySquash" => json_error_response(
294 501,
295 "NotImplementedError",
296 "MergeBranchesBySquash is not yet implemented in winterbaume-codecommit",
297 ),
298 "MergeBranchesByThreeWay" => json_error_response(
299 501,
300 "NotImplementedError",
301 "MergeBranchesByThreeWay is not yet implemented in winterbaume-codecommit",
302 ),
303 "MergePullRequestByFastForward" => json_error_response(
304 501,
305 "NotImplementedError",
306 "MergePullRequestByFastForward is not yet implemented in winterbaume-codecommit",
307 ),
308 "MergePullRequestBySquash" => json_error_response(
309 501,
310 "NotImplementedError",
311 "MergePullRequestBySquash is not yet implemented in winterbaume-codecommit",
312 ),
313 "MergePullRequestByThreeWay" => json_error_response(
314 501,
315 "NotImplementedError",
316 "MergePullRequestByThreeWay is not yet implemented in winterbaume-codecommit",
317 ),
318 "OverridePullRequestApprovalRules" => json_error_response(
319 501,
320 "NotImplementedError",
321 "OverridePullRequestApprovalRules is not yet implemented in winterbaume-codecommit",
322 ),
323 "PostCommentForComparedCommit" => json_error_response(
324 501,
325 "NotImplementedError",
326 "PostCommentForComparedCommit is not yet implemented in winterbaume-codecommit",
327 ),
328 "PostCommentForPullRequest" => json_error_response(
329 501,
330 "NotImplementedError",
331 "PostCommentForPullRequest is not yet implemented in winterbaume-codecommit",
332 ),
333 "PostCommentReply" => json_error_response(
334 501,
335 "NotImplementedError",
336 "PostCommentReply is not yet implemented in winterbaume-codecommit",
337 ),
338 "PutCommentReaction" => json_error_response(
339 501,
340 "NotImplementedError",
341 "PutCommentReaction is not yet implemented in winterbaume-codecommit",
342 ),
343 "PutRepositoryTriggers" => json_error_response(
344 501,
345 "NotImplementedError",
346 "PutRepositoryTriggers is not yet implemented in winterbaume-codecommit",
347 ),
348 "TestRepositoryTriggers" => json_error_response(
349 501,
350 "NotImplementedError",
351 "TestRepositoryTriggers is not yet implemented in winterbaume-codecommit",
352 ),
353 "UpdateApprovalRuleTemplateContent" => json_error_response(
354 501,
355 "NotImplementedError",
356 "UpdateApprovalRuleTemplateContent is not yet implemented in winterbaume-codecommit",
357 ),
358 "UpdateApprovalRuleTemplateDescription" => json_error_response(
359 501,
360 "NotImplementedError",
361 "UpdateApprovalRuleTemplateDescription is not yet implemented in winterbaume-codecommit",
362 ),
363 "UpdateApprovalRuleTemplateName" => json_error_response(
364 501,
365 "NotImplementedError",
366 "UpdateApprovalRuleTemplateName is not yet implemented in winterbaume-codecommit",
367 ),
368 "UpdateComment" => json_error_response(
369 501,
370 "NotImplementedError",
371 "UpdateComment is not yet implemented in winterbaume-codecommit",
372 ),
373 "UpdatePullRequestApprovalRuleContent" => json_error_response(
374 501,
375 "NotImplementedError",
376 "UpdatePullRequestApprovalRuleContent is not yet implemented in winterbaume-codecommit",
377 ),
378 "UpdatePullRequestApprovalState" => json_error_response(
379 501,
380 "NotImplementedError",
381 "UpdatePullRequestApprovalState is not yet implemented in winterbaume-codecommit",
382 ),
383 "UpdatePullRequestDescription" => json_error_response(
384 501,
385 "NotImplementedError",
386 "UpdatePullRequestDescription is not yet implemented in winterbaume-codecommit",
387 ),
388 "UpdatePullRequestTitle" => json_error_response(
389 501,
390 "NotImplementedError",
391 "UpdatePullRequestTitle is not yet implemented in winterbaume-codecommit",
392 ),
393 "UpdateRepositoryEncryptionKey" => json_error_response(
394 501,
395 "NotImplementedError",
396 "UpdateRepositoryEncryptionKey is not yet implemented in winterbaume-codecommit",
397 ),
398 _ => json_error_response(
399 400,
400 "InvalidAction",
401 &format!("Could not find operation {action} for CodeCommit"),
402 ),
403 }
404 }
405
406 async fn handle_create_repository(
407 &self,
408 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
409 body: &[u8],
410 account_id: &str,
411 region: &str,
412 ) -> MockResponse {
413 let input = match wire::deserialize_create_repository_request(body) {
414 Ok(v) => v,
415 Err(e) => return json_error_response(400, "ValidationException", &e),
416 };
417 if input.repository_name.is_empty() {
418 return json_error_response(
419 400,
420 "RepositoryNameRequiredException",
421 "Repository name is required",
422 );
423 }
424 let description = input.repository_description.as_deref().unwrap_or("");
425
426 let mut state = state.write().await;
427 match state.create_repository(&input.repository_name, description, account_id, region) {
428 Ok(repo) => wire::serialize_create_repository_response(&wire::CreateRepositoryOutput {
429 repository_metadata: Some(repo_to_wire(repo)),
430 }),
431 Err(e) => codecommit_error_response(&e),
432 }
433 }
434
435 async fn handle_get_repository(
436 &self,
437 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
438 body: &[u8],
439 ) -> MockResponse {
440 let input = match wire::deserialize_get_repository_request(body) {
441 Ok(v) => v,
442 Err(e) => return json_error_response(400, "ValidationException", &e),
443 };
444 if input.repository_name.is_empty() {
445 return json_error_response(
446 400,
447 "RepositoryNameRequiredException",
448 "Repository name is required",
449 );
450 }
451
452 let state = state.read().await;
453 match state.get_repository(&input.repository_name) {
454 Ok(repo) => wire::serialize_get_repository_response(&wire::GetRepositoryOutput {
455 repository_metadata: Some(repo_to_wire(repo)),
456 }),
457 Err(e) => codecommit_error_response(&e),
458 }
459 }
460
461 async fn handle_delete_repository(
462 &self,
463 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
464 body: &[u8],
465 ) -> MockResponse {
466 let input = match wire::deserialize_delete_repository_request(body) {
467 Ok(v) => v,
468 Err(e) => return json_error_response(400, "ValidationException", &e),
469 };
470 if input.repository_name.is_empty() {
471 return json_error_response(
472 400,
473 "RepositoryNameRequiredException",
474 "Repository name is required",
475 );
476 }
477
478 let mut state = state.write().await;
479 let repo_id = state.delete_repository(&input.repository_name);
480 let repo_id_opt = if repo_id.is_empty() {
481 None
482 } else {
483 Some(repo_id)
484 };
485 wire::serialize_delete_repository_response(&wire::DeleteRepositoryOutput {
486 repository_id: repo_id_opt,
487 })
488 }
489
490 async fn handle_list_repositories(
491 &self,
492 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
493 body: &[u8],
494 ) -> MockResponse {
495 if let Err(e) = wire::deserialize_list_repositories_request(body) {
496 return json_error_response(400, "ValidationException", &e);
497 }
498 let state = state.read().await;
499 let repos = state.list_repositories();
500 wire::serialize_list_repositories_response(&wire::ListRepositoriesOutput {
501 repositories: Some(
502 repos
503 .iter()
504 .map(|r| wire::RepositoryNameIdPair {
505 repository_id: Some(r.repository_id.clone()),
506 repository_name: Some(r.repository_name.clone()),
507 })
508 .collect(),
509 ),
510 next_token: None,
511 })
512 }
513
514 async fn handle_update_repository_description(
515 &self,
516 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
517 body: &[u8],
518 ) -> MockResponse {
519 let input = match wire::deserialize_update_repository_description_request(body) {
520 Ok(v) => v,
521 Err(e) => return json_error_response(400, "ValidationException", &e),
522 };
523 if input.repository_name.is_empty() {
524 return json_error_response(
525 400,
526 "RepositoryNameRequiredException",
527 "Repository name is required",
528 );
529 }
530 let description = input.repository_description.as_deref();
531
532 let mut state = state.write().await;
533 match state.update_repository_description(&input.repository_name, description) {
534 Ok(()) => wire::serialize_update_repository_description_response(),
535 Err(e) => codecommit_error_response(&e),
536 }
537 }
538
539 async fn handle_update_repository_name(
540 &self,
541 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
542 body: &[u8],
543 account_id: &str,
544 region: &str,
545 ) -> MockResponse {
546 let input = match wire::deserialize_update_repository_name_request(body) {
547 Ok(v) => v,
548 Err(e) => return json_error_response(400, "ValidationException", &e),
549 };
550 if input.old_name.is_empty() {
551 return json_error_response(
552 400,
553 "RepositoryNameRequiredException",
554 "Old repository name is required",
555 );
556 }
557 if input.new_name.is_empty() {
558 return json_error_response(
559 400,
560 "RepositoryNameRequiredException",
561 "New repository name is required",
562 );
563 }
564
565 let mut state = state.write().await;
566 match state.update_repository_name(&input.old_name, &input.new_name, region, account_id) {
567 Ok(()) => wire::serialize_update_repository_name_response(),
568 Err(e) => codecommit_error_response(&e),
569 }
570 }
571
572 async fn handle_create_branch(
575 &self,
576 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
577 body: &[u8],
578 ) -> MockResponse {
579 let input = match wire::deserialize_create_branch_request(body) {
580 Ok(v) => v,
581 Err(e) => return json_error_response(400, "ValidationException", &e),
582 };
583 if input.repository_name.is_empty() {
584 return json_error_response(
585 400,
586 "RepositoryNameRequiredException",
587 "Repository name is required",
588 );
589 }
590 if input.branch_name.is_empty() {
591 return json_error_response(
592 400,
593 "BranchNameRequiredException",
594 "Branch name is required",
595 );
596 }
597 if input.commit_id.is_empty() {
598 return json_error_response(400, "CommitIdRequiredException", "Commit ID is required");
599 }
600
601 let mut state = state.write().await;
602 match state.create_branch(&input.repository_name, &input.branch_name, &input.commit_id) {
603 Ok(()) => wire::serialize_create_branch_response(),
604 Err(e) => codecommit_error_response(&e),
605 }
606 }
607
608 async fn handle_get_branch(
609 &self,
610 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
611 body: &[u8],
612 ) -> MockResponse {
613 let input = match wire::deserialize_get_branch_request(body) {
614 Ok(v) => v,
615 Err(e) => return json_error_response(400, "ValidationException", &e),
616 };
617 let repo_name = match input.repository_name.as_deref() {
618 Some(n) if !n.is_empty() => n,
619 _ => {
620 return json_error_response(
621 400,
622 "RepositoryNameRequiredException",
623 "Repository name is required",
624 );
625 }
626 };
627 let branch_name = match input.branch_name.as_deref() {
628 Some(n) if !n.is_empty() => n,
629 _ => {
630 return json_error_response(
631 400,
632 "BranchNameRequiredException",
633 "Branch name is required",
634 );
635 }
636 };
637
638 let state = state.read().await;
639 match state.get_branch(repo_name, branch_name) {
640 Ok(branch) => wire::serialize_get_branch_response(&wire::GetBranchOutput {
641 branch: Some(wire::BranchInfo {
642 branch_name: Some(branch.branch_name.clone()),
643 commit_id: Some(branch.commit_id.clone()),
644 }),
645 }),
646 Err(e) => codecommit_error_response(&e),
647 }
648 }
649
650 async fn handle_list_branches(
651 &self,
652 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
653 body: &[u8],
654 ) -> MockResponse {
655 let input = match wire::deserialize_list_branches_request(body) {
656 Ok(v) => v,
657 Err(e) => return json_error_response(400, "ValidationException", &e),
658 };
659 if input.repository_name.is_empty() {
660 return json_error_response(
661 400,
662 "RepositoryNameRequiredException",
663 "Repository name is required",
664 );
665 }
666
667 let state = state.read().await;
668 match state.list_branches(&input.repository_name) {
669 Ok(branches) => wire::serialize_list_branches_response(&wire::ListBranchesOutput {
670 branches: Some(branches),
671 next_token: None,
672 }),
673 Err(e) => codecommit_error_response(&e),
674 }
675 }
676
677 async fn handle_delete_branch(
678 &self,
679 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
680 body: &[u8],
681 ) -> MockResponse {
682 let input = match wire::deserialize_delete_branch_request(body) {
683 Ok(v) => v,
684 Err(e) => return json_error_response(400, "ValidationException", &e),
685 };
686 if input.repository_name.is_empty() {
687 return json_error_response(
688 400,
689 "RepositoryNameRequiredException",
690 "Repository name is required",
691 );
692 }
693 if input.branch_name.is_empty() {
694 return json_error_response(
695 400,
696 "BranchNameRequiredException",
697 "Branch name is required",
698 );
699 }
700
701 let mut state = state.write().await;
702 match state.delete_branch(&input.repository_name, &input.branch_name) {
703 Ok(branch) => wire::serialize_delete_branch_response(&wire::DeleteBranchOutput {
704 deleted_branch: Some(wire::BranchInfo {
705 branch_name: Some(branch.branch_name.clone()),
706 commit_id: Some(branch.commit_id.clone()),
707 }),
708 }),
709 Err(e) => codecommit_error_response(&e),
710 }
711 }
712
713 async fn handle_update_default_branch(
714 &self,
715 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
716 body: &[u8],
717 ) -> MockResponse {
718 let input = match wire::deserialize_update_default_branch_request(body) {
719 Ok(v) => v,
720 Err(e) => return json_error_response(400, "ValidationException", &e),
721 };
722 if input.repository_name.is_empty() {
723 return json_error_response(
724 400,
725 "RepositoryNameRequiredException",
726 "Repository name is required",
727 );
728 }
729 if input.default_branch_name.is_empty() {
730 return json_error_response(
731 400,
732 "BranchNameRequiredException",
733 "Default branch name is required",
734 );
735 }
736
737 let mut state = state.write().await;
738 match state.update_default_branch(&input.repository_name, &input.default_branch_name) {
739 Ok(()) => wire::serialize_update_default_branch_response(),
740 Err(e) => codecommit_error_response(&e),
741 }
742 }
743
744 async fn handle_create_commit(
747 &self,
748 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
749 body: &[u8],
750 ) -> MockResponse {
751 let input = match wire::deserialize_create_commit_request(body) {
752 Ok(v) => v,
753 Err(e) => return json_error_response(400, "ValidationException", &e),
754 };
755 if input.repository_name.is_empty() {
756 return json_error_response(
757 400,
758 "RepositoryNameRequiredException",
759 "Repository name is required",
760 );
761 }
762 if input.branch_name.is_empty() {
763 return json_error_response(
764 400,
765 "BranchNameRequiredException",
766 "Branch name is required",
767 );
768 }
769 let parent_commit_id = input.parent_commit_id.as_deref();
770 let author_name = input.author_name.as_deref();
771 let author_email = input.email.as_deref();
772 let commit_message = input.commit_message.as_deref();
773
774 let put_files: Vec<(String, String)> = input
776 .put_files
777 .unwrap_or_default()
778 .into_iter()
779 .map(|item| {
780 let mode = item.file_mode.unwrap_or_else(|| "NORMAL".to_string());
781 (item.file_path, mode)
782 })
783 .collect();
784
785 let delete_files: Vec<String> = input
787 .delete_files
788 .unwrap_or_default()
789 .into_iter()
790 .map(|item| item.file_path)
791 .collect();
792
793 let mut state = state.write().await;
794 match state.create_commit(
795 &input.repository_name,
796 &input.branch_name,
797 parent_commit_id,
798 author_name,
799 author_email,
800 commit_message,
801 put_files,
802 delete_files,
803 ) {
804 Ok(commit) => wire::serialize_create_commit_response(&wire::CreateCommitOutput {
805 commit_id: Some(commit.commit_id.clone()),
806 tree_id: Some(commit.tree_id.clone()),
807 files_added: None,
808 files_updated: None,
809 files_deleted: None,
810 }),
811 Err(e) => codecommit_error_response(&e),
812 }
813 }
814
815 async fn handle_get_commit(
816 &self,
817 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
818 body: &[u8],
819 ) -> MockResponse {
820 let input = match wire::deserialize_get_commit_request(body) {
821 Ok(v) => v,
822 Err(e) => return json_error_response(400, "ValidationException", &e),
823 };
824 if input.repository_name.is_empty() {
825 return json_error_response(
826 400,
827 "RepositoryNameRequiredException",
828 "Repository name is required",
829 );
830 }
831 if input.commit_id.is_empty() {
832 return json_error_response(400, "CommitIdRequiredException", "Commit ID is required");
833 }
834
835 let state = state.read().await;
836 match state.get_commit(&input.repository_name, &input.commit_id) {
837 Ok(commit) => wire::serialize_get_commit_response(&wire::GetCommitOutput {
838 commit: Some(commit_to_wire(commit)),
839 }),
840 Err(e) => codecommit_error_response(&e),
841 }
842 }
843
844 async fn handle_get_differences(
845 &self,
846 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
847 body: &[u8],
848 ) -> MockResponse {
849 let input = match wire::deserialize_get_differences_request(body) {
850 Ok(v) => v,
851 Err(e) => return json_error_response(400, "ValidationException", &e),
852 };
853 if input.repository_name.is_empty() {
854 return json_error_response(
855 400,
856 "RepositoryNameRequiredException",
857 "Repository name is required",
858 );
859 }
860 if input.after_commit_specifier.is_empty() {
861 return json_error_response(
862 400,
863 "CommitRequiredException",
864 "afterCommitSpecifier is required",
865 );
866 }
867 let before_commit = input.before_commit_specifier.as_deref();
868
869 let state = state.read().await;
870 match state.get_differences(
871 &input.repository_name,
872 &input.after_commit_specifier,
873 before_commit,
874 ) {
875 Ok(diffs) => wire::serialize_get_differences_response(&wire::GetDifferencesOutput {
876 differences: Some(
877 diffs
878 .iter()
879 .map(|(before, after, change_type)| wire::Difference {
880 before_blob: before.as_ref().map(|fe| wire::BlobMetadata {
881 blob_id: Some(fe.blob_id.clone()),
882 path: Some(fe.file_path.clone()),
883 mode: Some(fe.file_mode.clone()),
884 }),
885 after_blob: after.as_ref().map(|fe| wire::BlobMetadata {
886 blob_id: Some(fe.blob_id.clone()),
887 path: Some(fe.file_path.clone()),
888 mode: Some(fe.file_mode.clone()),
889 }),
890 change_type: Some(change_type.clone()),
891 })
892 .collect(),
893 ),
894 next_token: None,
895 }),
896 Err(e) => codecommit_error_response(&e),
897 }
898 }
899
900 async fn handle_get_file(
901 &self,
902 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
903 body: &[u8],
904 ) -> MockResponse {
905 let input = match wire::deserialize_get_file_request(body) {
906 Ok(v) => v,
907 Err(e) => return json_error_response(400, "ValidationException", &e),
908 };
909 if input.repository_name.is_empty() {
910 return json_error_response(
911 400,
912 "RepositoryNameRequiredException",
913 "Repository name is required",
914 );
915 }
916 if input.file_path.is_empty() {
917 return json_error_response(400, "FilePathRequiredException", "File path is required");
918 }
919 let commit_specifier = input.commit_specifier.as_deref();
920
921 let state = state.read().await;
922 match state.get_file(&input.repository_name, commit_specifier, &input.file_path) {
923 Ok((commit, file)) => wire::serialize_get_file_response(&wire::GetFileOutput {
924 commit_id: Some(commit.commit_id.clone()),
925 blob_id: Some(file.blob_id.clone()),
926 file_path: Some(file.file_path.clone()),
927 file_mode: Some(file.file_mode.clone()),
928 file_size: Some(0i64),
929 file_content: Some(String::new()),
930 }),
931 Err(e) => codecommit_error_response(&e),
932 }
933 }
934
935 async fn handle_get_folder(
936 &self,
937 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
938 body: &[u8],
939 ) -> MockResponse {
940 let input = match wire::deserialize_get_folder_request(body) {
941 Ok(v) => v,
942 Err(e) => return json_error_response(400, "ValidationException", &e),
943 };
944 if input.repository_name.is_empty() {
945 return json_error_response(
946 400,
947 "RepositoryNameRequiredException",
948 "Repository name is required",
949 );
950 }
951 let folder_path = if input.folder_path.is_empty() {
952 "/"
953 } else {
954 input.folder_path.as_str()
955 };
956 let commit_specifier = input.commit_specifier.as_deref();
957
958 let state = state.read().await;
959 match state.get_folder(&input.repository_name, commit_specifier, folder_path) {
960 Ok((commit_id, files, sub_folders)) => {
961 wire::serialize_get_folder_response(&wire::GetFolderOutput {
962 commit_id: Some(commit_id),
963 folder_path: Some(folder_path.to_string()),
964 tree_id: None,
965 sub_folders: Some(
966 sub_folders
967 .iter()
968 .map(|sf| wire::Folder {
969 absolute_path: Some(format!("{folder_path}/{sf}")),
970 relative_path: Some(sf.clone()),
971 tree_id: None,
972 })
973 .collect(),
974 ),
975 files: Some(
976 files
977 .iter()
978 .map(|fe| wire::File {
979 absolute_path: Some(fe.file_path.clone()),
980 blob_id: Some(fe.blob_id.clone()),
981 file_mode: Some(fe.file_mode.clone()),
982 relative_path: Some(
983 fe.file_path
984 .strip_prefix(folder_path.trim_start_matches('/'))
985 .unwrap_or(&fe.file_path)
986 .to_string(),
987 ),
988 })
989 .collect(),
990 ),
991 sub_modules: None,
992 symbolic_links: None,
993 })
994 }
995 Err(e) => codecommit_error_response(&e),
996 }
997 }
998
999 async fn handle_put_file(
1000 &self,
1001 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1002 body: &[u8],
1003 ) -> MockResponse {
1004 let input = match wire::deserialize_put_file_request(body) {
1005 Ok(v) => v,
1006 Err(e) => return json_error_response(400, "ValidationException", &e),
1007 };
1008 if input.repository_name.is_empty() {
1009 return json_error_response(
1010 400,
1011 "RepositoryNameRequiredException",
1012 "Repository name is required",
1013 );
1014 }
1015 if input.branch_name.is_empty() {
1016 return json_error_response(
1017 400,
1018 "BranchNameRequiredException",
1019 "Branch name is required",
1020 );
1021 }
1022 if input.file_path.is_empty() {
1023 return json_error_response(400, "FilePathRequiredException", "File path is required");
1024 }
1025 if input.file_content.is_empty() {
1026 return json_error_response(
1027 400,
1028 "FileContentRequiredException",
1029 "File content is required",
1030 );
1031 }
1032 let parent_commit_id = match input.parent_commit_id.as_deref() {
1033 Some(c) if !c.is_empty() => c,
1034 _ => {
1035 return json_error_response(
1036 400,
1037 "ParentCommitIdRequiredException",
1038 "Parent commit ID is required",
1039 );
1040 }
1041 };
1042 let file_mode = input.file_mode.as_deref();
1043 let author_name = input.name.as_deref();
1044 let author_email = input.email.as_deref();
1045 let commit_message = input.commit_message.as_deref();
1046
1047 let mut state = state.write().await;
1048 match state.put_file(
1049 &input.repository_name,
1050 &input.branch_name,
1051 parent_commit_id,
1052 &input.file_path,
1053 file_mode,
1054 author_name,
1055 author_email,
1056 commit_message,
1057 ) {
1058 Ok(commit) => wire::serialize_put_file_response(&wire::PutFileOutput {
1059 commit_id: Some(commit.commit_id.clone()),
1060 tree_id: Some(commit.tree_id.clone()),
1061 blob_id: None,
1062 }),
1063 Err(e) => codecommit_error_response(&e),
1064 }
1065 }
1066
1067 async fn handle_delete_file(
1068 &self,
1069 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1070 body: &[u8],
1071 ) -> MockResponse {
1072 let input = match wire::deserialize_delete_file_request(body) {
1073 Ok(v) => v,
1074 Err(e) => return json_error_response(400, "ValidationException", &e),
1075 };
1076 if input.repository_name.is_empty() {
1077 return json_error_response(
1078 400,
1079 "RepositoryNameRequiredException",
1080 "Repository name is required",
1081 );
1082 }
1083 if input.branch_name.is_empty() {
1084 return json_error_response(
1085 400,
1086 "BranchNameRequiredException",
1087 "Branch name is required",
1088 );
1089 }
1090 if input.file_path.is_empty() {
1091 return json_error_response(400, "FilePathRequiredException", "File path is required");
1092 }
1093 if input.parent_commit_id.is_empty() {
1094 return json_error_response(
1095 400,
1096 "ParentCommitIdRequiredException",
1097 "Parent commit ID is required",
1098 );
1099 }
1100 let author_name = input.name.as_deref();
1101 let author_email = input.email.as_deref();
1102 let commit_message = input.commit_message.as_deref();
1103
1104 let mut state = state.write().await;
1105 match state.delete_file(
1106 &input.repository_name,
1107 &input.branch_name,
1108 &input.parent_commit_id,
1109 &input.file_path,
1110 author_name,
1111 author_email,
1112 commit_message,
1113 ) {
1114 Ok(commit) => wire::serialize_delete_file_response(&wire::DeleteFileOutput {
1115 commit_id: Some(commit.commit_id.clone()),
1116 tree_id: Some(commit.tree_id.clone()),
1117 blob_id: None,
1118 file_path: Some(input.file_path.clone()),
1119 }),
1120 Err(e) => codecommit_error_response(&e),
1121 }
1122 }
1123
1124 async fn handle_create_pull_request(
1127 &self,
1128 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1129 body: &[u8],
1130 ) -> MockResponse {
1131 let input = match wire::deserialize_create_pull_request_request(body) {
1132 Ok(v) => v,
1133 Err(e) => return json_error_response(400, "ValidationException", &e),
1134 };
1135 if input.title.is_empty() {
1136 return json_error_response(
1137 400,
1138 "TitleRequiredException",
1139 "Pull request title is required",
1140 );
1141 }
1142 let description = input.description.as_deref().unwrap_or("");
1143
1144 if input.targets.is_empty() {
1145 return json_error_response(
1146 400,
1147 "TargetsRequiredException",
1148 "At least one target is required",
1149 );
1150 }
1151
1152 let target = &input.targets[0];
1153 if target.repository_name.is_empty() {
1154 return json_error_response(
1155 400,
1156 "RepositoryNameRequiredException",
1157 "Repository name is required in target",
1158 );
1159 }
1160 if target.source_reference.is_empty() {
1161 return json_error_response(
1162 400,
1163 "SourceBranchRequiredException",
1164 "Source reference is required",
1165 );
1166 }
1167 let dest_ref = target
1168 .destination_reference
1169 .as_deref()
1170 .unwrap_or("refs/heads/main");
1171
1172 let mut state = state.write().await;
1173 match state.create_pull_request(
1174 &input.title,
1175 description,
1176 &target.repository_name,
1177 &target.source_reference,
1178 dest_ref,
1179 ) {
1180 Ok(pr) => {
1181 wire::serialize_create_pull_request_response(&wire::CreatePullRequestOutput {
1182 pull_request: Some(pr_to_wire(&pr)),
1183 })
1184 }
1185 Err(e) => codecommit_error_response(&e),
1186 }
1187 }
1188
1189 async fn handle_get_pull_request(
1190 &self,
1191 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1192 body: &[u8],
1193 ) -> MockResponse {
1194 let input = match wire::deserialize_get_pull_request_request(body) {
1195 Ok(v) => v,
1196 Err(e) => return json_error_response(400, "ValidationException", &e),
1197 };
1198 if input.pull_request_id.is_empty() {
1199 return json_error_response(
1200 400,
1201 "PullRequestIdRequiredException",
1202 "Pull request ID is required",
1203 );
1204 }
1205
1206 let state = state.read().await;
1207 match state.get_pull_request(&input.pull_request_id) {
1208 Ok(pr) => wire::serialize_get_pull_request_response(&wire::GetPullRequestOutput {
1209 pull_request: Some(pr_to_wire(pr)),
1210 }),
1211 Err(e) => codecommit_error_response(&e),
1212 }
1213 }
1214
1215 async fn handle_list_pull_requests(
1216 &self,
1217 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1218 body: &[u8],
1219 ) -> MockResponse {
1220 let input = match wire::deserialize_list_pull_requests_request(body) {
1221 Ok(v) => v,
1222 Err(e) => return json_error_response(400, "ValidationException", &e),
1223 };
1224 if input.repository_name.is_empty() {
1225 return json_error_response(
1226 400,
1227 "RepositoryNameRequiredException",
1228 "Repository name is required",
1229 );
1230 }
1231 let status = input.pull_request_status.as_deref();
1232
1233 let state = state.read().await;
1234 let pr_ids = state.list_pull_requests(&input.repository_name, status);
1235 wire::serialize_list_pull_requests_response(&wire::ListPullRequestsOutput {
1236 pull_request_ids: Some(pr_ids),
1237 next_token: None,
1238 })
1239 }
1240
1241 async fn handle_update_pull_request_status(
1242 &self,
1243 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1244 body: &[u8],
1245 ) -> MockResponse {
1246 let input = match wire::deserialize_update_pull_request_status_request(body) {
1247 Ok(v) => v,
1248 Err(e) => return json_error_response(400, "ValidationException", &e),
1249 };
1250 if input.pull_request_id.is_empty() {
1251 return json_error_response(
1252 400,
1253 "PullRequestIdRequiredException",
1254 "Pull request ID is required",
1255 );
1256 }
1257 if input.pull_request_status.is_empty() {
1258 return json_error_response(
1259 400,
1260 "PullRequestStatusRequiredException",
1261 "Pull request status is required",
1262 );
1263 }
1264
1265 let mut state = state.write().await;
1266 match state.update_pull_request_status(&input.pull_request_id, &input.pull_request_status) {
1267 Ok(pr) => wire::serialize_update_pull_request_status_response(
1268 &wire::UpdatePullRequestStatusOutput {
1269 pull_request: Some(pr_to_wire(&pr)),
1270 },
1271 ),
1272 Err(e) => codecommit_error_response(&e),
1273 }
1274 }
1275
1276 async fn handle_tag_resource(
1279 &self,
1280 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1281 body: &[u8],
1282 ) -> MockResponse {
1283 let input = match wire::deserialize_tag_resource_request(body) {
1284 Ok(v) => v,
1285 Err(e) => return json_error_response(400, "ValidationException", &e),
1286 };
1287 if input.resource_arn.is_empty() {
1288 return json_error_response(
1289 400,
1290 "ResourceArnRequiredException",
1291 "Resource ARN is required",
1292 );
1293 }
1294
1295 let repo_name = input.resource_arn.split(':').next_back().unwrap_or("");
1297
1298 let mut state = state.write().await;
1299 match state.tag_resource(repo_name, input.tags) {
1300 Ok(()) => wire::serialize_tag_resource_response(),
1301 Err(e) => codecommit_error_response(&e),
1302 }
1303 }
1304
1305 async fn handle_untag_resource(
1306 &self,
1307 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1308 body: &[u8],
1309 ) -> MockResponse {
1310 let input = match wire::deserialize_untag_resource_request(body) {
1311 Ok(v) => v,
1312 Err(e) => return json_error_response(400, "ValidationException", &e),
1313 };
1314 if input.resource_arn.is_empty() {
1315 return json_error_response(
1316 400,
1317 "ResourceArnRequiredException",
1318 "Resource ARN is required",
1319 );
1320 }
1321
1322 let repo_name = input.resource_arn.split(':').next_back().unwrap_or("");
1323
1324 let mut state = state.write().await;
1325 match state.untag_resource(repo_name, &input.tag_keys) {
1326 Ok(()) => wire::serialize_untag_resource_response(),
1327 Err(e) => codecommit_error_response(&e),
1328 }
1329 }
1330
1331 async fn handle_list_tags_for_resource(
1332 &self,
1333 state: &Arc<tokio::sync::RwLock<CodeCommitState>>,
1334 body: &[u8],
1335 ) -> MockResponse {
1336 let input = match wire::deserialize_list_tags_for_resource_request(body) {
1337 Ok(v) => v,
1338 Err(e) => return json_error_response(400, "ValidationException", &e),
1339 };
1340 if input.resource_arn.is_empty() {
1341 return json_error_response(
1342 400,
1343 "ResourceArnRequiredException",
1344 "Resource ARN is required",
1345 );
1346 }
1347
1348 let state = state.read().await;
1349 match state.list_tags_for_resource(&input.resource_arn) {
1350 Ok(tags) => {
1351 wire::serialize_list_tags_for_resource_response(&wire::ListTagsForResourceOutput {
1352 tags: Some(tags),
1353 next_token: None,
1354 })
1355 }
1356 Err(e) => codecommit_error_response(&e),
1357 }
1358 }
1359}
1360
1361fn repo_to_wire(repo: &crate::types::Repository) -> wire::RepositoryMetadata {
1362 wire::RepositoryMetadata {
1363 account_id: Some(repo.account_id.clone()),
1364 repository_id: Some(repo.repository_id.clone()),
1365 repository_name: Some(repo.repository_name.clone()),
1366 repository_description: Some(repo.description.clone()),
1367 arn: Some(repo.arn.clone()),
1368 clone_url_http: Some(repo.clone_url_http.clone()),
1369 clone_url_ssh: Some(repo.clone_url_ssh.clone()),
1370 creation_date: Some(repo.creation_date.timestamp() as f64),
1371 last_modified_date: Some(repo.last_modified_date.timestamp() as f64),
1372 default_branch: repo.default_branch.clone(),
1373 ..Default::default()
1374 }
1375}
1376
1377fn commit_to_wire(commit: &crate::types::CommitRecord) -> wire::Commit {
1378 wire::Commit {
1379 commit_id: Some(commit.commit_id.clone()),
1380 tree_id: Some(commit.tree_id.clone()),
1381 parents: Some(commit.parent_ids.clone()),
1382 message: Some(commit.message.clone()),
1383 author: Some(wire::UserInfo {
1384 name: Some(commit.author_name.clone()),
1385 email: Some(commit.author_email.clone()),
1386 date: Some(commit.date.to_rfc3339()),
1387 }),
1388 committer: Some(wire::UserInfo {
1389 name: Some(commit.author_name.clone()),
1390 email: Some(commit.author_email.clone()),
1391 date: Some(commit.date.to_rfc3339()),
1392 }),
1393 additional_data: None,
1394 }
1395}
1396
1397fn pr_to_wire(pr: &crate::types::PullRequestRecord) -> wire::PullRequest {
1398 wire::PullRequest {
1399 pull_request_id: Some(pr.pull_request_id.clone()),
1400 title: Some(pr.title.clone()),
1401 description: Some(pr.description.clone()),
1402 pull_request_status: Some(pr.status.clone()),
1403 creation_date: Some(pr.creation_date.timestamp() as f64),
1404 last_activity_date: Some(pr.last_activity_date.timestamp() as f64),
1405 author_arn: Some(pr.author_arn.clone()),
1406 revision_id: None,
1407 client_request_token: None,
1408 approval_rules: None,
1409 pull_request_targets: Some(vec![wire::PullRequestTarget {
1410 repository_name: Some(pr.repository_name.clone()),
1411 source_reference: Some(pr.source_reference.clone()),
1412 destination_reference: Some(pr.destination_reference.clone()),
1413 source_commit: Some(pr.source_commit.clone()),
1414 destination_commit: Some(pr.destination_commit.clone()),
1415 merge_base: None,
1416 merge_metadata: Some(wire::MergeMetadata {
1417 is_merged: Some(pr.status == "CLOSED"),
1418 merge_commit_id: None,
1419 merge_option: None,
1420 merged_by: None,
1421 }),
1422 }]),
1423 }
1424}
1425
1426fn codecommit_error_response(err: &CodeCommitError) -> MockResponse {
1427 let (status, error_type) = match err {
1428 CodeCommitError::RepositoryAlreadyExists { .. } => {
1429 (400u16, "RepositoryNameExistsException")
1430 }
1431 CodeCommitError::RepositoryNameTaken { .. } => (400, "RepositoryNameExistsException"),
1432 CodeCommitError::RepositoryDoesNotExist { .. } => (400, "RepositoryDoesNotExistException"),
1433 CodeCommitError::RepositoryDoesNotExistByArn { .. } => {
1434 (400, "RepositoryDoesNotExistException")
1435 }
1436 CodeCommitError::BranchAlreadyExists { .. } => (400, "BranchNameExistsException"),
1437 CodeCommitError::BranchDoesNotExist { .. } => (400, "BranchDoesNotExistException"),
1438 CodeCommitError::BranchNotFound { .. } => (400, "BranchDoesNotExistException"),
1439 CodeCommitError::DefaultBranchCannotBeDeleted => {
1440 (400, "DefaultBranchCannotBeDeletedException")
1441 }
1442 CodeCommitError::CommitDoesNotExist { .. } => (400, "CommitDoesNotExistException"),
1443 CodeCommitError::SpecifierDoesNotResolve { .. } => (400, "CommitDoesNotExistException"),
1444 CodeCommitError::FileDoesNotExist { .. } => (400, "FileDoesNotExistException"),
1445 CodeCommitError::PullRequestDoesNotExist { .. } => {
1446 (400, "PullRequestDoesNotExistException")
1447 }
1448 CodeCommitError::RepositoryEmpty => (400, "RepositoryEmptyException"),
1449 };
1450 let body = json!({
1451 "__type": error_type,
1452 "message": err.to_string(),
1453 });
1454 MockResponse::json(status, body.to_string())
1455}