통합 개발 지침

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

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

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

새로운 통합 추가

통합 정의

  1. Integration에서 확장된 새로운 모델을 app/models/integrations에 추가합니다.
    • 예: app/models/integrations/foo_bar.rb에 Integrations::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 위키 페이지가 생성되거나 업데이트됩니다.

이벤트 예제

이 예제는 commit 및 merge_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_CONNECTION 및 SECTION_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에서 프런트엔드 상수 integrationFormSections 및 integrationFormSectionComponents를 업데이트합니다.

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

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

프론트엔드 폼 예제

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

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 통합과 유사한 사용자 정의 이슈 뷰가 있는 외부 이슈 트래커.