InfoGrab DocsInfoGrab Docs

인테그레이션 개발 가이드라인

요약

이 페이지는 GitLab 인테그레이션을 구현하기 위한 개발 가이드라인을 제공합니다. 또한 인테그레이션에 대한 전략 개요를 확인하려면 방향성 페이지를 참조하세요. 이 가이드는 현재 작성 중입니다. 예를 들어, app/models/integrations/foo_bar.rb에 Integrations::FooBar를 추가합니다.

이 페이지는 GitLab 인테그레이션을 구현하기 위한 개발 가이드라인을 제공합니다. 인테그레이션은 메인 Rails 프로젝트의 일부입니다.

또한 인테그레이션에 대한 전략 개요를 확인하려면 방향성 페이지를 참조하세요.

이 가이드는 현재 작성 중입니다. 설명이 필요하거나 오래된 정보를 발견한 경우 @gitlab-org/foundations/import-and-integrate에 ping하세요.

새 인테그레이션 추가#

인테그레이션 정의#

  • Integration을 상속받는 새 모델을 app/models/integrations에 추가합니다.

예를 들어, app/models/integrations/foo_bar.rbIntegrations::FooBar를 추가합니다.

  • 특정 유형의 인테그레이션에는 다음 베이스 모듈을 포함할 수 있습니다:

Integrations::Base::ChatNotification

  • Integrations::Base::Ci

  • Integrations::Base::IssueTracker

  • Integrations::Base::Monitoring

  • Integrations::Base::SlashCommands

  • Integrations::Base::ThirdPartyWiki

  • 주로 외부 서비스에 대한 HTTP 호출을 트리거하는 인테그레이션의 경우, Integrations::HasWebHook concern을 사용할 수도 있습니다. 이는 연관된 ServiceHook 모델을 통해 GitLab의 웹훅 기능을 재사용하며, 인테그레이션 설정에서 조회할 수 있는 요청 로그를 자동으로 기록합니다.

  • 인테그레이션의 언더스코어 이름('foo_bar')을 Integration::INTEGRATION_NAMES에 추가합니다.

  • 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=, #url_changed?가 생성됩니다. 이러한 접근자는 다른 ActiveRecord 속성과 마찬가지로 모델에서 직접 Integration#properties에 저장된 필드에 접근해야 합니다.

항상 getters를 통해 필드에 접근하고 properties 해시와 직접 상호작용하지 않아야 합니다. properties 해시에 직접 쓰면 안 되며, 대신 생성된 setter 메서드를 반드시 사용해야 합니다. 이 해시에 직접 쓰면 데이터가 유지되지 않습니다.

프론트엔드 폼에서 이러한 필드가 어떻게 노출되는지 확인하려면 프론트엔드 폼 커스터마이즈를 참조하세요.

이전 버전의 인테그레이션에서는 Integration.prop_accessor 또는 Integration.data_field를 사용하는 방식도 있습니다. 새로운 인테그레이션에는 이러한 방식을 사용하지 않아야 합니다.

유효성 검사 정의#

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

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

required: 속성이 있는 필드는 required: 필드 속성이 프론트엔드용이므로 presence에 대한 해당 유효성 검사가 있어야 합니다.

예시:

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 이벤트 alert 새로운 고유한 alert가 기록됩니다.
Commit 이벤트 commit commit이 생성되거나 업데이트됩니다.
Deployment 이벤트 deployment 배포가 시작되거나 완료됩니다.
Work item 이벤트 issue work item이 생성, 업데이트 또는 종료됩니다.
기밀 이슈 이벤트 confidential_issue 기밀 work item이 생성, 업데이트 또는 종료됩니다.
Job 이벤트 job
머지 리퀘스트 이벤트 merge_request 머지 리퀘스트가 생성, 업데이트 또는 병합됩니다.
Comment 이벤트 comment 새 댓글이 추가됩니다.
기밀 comment 이벤트 confidential_note 기밀 work item에 새 댓글이 추가됩니다.
파이프라인 이벤트 pipeline 파이프라인 상태가 변경됩니다.
Push 이벤트 push 리포지터리에 푸시됩니다.
태그 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

