Skip to content

[REFACTOR] 깃허브 토큰이 레포지토리 접근 권한 없을 때의 에러코드 구분#78

Merged
coli-geonwoo merged 2 commits intodevelopfrom
refactor/#72
Aug 18, 2025
Merged

[REFACTOR] 깃허브 토큰이 레포지토리 접근 권한 없을 때의 에러코드 구분#78
coli-geonwoo merged 2 commits intodevelopfrom
refactor/#72

Conversation

@coli-geonwoo
Copy link
Collaborator

@coli-geonwoo coli-geonwoo commented Aug 18, 2025

🚩 연관 JIRA 이슈

close #72

🔂 변경 내역

Github에서는 잘못된 url이거나 토큰의 접근권한이 없는 레포지토리로부터 웹훅 심기, 정보 얻기 등의 api를 호출하면 일관적으로 404 NOT_FOUND를 반환합니다.

{
    "message": "Not Found",
    "documentation_url": "https://docs.github.com/rest/repos/webhooks#create-a-repository-webhook",
    "status": "404"
}

이에 따라 POST /api/repositories에서 권한 문제가 발생할 경우, OAuth APP 리다이렉트를 통해 접근권한을 허용하는 플로우를 가져가고자 다음과 같은 cusom errorcode를 통해 등록에 실패한 상황을 구분합니다.

{
  "code": "REGISTRY_GITHUB_REPOSITORY_NOT_FOUND",
  "status": "BAD_REQUEST",
  "message": "입력한 깃허브 레포지토리를 등록할 수 없습니다"
}

🗣️ 리뷰 요구사항 (선택)

Summary by CodeRabbit

  • 버그 수정

    • 깃허브 레포지토리 웹훅 등록 실패 시, “입력한 깃허브 레포지토리를 등록할 수 없습니다” 오류로 명확히 안내되도록 오류 매핑을 개선했습니다.
  • 테스트

    • 위 오류 상황에 대한 단위 테스트를 추가해 동작을 검증했습니다.
  • 작업 정리(Chores)

    • PR 템플릿에서 JIRA 전용 항목과 선택 리뷰 요구사항을 제거하고, 일반적인 연관 이슈 헤더로 단순화했습니다.

@coli-geonwoo coli-geonwoo linked an issue Aug 18, 2025 that may be closed by this pull request
@coli-geonwoo coli-geonwoo self-assigned this Aug 18, 2025
@coderabbitai
Copy link

coderabbitai bot commented Aug 18, 2025

Walkthrough

RepositoryFacadeService.save now catches GithubNotFoundException and rethrows GssException with ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND. A corresponding error code was added to ErrorCode enum. Tests were added to verify exception translation on webhook creation failure. The PR template was simplified by removing Jira-specific sections.

Changes

Cohort / File(s) Summary
Service error handling
gss-api-app/src/main/java/.../RepositoryFacadeService.java
Wrap save(...) with try-catch for GithubNotFoundException and rethrow GssException with REGISTRY_GITHUB_REPOSITORY_NOT_FOUND; imports updated.
Error code enum
gss-common/src/main/java/.../ErrorCode.java
Added REGISTRY_GITHUB_REPOSITORY_NOT_FOUND(400, "...레포지토리를 등록할 수 없습니다").
Tests for exception mapping
gss-api-app/src/test/java/.../RepositoryFacadeServiceTest.java
Added test to assert GssException when webhook creation throws GithubNotFoundException; introduced helper mockingErrorWhenCreateWebHook().
Repo meta (PR template)
.github/pull_request_template.md
Removed Jira-specific related-issue and optional review sections; added generic related-issue header.

Sequence Diagram(s)

sequenceDiagram
  actor Client
  participant Facade as RepositoryFacadeService
  participant GH as GitHubClient
  participant Repo as RepositoryService
  participant Pub as EventPublisher

  Client->>Facade: save(request)
  Facade->>GH: getRepositoryInfo()
  GH-->>Facade: RepoInfo
  Facade->>GH: createWebhook()
  alt Webhook created
    Facade->>Repo: createRepository()
    Repo-->>Facade: Repository
    Facade->>Pub: publishRegisteredEvent()
    Pub-->>Facade: ack
    Facade-->>Client: success
  else GithubNotFoundException
    Facade-->>Client: throw GssException(REGISTRY_GITHUB_REPOSITORY_NOT_FOUND)
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Assessment against linked issues

