통합 개발 지침

이 페이지는 GitLab 통합 구현을 위한 개발 지침을 제공합니다. 이는 우리의 주요 Rails 프로젝트의 일부입니다.

통합에 대한 전략 개요는 방향 페이지를 참조하세요.

이 가이드는 진행 중인 작업입니다. 설명이 필요하거나 오래된 정보를 발견하신 경우 @gitlab-org/manage/import-and-integrate에 문의해 주세요.

새로운 통합 추가

통합 정의

  1. Integration에서 확장된 새로운 모델을 app/models/integrations에 추가합니다.
    • 예: app/models/integrations/foo_bar.rbIntegrations::FooBar.
    • 특정 유형의 통합의 경우, 다음 기본 클래스를 기반으로 구축할 수도 있습니다:
      • Integrations::BaseChatNotification
      • Integrations::BaseCi
      • Integrations::BaseIssueTracker
      • Integrations::BaseMonitoring
      • Integrations::BaseSlashCommands
      • Integrations::BaseThirdPartyWiki
    • 외부 서비스에 HTTP 호출을 주로 트리거하는 통합의 경우,
      Integrations::HasWebHook 컨cern을 사용할 수도 있습니다. 이는 연관된 ServiceHook 모델을 통해 GitLab의 웹후크 기능을 재사용하며,
      통합 설정에서 볼 수 있는 요청 로그를 자동으로 기록합니다.
  2. 통합의 언더스코어 이름('foo_bar')을 Integration::INTEGRATION_NAMES에 추가합니다.
  3. Project에서 통합을 연관으로 추가합니다:

    has_one :foo_bar_integration, class_name: 'Integrations::FooBar'
    

필드 정의

통합은 Integration.field 클래스 메서드를 사용하여 구성 저장을 위한 임의의 필드를 정의할 수 있습니다.
값은 integrations.encrypted_properties 열에 암호화된 JSON 해시로 저장됩니다.

예를 들어:

module Integrations
  class FooBar < Integration
    field :url
    field :tags
  end
end

Integration.field는 클래스에 접근자 메서드를 설치합니다.
여기에서 우리는 #url, #url=, #url_changed?를 사용하여 url 필드를 관리할 수 있게 됩니다.
이 접근자들은 ActiveRecord 속성과 마찬가지로 모델의 Integration#properties에서 직접 필드에 접근해야 합니다.

항상 접근자를 통해 필드에 접근해야 하며 properties 해시와 직접 상호작용하지 않아야 합니다.
properties 해시에 직접 쓰기를 해선 안 되며, 생성된 setter 메서드를 대신 사용해야 합니다.
이 해시에 대한 직접 쓰기는 영속되지 않습니다.

이 필드가 통합의 프론트엔드 양식에서 어떻게 노출되는지 보려면,
프론트엔드 양식 사용자화를 참조하세요.

다른 접근법으로는 이전 버전의 통합에서 볼 수 있는 Integration.prop_accessor 또는 Integration.data_field가 있습니다.
새로운 통합에 대해서는 이 접근법을 사용하지 않아야 합니다.

유효성 검사 정의

모든 필드에 대해 Rails 유효성 검사를 정의해야 합니다.

유효성 검사는 통합이 활성화된 경우에만 적용되어야 하며, #activated? 메서드를 테스트하여 확인합니다.

required: 속성이 있는 필드는 presence에 대한 유효성 검증이 있어야 합니다.
required: 필드 속성은 프론트엔드 전용이기 때문입니다.

예를 들어:

module Integrations
  class FooBar < Integration
    with_options if: :activated? do
      validates :key, presence: true, format: { with: KEY_REGEX }
      validates :bar, inclusion: [true, false]
    end

    field :key, required: true
    field :bar, type: :checkbox
  end
end

트리거 이벤트 정의