이벤트 속성 기본값 정의#

인테그레이션에는 이슈 #382999에서 추적 중인 문제가 있습니다. 대부분의 이벤트 속성의 기본값이 true이기 때문에 필요 이상으로 자주 인테그레이션이 로드됩니다. 해당 이슈를 해결할 때까지 인테그레이션은 다음과 같은 방식으로 모든 이벤트 attribute 속성을 정의해야 합니다:

  • 알림 인테그레이션(Integrations::Base::ChatNotification을 포함하는 것)의 경우, 모든 이벤트 속성을 false로 설정합니다. 이렇게 하면 기본적으로 체크 해제된 이벤트 트리거별 체크박스가 있는 폼이 표시됩니다.

  • 다른 인테그레이션의 경우:

인테그레이션의 트리거 이벤트와 일치하는 이벤트 속성을 true로 설정합니다.

  • 다른 모든 이벤트 attributesfalse로 설정합니다.

예를 들어, commit과 머지 리퀘스트 트리거 이벤트에만 응답하는 인테그레이션은 이벤트 속성을 다음과 같이 설정해야 합니다:

attribute :commit_events, default: true
attribute :merge_requests_events, default: true

attribute :alert_events, default: false
attribute :incident_events, default: false
attribute :confidential_issues_events, default: false
attribute :confidential_note_events, default: false
attribute :issues_events, default: false
attribute :job_events, default: false
attribute :note_events, default: false
attribute :pipeline_events, default: false
attribute :push_events, default: false
attribute :tag_push_events, default: false
attribute :wiki_page_events, default: false

이벤트 속성 기본값 변경#

기존 인테그레이션의 이벤트 속성이 true로 변경되는 경우, 이전 레코드에 대한 속성 값을 채우기 위해 데이터 마이그레이션이 필요합니다.

메트릭 정의#

모든 새 인테그레이션에는 다섯 가지 메트릭이 있어야 합니다:

  • 해당 인테그레이션이 활성화된 프로젝트 수

  • 해당 인테그레이션을 상속하는 활성 프로젝트 수

  • 해당 인테그레이션이 활성화된 그룹 수

  • 해당 인테그레이션을 상속하는 활성 그룹 수

  • 해당 인테그레이션에 대한 활성 인스턴스 수준 인테그레이션 수

메트릭은 인테그레이션의 모델 클래스가 있어야 작동합니다. 메트릭은 모델과 함께 또는 모델 이후에만 추가할 수 있습니다.

메트릭 정의를 생성하려면:

  • 기존 활성 인테그레이션에 대해 생성된 메트릭을 복사합니다.

  • 이전 인테그레이션 이름이 있는 모든 항목을 새 인테그레이션 이름으로 교체합니다.

  • milestone을 현재 마일스톤으로, introduced_by_url을 머지 리퀘스트 링크로 교체합니다.

  • 메트릭 가이드를 확인하여 다른 모든 속성이 올바른 값을 가지는지 검증합니다.

예를 들어 Slack 인테그레이션에 대한 메트릭 정의를 생성하려면 다음 메트릭을 복사한 후 Slack을 새 인테그레이션 이름으로 교체합니다:

보안 요구사항#

모든 HTTP 호출은 Integrations::Clients::HTTP를 사용해야 합니다#

인테그레이션은 항상 Integrations::Clients::HTTP를 사용하여 HTTP 호출을 해야 합니다. 이 모듈은:

  • HTTP 호출에 대해 네트워크 설정이 적용되도록 보장합니다.

  • 추가적인 보안 강화 기능을 갖추고 있습니다.

  • 안전한 HTTP 호출을 위한 단일 진실 공급원(Single Source Of Truth, SSOT)입니다.

  • 모든 응답 크기의 유효성을 검증합니다.

채널 값 마스킹#

Integrations::Base::ChatNotification에서 포함하는 인테그레이션은 채널 입력 필드의 값을 숨길 수 있습니다. 필드에 인증 토큰과 같은 민감한 정보가 포함된 경우 인테그레이션은 이러한 값을 숨겨야 합니다.

