作業項目ウィジェット

フロントエンドアーキテクチャ

ワークアイテムのウィジェットは、フロントエンドのウィジェットに大きくインスパイアされています。ワークアイテムはissueableとはアーキテクチャが異なるため、いくつかの違いがあることをご想像ください。

GraphQL (Vue Apollo) は、ワークアイテムウィジェットのスタックの Core を構成しています。

ワークアイテムのウィジェット情報の取得

ワークアイテムのページを表示するために、フロントエンドは表示しようとしているワークアイテムで利用可能なウィジェットを知る必要があります。そのためには、次のようなクエリを使って、ウィジェットのリストを取得する必要があります:

query WorkItem($workItemId: ID!) {
  workItem(workItemId: $id) @client {
    id
    type
    widgets {
      nodes {
        type
      }
    }
  }
}

GraphQL クエリとミューテーション

GraphQL クエリと変異はワークアイテムに依存しません。ワークアイテムのクエリおよび変異はウィジェットレベルで発生するため、ウィジェットはスタンドアロンの再利用可能なコンポーネントです。ワークアイテムのクエリと変異は、あらゆるワークアイテムのタイプをサポートし、動的であるべきです。ウィジェットの識別子を指定することで、ワークアイテムの属性をクエリし、変異させることができます。

このクエリの例では、説明ウィジェットがクエリと変異を使用して、任意のワークアイテムの説明を表示および更新します:

query {
  workItem(input: {
    workItemId: "gid://gitlab/AnyWorkItem/2207",
    widgetIdentifier: "description",
  }) {
    id
    type
    widgets {
      nodes {
        ... on DescriptionWidget {
          contentText
        }
      }
    }
  }
}

変異の例

mutation {
  updateWorkItem(input: {
    workItemId: "gid://gitlab/AnyWorkItem/2207",
    widgetIdentifier: "description",
    value: "the updated description"
  }) {
    workItem {
      id
      description
    }
  }
}

ウィジェットの責任と構造

ウィジェットは、タイトル、説明、ラベルなどの単一の属性の表示と更新を担当します。ウィジェットは、あらゆるタイプのワークアイテムをサポートしなければなりません。コンポーネントの再利用性を最大化するために、ウィジェットは、担当する属性のワークアイテムクエリと変異を所有するフィールドラッパーでなければなりません。

フィールドコンポーネントは、一般的で単純なコンポーネントです。入力フィールド、日付セレクタ、ドロップダウンリストのような属性やワークアイテムの詳細に関する知識はありません。

ウィジェットは、ワークアイテムに応じて、さまざまなユースケースをサポートするように設定できなければなりません。ウィジェットを構築するときは、プロップや注入属性の使用を最小限に抑えながら、スロットを使用して追加のコンテキストを提供します。

使用例

参考としてドロップダウンリストコンポーネントを用意しました。

どのワークアイテムウィジェットもドロップダウンリストをラップすることができます。ウィジェットは、それが変異させる属性の知識を持ち、その属性の変異を所有します。複数のウィジェットが同じフィールドコンポーネントを使用できます。例えば

  • タイトルウィジェットと説明ウィジェットは input フィールドコンポーネントを使います。
  • 開始日と終了日は日付セレクタコンポーネントを使用します。
  • ラベル、マイルストーン、担当者セレクタはドロップダウンリストを使用します。

いくつかのフロントエンドウィジェットはすでにドロップダウンリストを使っています。ワークアイテムウィジェットを開発する際の参考にしてください:

  • ee/app/assets/javascripts/boards/components/assignee_select.vue
  • ee/app/assets/javascripts/boards/components/milestone_select.vue

ウィジェットのワークアイテムタイプへのマッピング