통합은 GitLab 이벤트에 대한 응답으로 #execute 메서드를 호출하여 트리거되며, 이 메서드는 이벤트에 대한 세부 정보를 포함하는 페이로드 해시를 전달받습니다.

지원되는 이벤트는 웹훅 이벤트와 일부 겹치며, 동일한 페이로드를 수신합니다. 모델에서 클래스 메서드 Integration.supported_events를 재정의하여 관심 있는 이벤트를 지정할 수 있습니다.

다음 이벤트가 통합을 위해 지원됩니다:

이벤트 유형 기본 트리거
경고 이벤트   alert 새로운 고유 경고가 기록됩니다.
커밋 이벤트 commit 커밋이 생성되거나 업데이트됩니다.
배포 이벤트   deployment 배포가 시작되거나 완료됩니다.
이슈 이벤트 issue 이슈가 생성, 업데이트 또는 종료됩니다.
비밀 이슈 이벤트 confidential_issue 비밀 이슈가 생성, 업데이트 또는 종료됩니다.
작업 이벤트   job  
병합 요청 이벤트 merge_request 병합 요청이 생성, 업데이트 또는 병합됩니다.
댓글 이벤트   comment 새로운 댓글이 추가됩니다.
비밀 댓글 이벤트   confidential_note 비밀 이슈에 새로운 댓글이 추가됩니다.
파이프라인 이벤트   pipeline 파이프라인 상태가 변경됩니다.
푸시 이벤트 push 리포지토리에 푸시가 발생합니다.
태그 푸시 이벤트 tag_push 새로운 태그가 리포지토리에 푸시됩니다.
취약점 이벤트   vulnerability 새로운 고유의 취약점이 기록됩니다. Ultimate 전용.
위키 페이지 이벤트 wiki_page 위키 페이지가 생성되거나 업데이트됩니다.

이벤트 예제

이 예제는 commitmerge_request 이벤트에 응답하는 통합을 정의합니다:

module Integrations
  class FooBar < Integration
    def self.supported_events
      %w[commit merge_request]
    end
  end
end

통합은 또한 이벤트에 응답하지 않도록 설정하고, 다른 방식으로 사용자 정의 기능을 구현할 수 있습니다:

module Integrations
  class FooBar < Integration
    def self.supported_events
      []
    end
  end
end

보안 강화 기능

채널 값 마스킹

Integrations::BaseChatNotification에서 상속된 통합은 채널 입력 필드의 값을 숨길 수 있습니다. 통합은 필드에 인증 토큰과 같은 민감한 정보가 포함될 때마다 이 값을 숨겨야 합니다.

기본적으로 #mask_configurable_channels?false를 반환합니다. 채널 값을 마스킹하려면 통합에서 #mask_configurable_channels? 메서드를 재정의하여 true를 반환해야 합니다:

override :mask_configurable_channels?
def mask_configurable_channels?
  true
end

구성 테스트 정의

선택적으로, 통합 설정의 구성 테스트를 정의할 수 있습니다. 테스트는 통합 양식의 테스트 버튼에서 실행되며, 결과는 사용자에게 반환됩니다.

좋은 구성 테스트는 다음과 같은 특징이 있습니다:

  • 서비스의 데이터를 변경하지 않습니다. 예를 들어, CI 빌드를 트리거해서는 안 됩니다. 메시지를 보내는 것은 괜찮습니다.
  • 의미가 있으며 최대한 철저합니다.

위 지침을 따르는 것이 불가능한 경우, 구성 테스트를 추가하지 않는 것을 고려하세요.

구성 테스트를 추가하려면 통합 모델에 대해 #test 메서드를 정의합니다.

이 메서드는 data를 수신하며, 이는 테스트 푸시 이벤트 페이로드입니다.

해시는 다음과 같은 키를 포함해야 합니다:

  • success (필수): 구성 테스트가 통과했는지를 나타내는 부울 값입니다.
  • result (선택적): 구성 테스트가 실패했을 때 사용자에게 반환되는 메시지입니다.