기본적으로 #mask_configurable_channels?false를 반환합니다. 채널 값을 마스킹하려면 인테그레이션에서 #mask_configurable_channels? 메서드를 재정의하여 true를 반환하도록 합니다:

override :mask_configurable_channels?
def mask_configurable_channels?
  true
end

HTTP 호출을 하는 Ruby gem 금지#

GitLab 인테그레이션은 HTTP 호출을 하는 Ruby gem을 추가해서는 안 됩니다. 작은 추상화를 추가하는 다른 gem도 추가하지 않아야 합니다.

atlassian-jwt gem과 같이 공식 소스에서 제공하는 특정 유틸리티 gem은 필요한 경우 사용할 수 있습니다.

서드파티 서비스와의 상호작용을 래핑하는 gem은 처음에는 편리해 보일 수 있지만, 관련 비용에 비해 이점이 최소화됩니다:

  • 보안 문제의 잠재적 범위와 수정에 필요한 노력이 증가합니다.

  • 이러한 gem은 대개 사용자 대신 HTTP 호출을 합니다. 인테그레이션은 사용자가 구성한 원격 서버에 HTTP 호출을 할 수 있기 때문에, 네트워크 호출을 완전히 제어하는 것이 매우 중요합니다.

  • gem 업그레이드 관리에 유지보수 비용이 발생합니다.

  • 새로운 기능 사용을 막을 수 있습니다.

설정 테스트 정의#

선택적으로 인테그레이션 설정에 대한 설정 테스트를 정의할 수 있습니다. 테스트는 인테그레이션 폼의 테스트 버튼에서 실행되며 결과가 사용자에게 반환됩니다.

좋은 설정 테스트는:

  • 서비스의 데이터를 변경하지 않습니다. 예를 들어, CI 빌드를 트리거해서는 안 됩니다. 메시지 전송은 괜찮습니다.

  • 의미 있고 가능한 한 철저합니다.

위 가이드라인을 따르기 어려운 경우 설정 테스트를 추가하지 않는 것을 고려하세요.

설정 테스트를 추가하려면 인테그레이션 모델에 #test 메서드를 정의합니다.

메서드는 테스트 push 이벤트 페이로드인 data를 수신합니다. 다음 키를 포함하는 해시를 반환해야 합니다:

  • success (필수): 설정 테스트 통과 여부를 나타내는 boolean.

  • result (선택): 설정 테스트가 실패했을 때 사용자에게 반환되는 메시지.

예시:

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

      { success: success, result: 'API key is invalid' }
    end
  end
end

프론트엔드 폼 커스터마이즈#

프론트엔드 폼은 모델에 정의된 메타데이터를 기반으로 동적으로 생성됩니다.

기본적으로 인테그레이션 폼은 다음을 제공합니다:

  • 인테그레이션을 활성화하거나 비활성화하는 체크박스.

  • Integration#configurable_events에서 반환된 각 트리거 이벤트에 대한 체크박스.

Integration#help를 재정의하거나 app/views/shared/integrations/$INTEGRATION_NAME/_help.html.haml에 템플릿을 제공하여 폼 상단에 도움말 텍스트를 추가할 수도 있습니다.

커스텀 속성을 폼에 추가하려면 Integration#fields에서 메타데이터를 정의할 수 있습니다.

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

타입 필수 기본값 설명
type: symbol true :text 폼 필드의 유형. :text, :number, :textarea, :password, :checkbox, :string_array, :select 중 하나.
section: symbol false 필드가 속하는 섹션을 지정합니다.
name: string true 폼 필드의 속성 이름.
required: boolean false false 폼 필드가 필수인지 선택인지 지정합니다. 백엔드의 presence 유효성 검사도 여전히 필요합니다.
title: string false name:의 대문자 값 폼 필드의 라벨.
placeholder: string false 폼 필드의 placeholder.
help: string false 폼 필드 아래에 표시되는 도움말 텍스트.
api_only: boolean false false 필드가 API를 통해서만 사용 가능하고 프론트엔드 폼에서 제외되는지 지정합니다.
description string false API 필드 설명.
if: boolean 또는 lambda false true 필드 사용 가능 여부를 지정합니다. 값은 boolean 또는 lambda일 수 있습니다.

