GitLab データシーダー
GitLab Data Seeder(GDS) はテストデータのシードハーネスで、ユーザーやグループのネームスペースにテストデータをシードすることができます。
Data SeederはバックエンドでFactoryBotを使用しており、メンテナンスが簡単です。モデルが変更されると、FactoryBotはすでにその変更を反映します。
Dockerセットアップ
GDKセットアップ
$ gdk start db
ok: run: services/postgresql: (pid n) 0s, normally down
ok: run: services/redis: (pid n) 74s, normally down
$ bundle install
Bundle complete!
$ bundle exec rake db:migrate
main: migrated
ci: migrated
実行
ee:gitlab:seed:data_seeder
Rakeタスクは2つの引数を取ります。:name
と:namespace_id
。
$ bundle exec rake "ee:gitlab:seed:data_seeder[data_seeder,1]"
Seeding Data for Administrator
:name
:name
はファイル名です。(この名前は、ee/db/seeds/data_seeder
にある相対.rb
、.yml
、.json
ファイル、またはシード ファイルへの絶対パスを反映します)。
:namespace_id
:namespace_id
はユーザーまたはグループ・ネームスペースの ID です。
開発者
Data Seederは、spec/factories
のFactoryBot定義を使用しています。
- 開発者の時間を節約
- 読みやすい
- メンテナーが簡単
- 将来変更される可能性のあるAPIに依存しないこと
- 常に最新であること
- 可能な限り最下位レベル(
ActiveRecord
)で実行し、可能な限り迅速にデータを作成します。
FactoryBot READMEより:
factory_bot
はフィクスチャを置き換えるもので、わかりやすい定義構文、複数のビルド戦略(保存インスタンス、未保存インスタンス、属性ハッシュ、スタブオブジェクト)のサポート、ファクトリ継承を含む同じクラスに対する複数のファクトリのサポートがあります。
ファクトリはspec/factories/*
に存在し、app/models/*
にある Rails モデルのフィクスチャです。たとえば、app/models/issue.rb
という名前のモデルの場合、ファクトリはspec/factories/issues.rb
という名前になります。app/models/project.rb
という名前のモデルの場合、ファクトリはapp/models/projects.rb
という名前になります。
GitLab Data Seederがサポートするパーサーは3つあります。Ruby、YAML、JSONです。
Ruby
すべての Ruby Seeds は#seed
インスタンスメソッドを持つDataSeeder
クラスを定義しなければなりません。Rubyクラスの構造は自由です。すべての FactoryBotメソッド(create
,build
,create_list
) は自動的にクラスに含まれ、呼び出すことができます。
DataSeeder
クラスには、播種時に定義される以下のインスタンス変数が含まれます:
-
@seed_file
-File
オブジェクト。 -
@owner
- シードデータのオーナー。 -
@name
- シードの名前。拡張子を除いたシードファイル名。 -
@group
- すべてのシード・データが作成されるルート・グループ。
# frozen_string_literal: true
class DataSeeder
def seed
my_group = create(:group, name: 'My Group', path: 'my-group-path', parent: @group)
my_project = create(:project, :public, name: 'My Project', namespace: my_group, creator: @owner)
end
end
YAML
YAML パーサーはファクトリ定義をサポートする DSL で、人間が読めるフォーマットを使ってデータをシードできます。
name: My Seeder
groups:
- _id: my_group
name: My Group
path: my-group-path
projects:
- _id: my_project
name: My Project
namespace_id: <%= groups.my_group.id %>
creator_id: <%= @owner.id %>
traits:
- public
JSON
JSONパーサーを使用すると、JSON形式のシードファイルを格納できます。
{
"name": "My Seeder",
"groups": [
{ "_id": "my_group", "name": "My Group", "path": "my-group-path" }
],
"projects": [
{
"_id": "my_project",
"name": "My Project",
"namespace_id": "<%= groups.my_group.id %>",
"creator_id": "<%= @owner.id %>",
"traits": ["public"]
}
]
}
ファクトリーの分類法
ファクトリーは3つの主要な部分から構成されます - ファクトリーの名前、特性、属性。
与えられたcreate(:iteration, :with_title, :current, title: 'My Iteration')
これはファクトリーの名前です。ファイル名はこのNameの複数形になり、spec/factories/iterations.rb またはee/spec/factories/iterations.rb の下に存在します。 | :with_title | ファクトリーの特性です。定義方法を参照してください。 | :current | これはファクトリーの特性です。定義方法を参照してください。 | title: ‘My Iteration’(私の反復) ファクトリーの属性で、作成時にモデルに渡されます。 |
使用例
これらの例では、インスタンス変数@owner
が表示されます。これはroot
ユーザー (User.first
) です。
グループの作成
my_group = create(:group, name: 'My Group', path: 'my-group-path')
プロジェクトの作成
# create a Project belonging to a Group
my_project = create(:project, :public, name: 'My Project', namespace: my_group, creator: @owner)
イシューの作成
# create an Issue belonging to a Project
my_issue = create(:issue, title: 'My Issue', project: my_project, weight: 2)
イテレーションの作成
# create an Iteration under a Group
my_iteration = create(:iteration, :with_title, :current, title: 'My Iteration', group: my_group)
よく遭遇するイシュー
ActiveRecord::RecordInvalid:検証に失敗しました:電子メールはすでに取得されています。
これは、デフォルトでは、ファクトリは欠落しているデータを埋め戻すように記述されているためです。インスタンスンス、プロジェクトが作成されるとき、プロジェクトにはそれを作成した誰かがいなければなりません。オーナーが指定されていない場合、ファクトリーはそれを作成しようとします。
修正方法
それぞれのファクトリーで必要なキーを確認してください。通常、:author
または:owner
。
# This throws ActiveRecord::RecordInvalid
create(:project, name: 'Throws Error', namespace: create(:group, name: 'Some Group'))
# Specify the user where @owner is a [User] record
create(:project, name: 'No longer throws error', owner: @owner, namespace: create(:group, name: 'Some Group'))
create(:epic, group: create(:group), author: @owner)
parsing id "my id" as "my_id"
変数の指定を参照してください。
id is invalid
Ruby以外のパーサはIDをRubyオブジェクトとしてパースするので、IDを指定するときはRubyの命名規則に従わなければなりません。
無効なIDの例
- 数字で始まるID
- 特殊文字を含むID (-, !, $, @, `, =,<, >, ;, :)
ActiveRecord::AssociationTypeMismatch:期待されたモデルは、文字列のインスタンスである … を得ました。
これはシーダーの制限です。
生のRubyオブジェクトのパース許可に関するイシューを参照してください。
YAML ファクトリ
_n個の_レコードを生成するジェネレータ
グループラベル
group_labels:
# Group Label with Name and a Color
- name: Group Label 1
group_id: <%= @group.id %>
color: "#FF0000"
グループのマイルストーン
group_milestones:
# Past Milestone
- name: Past Milestone
group_id: <%= @group.id %>
group:
start_date: <%= 1.month.ago %>
due_date: <%= 1.day.ago %>
# Ongoing Milestone
- name: Ongoing Milestone
group_id: <%= @group.id %>
group:
start_date: <%= 1.day.ago %>
due_date: <%= 1.month.from_now %>
# Future Milestone
- name: Ongoing Milestone
group_id: <%= @group.id %>
group:
start_date: <%= 1.month.from_now %>
due_date: <%= 2.months.from_now %>
癖
-
group:
を指定し、内部が空である_必要があります_。これは、マイルストーンのファクトリーがafter(:build)
でファクトリーを操作するためです。これが存在しないと、マイルストーンをグループと正しく関連付けることができません。
エピック
epics:
# Simple Epic
- title: Simple Epic
group_id: <%= @group.id %>
author_id: <%= @owner.id %>
# Epic with detailed Markdown description
- title: Detailed Epic
group_id: <%= @group.id %>
author_id: <%= @owner.id %>
description: |
# Markdown
**Description**
# Epic with dates
- title: Epic with dates
group_id: <%= @group.id %>
author_id: <%= @owner.id %>
start_date: <%= 1.day.ago %>
due_date: <%= 1.month.from_now %>
変数
作成された各ファクトリーには、将来のシーディングで使用する識別子を割り当てることができます。
作成されたファクトリーには、後でシードファイルで使用するIDを指定できます。
変数の指定
Ruby以外のパーサで後で参照するために、どのファクトリでも_id
属性を渡すことができます。
変数はファクトリ定義の下にあります。
---
group_labels:
- _id: my_label #=> group_labels.my_label
projects:
- _id: my_project #=> projects.my_project
変数
変数の参照
YAMLシードファイルがある場合
---
group_labels:
- _id: my_group_label #=> group_labels.my_group_label
name: My Group Label
color: "#FF0000"
- _id: my_other_group_label #=> group_labels.my_other_group_label
color: <%= group_labels.my_group_label.color %>
projects:
- _id: my_project #=> projects.my_project
name: My Project
変数を参照するとき、変数は_すでにシードされた_モデルを参照します。言い換えると、モデルのid
属性に値が入ります。