Commit 324b2772 authored by Luke Duncalfe's avatar Luke Duncalfe

Export Design data in project import/export

https://gitlab.com/gitlab-org/gitlab-ee/issues/11090
parent b6689e03
# frozen_string_literal: true
module EE
module ExportHelper
extend ::Gitlab::Utils::Override
override :project_export_descriptions
def project_export_descriptions
super + [_('Design Management files and data')]
end
end
end
......@@ -2,6 +2,7 @@
module DesignManagement
class Design < ApplicationRecord
include Importable
include Noteable
include Gitlab::FileTypeDetection
include Gitlab::Utils::StrongMemoize
......@@ -16,7 +17,8 @@ module DesignManagement
# data
has_many :notes, as: :noteable, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
validates :project, :issue, :filename, presence: true
validates :project, :filename, presence: true
validates :issue, presence: true, unless: :importing?
validates :filename, uniqueness: { scope: :issue_id }
validate :validate_file_is_image
......
......@@ -2,6 +2,7 @@
module DesignManagement
class Version < ApplicationRecord
include Importable
include ShaAttribute
NotSameIssue = Class.new(StandardError)
......@@ -34,13 +35,13 @@ module DesignManagement
source: :design,
inverse_of: :versions
validates :designs, presence: true
validates :designs, presence: true, unless: :importing?
validates :sha, presence: true
validates :sha, uniqueness: { case_sensitive: false, scope: :issue_id }
# We are not validating the issue object as it incurs an extra query to fetch
# the record from the DB. Instead, we rely on the foreign key constraint to
# ensure referential integrity.
validates :issue_id, presence: true
validates :issue_id, presence: true, unless: :importing?
sha_attribute :sha
......
---
title: Allow Design Management files and data to be included in the project exporter/importer
merge_request: 14702
author:
type: added
# frozen_string_literal: true
module EE
module Gitlab
module ImportExport
module GroupProjectObjectBuilder
extend ::Gitlab::Utils::Override
extend ActiveSupport::Concern
private
override :where_clause_for_klass
def where_clause_for_klass
return attrs_to_arel(attributes.slice('filename')).and(table[:issue_id].eq(nil)) if design?
super
end
def design?
klass == DesignManagement::Design
end
end
end
end
end
# frozen_string_literal: true
module EE
module Gitlab
module ImportExport
module ProjectTreeRestorer
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
private
attr_accessor :project
override :remove_feature_dependent_sub_relations
def remove_feature_dependent_sub_relations(relation_item)
if relation_item.is_a?(Hash) && ::Feature.disabled?(:export_designs, project, default_enabled: true)
relation_item.except!('designs', 'design_versions')
end
end
end
end
end
end
......@@ -7,10 +7,16 @@ module EE
extend ActiveSupport::Concern
EE_OVERRIDES = {
design: 'DesignManagement::Design',
designs: 'DesignManagement::Design',
design_versions: 'DesignManagement::Version',
actions: 'DesignManagement::Action',
deploy_access_levels: 'ProtectedEnvironment::DeployAccessLevel',
unprotect_access_levels: 'ProtectedBranch::UnprotectAccessLevel'
}.freeze
EE_EXISTING_OBJECT_CHECK = %i[DesignManagement::Design].freeze
class_methods do
extend ::Gitlab::Utils::Override
......@@ -18,6 +24,11 @@ module EE
def overrides
super.merge(EE_OVERRIDES)
end
override :existing_object_check
def existing_object_check
super + EE_EXISTING_OBJECT_CHECK
end
end
end
end
......
......@@ -49,7 +49,6 @@ describe Security::PipelineVulnerabilitiesFinder do
let(:params) { { report_type: %w[dast] } }
it 'includes only dast' do
# binding.pry
expect(subject.count).to eq dast_count
end
end
......
This diff is collapsed.
......@@ -84,9 +84,9 @@ describe Mutations::DesignManagement::Delete do
end
end
it 'runs no more than 27 queries' do
it 'runs no more than 28 queries' do
filenames.each(&:present?) # ignore setup
# Queries: as of 2019-08-08
# Queries: as of 2019-08-28
# -------------
# 01. routing query
# 02. find project by id
......@@ -112,10 +112,11 @@ describe Mutations::DesignManagement::Delete do
# 23. create version with sha and issue
# 24. create design-version links
# 25. validate version.actions.present?
# 26. validate version.sha is unique
# 27. leave transaction 1
# 26. validate version.issue.present?
# 27. validate version.sha is unique
# 28. leave transaction 1
#
expect { run_mutation }.not_to exceed_query_limit(27)
expect { run_mutation }.not_to exceed_query_limit(28)
end
end
......
# frozen_string_literal: true
require 'spec_helper'
describe ExportHelper do
describe '#project_export_descriptions' do
it 'includes design management' do
expect(project_export_descriptions).to include('Design Management files and data')
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeRestorer do
include ImportExport::CommonUtil
using RSpec::Parameterized::TableSyntax
set(:user) { create(:user) }
set(:project) { create(:project, :builds_disabled, :issues_disabled, name: 'project', path: 'project') }
let(:shared) { project.import_export_shared }
let(:project_tree_restorer) { described_class.new(user: user, shared: shared, project: project) }
let(:restored_project_json) { project_tree_restorer.restore }
before do
allow(shared).to receive(:export_path).and_return('spec/fixtures/lib/gitlab/import_export/')
project_tree_restorer.instance_variable_set(:@path, 'ee/spec/fixtures/lib/gitlab/import_export/project.designs.json')
end
describe 'restoring design management data' do
context 'when the `export_designs` feature is enabled' do
before do
restored_project_json
end
it_behaves_like 'restores project correctly', issues: 2
it 'restores project associations correctly' do
expect(project.designs.size).to eq(7)
end
describe 'restores issue associations correctly' do
let(:issue) { project.issues.offset(index).first }
where(:index, :design_filenames, :version_shas) do
0 | %w[chirrido3.jpg jonathan_richman.jpg mariavontrap.jpeg] | %w[27702d08f5ee021ae938737f84e8fe7c38599e85 9358d1bac8ff300d3d2597adaa2572a20f7f8703 e1a4a501bcb42f291f84e5d04c8f927821542fb6]
1 | ['1 (1).jpeg', '2099743.jpg', 'a screenshot (1).jpg', 'chirrido3.jpg'] | %w[73f871b4c8c1d65c62c460635e023179fb53abc4 8587e78ab6bda3bc820a9f014c3be4a21ad4fcc8 c9b5f067f3e892122a4b12b0a25a8089192f3ac8]
end
with_them do
it do
expect(issue.designs.pluck(:filename)).to contain_exactly(*design_filenames)
expect(issue.design_versions.pluck(:sha)).to contain_exactly(*version_shas)
end
end
end
describe 'restores design version associations correctly' do
let(:project_designs) { project.designs.reorder(:filename, :issue_id) }
let(:design) { project_designs.offset(index).first }
where(:index, :version_shas) do
0 | %w[73f871b4c8c1d65c62c460635e023179fb53abc4 c9b5f067f3e892122a4b12b0a25a8089192f3ac8]
1 | %w[73f871b4c8c1d65c62c460635e023179fb53abc4]
2 | %w[c9b5f067f3e892122a4b12b0a25a8089192f3ac8]
3 | %w[27702d08f5ee021ae938737f84e8fe7c38599e85 9358d1bac8ff300d3d2597adaa2572a20f7f8703 e1a4a501bcb42f291f84e5d04c8f927821542fb6]
4 | %w[8587e78ab6bda3bc820a9f014c3be4a21ad4fcc8]
5 | %w[27702d08f5ee021ae938737f84e8fe7c38599e85 e1a4a501bcb42f291f84e5d04c8f927821542fb6]
6 | %w[27702d08f5ee021ae938737f84e8fe7c38599e85]
end
with_them do
it do
expect(design.versions.pluck(:sha)).to contain_exactly(*version_shas)
end
end
end
end
context 'when the `export_designs` feature is disabled' do
before do
stub_feature_flags(export_designs: false)
restored_project_json
end
it_behaves_like 'restores project correctly', issues: 2
it 'does not restore any Designs' do
expect(DesignManagement::Design).not_to exist
end
it 'does not restore any Versions' do
expect(DesignManagement::Version.exists?).to be false
end
it 'does not restore any DesignVersions' do
expect(DesignManagement::Action.exists?).to be false
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeSaver do
describe 'saves the project tree into a json object' do
set(:user) { create(:user) }
set(:project) { create(:project) }
set(:issue) { create(:issue, project: project) }
set(:design) { create(:design, :with_file, versions_count: 2, issue: issue) }
set(:note) { create(:diff_note_on_design, noteable: design, project: project, author: user) }
set(:note2) { create(:note, noteable: issue, project: project, author: user) }
let(:shared) { project.import_export_shared }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec_ee" }
let(:project_tree_saver) { described_class.new(project: project, current_user: user, shared: shared) }
let(:saved_project_json) do
project_tree_saver.save
project_json(project_tree_saver.full_path)
end
before do
project.add_maintainer(user)
end
after do
FileUtils.rm_rf(export_path)
end
it 'saves successfully' do
expect(project_tree_saver.save).to be true
end
describe 'the designs json' do
let(:issue_json) { saved_project_json['issues'].first }
it 'saves issue.designs correctly' do
expect(issue_json['designs'].size).to eq(1)
end
it 'saves issue.design_versions correctly' do
actions = issue_json['design_versions'].map do |v|
v['actions']
end.flatten
expect(issue_json['design_versions'].size).to eq(2)
expect(actions.size).to eq(2)
actions.each do |action|
expect(action['design']).to be_present
end
end
end
end
def project_json(filename)
JSON.parse(IO.read(filename))
end
end
......@@ -129,3 +129,5 @@ module Gitlab
end
end
end
Gitlab::ImportExport::GroupProjectObjectBuilder.prepend_if_ee('EE::Gitlab::ImportExport::GroupProjectObjectBuilder')
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