Commit a563eba7 authored by Rémy Coutable's avatar Rémy Coutable

Merge branch '212566-foss-design-management-graphql-backend' into 'master'

Move Design Management GraphQL classes into FOSS

See merge request gitlab-org/gitlab!31462
parents f59106fc f7af6ee2
......@@ -85,6 +85,14 @@ module Types
field :task_completion_status, Types::TaskCompletionStatus, null: false,
description: 'Task completion status of the issue'
field :designs, Types::DesignManagement::DesignCollectionType, null: true,
method: :design_collection,
deprecated: { reason: 'Use `designCollection`', milestone: '12.2' },
description: 'The designs associated with this issue'
field :design_collection, Types::DesignManagement::DesignCollectionType, null: true,
description: 'Collection of design images associated with this issue'
end
end
......
......@@ -42,6 +42,8 @@ module Types
mount_mutation Mutations::Snippets::Create
mount_mutation Mutations::Snippets::MarkAsSpam
mount_mutation Mutations::JiraImport::Start
mount_mutation Mutations::DesignManagement::Upload, calls_gitaly: true
mount_mutation Mutations::DesignManagement::Delete, calls_gitaly: true
end
end
......
......@@ -17,6 +17,8 @@ module Types
Types::MergeRequestType
when Snippet
Types::SnippetType
when ::DesignManagement::Design
Types::DesignManagement::DesignType
else
raise "Unknown GraphQL type for #{object}"
end
......@@ -25,5 +27,3 @@ module Types
end
end
end
Types::Notes::NoteableType.extend_if_ee('::EE::Types::Notes::NoteableType')
......@@ -6,11 +6,9 @@ module Types
description 'Check permissions for the current user on a issue'
graphql_name 'IssuePermissions'
abilities :read_issue, :admin_issue,
:update_issue, :create_note,
:reopen_issue
abilities :read_issue, :admin_issue, :update_issue, :reopen_issue,
:read_design, :create_design, :destroy_design,
:create_note
end
end
end
Types::PermissionTypes::Issue.prepend_if_ee('::EE::Types::PermissionTypes::Issue')
......@@ -17,7 +17,7 @@ module Types
:admin_wiki, :admin_project, :update_pages,
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
:create_pages, :destroy_pages, :read_pages_content, :admin_operations,
:read_merge_request
:read_merge_request, :read_design, :create_design, :destroy_design
permission_field :create_snippet
......@@ -27,5 +27,3 @@ module Types
end
end
end
Types::PermissionTypes::Project.prepend_if_ee('EE::Types::PermissionTypes::Project')
......@@ -4,6 +4,9 @@ module Types
class QueryType < ::Types::BaseObject
graphql_name 'Query'
# The design management context object needs to implement #issue
DesignManagementObject = Struct.new(:issue)
field :project, Types::ProjectType,
null: true,
resolver: Resolvers::ProjectResolver,
......@@ -40,9 +43,17 @@ module Types
resolver: Resolvers::SnippetsResolver,
description: 'Find Snippets visible to the current user'
field :design_management, Types::DesignManagementType,
null: false,
description: 'Fields related to design management'
field :echo, GraphQL::STRING_TYPE, null: false,
description: 'Text to echo back',
resolver: Resolvers::EchoResolver
def design_management
DesignManagementObject.new(nil)
end
end
end
......
......@@ -5,6 +5,7 @@ module Types
value 'COMMIT', value: 'Commit', description: 'A Commit'
value 'ISSUE', value: 'Issue', description: 'An Issue'
value 'MERGEREQUEST', value: 'MergeRequest', description: 'A MergeRequest'
value 'DESIGN', value: 'DesignManagement::Design', description: 'A Design'
end
end
......
......@@ -13,14 +13,6 @@ module EE
description: 'Weight of the issue',
resolve: -> (obj, _args, _ctx) { obj.supports_weight? ? obj.weight : nil }
field :designs, ::Types::DesignManagement::DesignCollectionType, null: true,
method: :design_collection,
deprecated: { reason: 'Use `designCollection`', milestone: '12.2' },
description: 'The designs associated with this issue'
field :design_collection, ::Types::DesignManagement::DesignCollectionType, null: true,
description: 'Collection of design images associated with this issue'
field :health_status,
::Types::HealthStatusEnum,
null: true,
......
......@@ -6,8 +6,6 @@ module EE
extend ActiveSupport::Concern
prepended do
mount_mutation ::Mutations::DesignManagement::Upload, calls_gitaly: true
mount_mutation ::Mutations::DesignManagement::Delete, calls_gitaly: true
mount_mutation ::Mutations::Issues::SetWeight
mount_mutation ::Mutations::EpicTree::Reorder
mount_mutation ::Mutations::Epics::Update
......
# frozen_string_literal: true
module EE
module Types
module Notes
module NoteableType
extend ::Gitlab::Utils::Override
override :resolve_type
def resolve_type(object, context)
case object
when DesignManagement::Design
::Types::DesignManagement::DesignType
else
super
end
end
end
end
end
end
# frozen_string_literal: true
module EE
module Types
module PermissionTypes
module Issue
extend ActiveSupport::Concern
prepended do
abilities :read_design, :create_design, :destroy_design
end
end
end
end
end
# frozen_string_literal: true
module EE
module Types
module PermissionTypes
module Project
extend ActiveSupport::Concern
prepended do
abilities :read_design, :create_design, :destroy_design
end
end
end
end
end
......@@ -5,9 +5,6 @@ module EE
module QueryType
extend ActiveSupport::Concern
# The design management context object needs to implement #issue
DesignManagementObject = Struct.new(:issue)
prepended do
field :vulnerabilities,
::Types::VulnerabilityType.connection_type,
......@@ -21,10 +18,6 @@ module EE
description: "Number of vulnerabilities per severity level, per day, for the projects on the current user's instance security dashboard",
resolver: ::Resolvers::VulnerabilitiesHistoryResolver
field :design_management, ::Types::DesignManagementType,
null: false,
description: 'Fields related to design management'
field :geo_node, ::Types::Geo::GeoNodeType,
null: true,
resolver: ::Resolvers::Geo::GeoNodeResolver,
......@@ -34,10 +27,6 @@ module EE
null: true,
resolver: ::Resolvers::InstanceSecurityDashboardResolver,
description: 'Fields related to Instance Security Dashboard'
def design_management
DesignManagementObject.new(nil)
end
end
end
end
......
......@@ -6,7 +6,6 @@ module EE
extend ActiveSupport::Concern
prepended do
value 'DESIGN', value: 'DesignManagement::Design', description: 'A Design'
value 'EPIC', value: 'Epic', description: 'An Epic'
end
end
......
# frozen_string_literal: true
require 'spec_helper'
describe Types::Notes::NoteableType do
describe ".resolve_type" do
it 'knows the correct type for EE objects' do
expect(described_class.resolve_type(build(:design), {})).to eq(Types::DesignManagement::DesignType)
end
end
end
......@@ -7,9 +7,5 @@ describe GitlabSchema.types['Issue'] do
it { expect(described_class).to have_graphql_field(:weight) }
it { expect(described_class).to have_graphql_field(:designs) }
it { expect(described_class).to have_graphql_field(:design_collection) }
it { expect(described_class).to have_graphql_field(:health_status) }
end
# frozen_string_literal: true
require 'spec_helper'
describe Types::PermissionTypes::Issue do
it "exposes design permissions" do
expected_permissions = [
:read_design, :create_design, :destroy_design
]
expected_permissions.each do |permission|
expect(described_class).to have_graphql_field(permission)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Types::PermissionTypes::Project do
it "exposes design permissions" do
expected_permissions = [
:read_design, :create_design, :destroy_design
]
expected_permissions.each do |permission|
expect(described_class).to have_graphql_field(permission)
end
end
end
......@@ -5,7 +5,6 @@ require 'spec_helper'
describe GitlabSchema.types['Query'] do
specify do
expect(described_class).to have_graphql_fields(
:design_management,
:geo_node,
:vulnerabilities,
:instance_security_dashboard,
......
......@@ -6,7 +6,6 @@ describe 'getting project information' do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:design_todo) { create(:todo, user: current_user, target: create(:design)) }
let_it_be(:epic_todo) { create(:todo, user: current_user, target: create(:epic)) }
let(:fields) do
<<~QUERY
......@@ -29,7 +28,6 @@ describe 'getting project information' do
it 'returns Todos for all target types' do
is_expected.to include(
a_hash_including('targetType' => 'DESIGN'),
a_hash_including('targetType' => 'EPIC')
)
end
......
......@@ -5,94 +5,6 @@ require 'spec_helper'
describe 'Query' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:developer) { create(:user) }
let(:current_user) { developer }
describe '.designManagement' do
include DesignManagementTestHelpers
let_it_be(:version) { create(:design_version, issue: issue) }
let_it_be(:design) { version.designs.first }
let(:query_result) { graphql_data.dig(*path) }
let(:query) { graphql_query_for(:design_management, nil, dm_fields) }
before do
enable_design_management
project.add_developer(developer)
post_graphql(query, current_user: current_user)
end
shared_examples 'a query that needs authorization' do
context 'the current user is not able to read designs' do
let(:current_user) { create(:user) }
it 'does not retrieve the record' do
expect(query_result).to be_nil
end
it 'raises an error' do
expect(graphql_errors).to include(
a_hash_including('message' => a_string_matching(%r{you don't have permission}))
)
end
end
end
describe '.version' do
let(:path) { %w[designManagement version] }
let(:dm_fields) do
query_graphql_field(:version, { 'id' => global_id_of(version) }, 'id sha')
end
it_behaves_like 'a working graphql query'
it_behaves_like 'a query that needs authorization'
context 'the current user is able to read designs' do
it 'fetches the expected data' do
expect(query_result).to eq('id' => global_id_of(version), 'sha' => version.sha)
end
end
end
describe '.designAtVersion' do
let_it_be(:design_at_version) do
::DesignManagement::DesignAtVersion.new(design: design, version: version)
end
let(:path) { %w[designManagement designAtVersion] }
let(:dm_fields) do
query_graphql_field(:design_at_version, { 'id' => global_id_of(design_at_version) }, <<~FIELDS)
id
filename
version { id sha }
design { id }
issue { title iid }
project { id fullPath }
FIELDS
end
it_behaves_like 'a working graphql query'
it_behaves_like 'a query that needs authorization'
context 'the current user is able to read designs' do
it 'fetches the expected data, including the correct associations' do
expect(query_result).to eq(
'id' => global_id_of(design_at_version),
'filename' => design_at_version.design.filename,
'version' => { 'id' => global_id_of(version), 'sha' => version.sha },
'design' => { 'id' => global_id_of(design) },
'issue' => { 'title' => issue.title, 'iid' => issue.iid.to_s },
'project' => { 'id' => global_id_of(project), 'fullPath' => project.full_path }
)
end
end
end
end
describe '.vulnerabilitiesCountByDayAndSeverity' do
let(:query_result) { graphql_data.dig('vulnerabilitiesCountByDayAndSeverity', 'nodes') }
......@@ -117,8 +29,10 @@ describe 'Query' do
it "fetches historical vulnerability data from the start date to the end date for projects on the current user's instance security dashboard" do
Timecop.freeze(Time.zone.parse('2019-10-31')) do
project = create(:project)
current_user = create(:user)
current_user.security_dashboard_projects << project
project.add_developer(developer)
project.add_developer(current_user)
create(:vulnerability, :critical, created_at: 15.days.ago, dismissed_at: 10.days.ago, project: project)
create(:vulnerability, :high, created_at: 15.days.ago, dismissed_at: 11.days.ago, project: project)
......
......@@ -76,24 +76,6 @@ describe API::Internal::Base do
let_it_be(:key) { create(:key, user: user) }
let(:secret_token) { Gitlab::Shell.secret_token }
context "for design repositories" do
let_it_be(:project) { create(:project) }
let(:gl_repository) { ::Gitlab::GlRepository::DESIGN.identifier_for_container(project) }
it "does not allow access" do
post(api("/internal/allowed"),
params: {
key_id: key.id,
project: project.full_path,
gl_repository: gl_repository,
secret_token: secret_token,
protocol: 'ssh'
})
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
context "project alias" do
let(:project) { create(:project, :public, :repository) }
let(:project_alias) { create(:project_alias, project: project) }
......
......@@ -26,12 +26,6 @@ describe API::Todos do
create(:todo, project: nil, group: new_group, author: author_1, user: user, target: new_epic)
end
shared_examples 'an endpoint that responds with success' do
specify do
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'when there is an Epic Todo' do
let!(:epic_todo) { create_todo_for_new_epic }
......@@ -39,7 +33,9 @@ describe API::Todos do
get api('/todos', personal_access_token: pat)
end
it_behaves_like 'an endpoint that responds with success'
specify do
expect(response).to have_gitlab_http_status(:ok)
end
it 'avoids N+1 queries', :request_store do
create_todo_for_new_epic
......
......@@ -17,6 +17,14 @@ describe Mutations::DesignManagement::Delete do
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
before do
# TODO these tests are being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the services have not yet been moved,
# which are used by this mutation.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
stub_const('Errors', Gitlab::Graphql::Errors, transfer_nested_constants: true)
end
......
......@@ -37,6 +37,14 @@ describe Mutations::DesignManagement::Upload do
context "when the feature is available" do
before do
# TODO these tests are being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the services have not yet been moved,
# which are used by this mutation.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
enable_design_management
end
......
......@@ -14,7 +14,8 @@ describe GitlabSchema.types['Issue'] do
it 'has specific fields' do
fields = %i[iid title description state reference author assignees participants labels milestone due_date
confidential discussion_locked upvotes downvotes user_notes_count web_path web_url relative_position
subscribed time_estimate total_time_spent closed_at created_at updated_at task_completion_status]
subscribed time_estimate total_time_spent closed_at created_at updated_at task_completion_status
designs design_collection]
fields.each do |field_name|
expect(described_class).to have_graphql_field(field_name)
......
......@@ -8,6 +8,7 @@ describe Types::Notes::NoteableType do
it 'knows the correct type for objects' do
expect(described_class.resolve_type(build(:issue), {})).to eq(Types::IssueType)
expect(described_class.resolve_type(build(:merge_request), {})).to eq(Types::MergeRequestType)
expect(described_class.resolve_type(build(:design), {})).to eq(Types::DesignManagement::DesignType)
end
end
end
......@@ -5,8 +5,9 @@ require 'spec_helper'
describe Types::PermissionTypes::Issue do
it do
expected_permissions = [
:read_issue, :admin_issue, :update_issue,
:create_note, :reopen_issue
:read_issue, :admin_issue, :update_issue, :reopen_issue,
:read_design, :create_design, :destroy_design,
:create_note
]
expected_permissions.each do |permission|
......
......@@ -13,7 +13,7 @@ describe Types::PermissionTypes::Project do
:create_merge_request_from, :create_wiki, :push_code, :create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages, :admin_remote_mirror, :create_label,
:update_wiki, :destroy_wiki, :create_pages, :destroy_pages, :read_pages_content,
:read_merge_request
:read_merge_request, :read_design, :create_design, :destroy_design
]
expected_permissions.each do |permission|
......
......@@ -8,7 +8,7 @@ describe GitlabSchema.types['Query'] do
end
it 'has the expected fields' do
expected_fields = %i[project namespace group echo metadata current_user snippets]
expected_fields = %i[project namespace group echo metadata current_user snippets design_management]
expect(described_class).to have_graphql_fields(*expected_fields).at_least
end
......
......@@ -21,16 +21,7 @@ describe Gitlab::GitAccessDesign do
end
context 'when the user is allowed to manage designs' do
# TODO This test is being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the policies have not yet been moved
# which means that although the `GitAccessDesign` class has moved, the
# user will always be denied access in FOSS.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
it do
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
is_expected.to be_a(::Gitlab::GitAccessResult::Success)
end
end
......
......@@ -9,6 +9,7 @@ describe 'Query current user todos' do
let_it_be(:commit_todo) { create(:on_commit_todo, user: current_user, project: create(:project, :repository)) }
let_it_be(:issue_todo) { create(:todo, user: current_user, target: create(:issue)) }
let_it_be(:merge_request_todo) { create(:todo, user: current_user, target: create(:merge_request)) }
let_it_be(:design_todo) { create(:todo, user: current_user, target: create(:design)) }
let(:fields) do
<<~QUERY
......@@ -34,7 +35,8 @@ describe 'Query current user todos' do
is_expected.to include(
a_hash_including('id' => commit_todo.to_global_id.to_s),
a_hash_including('id' => issue_todo.to_global_id.to_s),
a_hash_including('id' => merge_request_todo.to_global_id.to_s)
a_hash_including('id' => merge_request_todo.to_global_id.to_s),
a_hash_including('id' => design_todo.to_global_id.to_s)
)
end
......@@ -42,7 +44,8 @@ describe 'Query current user todos' do
is_expected.to include(
a_hash_including('targetType' => 'COMMIT'),
a_hash_including('targetType' => 'ISSUE'),
a_hash_including('targetType' => 'MERGEREQUEST')
a_hash_including('targetType' => 'MERGEREQUEST'),
a_hash_including('targetType' => 'DESIGN')
)
end
end
......@@ -29,6 +29,14 @@ describe "deleting designs" do
end
before do
# TODO these tests are being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the services have not yet been moved,
# which are used by this mutation.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
enable_design_management
project.add_developer(developer)
......
......@@ -24,6 +24,14 @@ describe "uploading designs" do
let(:mutation_response) { graphql_mutation_response(:design_management_upload) }
before do
# TODO these tests are being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the services have not yet been moved,
# which are used by this mutation.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
enable_design_management
project.add_developer(current_user)
......
......@@ -6,6 +6,16 @@ describe 'Getting designs related to an issue' do
include GraphqlHelpers
include DesignManagementTestHelpers
before_all do
# TODO these tests are being temporarily skipped unless run in EE,
# as we are in the process of moving Design Management to FOSS in 13.0
# in steps. In the current step the services have not yet been moved,
# which are used by the `:with_smaller_image_versions` factory trait.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283.
skip 'See https://gitlab.com/gitlab-org/gitlab/-/issues/212566#note_327724283' unless Gitlab.ee?
end
let_it_be(:design) { create(:design, :with_smaller_image_versions, versions_count: 1) }
let_it_be(:current_user) { design.project.owner }
let(:design_query) do
......
# frozen_string_literal: true
require 'spec_helper'
describe 'Query' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:developer) { create(:user) }
let(:current_user) { developer }
describe '.designManagement' do
include DesignManagementTestHelpers
let_it_be(:version) { create(:design_version, issue: issue) }
let_it_be(:design) { version.designs.first }
let(:query_result) { graphql_data.dig(*path) }
let(:query) { graphql_query_for(:design_management, nil, dm_fields) }
before do
enable_design_management
project.add_developer(developer)
post_graphql(query, current_user: current_user)
end
shared_examples 'a query that needs authorization' do
context 'the current user is not able to read designs' do
let(:current_user) { create(:user) }
it 'does not retrieve the record' do
expect(query_result).to be_nil
end
it 'raises an error' do
expect(graphql_errors).to include(
a_hash_including('message' => a_string_matching(%r{you don't have permission}))
)
end
end
end
describe '.version' do
let(:path) { %w[designManagement version] }
let(:dm_fields) do
query_graphql_field(:version, { 'id' => global_id_of(version) }, 'id sha')
end
it_behaves_like 'a working graphql query'
it_behaves_like 'a query that needs authorization'
context 'the current user is able to read designs' do
it 'fetches the expected data' do
expect(query_result).to eq('id' => global_id_of(version), 'sha' => version.sha)
end
end
end
describe '.designAtVersion' do
let_it_be(:design_at_version) do
::DesignManagement::DesignAtVersion.new(design: design, version: version)
end
let(:path) { %w[designManagement designAtVersion] }
let(:dm_fields) do
query_graphql_field(:design_at_version, { 'id' => global_id_of(design_at_version) }, <<~FIELDS)
id
filename
version { id sha }
design { id }
issue { title iid }
project { id fullPath }
FIELDS
end
it_behaves_like 'a working graphql query'
it_behaves_like 'a query that needs authorization'
context 'the current user is able to read designs' do
it 'fetches the expected data, including the correct associations' do
expect(query_result).to eq(
'id' => global_id_of(design_at_version),
'filename' => design_at_version.design.filename,
'version' => { 'id' => global_id_of(version), 'sha' => version.sha },
'design' => { 'id' => global_id_of(design) },
'issue' => { 'title' => issue.title, 'iid' => issue.iid.to_s },
'project' => { 'id' => global_id_of(project), 'fullPath' => project.full_path }
)
end
end
end
end
end
......@@ -917,6 +917,23 @@ describe API::Internal::Base do
expect(json_response['status']).to be_falsy
end
end
context 'for design repositories' do
let(:gl_repository) { Gitlab::GlRepository::DESIGN.identifier_for_container(project) }
it 'does not allow access' do
post(api('/internal/allowed'),
params: {
key_id: key.id,
project: project.full_path,
gl_repository: gl_repository,
secret_token: secret_token,
protocol: 'ssh'
})
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
end
describe 'POST /internal/post_receive', :clean_gitlab_redis_shared_state do
......
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