Commit b0b1b3de authored by Ash McKenzie's avatar Ash McKenzie

Merge branch 'jhyson/export-board-milestone' into 'master'

Export Board Milestones

See merge request gitlab-org/gitlab!24606
parents 7e0abff5 40ed66df
...@@ -3,7 +3,13 @@ ...@@ -3,7 +3,13 @@
class Milestone < ApplicationRecord class Milestone < ApplicationRecord
# Represents a "No Milestone" state used for filtering Issues and Merge # Represents a "No Milestone" state used for filtering Issues and Merge
# Requests that have no milestone assigned. # Requests that have no milestone assigned.
MilestoneStruct = Struct.new(:title, :name, :id) MilestoneStruct = Struct.new(:title, :name, :id) do
# Ensure these models match the interface required for exporting
def serializable_hash(_opts = {})
{ title: title, name: name, id: id }
end
end
None = MilestoneStruct.new('No Milestone', 'No Milestone', 0) None = MilestoneStruct.new('No Milestone', 'No Milestone', 0)
Any = MilestoneStruct.new('Any Milestone', '', -1) Any = MilestoneStruct.new('Any Milestone', '', -1)
Upcoming = MilestoneStruct.new('Upcoming', '#upcoming', -2) Upcoming = MilestoneStruct.new('Upcoming', '#upcoming', -2)
...@@ -128,11 +134,12 @@ class Milestone < ApplicationRecord ...@@ -128,11 +134,12 @@ class Milestone < ApplicationRecord
reorder(nil).group(:state).count reorder(nil).group(:state).count
end end
def predefined_id?(id)
[Any.id, None.id, Upcoming.id, Started.id].include?(id)
end
def predefined?(milestone) def predefined?(milestone)
milestone == Any || predefined_id?(milestone&.id)
milestone == None ||
milestone == Upcoming ||
milestone == Started
end end
end end
......
---
title: Export Board Milestones in Group Export
merge_request: 24606
author:
type: added
...@@ -87,7 +87,8 @@ ...@@ -87,7 +87,8 @@
"created_at": "2019-11-20T17:27:41.118Z", "created_at": "2019-11-20T17:27:41.118Z",
"updated_at": "2019-11-20T17:27:41.118Z", "updated_at": "2019-11-20T17:27:41.118Z",
"name": "first board", "name": "first board",
"milestone_id": 7638, "milestone_id": -2,
"milestone": {"id": -2, "name": "#upcoming", "title": "Upcoming"},
"group_id": 4351, "group_id": 4351,
"weight": null, "weight": null,
"labels": [], "labels": [],
...@@ -153,6 +154,31 @@ ...@@ -153,6 +154,31 @@
} }
} }
] ]
},
{
"id": 57,
"project_id": null,
"created_at": "2019-11-20T17:27:41.118Z",
"updated_at": "2019-11-20T17:27:41.118Z",
"name": "second board",
"milestone_id": 7642,
"milestone": {
"id": 7642,
"title": "v4.0",
"project_id": null,
"description": "Et laudantium enim omnis ea reprehenderit iure.",
"due_date": null,
"created_at": "2019-11-20T17:02:14.336Z",
"updated_at": "2019-11-20T17:02:14.336Z",
"state": "closed",
"iid": 5,
"start_date": null,
"group_id": 4351
},
"group_id": 4351,
"weight": null,
"labels": [],
"lists": []
} }
], ],
"members": [ "members": [
......
...@@ -46,5 +46,17 @@ describe Gitlab::ImportExport::Group::TreeRestorer do ...@@ -46,5 +46,17 @@ describe Gitlab::ImportExport::Group::TreeRestorer do
expect(lists.map(&:list_type)).to contain_exactly('assignee', 'milestone') expect(lists.map(&:list_type)).to contain_exactly('assignee', 'milestone')
end end
end end
context 'boards' do
it 'has user generated milestones' do
board = group.boards.find_by(name: 'second board')
expect(board.milestone.title).to eq 'v4.0'
end
it 'does not have predefined milestones' do
board = group.boards.find_by(name: 'first board')
expect(board.milestone).to be_nil
end
end
end end
end end
...@@ -138,6 +138,51 @@ describe Gitlab::ImportExport::Group::TreeSaver do ...@@ -138,6 +138,51 @@ describe Gitlab::ImportExport::Group::TreeSaver do
expect(assignee_list['user_id']).to eq(user.id) expect(assignee_list['user_id']).to eq(user.id)
end end
end end
context 'when there are boards with predefined milestones' do
let(:milestone) { Milestone::Upcoming }
before do
create(:board, group: group, milestone_id: milestone.id)
end
it 'saves the milestone data' do
expect_successful_save(group_tree_saver)
board_data = saved_group_json['boards'].last
expect(board_data).to include(
'milestone_id' => milestone.id,
'milestone' => {
'id' => milestone.id,
'name' => milestone.name,
'title' => milestone.title
}
)
end
end
context 'when there are boards with persisted milestones' do
let(:milestone) { create(:milestone) }
before do
create(:board, group: group, milestone: milestone)
end
it 'saves the milestone data' do
expect_successful_save(group_tree_saver)
board_data = saved_group_json['boards'].last
expect(board_data).to include(
'milestone_id' => milestone.id,
'milestone' => a_hash_including(
'id' => milestone.id,
'title' => milestone.title
)
)
end
end
end end
def expect_successful_save(group_tree_saver) def expect_successful_save(group_tree_saver)
......
...@@ -67,7 +67,7 @@ module Gitlab ...@@ -67,7 +67,7 @@ module Gitlab
# the relation_hash, updating references with new object IDs, mapping users using # the relation_hash, updating references with new object IDs, mapping users using
# the "members_mapper" object, also updating notes if required. # the "members_mapper" object, also updating notes if required.
def create def create
return if invalid_relation? return if invalid_relation? || predefined_relation?
setup_base_models setup_base_models
setup_models setup_models
...@@ -89,6 +89,10 @@ module Gitlab ...@@ -89,6 +89,10 @@ module Gitlab
false false
end end
def predefined_relation?
relation_class.try(:predefined_id?, @relation_hash['id'])
end
def setup_models def setup_models
raise NotImplementedError raise NotImplementedError
end end
......
...@@ -70,6 +70,7 @@ ee: ...@@ -70,6 +70,7 @@ ee:
- :push_event_payload - :push_event_payload
- boards: - boards:
- :board_assignee - :board_assignee
- :milestone
- labels: - labels:
- :priorities - :priorities
- lists: - lists:
......
...@@ -33,6 +33,15 @@ describe Gitlab::ImportExport::Base::RelationFactory do ...@@ -33,6 +33,15 @@ describe Gitlab::ImportExport::Base::RelationFactory do
end end
end end
context 'when the relation is predefined' do
let(:relation_sym) { :milestone }
let(:relation_hash) { { 'name' => '#upcoming', 'title' => 'Upcoming', 'id' => -2 } }
it 'returns without creating a new relation' do
expect(subject).to be_nil
end
end
context 'when #setup_models is not implemented' do context 'when #setup_models is not implemented' do
it 'raises NotImplementedError' do it 'raises NotImplementedError' do
expect { subject }.to raise_error(NotImplementedError) expect { subject }.to raise_error(NotImplementedError)
......
...@@ -189,7 +189,7 @@ describe Gitlab::ImportExport::Group::TreeSaver do ...@@ -189,7 +189,7 @@ describe Gitlab::ImportExport::Group::TreeSaver do
create(:group_badge, group: group) create(:group_badge, group: group)
group_label = create(:group_label, group: group) group_label = create(:group_label, group: group)
create(:label_priority, label: group_label, priority: 1) create(:label_priority, label: group_label, priority: 1)
board = create(:board, group: group) board = create(:board, group: group, milestone_id: Milestone::Upcoming.id)
create(:list, board: board, label: group_label) create(:list, board: board, label: group_label)
create(:group_badge, group: group) create(:group_badge, group: group)
......
...@@ -3,6 +3,18 @@ ...@@ -3,6 +3,18 @@
require 'spec_helper' require 'spec_helper'
describe Milestone do describe Milestone do
describe 'MilestoneStruct#serializable_hash' do
let(:predefined_milestone) { described_class::MilestoneStruct.new('Test Milestone', '#test', 1) }
it 'presents the predefined milestone as a hash' do
expect(predefined_milestone.serializable_hash).to eq(
title: predefined_milestone.title,
name: predefined_milestone.name,
id: predefined_milestone.id
)
end
end
describe 'modules' do describe 'modules' do
context 'with a project' do context 'with a project' do
it_behaves_like 'AtomicInternalId' do it_behaves_like 'AtomicInternalId' do
...@@ -179,6 +191,16 @@ describe Milestone do ...@@ -179,6 +191,16 @@ describe Milestone do
end end
end end
describe '.predefined_id?' do
it 'returns true for a predefined Milestone ID' do
expect(Milestone.predefined_id?(described_class::Upcoming.id)).to be true
end
it 'returns false for a Milestone ID that is not predefined' do
expect(Milestone.predefined_id?(milestone.id)).to be false
end
end
describe '.order_by_name_asc' do describe '.order_by_name_asc' do
it 'sorts by name ascending' do it 'sorts by name ascending' do
milestone1 = create(:milestone, title: 'Foo') milestone1 = create(:milestone, title: 'Foo')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment