インテグレーション開発者向けガイドライン

このページでは、メインのRailsプロジェクトの一部であるGitLabインテグレーションを実装するための開発ガイドラインを提供します。

インテグレーションに関する私たちの戦略の概要については、方向性のページもご覧ください。

このガイドは進行中です。説明が必要な場合や古い情報を見つけた場合は、@gitlab-org/manage/integrations までご連絡ください。

新しいインテグレーションを追加

インテグレーションを定義します。

  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 を使うこともできます。これは、関連するServiceHook モデルを通して GitLab のWebhook 機能を再利用し、インテグレーション設定で見ることができるリクエストログを自動的に記録します。
  2. Integration::INTEGRATION_NAMES にインテグレーション名 ('foo_bar') を追加します。
  3. Project にインテグレーションをアソシエーションとして追加します:

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

プロパティの定義

インテグレーションは、クラスメソッドIntegration.prop_accessor を使って、設定を保存するための任意のプロパティを定義できます。値は暗号化された JSON ハッシュとしてintegrations.encrypted_properties カラムに保存されます。

使用例:

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

Integration.prop_accessor はクラスにアクセサ・メソッドをインストールします。ここでは、url フィールドを管理するために、#url#url=#url_changed?を用意します。Integration#properties に格納されたフィールドは、他のActiveRecordの属性と同じように、モデル上で直接これらのアクセサによってアクセスされなければなりません。

プロパティへのアクセスは常にgetters を通して行い、properties ハッシュを直接 properties操作してはいけません。properties ハッシュへの書き込み propertiesproperties 行わず properties、生成されたセッターメソッドを使用する必要があります。このハッシュへの直接の書き込みは永続化されません。

また、すべてのプロパティに対してバリデーションを定義する必要があります。

これらのプロパティがインテグレーション用のフロントエンドフォームでどのように公開されるかについては、以下のフロントエンドフォームのカスタマイズのセクションも参照してください。

Integration.data_field を使った別のアプローチもあります。データフィールドでは、値はインテグレーションごとに別のテーブルに保存されます。現時点では、新しいインテグレーションにこの方法を使用することはお勧めしません。

トリガーイベントの定義

インテグレーションはGitLabのイベントに応じて#execute メソッドを呼び出すことでトリガーされます。 メソッドにはイベントの詳細が書かれたペイロードハッシュが渡されます。

サポートされているイベントはWebhook イベントと重複しており、同じペイロードを受け取ります。モデルのクラスメソッドIntegration.supported_events をオーバーライドすることで、興味のあるイベントを指定することができます。

以下のイベントがインテグレーションでサポートされています:

イベントタイプデフォルトトリガー
アラートイベント alert新しい固有のアラートが記録されます。
コミットイベントcommitコミットが作成または更新されます。
デプロイイベント deploymentデプロイが開始または終了します。
イシューイベントissueイシューを作成、更新、クローズします。
コンフィデンシャル・イシュー・イベントconfidential_issue機密イシューが作成、更新、クローズされました。
ジョブイベント job 
マージリクエストイベントmerge_requestマージリクエストが作成、更新、マージされました。
コメントイベント comment新しいコメントが追加されました。
秘密コメントイベント confidential_note機密イシューに関する新しいコメントが追加されました。
パイプラインイベント pipelineパイプラインのステータスが変更されました。
プッシュイベントpushリポジトリにプッシュが行われます。
タグプッシュイベントtag_push新しいタグがリポジトリにプッシュされます。
脆弱性イベント  vulnerability新しい固有の脆弱性が記録されます。
Wiki ページイベントwiki_pageWiki ページが作成または更新されました。

イベントの例

この例では、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 key is invalid' }
    end
  end
end

フロントエンドのフォームをカスタマイズ

フロントエンドのフォームはモデルで定義されたメタデータに基づいて動的に生成されます。

デフォルトでは、インテグレーションフォームは以下を提供します:

  • インテグレーションを有効または無効にするチェックボックス。
  • Integration#configurable_events から返される各トリガーイベントのチェックボックス。

また、Integration#help をオーバーライドするか、app/views/shared/integrations/$INTEGRATION_NAME/_help.html.hamlでテンプレートを提供することで、フォームの上部にヘルプテキストを追加することもできます。

カスタムプロパティをフォームに追加するには、Integration#fields でメタデータを定義します。

このメソッドは、各フィールドのハッシュの配列を返す必要があります:

キー種類必須デフォルト説明
type:文字列です。true フォームフィールドのタイプ。text,textarea,password,checkbox, またはselectのいずれかです。
name:文字列です。true フォーム・フィールドのプロパティ名。これは、クラスで定義されているprop_accessor と一致しなければなりません。
required:booleanfalsefalseフォームフィールドが必須か任意かを指定します。
title:文字列です。falseの資本値name: フォームフィールドのラベル。
placeholder:文字列です。false フォームフィールドのプレースホルダ。
help:文字列です。false フォームフィールドの下に表示されるヘルプテキスト。
api_only:booleanfalsefalseフィールドをAPIからのみ利用可能とし、フロントエンドのフォームからは除外する場合に指定します。
if:boolean あるいは lambdafalsetrueフィールドを使用可能にするかどうかを指定します。値はブール値かラムダです。

の追加キー。type: 'checkbox'

キー種類必須デフォルト説明
checkbox_label:文字列です。falseの値title: チェックボックスの横に表示されるカスタムラベル。

の追加キー。type: 'select'

キー種類必須デフォルト説明
choices:アレイtrue  [label, value] タプルの入れ子配列。

の追加キー。type: 'password'

キー種類必須デフォルト説明
non_empty_password_title:文字列です。falseの値title: 値が既に格納されている場合に表示される代替ラベル。
non_empty_password_help:文字列です。falseの値help: 値が既に格納されている場合に表示される代替ヘルプテキスト。

フロントエンド・フォームの例

この例では、必須のurl フィールドと、オプションのusernamepassword フィールドを定義します:

module Integrations
  class FooBar < Integration
    prop_accessor :url, :username, :password

    def fields
      [
        {
          type: 'text',
          name: 'url',
          title: s_('FooBarIntegration|Server URL'),
          placeholder: 'https://example.com/',
          required: true
        },
        {
          type: 'text',
          name: 'username',
          title: s_('FooBarIntegration|Username'),
        },
        {
          type: 'password',
          name: 'password',
          title: s_('FoobarIntegration|Password'
          non_empty_password_title: s_('FooBarIntegration|Enter new password')
        }
      ]
    end
  end
end

REST API でインテグレーションを公開します。

インテグレーションをREST API に公開します:

  1. API::Helpers::IntegrationsHelpers.integration_classes にインテグレーションクラス (::Integrations::FooBar) を追加します。
  2. API::Helpers::IntegrationsHelpers.integrations に、公開すべきすべてのプロパティを追加します。
  3. doc/api/integrations.md のリファレンス・ドキュメントを更新し、インテグレーション用の新しいセクションを追加して、すべてのプロパティを文書化してください。

また、REST APIスタイルガイドを参照することもできます。

センシティブなフィールドはAPI上で公開されません。機密フィールドとは、名前に以下のいずれかが含まれるフィールドのことです:

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

インテグレーションの有無

デフォルトでは、インテグレーションはプロジェクト、グループ、インスタンスレベルで利用できます。ほとんどのインテグレーションはプロジェクトコンテキストでのみ動作しますが、グループとインスタンスレベルからも設定できます。

インテグレーションによっては、プロジェクトレベルでのみ利用できるようにすることもできます。そのためには、インテグレーションをIntegration::INTEGRATION_NAMES から削除し、代わりにIntegration::PROJECT_SPECIFIC_INTEGRATION_NAMES に追加する必要があります。

新しいインテグレーションを開発する場合、Integration.available_integration_names機能フラグの後ろに、利用可能かどうかをゲートすることをお勧めします。

ドキュメント

フロントエンドフォームのカスタマイズ で説明したように、インテグレーションフォームにヘルプテキストを記述することができます。ヘルプテキストについてはユーザビリティガイドラインを参照してください。

より詳細なドキュメントについては、doc/user/project/integrations にページを作成し、インテグレーション概要からリンクしてください。

また、一般的なドキュメントのガイドラインを参照することもできます。

テスト

テストを設定テストの定義と混同してはいけません。

spec/models/integrations にインテグレーションモデルのテストを追加し、spec/factories/integrations.rb に設定例のファクトリーのテストを追加すれば十分です。

各インテグレーションは一般化されたテストの一部としてもテストされます。たとえば、すべてのインテグレーションで設定フォームが正しくレンダリングされることを検証する機能仕様があります。

もしあなたのインテグレーションがカスタムな振る舞いを実装しているのなら、 特にフロントエンドにおいて、追加のテストが必要です。

一般的なテストのガイドラインも参照ください。

国際化

すべてのUI文字列は、私たちの国際化ガイドラインに従って、翻訳のために準備されるべきです。

文字列はインテグレーション名を名前空間として使用する必要があります。例えば、s_('FooBarIntegration|My string').

インテグレーションの廃止と削除

インテグレーションを削除するには、まずインテグレーションを非推奨にする必要があります。詳細については、機能の非推奨ガイドラインを参照してください。

インテグレーションの非推奨化

非推奨を発表するのは、廃止予定の3つ前のマイルストーンまでにしてください。インテグレーションを非推奨にするには

インテグレーションを削除

インテグレーションを安全に削除するには、2つのマイルストーンにわたって削除をステージする必要があります。

削除予定の主要マイルストーン(M.0)では、インテグレーションを無効にし、データベースからレコードを削除します:

次のマイナーリリース(M.1)で:

  • インテグレーションのモデルと残りのコードを削除します。
  • インテグレーションのラベル (~Integration::<name>) を持つイシュー、マージリクエスト、エピックはすべて閉じます。
  • gitlab-org から、インテグレーションのラベル (~Integration::<name>) を削除します。

進行中のマイグレーションとリファクタリング

開発者は、インテグレーションチームがインテグレーションプロパティの定義方法を統一している最中であることを知っておく必要があります。

インテグレーション例

新しいインテグレーションを追加する例については、以下のイシューを参照してください:

  • Datadog:Prometheusインテグレーションと同様のメトリクスコレクター。
  • EWM/RTC: 外部イシュー・トラッカー。
  • Shimo:Confluence と External Wiki のインテグレーションに似た外部 Wiki。
  • Webex Teams:チャット通知。
  • ZenTao: Jiraインテグレーションに似たカスタムイシュービューを持つ外部イシュートラッカー。