예를 들면 다음과 같습니다:

module Integrations
  class FooBar < Integration
    def test(data)
      success = test_api_key(data)

      { success: success, result: 'API 키가 유효하지 않습니다' }
    end
  end
end

프런트엔드 양식 사용자 정의

프런트엔드 양식은 모델에 정의된 메타데이터에 따라 동적으로 생성됩니다.

기본적으로 통합 양식은 다음을 제공합니다:

  • 통합을 활성화하거나 비활성화하는 체크박스.
  • Integration#configurable_events에서 반환된 각 트리거 이벤트에 대한 체크박스.

양식 상단에 도움말 텍스트를 추가하려면 Integration#help를 재정의하거나 app/views/shared/integrations/$INTEGRATION_NAME/_help.html.haml에 템플릿을 제공하면 됩니다.

양식에 사용자 정의 속성을 추가하려면 Integration#fields에서 메타데이터를 정의할 수 있습니다.

이 메서드는 각 필드에 대해 해시 배열을 반환해야 하며, 여기서 키는 다음과 같습니다:

유형 필수 기본값 설명
type: 심볼 true :text 양식 필드의 유형. :text, :textarea, :password, :checkbox, 또는 :select가 될 수 있습니다.
section: 심볼 false   필드가 속하는 섹션을 지정합니다.
name: 문자열 true   양식 필드의 속성 이름.
required: 부울 false false 양식 필드가 필수인지 선택적인지 지정합니다. 존재에 대한 백엔드 검증이 여전히 필요합니다.
title: 문자열 false name:의 대문자 값 양식 필드의 레이블.
placeholder: 문자열 false   양식 필드에 대한 플레이스홀더.
help: 문자열 false   양식 필드 아래에 표시되는 도움말 텍스트.
api_only: 부울 false false 필드가 API를 통해서만 사용 가능하고 프런트엔드 양식에서 제외되어야 하는지 지정합니다.
description 문자열 false   API 필드에 대한 설명.
if: 부울 또는 람다 false true 필드가 사용할 수 있는지를 지정합니다. 값은 부울 또는 람다가 될 수 있습니다.

type: :checkbox에 대한 추가 키

유형 필수 기본값 설명
checkbox_label: 문자열 아니요 title:의 값 체크박스 옆에 표시되는 사용자 정의 레이블입니다.

type: :select에 대한 추가 키

유형 필수 기본값 설명
choices: 배열   [레이블, 값] 튜플의 중첩 배열입니다.

type: :password에 대한 추가 키

유형 필수 기본값 설명
non_empty_password_title: 문자열 아니요 title:의 값 값이 이미 저장된 경우 표시되는 대체 레이블입니다.
non_empty_password_help: 문자열 아니요 help:의 값 값이 이미 저장된 경우 표시되는 대체 도움말 텍스트입니다.

섹션 정의

모든 통합은 폼을 더 작은 섹션으로 나누는 Integration#sections를 정의해야 하며,

사용자가 통합을 쉽게 설정할 수 있도록 합니다.

가장 일반적으로 사용되는 섹션은 미리 정의되어 있으며 일부 UI를 이미 포함하고 있습니다:

  • SECTION_TYPE_CONNECTION: url, username, password와 같은 기본 필드를 포함하며, 이는 통합에 연결하고 인증하는 데 필요합니다.
  • SECTION_TYPE_CONFIGURATION: 통합이 작동하는 방식에 관한 더 고급 구성 및 선택적 설정을 포함합니다.
  • SECTION_TYPE_TRIGGER: 통합을 트리거할 이벤트 목록을 포함합니다.

SECTION_TYPE_CONNECTIONSECTION_TYPE_CONFIGURATION는 내부적으로 dynamic-field 구성 요소를 렌더링합니다. dynamic-field 구성 요소는 통합 type에 따라 checkbox, input, select 또는 textarea를 렌더링합니다.