Objective Addressed Explanation
토큰 권한이 없을 때 에러코드 구분 (#72) 토큰 권한 부족(401/403)에 대한 별도 에러코드/매핑이 추가되지 않음. 404 매핑만 도입됨.

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
PR 템플릿에서 JIRA 관련 섹션 제거 및 일반화 (.github/pull_request_template.md) 코드 기능과 무관하며, 토큰 권한 에러코드 구분(#72) 목표와 직접적인 연관이 없음.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/#72

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

Test Results

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
0 files    0 ❌

Results for commit 43922f8.

@github-actions
Copy link

📝 Test Coverage Report

Overall Project NaN% NaN% 🍏

There is no coverage information present for the Files changed

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
.github/pull_request_template.md (1)

1-4: Jira 의존성 제거는 좋습니다. GitHub 이슈 자동 연결 문구를 안내로 추가 제안

조직 도구에 중립적인 템플릿으로 간결해졌습니다. 다만 기여자들이 PR 머지 시 이슈가 자동으로 닫히도록 “Closes #번호” 형태의 안내를 한 줄 넣어두면 실수 방지에 도움 됩니다.

적용 예시(해당 블록 중간에 한 줄 안내 추가):

 # 🚩 연관 이슈
+
+- GitHub 이슈 예: Closes #123

 # 🔂 변경 내역
gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java (1)

9-11: 이름 충돌(혼동) 가능성: NOT_FOUND 네이밍이 4xx/5xx에 혼재

REGISTRY_GITHUB_REPOSITORY_NOT_FOUND(400)와 GITHUB_REPOSITORY_NOT_FOUND(500)이 공존해 의미가 모호해질 수 있습니다. 현재 PR 의도는 “GitHub 404를 400으로 변환”인데, 식별자에 NOT_FOUND가 들어가면 404를 연상시키는 여지가 있습니다. 운영상 혼선을 줄이려면:

  • 주석/Javadoc로 용례를 명확히 기록, 또는
  • REGISTRY_GITHUB_REPOSITORY_REGISTRATION_FAILED처럼 결과(등록 실패) 중심 네이밍 고려

메시지는 그대로 두고 식별자만 구체화하는 것도 방법입니다.

Also applies to: 29-29

gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java (1)

34-45: 트랜잭션 경계 내 외부 I/O 호출 최소화 제안

현재 save 트랜잭션 내에서 웹훅 등록(외부 네트워크 I/O)과 이벤트 발행이 수행됩니다. 타임아웃/재시도 시 장기 트랜잭션이 될 수 있어 DB 락 유지 시간이 늘어날 우려가 있습니다. 선택지:

  • 웹훅 등록을 트랜잭션 밖에서 수행(실패 시 보상/정리 로직 포함)
  • outbox/event-relay 패턴 도입으로 이벤트 발행 비동기화
gss-api-app/src/test/java/com/devoops/service/facade/RepositoryFacadeServiceTest.java (3)

94-102: 불필요한 지역변수 제거

mockingErrorWhenCreateWebHook 내 WebHookCreateResponse 로컬 변수는 사용되지 않습니다. 제거해 주세요.

적용 diff:

         private void mockingErrorWhenCreateWebHook() {
             GithubRepoInfoResponse mockResponse = new GithubRepoInfoResponse(123, "testName", "testUrl",
                     new OwnerResponse("김건우"));
-            WebHookCreateResponse mockWebHookCreateResponse = new WebHookCreateResponse(123);
             Mockito.when(gitHubClient.getRepositoryInfo(anyString(), anyString(), anyString()))
                     .thenReturn(mockResponse);
             Mockito.when(gitHubClient.createWebhook(any(), any(), any(), any()))
                     .thenThrow(new GithubNotFoundException("mocking error"));
         }

60-69: 레포지토리 정보 조회 단계 실패 케이스 추가 테스트 제안

현재는 “웹훅 등록” 단계의 404만 커버합니다. GitHub 레포지토리 정보 조회(getRepositoryInfo) 단계에서의 404도 동일하게 변환되는지 보강 테스트를 권장합니다.

예시 테스트(새 메서드 추가):

@Test
void 레포지토리_정보_조회_404_시_애플리케이션_에러로_전환한다() {
    User user = userGenerator.generate("김건우");
    RepositorySaveRequest request = new RepositorySaveRequest("https://github.com/octocat/Hello-World");

    // getRepositoryInfo가 404를 유발하도록 설정
    Mockito.when(gitHubClient.getRepositoryInfo(anyString(), anyString(), anyString()))
           .thenThrow(new GithubNotFoundException("mocking error"));

    assertThatThrownBy(() -> repositoryFacadeService.save(request, user))
        .isInstanceOf(GssException.class)
        // 가능하면 에러코드 단정으로 교체 (앞선 코멘트 참고)
        .hasMessage(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND.getMessage());

    // 단락 평가 검증: 웹훅 생성은 시도되지 않아야 함
    Mockito.verify(gitHubClient, times(0)).createWebhook(any(), any(), any(), any());
}

원하시면 제가 바로 추가 PR 패치까지 준비하겠습니다.


60-69: 예외 메시지 대신 에러 코드로 단정 검증 권장

현재 테스트는 메시지 문자열(.hasMessage(...))에 의존해 있어, 국제화나 문구 변경 시 깨질 위험이 있습니다.
GssException에는 Lombok @Getter로 생성된 getErrorCode() 접근자가 있으므로, 메시지 대신 에러 코드를 검증하도록 리팩토링하는 것이 더 견고합니다.

예시 변경안:

assertThatThrownBy(() -> repositoryFacadeService.save(request, user))
    .isInstanceOf(GssException.class)
    .satisfies(ex -> assertThat(((GssException) ex).getErrorCode())
        .isEqualTo(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND));
  • 파일: gss-api-app/src/test/java/com/devoops/service/facade/RepositoryFacadeServiceTest.java
  • 위치: 테스트 메소드 내 assertThatThrownBy 호출부
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3bbd8d6 and 43922f8.

📒 Files selected for processing (4)
  • .github/pull_request_template.md (1 hunks)
  • gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java (2 hunks)
  • gss-api-app/src/test/java/com/devoops/service/facade/RepositoryFacadeServiceTest.java (2 hunks)
  • gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java (1)
gss-client/gss-github-client/src/main/java/com/devoops/exception/GithubNotFoundException.java (1)
  • GithubNotFoundException (3-7)
🔇 Additional comments (3)
gss-common/src/main/java/com/devoops/exception/errorcode/ErrorCode.java (2)

9-9: 새 에러코드 추가 방향성 적절

GitHub의 404 응답을 사용자 입력 오류(권한 미보유/존재하지 않는 URL)로 번역해 400 계열로 노출하려는 의도가 명확합니다. 메시지도 사용자 친화적입니다.


9-9: [예외 처리기 확인] ErrorCode.statusCode가 HTTP 응답 상태로 정상 매핑됨

  • GlobalExceptionHandler.java (라인 88)에서
    ResponseEntity.status(errorCode.getStatusCode())를 사용하여
    ErrorCode.statusCode(예: 400)가 그대로 HTTP 상태 코드로 설정됩니다.
  • 따라서 REGISTRY_GITHUB_REPOSITORY_NOT_FOUND 예외 발생 시 400 상태가 올바르게 반영됩니다.

추가 검증이나 수정은 필요하지 않습니다.

gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java (1)

36-44: GithubNotFoundException → 도메인 예외 변환 방향성 적절

웹훅 등록/레포 조회 단계에서의 GitHub 404를 애플리케이션 400 에러코드로 일관 변환하는 흐름이 PR 목적에 부합합니다. 트랜잭션 롤백도 유지됩니다.

Comment on lines +36 to +44
try {
GithubRepoUrl repoUrl = new GithubRepoUrl(request.url());
GithubRepository savedRepository = saveRepository(repoUrl, user);
webHookService.registerWebhook(user, savedRepository.getId());
eventPublisher.publishEvent(new AnalyzeMyPrEvent(repoUrl, user, this));
return savedRepository;
} catch (GithubNotFoundException githubNotFoundException) {
throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

원인 예외 보존 또는 로깅 고려

운영 트러블슈팅을 위해 원인 예외를 GssException에 첨부하거나 최소 로그로 남기는 것을 권장합니다. GssException이 cause를 받는 생성자/팩토리를 제공한다면 다음 형태가 바람직합니다: new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND, githubNotFoundException)

다음 스크립트로 GssException 생성자 시그니처를 확인해 주세요:

가능 시 적용 예:

-        } catch (GithubNotFoundException githubNotFoundException) {
-            throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND);
+        } catch (GithubNotFoundException githubNotFoundException) {
+            // cause를 보존하거나 적어도 로그로 남기는 것을 권장
+            throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND, githubNotFoundException);
         }