すべてのWork Itemタイプは、定義済みのウィジェットの同じプールを共有し、特定のタイプでどのウィジェットがアクティビティであるかによってカスタマイズされます。ユーザが新しいワークアイテム・タイプを作成し、それに対してウィジェットのセットを定義できるようにする予定であるため、各ワークアイテム・タイプに対するウィジェットのマッピングはデータベースに保存されます。ウィジェットのマッピングはwidget_definitionsテーブルに格納され、デフォルトのWork Itemタイプだけでなく、将来的にはカスタムタイプのウィジェット定義にも使用できます。予想されるデータベース・テーブル構造の詳細については、このイシューの説明を参照してください。

ワークアイテム・タイプへの新しいウィジェットの追加

各作業項目タイプに割り当てられているウィジェットの情報はデータベースに保存されているため、作業項目タイプに新しいウィジェットを追加するには、データベースのマイグレーションが必要です。また、ウィジェットインポーター(lib/gitlab/database_importers/work_items/widgets_importer.rb)も更新する必要があります。

ウィジェット定義テーブルの構造

テーブルの各レコードは、ウィジェットのワークアイテムタイプへのマッピングを定義します。現在、”グローバル “定義(NULLnamespace_id を持つ定義)のみが使用されています。次の反復では、これらのマッピングをカスタマイズできるようにする予定です。例えば、以下のテーブルでは以下のように定義されています:

  • 重量ウィジェットは、ワークアイテムタイプ 0 および 1 で有効です。
  • において、WeightウィジェットはMyWeightにリネームされます。ユーザーがウィジェットの名前を変更すると、名前空間内のすべてのウィジェット・マッピングの名前を変更することは理にかなっています。name 属性が非正規化されているため、このウィジェット・タイプに対して、すべてのワークアイテム・タイプの名前空間マッピングを作成する必要があります。
  • ウェイト・ウィジェットは、特定のワークアイテム・タイプに対して無効にすることができます (名前空間 3 では、ワークアイテム・タイプ 0 に対して無効になり、ワークアイテム・タイプ 1 に対して有効になります)。
IDnamespace_idwork_item_type_idwidget_type_enumポジション名前無効
1 011ウェイトfalse
2 111ウェイトfalse
31010マイウェイトfalse
41110マイウェイトfalse
52011その他の重量false
63011ウェイトtrue

バックエンドアーキテクチャ

ウィジェットの更新は、カスタムの細かい変異(たとえば、WorkItemCreateFromTask )を使用するか、workItemCreate またはworkItemUpdate 変異の一部として行うことができます。

ウィジェットコールバック

ワークアイテムの変異と一緒にウィジェットを更新する場合、バックエンドのコードはWorkItems::Callbacks::Base を継承したコールバッククラスを使用して実装する必要があります。 これらのクラスは、ActiveRecord コールバックと似た名前のコールバックメソッドを持っており、同じような動作をします。

ウィジェットと同じ名前のコールバック・クラスが自動的に使用されます。例えば、WorkItems::Callbacks::AwardEmoji は、ワーク・アイテムにAwardEmoji ウィジェットがある場合に呼び出されます。別のクラスを使用するには、callback_class クラス・メソッドをオーバーライドします。

コールバック・クラスがマージ・リクエストやエピックなどの他の発行物にも使用される場合は、Issuable::Callbacks でクラスを定義し、IssuableBaseService#available_callbacks のリストにそのクラスを追加します。これらはワーク・アイテムの更新とレガシーissue、マージ・リクエスト、またはエピックの更新の両方で実行されます。

利用可能なコールバック

  • after_initialize は、BuildService によってワークアイテムが初期化された後、CreateService およびUpdateServiceによってワークアイテムが保存される前に呼び出されます。このコールバックは、作成または更新データベース・トランザクションの外部で実行されます。
  • before_update は、UpdateService によってワークアイテムが保存される前に呼び出されます。 このコールバックは、更新データベース・トランザクション内で実行されます。
  • after_update_commit は、UpdateService によって DB 更新トランザクションがコミットされた後に呼び出されます。
  • after_save_commit は、CreateService またはUpdateService によって作成または DB 更新トランザクションがコミットされた後に呼び出されます。