type: :checkbox에 대한 추가 키#

타입 필수 기본값 설명
checkbox_label: string false title:의 값 체크박스 옆에 표시되는 커스텀 라벨.

type: :select에 대한 추가 키#

타입 필수 기본값 설명
choices: array true [라벨, 값] 튜플의 중첩 배열.

type: :password에 대한 추가 키#

타입 필수 기본값 설명
non_empty_password_title: string false title:의 값 값이 이미 저장되어 있을 때 표시되는 대체 라벨.
non_empty_password_help: string false 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 컴포넌트는 인테그레이션에 대해 checkbox, number, 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 커스터마이즈 요구사항을 충족하지 않는 경우 새 커스텀 섹션을 만들 수 있습니다:

  • 새 상수 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
  • ~/integrations/constants.js의 프론트엔드 상수 integrationFormSectionsintegrationFormSectionComponents를 업데이트합니다.

  • app/assets/javascripts/integrations/edit/components/sections/*에 새 섹션 컴포넌트를 추가합니다.

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

프론트엔드 폼 예시#

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

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에서 인테그레이션을 노출하려면:

  • 인테그레이션 클래스(::Integrations::FooBar)를 API::Helpers::IntegrationsHelpers.integration_classes에 추가합니다.

  • API::Helpers::IntegrationsHelpers.integrations에 인테그레이션의 API 인수를 추가합니다. 예시:

'foo-bar' => ::Integrations::FooBar.api_arguments
  • doc/api/project_integrations.mddoc/api/group_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: 메트릭 수집기.

  • EWM/RTC: 외부 이슈 트래커.

  • Webex Teams: 채팅 알림.

  • ZenTao: Jira 이슈 인테그레이션과 유사한 커스텀 이슈 보기가 있는 외부 이슈 트래커.

인테그레이션 개발 가이드라인

GitLab v19.1
원문 보기
요약

이 페이지는 GitLab 인테그레이션을 구현하기 위한 개발 가이드라인을 제공합니다. 또한 인테그레이션에 대한 전략 개요를 확인하려면 방향성 페이지를 참조하세요. 이 가이드는 현재 작성 중입니다. 예를 들어, app/models/integrations/foo_bar.rb에 Integrations::FooBar를 추가합니다.

이 페이지는 GitLab 인테그레이션을 구현하기 위한 개발 가이드라인을 제공합니다. 인테그레이션은 메인 Rails 프로젝트의 일부입니다.

또한 인테그레이션에 대한 전략 개요를 확인하려면 방향성 페이지를 참조하세요.

이 가이드는 현재 작성 중입니다. 설명이 필요하거나 오래된 정보를 발견한 경우 @gitlab-org/foundations/import-and-integrate에 ping하세요.

새 인테그레이션 추가#

인테그레이션 정의#

  • Integration을 상속받는 새 모델을 app/models/integrations에 추가합니다.

예를 들어, app/models/integrations/foo_bar.rbIntegrations::FooBar를 추가합니다.

  • 특정 유형의 인테그레이션에는 다음 베이스 모듈을 포함할 수 있습니다:

Integrations::Base::ChatNotification

  • Integrations::Base::Ci

  • Integrations::Base::IssueTracker

  • Integrations::Base::Monitoring

  • Integrations::Base::SlashCommands

  • Integrations::Base::ThirdPartyWiki

  • 주로 외부 서비스에 대한 HTTP 호출을 트리거하는 인테그레이션의 경우, Integrations::HasWebHook concern을 사용할 수도 있습니다. 이는 연관된 ServiceHook 모델을 통해 GitLab의 웹훅 기능을 재사용하며, 인테그레이션 설정에서 조회할 수 있는 요청 로그를 자동으로 기록합니다.

  • 인테그레이션의 언더스코어 이름('foo_bar')을 Integration::INTEGRATION_NAMES에 추가합니다.

  • 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=, #url_changed?가 생성됩니다. 이러한 접근자는 다른 ActiveRecord 속성과 마찬가지로 모델에서 직접 Integration#properties에 저장된 필드에 접근해야 합니다.

항상 getters를 통해 필드에 접근하고 properties 해시와 직접 상호작용하지 않아야 합니다. properties 해시에 직접 쓰면 안 되며, 대신 생성된 setter 메서드를 반드시 사용해야 합니다. 이 해시에 직접 쓰면 데이터가 유지되지 않습니다.

프론트엔드 폼에서 이러한 필드가 어떻게 노출되는지 확인하려면 프론트엔드 폼 커스터마이즈를 참조하세요.

이전 버전의 인테그레이션에서는 Integration.prop_accessor 또는 Integration.data_field를 사용하는 방식도 있습니다. 새로운 인테그레이션에는 이러한 방식을 사용하지 않아야 합니다.

유효성 검사 정의#

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

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

required: 속성이 있는 필드는 required: 필드 속성이 프론트엔드용이므로 presence에 대한 해당 유효성 검사가 있어야 합니다.

예시:

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 이벤트 alert 새로운 고유한 alert가 기록됩니다.
Commit 이벤트 commit commit이 생성되거나 업데이트됩니다.
Deployment 이벤트 deployment 배포가 시작되거나 완료됩니다.
Work item 이벤트 issue work item이 생성, 업데이트 또는 종료됩니다.
기밀 이슈 이벤트 confidential_issue 기밀 work item이 생성, 업데이트 또는 종료됩니다.
Job 이벤트 job
머지 리퀘스트 이벤트 merge_request 머지 리퀘스트가 생성, 업데이트 또는 병합됩니다.
Comment 이벤트 comment 새 댓글이 추가됩니다.
기밀 comment 이벤트 confidential_note 기밀 work item에 새 댓글이 추가됩니다.
파이프라인 이벤트 pipeline 파이프라인 상태가 변경됩니다.
Push 이벤트 push 리포지터리에 푸시됩니다.
태그 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

이벤트 속성 기본값 정의#

인테그레이션에는 이슈 #382999에서 추적 중인 문제가 있습니다. 대부분의 이벤트 속성의 기본값이 true이기 때문에 필요 이상으로 자주 인테그레이션이 로드됩니다. 해당 이슈를 해결할 때까지 인테그레이션은 다음과 같은 방식으로 모든 이벤트 attribute 속성을 정의해야 합니다:

  • 알림 인테그레이션(Integrations::Base::ChatNotification을 포함하는 것)의 경우, 모든 이벤트 속성을 false로 설정합니다. 이렇게 하면 기본적으로 체크 해제된 이벤트 트리거별 체크박스가 있는 폼이 표시됩니다.

  • 다른 인테그레이션의 경우:

인테그레이션의 트리거 이벤트와 일치하는 이벤트 속성을 true로 설정합니다.

  • 다른 모든 이벤트 attributesfalse로 설정합니다.

예를 들어, commit과 머지 리퀘스트 트리거 이벤트에만 응답하는 인테그레이션은 이벤트 속성을 다음과 같이 설정해야 합니다:

attribute :commit_events, default: true
attribute :merge_requests_events, default: true

attribute :alert_events, default: false
attribute :incident_events, default: false
attribute :confidential_issues_events, default: false
attribute :confidential_note_events, default: false
attribute :issues_events, default: false
attribute :job_events, default: false
attribute :note_events, default: false
attribute :pipeline_events, default: false
attribute :push_events, default: false
attribute :tag_push_events, default: false
attribute :wiki_page_events, default: false

이벤트 속성 기본값 변경#

기존 인테그레이션의 이벤트 속성이 true로 변경되는 경우, 이전 레코드에 대한 속성 값을 채우기 위해 데이터 마이그레이션이 필요합니다.

메트릭 정의#

모든 새 인테그레이션에는 다섯 가지 메트릭이 있어야 합니다:

  • 해당 인테그레이션이 활성화된 프로젝트 수

  • 해당 인테그레이션을 상속하는 활성 프로젝트 수

  • 해당 인테그레이션이 활성화된 그룹 수

  • 해당 인테그레이션을 상속하는 활성 그룹 수

  • 해당 인테그레이션에 대한 활성 인스턴스 수준 인테그레이션 수

메트릭은 인테그레이션의 모델 클래스가 있어야 작동합니다. 메트릭은 모델과 함께 또는 모델 이후에만 추가할 수 있습니다.

메트릭 정의를 생성하려면:

  • 기존 활성 인테그레이션에 대해 생성된 메트릭을 복사합니다.

  • 이전 인테그레이션 이름이 있는 모든 항목을 새 인테그레이션 이름으로 교체합니다.

  • milestone을 현재 마일스톤으로, introduced_by_url을 머지 리퀘스트 링크로 교체합니다.

  • 메트릭 가이드를 확인하여 다른 모든 속성이 올바른 값을 가지는지 검증합니다.

예를 들어 Slack 인테그레이션에 대한 메트릭 정의를 생성하려면 다음 메트릭을 복사한 후 Slack을 새 인테그레이션 이름으로 교체합니다:

보안 요구사항#

모든 HTTP 호출은 Integrations::Clients::HTTP를 사용해야 합니다#

인테그레이션은 항상 Integrations::Clients::HTTP를 사용하여 HTTP 호출을 해야 합니다. 이 모듈은:

  • HTTP 호출에 대해 네트워크 설정이 적용되도록 보장합니다.

  • 추가적인 보안 강화 기능을 갖추고 있습니다.

  • 안전한 HTTP 호출을 위한 단일 진실 공급원(Single Source Of Truth, SSOT)입니다.

  • 모든 응답 크기의 유효성을 검증합니다.

채널 값 마스킹#

Integrations::Base::ChatNotification에서 포함하는 인테그레이션은 채널 입력 필드의 값을 숨길 수 있습니다. 필드에 인증 토큰과 같은 민감한 정보가 포함된 경우 인테그레이션은 이러한 값을 숨겨야 합니다.

기본적으로 #mask_configurable_channels?false를 반환합니다. 채널 값을 마스킹하려면 인테그레이션에서 #mask_configurable_channels? 메서드를 재정의하여 true를 반환하도록 합니다:

override :mask_configurable_channels?
def mask_configurable_channels?
  true
end

HTTP 호출을 하는 Ruby gem 금지#

GitLab 인테그레이션은 HTTP 호출을 하는 Ruby gem을 추가해서는 안 됩니다. 작은 추상화를 추가하는 다른 gem도 추가하지 않아야 합니다.

atlassian-jwt gem과 같이 공식 소스에서 제공하는 특정 유틸리티 gem은 필요한 경우 사용할 수 있습니다.

서드파티 서비스와의 상호작용을 래핑하는 gem은 처음에는 편리해 보일 수 있지만, 관련 비용에 비해 이점이 최소화됩니다:

  • 보안 문제의 잠재적 범위와 수정에 필요한 노력이 증가합니다.

  • 이러한 gem은 대개 사용자 대신 HTTP 호출을 합니다. 인테그레이션은 사용자가 구성한 원격 서버에 HTTP 호출을 할 수 있기 때문에, 네트워크 호출을 완전히 제어하는 것이 매우 중요합니다.

  • gem 업그레이드 관리에 유지보수 비용이 발생합니다.

  • 새로운 기능 사용을 막을 수 있습니다.

설정 테스트 정의#

선택적으로 인테그레이션 설정에 대한 설정 테스트를 정의할 수 있습니다. 테스트는 인테그레이션 폼의 테스트 버튼에서 실행되며 결과가 사용자에게 반환됩니다.

좋은 설정 테스트는:

  • 서비스의 데이터를 변경하지 않습니다. 예를 들어, CI 빌드를 트리거해서는 안 됩니다. 메시지 전송은 괜찮습니다.

  • 의미 있고 가능한 한 철저합니다.

위 가이드라인을 따르기 어려운 경우 설정 테스트를 추가하지 않는 것을 고려하세요.

설정 테스트를 추가하려면 인테그레이션 모델에 #test 메서드를 정의합니다.

메서드는 테스트 push 이벤트 페이로드인 data를 수신합니다. 다음 키를 포함하는 해시를 반환해야 합니다:

  • success (필수): 설정 테스트 통과 여부를 나타내는 boolean.

  • result (선택): 설정 테스트가 실패했을 때 사용자에게 반환되는 메시지.

예시:

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

      { success: success, result: 'API key is invalid' }
    end
  end
end

프론트엔드 폼 커스터마이즈#

프론트엔드 폼은 모델에 정의된 메타데이터를 기반으로 동적으로 생성됩니다.

기본적으로 인테그레이션 폼은 다음을 제공합니다:

  • 인테그레이션을 활성화하거나 비활성화하는 체크박스.

  • Integration#configurable_events에서 반환된 각 트리거 이벤트에 대한 체크박스.

Integration#help를 재정의하거나 app/views/shared/integrations/$INTEGRATION_NAME/_help.html.haml에 템플릿을 제공하여 폼 상단에 도움말 텍스트를 추가할 수도 있습니다.

커스텀 속성을 폼에 추가하려면 Integration#fields에서 메타데이터를 정의할 수 있습니다.

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

타입 필수 기본값 설명
type: symbol true :text 폼 필드의 유형. :text, :number, :textarea, :password, :checkbox, :string_array, :select 중 하나.
section: symbol false 필드가 속하는 섹션을 지정합니다.
name: string true 폼 필드의 속성 이름.
required: boolean false false 폼 필드가 필수인지 선택인지 지정합니다. 백엔드의 presence 유효성 검사도 여전히 필요합니다.
title: string false name:의 대문자 값 폼 필드의 라벨.
placeholder: string false 폼 필드의 placeholder.
help: string false 폼 필드 아래에 표시되는 도움말 텍스트.
api_only: boolean false false 필드가 API를 통해서만 사용 가능하고 프론트엔드 폼에서 제외되는지 지정합니다.
description string false API 필드 설명.
if: boolean 또는 lambda false true 필드 사용 가능 여부를 지정합니다. 값은 boolean 또는 lambda일 수 있습니다.

type: :checkbox에 대한 추가 키#

타입 필수 기본값 설명
checkbox_label: string false title:의 값 체크박스 옆에 표시되는 커스텀 라벨.

type: :select에 대한 추가 키#

타입 필수 기본값 설명
choices: array true [라벨, 값] 튜플의 중첩 배열.

type: :password에 대한 추가 키#

타입 필수 기본값 설명
non_empty_password_title: string false title:의 값 값이 이미 저장되어 있을 때 표시되는 대체 라벨.
non_empty_password_help: string false 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 컴포넌트는 인테그레이션에 대해 checkbox, number, 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 커스터마이즈 요구사항을 충족하지 않는 경우 새 커스텀 섹션을 만들 수 있습니다:

  • 새 상수 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
  • ~/integrations/constants.js의 프론트엔드 상수 integrationFormSectionsintegrationFormSectionComponents를 업데이트합니다.

  • app/assets/javascripts/integrations/edit/components/sections/*에 새 섹션 컴포넌트를 추가합니다.

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

프론트엔드 폼 예시#

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

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에서 인테그레이션을 노출하려면:

  • 인테그레이션 클래스(::Integrations::FooBar)를 API::Helpers::IntegrationsHelpers.integration_classes에 추가합니다.

  • API::Helpers::IntegrationsHelpers.integrations에 인테그레이션의 API 인수를 추가합니다. 예시:

'foo-bar' => ::Integrations::FooBar.api_arguments
  • doc/api/project_integrations.mddoc/api/group_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: 메트릭 수집기.

  • EWM/RTC: 외부 이슈 트래커.

  • Webex Teams: 채팅 알림.

  • ZenTao: Jira 이슈 인테그레이션과 유사한 커스텀 이슈 보기가 있는 외부 이슈 트래커.