🏁 Script executed:

#!/bin/bash
# GssException 생성자/팩토리 확인
rg -nP -C3 '\bclass\s+GssException\b'
rg -nP -C2 'GssException\s*\('
rg -nP -C2 'of\s*\(\s*ErrorCode'

Length of output: 32063


GssException에 원인 예외를 전달할 수 있는 생성자 추가 및 적용 필요

현재 GssExceptionErrorCode만 받는 생성자만 제공되어 원인 예외(cause)를 보존할 수 없습니다. 로깅 또는 추후 디버깅을 위해 아래와 같이 수정이 필요합니다.

수정 사항:

  • gss-common/src/main/java/com/devoops/exception/custom/GssException.javaThrowable 원인 예외를 받는 생성자 추가
  • gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java 등 예외를 던지는 모든 곳에 해당 생성자를 사용하여 cause를 전달

예시 코드:

// gss-common/src/main/java/com/devoops/exception/custom/GssException.java
public class GssException extends RuntimeException {
    private final ErrorCode errorCode;

    public GssException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }

    // 원인 예외 보존용 생성자 추가
    public GssException(ErrorCode errorCode, Throwable cause) {
        super(errorCode.getMessage(), cause);
        this.errorCode = errorCode;
    }

    // getter...
}
// gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java
-        } catch (GithubNotFoundException githubNotFoundException) {
-            throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND);
+        } catch (GithubNotFoundException githubNotFoundException) {
+            // 원인 예외를 보존하여 추후 로깅/디버깅에 활용
+            throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND, githubNotFoundException);
         }
  • 대상 파일 및 위치
    • gss-common/src/main/java/com/devoops/exception/custom/GssException.java
    • gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java: 라인 36–44 외 예외 처리부 전반

위 리팩터링을 통해 원인 예외가 손실되지 않고, 운영 중 발생한 오류를 보다 정확히 파악할 수 있습니다.

🤖 Prompt for AI Agents
In
gss-api-app/src/main/java/com/devoops/service/facade/RepositoryFacadeService.java
around lines 36–44 and in
gss-common/src/main/java/com/devoops/exception/custom/GssException.java, add a
GssException constructor that accepts (ErrorCode, Throwable) and use it when
rethrowing from the catch block so the original GithubNotFoundException is
preserved as the cause; update GssException to call
super(errorCode.getMessage(), cause) and set the errorCode field, then replace
throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND) with
throw new GssException(ErrorCode.REGISTRY_GITHUB_REPOSITORY_NOT_FOUND,
githubNotFoundException) (and apply same pattern to other locations that rethrow
GssException).

@coli-geonwoo coli-geonwoo merged commit 7989552 into develop Aug 18, 2025
5 checks passed
@coli-geonwoo coli-geonwoo deleted the refactor/#72 branch August 18, 2025 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[REFACTOR] 토큰 권한이 없을 때 에러코드 구분

1 participant