예를 들면:

module Integrations
  class FooBar < Integration
    def sections
      [
        {
          type: SECTION_TYPE_CONNECTION,
          title: s_('Integrations|Connection details'),
          description: help
        },
        {
          type: SECTION_TYPE_CONFIGURATION,
          title: _('Configuration'),
          description: s_('Advanced configuration for integration')
        }
      ]
    end
  end
end

특정 섹션에 필드를 추가하려면 필드 메타데이터에 section: 키를 추가할 수 있습니다.

새로운 사용자 정의 섹션

기존 섹션이 UI 사용자 정의 요구 사항을 충족하지 않으면 새로운 사용자 정의 섹션을 만들 수 있습니다:

  1. 새 섹션을 추가하려면 새로운 상수 SECTION_TYPE_*를 추가하고 #sections 메서드에 추가합니다:

    module Integrations
      class FooBar < Integration
        SECTION_TYPE_SUPER = :my_custom_section
    
        def sections
          [
            {
              type: SECTION_TYPE_SUPER,
              title: s_('Integrations|Custom section'),
              description: s_('Integrations|Help')
            }
          ]
        end
      end
    end
    
  2. ~/integrations/constants.js에서 프런트엔드 상수 integrationFormSectionsintegrationFormSectionComponents를 업데이트합니다.

  3. app/assets/javascripts/integrations/edit/components/sections/*에 새 섹션 구성 요소를 추가합니다.

  4. app/assets/javascripts/integrations/edit/components/integration_forms/section.vue에 새 섹션을 포함하고 렌더링합니다.

프론트엔드 폼 예제

이 예제는 Connection details 섹션 아래에 필수 url 필드와 선택적 usernamepassword 필드를 정의합니다:

module Integrations
  class FooBar < Integration
    field :url,
      section: SECTION_TYPE_CONNECTION,
      type: :text,
      title: s_('FooBarIntegration|Server URL'),
      placeholder: 'https://example.com/',
      required: true

    field :username,
      section: SECTION_TYPE_CONNECTION,
      type: :text,
      title: s_('FooBarIntegration|Username')

    field :password,
      section: SECTION_TYPE_CONNECTION,
      type: 'password',
      title: s_('FoobarIntegration|Password'),
      non_empty_password_title: s_('FooBarIntegration|Enter new password')

    def sections
      [
        {
          type: SECTION_TYPE_CONNECTION,
          title: s_('Integrations|Connection details'),
          description: s_('Integrations|Help')
        }
      ]
    end
  end
end

REST API에서 통합 노출

REST API에서 통합을 노출하려면:

  1. 통합의 클래스 (::Integrations::FooBar)를 API::Helpers::IntegrationsHelpers.integration_classes에 추가합니다.

  2. 통합의 API 인수를 API::Helpers::IntegrationsHelpers.integrations에 추가합니다. 예를 들어:

    'foo-bar' => ::Integrations::FooBar.api_arguments
    
  3. doc/api/integrations.md에서 참조 문서를 업데이트하고, 통합에 대한 새로운 섹션을 추가하고 모든 속성을 문서화합니다.

또한 우리의 REST API 스타일 가이드를 참조할 수 있습니다.

민감한 필드는 API를 통해 노출되지 않습니다. 민감한 필드는 이름에 다음 중 하나를 포함하는 필드입니다:

  • key
  • passphrase
  • password
  • secret
  • token
  • webhook

통합의 가용성

기본적으로 통합은 특정 프로젝트나 그룹 또는 전체 인스턴스에 적용할 수 있습니다.

대부분의 통합은 프로젝트 컨텍스트에서만 작동하지만, 여전히 그룹과 인스턴스에 대해 구성할 수 있습니다.

일부 통합의 경우 특정 수준(프로젝트, 그룹 또는 인스턴스)에서만 사용할 수 있도록 하는 것이 의미가 있을 수 있습니다.

이를 위해서는 통합을 Integration::INTEGRATION_NAMES에서 제거하고 대신 다음에 추가해야 합니다:

  • Integration::PROJECT_LEVEL_ONLY_INTEGRATION_NAMES는 프로젝트 수준에서만 활성화할 수 있도록 허용합니다.
  • Integration::INSTANCE_LEVEL_ONLY_INTEGRATION_NAMES는 인스턴스 수준에서만 활성화할 수 있도록 허용합니다.
  • Integration::PROJECT_AND_GROUP_LEVEL_ONLY_INTEGRATION_NAMES는 인스턴스 수준에서 활성화를 방지합니다.

새 통합을 개발할 때, Integration.available_integration_names에서 가용성을 기능 플래그로 제어하는 것도 권장합니다.

문서화

통합에 대한 문서를 추가합니다:

또한 우리의 일반 문서화 지침을 참조할 수 있습니다.

통합 폼에 도움말 텍스트를 제공할 수 있으며, 외부 문서에 대한 링크를 포함할 수 있습니다.

위에서 설명한 대로 프론트엔드 폼 사용자 정의를 참조하세요. 도움말 텍스트에 대한 사용성 지침을 참조하세요.

테스트

테스트는 구성 테스트 정의와 혼동해서는 안 됩니다.

spec/models/integrations에 통합 모델에 대한 테스트를 추가하는 것이 종종 충분하며,

spec/factories/integrations.rb에 예제 설정이 있는 팩토리를 추가합니다.

각 통합은 일반화된 테스트의 일환으로도 테스트됩니다. 예를 들어,

모든 통합에 대해 설정 양식이 올바르게 렌더링되고 있는지 확인하는 기능 사양이 있습니다.

통합이 프론트엔드에서 사용자 정의 동작을 구현하는 경우,

추가 테스트로 그 부분을 커버해야 합니다.

일반적인 테스트 가이드라인도 참조할 수 있습니다.

국제화

모든 UI 문자열은 국제화 가이드라인에 따라

번역을 위해 준비되어야 합니다.

문자열은 통합 이름을 네임스페이스로 사용해야 하며, 예를 들어 s_('FooBarIntegration|My string')와 같은 형식입니다.

통합을 사용 중단하고 제거하기

통합을 제거하려면 먼저 통합을 사용 중단해야 합니다. 자세한 내용은

기능 사용 중단 가이드라인을 참조하세요.

통합 사용 중단하기

통합 사용 중단은 예정된 제거 이전 세 번째 마일스톤까지 발표해야 합니다.

통합을 사용 중단하려면:

통합 제거하기

안전하게 통합을 제거하려면 두 개의 마일스톤에 걸쳐 제거를 준비해야 합니다.

예정된 제거의 주요 마일스톤(M.0)에서는 통합을 비활성화하고 데이터베이스에서 레코드를 삭제합니다:

다음 마이너 릴리스(M.1)에서는:

  • 통합의 모델과 남아 있는 코드를 제거합니다.
  • 통합의 레이블(~Integration::<name>)이 있는 모든 이슈, 병합 요청, 에픽을 닫습니다.
  • gitlab-org에서 통합의 레이블(~Integration::<name>)을 삭제합니다.

진행 중인 마이그레이션 및 리팩토링

개발자는 통합 팀이 통합 속성을 정의하는 방식을 통합 중 진행 중임을 알아야 합니다.

통합 예시

새로운 통합을 추가하는 예시를 보려면 다음 이슈를 참조하세요:

  • Datadog: Prometheus 통합과 유사한 메트릭 수집기.
  • EWM/RTC: 외부 이슈 트래커.
  • Webex Teams: 채팅 알림.
  • ZenTao: Jira 통합과 유사한 사용자 정의 이슈 뷰가 있는 외부 이슈 트래커.