Commit 94c78fd6 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch 'georgekoltsov/project-migration-members' into 'master'

Add BulkImports Project Members Migration & Refactor Group Members Migration

See merge request gitlab-org/gitlab!78455
parents ede5fb94 3b671ccd
......@@ -3,8 +3,12 @@
module BulkImports
module Groups
module Graphql
module GetIterationsQuery
extend self
class GetIterationsQuery
attr_reader :context
def initialize(context:)
@context = context
end
def to_s
<<-'GRAPHQL'
......@@ -31,7 +35,7 @@ module BulkImports
GRAPHQL
end
def variables(context)
def variables
{
full_path: context.entity.source_full_path,
cursor: context.tracker.next_page,
......
......@@ -3,16 +3,18 @@
require 'spec_helper'
RSpec.describe BulkImports::Groups::Graphql::GetIterationsQuery do
it 'has a valid query' do
tracker = create(:bulk_import_tracker)
context = BulkImports::Pipeline::Context.new(tracker)
let_it_be(:tracker) { create(:bulk_import_tracker) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
subject(:query) { described_class.new(context: context) }
query = GraphQL::Query.new(
it 'has a valid query' do
parsed_query = GraphQL::Query.new(
GitlabSchema,
described_class.to_s,
variables: described_class.variables(context)
query.to_s,
variables: query.variables
)
result = GitlabSchema.static_validator.validate(query)
result = GitlabSchema.static_validator.validate(parsed_query)
expect(result[:errors]).to be_empty
end
......@@ -21,7 +23,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetIterationsQuery do
it 'returns data path' do
expected = %w[data group iterations nodes]
expect(described_class.data_path).to eq(expected)
expect(query.data_path).to eq(expected)
end
end
......@@ -29,7 +31,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetIterationsQuery do
it 'returns pagination information path' do
expected = %w[data group iterations page_info]
expect(described_class.page_info_path).to eq(expected)
expect(query.page_info_path).to eq(expected)
end
end
end
......@@ -7,7 +7,7 @@ RSpec.describe BulkImports::Groups::Stage do
[
[0, BulkImports::Groups::Pipelines::GroupPipeline],
[1, BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline],
[1, BulkImports::Groups::Pipelines::MembersPipeline],
[1, BulkImports::Common::Pipelines::MembersPipeline],
[1, BulkImports::Common::Pipelines::LabelsPipeline],
[1, BulkImports::Common::Pipelines::MilestonesPipeline],
[1, BulkImports::Common::Pipelines::BadgesPipeline],
......
......@@ -8,6 +8,7 @@ RSpec.describe BulkImports::Projects::Stage do
[0, BulkImports::Projects::Pipelines::ProjectPipeline],
[1, BulkImports::Projects::Pipelines::RepositoryPipeline],
[1, BulkImports::Projects::Pipelines::ProjectAttributesPipeline],
[1, BulkImports::Common::Pipelines::MembersPipeline],
[2, BulkImports::Common::Pipelines::LabelsPipeline],
[2, BulkImports::Common::Pipelines::MilestonesPipeline],
[2, BulkImports::Common::Pipelines::BadgesPipeline],
......
......@@ -5,15 +5,16 @@ module BulkImports
module Extractors
class GraphqlExtractor
def initialize(options = {})
@query = options[:query]
@query_klass = options[:query]
end
def extract(context)
client = graphql_client(context)
query = query_klass.new(context: context)
response = client.execute(
client.parse(query.to_s),
query.variables(context)
query.variables
).original_hash.deep_dup
BulkImports::Pipeline::ExtractedData.new(
......@@ -24,7 +25,7 @@ module BulkImports
private
attr_reader :query
attr_reader :query_klass
def graphql_client(context)
@graphql_client ||= BulkImports::Clients::Graphql.new(
......
# frozen_string_literal: true
module BulkImports
module Groups
module Common
module Graphql
module GetMembersQuery
extend self
class GetMembersQuery
attr_reader :context
def initialize(context:)
@context = context
end
def to_s
<<-'GRAPHQL'
<<-GRAPHQL
query($full_path: ID!, $cursor: String, $per_page: Int) {
group(fullPath: $full_path) {
group_members: groupMembers(relations: DIRECT, first: $per_page, after: $cursor) {
portable: #{context.entity.entity_type}(fullPath: $full_path) {
members: #{members_type}(relations: [DIRECT, INHERITED], first: $per_page, after: $cursor) {
page_info: pageInfo {
next_page: endCursor
has_next_page: hasNextPage
......@@ -32,7 +37,7 @@ module BulkImports
GRAPHQL
end
def variables(context)
def variables
{
full_path: context.entity.source_full_path,
cursor: context.tracker.next_page,
......@@ -40,10 +45,6 @@ module BulkImports
}
end
def base_path
%w[data group group_members]
end
def data_path
base_path << 'nodes'
end
......@@ -51,6 +52,20 @@ module BulkImports
def page_info_path
base_path << 'page_info'
end
private
def base_path
%w[data portable members]
end
def members_type
if context.entity.group?
'groupMembers'
else
'projectMembers'
end
end
end
end
end
......
# frozen_string_literal: true
module BulkImports
module Common
module Pipelines
class MembersPipeline
include Pipeline
transformer Common::Transformers::ProhibitedAttributesTransformer
transformer BulkImports::Groups::Transformers::MemberAttributesTransformer
def extract(context)
graphql_extractor.extract(context)
end
def load(_context, data)
return unless data
user_id = data[:user_id]
# Current user is already a member
return if user_id == current_user.id
user_membership = existing_user_membership(user_id)
# User is already a member with higher existing (inherited) membership
return if user_membership && user_membership[:access_level] >= data[:access_level]
# Create new membership for any other access level
portable.members.create!(data)
end
private
def graphql_extractor
@graphql_extractor ||= BulkImports::Common::Extractors::GraphqlExtractor
.new(query: BulkImports::Common::Graphql::GetMembersQuery)
end
def existing_user_membership(user_id)
members_finder.execute.find_by_user_id(user_id)
end
def members_finder
@members_finder ||= if context.entity.group?
::GroupMembersFinder.new(portable, current_user)
else
::MembersFinder.new(portable, current_user)
end
end
end
end
end
end
......@@ -3,8 +3,12 @@
module BulkImports
module Groups
module Graphql
module GetGroupQuery
extend self
class GetGroupQuery
attr_reader :context
def initialize(context:)
@context = context
end
def to_s
<<-'GRAPHQL'
......@@ -29,7 +33,7 @@ module BulkImports
GRAPHQL
end
def variables(context)
def variables
{ full_path: context.entity.source_full_path }
end
......
......@@ -3,8 +3,12 @@
module BulkImports
module Groups
module Graphql
module GetProjectsQuery
extend self
class GetProjectsQuery
attr_reader :context
def initialize(context:)
@context = context
end
def to_s
<<-'GRAPHQL'
......@@ -25,7 +29,7 @@ module BulkImports
GRAPHQL
end
def variables(context)
def variables
{
full_path: context.entity.source_full_path,
cursor: context.tracker.next_page,
......
# frozen_string_literal: true
module BulkImports
module Groups
module Pipelines
class MembersPipeline
include Pipeline
extractor BulkImports::Common::Extractors::GraphqlExtractor,
query: BulkImports::Groups::Graphql::GetMembersQuery
transformer Common::Transformers::ProhibitedAttributesTransformer
transformer BulkImports::Groups::Transformers::MemberAttributesTransformer
def load(context, data)
return unless data
# Current user is already a member
return if data['user_id'].to_i == context.current_user.id
context.group.members.create!(data)
end
end
end
end
end
......@@ -16,7 +16,7 @@ module BulkImports
stage: 1
},
members: {
pipeline: BulkImports::Groups::Pipelines::MembersPipeline,
pipeline: BulkImports::Common::Pipelines::MembersPipeline,
stage: 1
},
labels: {
......
......@@ -5,50 +5,35 @@ module BulkImports
module Transformers
class MemberAttributesTransformer
def transform(context, data)
data
.then { |data| add_user(data, context) }
.then { |data| add_access_level(data) }
.then { |data| add_author(data, context) }
end
private
def add_user(data, context)
user = find_user(data&.dig('user', 'public_email'))
access_level = data&.dig('access_level', 'integer_value')
return unless data
return unless user
return unless valid_access_level?(access_level)
cache_source_user_id(data, user, context)
data
.except('user')
.merge('user_id' => user.id)
{
user_id: user.id,
access_level: access_level,
created_at: data['created_at'],
updated_at: data['updated_at'],
expires_at: data['expires_at'],
created_by_id: context.current_user.id
}
end
private
def find_user(email)
return unless email
User.find_by_any_email(email, confirmed: true)
end
def add_access_level(data)
access_level = data&.dig('access_level', 'integer_value')
return unless valid_access_level?(access_level)
data.merge('access_level' => access_level)
end
def valid_access_level?(access_level)
Gitlab::Access
.options_with_owner
.value?(access_level)
end
def add_author(data, context)
return unless data
data.merge('created_by_id' => context.current_user.id)
Gitlab::Access.options_with_owner.value?(access_level)
end
def cache_source_user_id(data, user, context)
......
......@@ -3,9 +3,8 @@
module BulkImports
module Projects
module Graphql
module GetProjectQuery
extend Queryable
extend self
class GetProjectQuery
include Queryable
def to_s
<<-'GRAPHQL'
......
......@@ -3,9 +3,8 @@
module BulkImports
module Projects
module Graphql
module GetRepositoryQuery
extend Queryable
extend self
class GetRepositoryQuery
include Queryable
def to_s
<<-'GRAPHQL'
......
......@@ -3,9 +3,8 @@
module BulkImports
module Projects
module Graphql
module GetSnippetRepositoryQuery
extend Queryable
extend self
class GetSnippetRepositoryQuery
include Queryable
def to_s
<<-'GRAPHQL'
......@@ -27,7 +26,7 @@ module BulkImports
GRAPHQL
end
def variables(context)
def variables
{
full_path: context.entity.source_full_path,
cursor: context.tracker.next_page,
......
......@@ -4,7 +4,13 @@ module BulkImports
module Projects
module Graphql
module Queryable
def variables(context)
attr_reader :context
def initialize(context:)
@context = context
end
def variables
{ full_path: context.entity.source_full_path }
end
......
......@@ -19,6 +19,10 @@ module BulkImports
pipeline: BulkImports::Projects::Pipelines::ProjectAttributesPipeline,
stage: 1
},
members: {
pipeline: BulkImports::Common::Pipelines::MembersPipeline,
stage: 1
},
labels: {
pipeline: BulkImports::Common::Pipelines::LabelsPipeline,
stage: 2
......
......@@ -8,12 +8,15 @@ RSpec.describe BulkImports::Common::Extractors::GraphqlExtractor do
let(:response) { double(original_hash: { 'data' => { 'foo' => 'bar' }, 'page_info' => {} }) }
let(:options) do
{
query: double(
to_s: 'test',
variables: {},
data_path: %w[data foo],
page_info_path: %w[data page_info]
)
query:
double(
new: double(
to_s: 'test',
variables: {},
data_path: %w[data foo],
page_info_path: %w[data page_info]
)
)
}
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Common::Graphql::GetMembersQuery do
let(:entity) { create(:bulk_import_entity, :group_entity) }
let(:tracker) { create(:bulk_import_tracker, entity: entity) }
let(:context) { BulkImports::Pipeline::Context.new(tracker) }
subject(:query) { described_class.new(context: context) }
it 'has a valid query' do
parsed_query = GraphQL::Query.new(
GitlabSchema,
query.to_s,
variables: query.variables
)
result = GitlabSchema.static_validator.validate(parsed_query)
expect(result[:errors]).to be_empty
end
describe '#data_path' do
it 'returns data path' do
expected = %w[data portable members nodes]
expect(query.data_path).to eq(expected)
end
end
describe '#page_info_path' do
it 'returns pagination information path' do
expected = %w[data portable members page_info]
expect(query.page_info_path).to eq(expected)
end
end
describe '#to_s' do
context 'when entity is group' do
it 'queries group & group members' do
expect(query.to_s).to include('group')
expect(query.to_s).to include('groupMembers')
end
end
context 'when entity is project' do
let(:entity) { create(:bulk_import_entity, :project_entity) }
it 'queries project & project members' do
expect(query.to_s).to include('project')
expect(query.to_s).to include('projectMembers')
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Common::Pipelines::MembersPipeline do
let_it_be(:user) { create(:user) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
let_it_be(:member_user1) { create(:user, email: 'email1@email.com') }
let_it_be(:member_user2) { create(:user, email: 'email2@email.com') }
let_it_be(:member_data) do
{
user_id: member_user1.id,
created_by_id: member_user2.id,
access_level: 30,
created_at: '2020-01-01T00:00:00Z',
updated_at: '2020-01-01T00:00:00Z',
expires_at: nil
}
end
let(:parent) { create(:group) }
let(:tracker) { create(:bulk_import_tracker, entity: entity) }
let(:context) { BulkImports::Pipeline::Context.new(tracker) }
let(:members) { portable.members.map { |m| m.slice(:user_id, :access_level) } }
subject(:pipeline) { described_class.new(context) }
def extracted_data(email:, has_next_page: false)
data = {
'created_at' => '2020-01-01T00:00:00Z',
'updated_at' => '2020-01-02T00:00:00Z',
'expires_at' => nil,
'access_level' => {
'integer_value' => 30
},
'user' => {
'public_email' => email
}
}
page_info = {
'has_next_page' => has_next_page,
'next_page' => has_next_page ? 'cursor' : nil
}
BulkImports::Pipeline::ExtractedData.new(data: data, page_info: page_info)
end
shared_examples 'members import' do
before do
portable.members.delete_all
end
describe '#run' do
it 'creates memberships for existing users' do
first_page = extracted_data(email: member_user1.email, has_next_page: true)
last_page = extracted_data(email: member_user2.email)
allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor|
allow(extractor).to receive(:extract).and_return(first_page, last_page)
end
expect { pipeline.run }.to change(portable.members, :count).by(2)
expect(members).to contain_exactly(
{ user_id: member_user1.id, access_level: 30 },
{ user_id: member_user2.id, access_level: 30 }
)
end
end
describe '#load' do
it 'creates new membership' do
expect { subject.load(context, member_data) }.to change(portable.members, :count).by(1)
member = portable.members.find_by_user_id(member_user1.id)
expect(member.user).to eq(member_user1)
expect(member.created_by).to eq(member_user2)
expect(member.access_level).to eq(30)
expect(member.created_at).to eq('2020-01-01T00:00:00Z')
expect(member.updated_at).to eq('2020-01-01T00:00:00Z')
expect(member.expires_at).to eq(nil)
end
context 'when user_id is current user id' do
it 'does not create new membership' do
data = { user_id: user.id }
expect { pipeline.load(context, data) }.not_to change(portable.members, :count)
end
end
context 'when data is nil' do
it 'does not create new membership' do
expect { pipeline.load(context, nil) }.not_to change(portable.members, :count)
end
end
context 'when user membership already exists with the same access level' do
it 'does not create new membership' do
portable.members.create!(member_data)
expect { pipeline.load(context, member_data) }.not_to change(portable.members, :count)
end
end
context 'when portable is in a parent group' do
let(:tracker) { create(:bulk_import_tracker, entity: entity_with_parent) }
before do
parent.members.create!(member_data)
end
context 'when the same membership exists in parent group' do
it 'does not create new membership' do
expect { pipeline.load(context, member_data) }.not_to change(portable_with_parent.members, :count)
end
end
context 'when membership with higher access level exists in parent group' do
it 'creates new direct membership' do
data = member_data.merge(access_level: Gitlab::Access::MAINTAINER)
expect { pipeline.load(context, data) }.to change(portable_with_parent.members, :count)
member = portable_with_parent.members.find_by_user_id(member_user1.id)
expect(member.access_level).to eq(Gitlab::Access::MAINTAINER)
end
end
context 'when membership with lower access level exists in parent group' do
it 'does not create new membership' do
data = member_data.merge(access_level: Gitlab::Access::GUEST)
expect { pipeline.load(context, data) }.not_to change(portable_with_parent.members, :count)
end
end
end
end
end
context 'when importing to group' do
let(:portable) { create(:group) }
let(:portable_with_parent) { create(:group, parent: parent) }
let(:entity) { create(:bulk_import_entity, :group_entity, group: portable, bulk_import: bulk_import) }
let(:entity_with_parent) { create(:bulk_import_entity, :group_entity, group: portable_with_parent, bulk_import: bulk_import) }
include_examples 'members import'
end
context 'when importing to project' do
let(:portable) { create(:project) }
let(:portable_with_parent) { create(:project, namespace: parent) }
let(:entity) { create(:bulk_import_entity, :project_entity, project: portable, bulk_import: bulk_import) }
let(:entity_with_parent) { create(:bulk_import_entity, :project_entity, project: portable_with_parent, bulk_import: bulk_import) }
include_examples 'members import'
end
end
......@@ -3,14 +3,27 @@
require 'spec_helper'
RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
let_it_be(:tracker) { create(:bulk_import_tracker) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
subject(:query) { described_class.new(context: context) }
it 'has a valid query' do
parsed_query = GraphQL::Query.new(
GitlabSchema,
query.to_s,
variables: query.variables
)
result = GitlabSchema.static_validator.validate(parsed_query)
expect(result[:errors]).to be_empty
end
describe '#variables' do
it 'returns query variables based on entity information' do
entity = double(source_full_path: 'test', bulk_import: nil)
tracker = double(entity: entity)
context = BulkImports::Pipeline::Context.new(tracker)
expected = { full_path: entity.source_full_path }
expected = { full_path: tracker.entity.source_full_path }
expect(described_class.variables(context)).to eq(expected)
expect(subject.variables).to eq(expected)
end
end
......@@ -18,7 +31,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
it 'returns data path' do
expected = %w[data group]
expect(described_class.data_path).to eq(expected)
expect(subject.data_path).to eq(expected)
end
end
......@@ -26,7 +39,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetGroupQuery do
it 'returns pagination information path' do
expected = %w[data group page_info]
expect(described_class.page_info_path).to eq(expected)
expect(subject.page_info_path).to eq(expected)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Groups::Graphql::GetMembersQuery do
it 'has a valid query' do
tracker = create(:bulk_import_tracker)
context = BulkImports::Pipeline::Context.new(tracker)
query = GraphQL::Query.new(
GitlabSchema,
described_class.to_s,
variables: described_class.variables(context)
)
result = GitlabSchema.static_validator.validate(query)
expect(result[:errors]).to be_empty
end
describe '#data_path' do
it 'returns data path' do
expected = %w[data group group_members nodes]
expect(described_class.data_path).to eq(expected)
end
end
describe '#page_info_path' do
it 'returns pagination information path' do
expected = %w[data group group_members page_info]
expect(described_class.page_info_path).to eq(expected)
end
end
end
......@@ -3,25 +3,25 @@
require 'spec_helper'
RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
describe '#variables' do
it 'returns valid variables based on entity information' do
tracker = create(:bulk_import_tracker)
context = BulkImports::Pipeline::Context.new(tracker)
query = GraphQL::Query.new(
GitlabSchema,
described_class.to_s,
variables: described_class.variables(context)
)
result = GitlabSchema.static_validator.validate(query)
expect(result[:errors]).to be_empty
end
let_it_be(:tracker) { create(:bulk_import_tracker) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
subject(:query) { described_class.new(context: context) }
it 'has a valid query' do
parsed_query = GraphQL::Query.new(
GitlabSchema,
query.to_s,
variables: query.variables
)
result = GitlabSchema.static_validator.validate(parsed_query)
expect(result[:errors]).to be_empty
end
context 'with invalid variables' do
it 'raises an error' do
expect { GraphQL::Query.new(GitlabSchema, described_class.to_s, variables: 'invalid') }.to raise_error(ArgumentError)
end
context 'with invalid variables' do
it 'raises an error' do
expect { GraphQL::Query.new(GitlabSchema, subject.to_s, variables: 'invalid') }.to raise_error(ArgumentError)
end
end
......@@ -29,7 +29,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
it 'returns data path' do
expected = %w[data group projects nodes]
expect(described_class.data_path).to eq(expected)
expect(subject.data_path).to eq(expected)
end
end
......@@ -37,7 +37,7 @@ RSpec.describe BulkImports::Groups::Graphql::GetProjectsQuery do
it 'returns pagination information path' do
expected = %w[data group projects page_info]
expect(described_class.page_info_path).to eq(expected)
expect(subject.page_info_path).to eq(expected)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Groups::Pipelines::MembersPipeline do
let_it_be(:member_user1) { create(:user, email: 'email1@email.com') }
let_it_be(:member_user2) { create(:user, email: 'email2@email.com') }
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
let_it_be(:entity) { create(:bulk_import_entity, bulk_import: bulk_import, group: group) }
let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
subject { described_class.new(context) }
describe '#run' do
it 'maps existing users to the imported group' do
first_page = extracted_data(email: member_user1.email, has_next_page: true)
last_page = extracted_data(email: member_user2.email)
allow_next_instance_of(BulkImports::Common::Extractors::GraphqlExtractor) do |extractor|
allow(extractor)
.to receive(:extract)
.and_return(first_page, last_page)
end
expect { subject.run }.to change(GroupMember, :count).by(2)
members = group.members.map { |m| m.slice(:user_id, :access_level) }
expect(members).to contain_exactly(
{ user_id: member_user1.id, access_level: 30 },
{ user_id: member_user2.id, access_level: 30 }
)
end
end
describe '#load' do
it 'does nothing when there is no data' do
expect { subject.load(context, nil) }.not_to change(GroupMember, :count)
end
it 'creates the member' do
data = {
'user_id' => member_user1.id,
'created_by_id' => member_user2.id,
'access_level' => 30,
'created_at' => '2020-01-01T00:00:00Z',
'updated_at' => '2020-01-01T00:00:00Z',
'expires_at' => nil
}
expect { subject.load(context, data) }.to change(GroupMember, :count).by(1)
member = group.members.last
expect(member.user).to eq(member_user1)
expect(member.created_by).to eq(member_user2)
expect(member.access_level).to eq(30)
expect(member.created_at).to eq('2020-01-01T00:00:00Z')
expect(member.updated_at).to eq('2020-01-01T00:00:00Z')
expect(member.expires_at).to eq(nil)
end
context 'when user_id is current user id' do
it 'does not create new member' do
data = { 'user_id' => user.id }
expect { subject.load(context, data) }.not_to change(GroupMember, :count)
end
end
end
describe 'pipeline parts' do
it { expect(described_class).to include_module(BulkImports::Pipeline) }
it { expect(described_class).to include_module(BulkImports::Pipeline::Runner) }
it 'has extractors' do
expect(described_class.get_extractor)
.to eq(
klass: BulkImports::Common::Extractors::GraphqlExtractor,
options: {
query: BulkImports::Groups::Graphql::GetMembersQuery
}
)
end
it 'has transformers' do
expect(described_class.transformers)
.to contain_exactly(
{ klass: BulkImports::Common::Transformers::ProhibitedAttributesTransformer, options: nil },
{ klass: BulkImports::Groups::Transformers::MemberAttributesTransformer, options: nil }
)
end
end
def extracted_data(email:, has_next_page: false)
data = {
'created_at' => '2020-01-01T00:00:00Z',
'updated_at' => '2020-01-01T00:00:00Z',
'expires_at' => nil,
'access_level' => {
'integer_value' => 30
},
'user' => {
'public_email' => email
}
}
page_info = {
'has_next_page' => has_next_page,
'next_page' => has_next_page ? 'cursor' : nil
}
BulkImports::Pipeline::ExtractedData.new(data: data, page_info: page_info)
end
end
......@@ -9,7 +9,7 @@ RSpec.describe BulkImports::Groups::Stage do
[
[0, BulkImports::Groups::Pipelines::GroupPipeline],
[1, BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline],
[1, BulkImports::Groups::Pipelines::MembersPipeline],
[1, BulkImports::Common::Pipelines::MembersPipeline],
[1, BulkImports::Common::Pipelines::LabelsPipeline],
[1, BulkImports::Common::Pipelines::MilestonesPipeline],
[1, BulkImports::Common::Pipelines::BadgesPipeline],
......
......@@ -48,12 +48,12 @@ RSpec.describe BulkImports::Groups::Transformers::MemberAttributesTransformer do
data = member_data(email: user.email)
expect(subject.transform(context, data)).to eq(
'access_level' => 30,
'user_id' => user.id,
'created_by_id' => user.id,
'created_at' => '2020-01-01T00:00:00Z',
'updated_at' => '2020-01-01T00:00:00Z',
'expires_at' => nil
access_level: 30,
user_id: user.id,
created_by_id: user.id,
created_at: '2020-01-01T00:00:00Z',
updated_at: '2020-01-01T00:00:00Z',
expires_at: nil
)
end
......@@ -62,12 +62,12 @@ RSpec.describe BulkImports::Groups::Transformers::MemberAttributesTransformer do
data = member_data(email: secondary_email)
expect(subject.transform(context, data)).to eq(
'access_level' => 30,
'user_id' => user.id,
'created_by_id' => user.id,
'created_at' => '2020-01-01T00:00:00Z',
'updated_at' => '2020-01-01T00:00:00Z',
'expires_at' => nil
access_level: 30,
user_id: user.id,
created_by_id: user.id,
created_at: '2020-01-01T00:00:00Z',
updated_at: '2020-01-01T00:00:00Z',
expires_at: nil
)
end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe BulkImports::Projects::Graphql::GetProjectQuery do
let_it_be(:tracker) { create(:bulk_import_tracker) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
subject(:query) { described_class.new(context: context) }
it 'has a valid query' do
parsed_query = GraphQL::Query.new(
GitlabSchema,
query.to_s,
variables: query.variables
)
result = GitlabSchema.static_validator.validate(parsed_query)
expect(result[:errors]).to be_empty
end
it 'queries project based on source_full_path' do
expected = { full_path: tracker.entity.source_full_path }
expect(subject.variables).to eq(expected)
end
end
......@@ -3,19 +3,29 @@
require 'spec_helper'
RSpec.describe BulkImports::Projects::Graphql::GetRepositoryQuery do
describe 'query repository based on full_path' do
let(:entity) { double(source_full_path: 'test', bulk_import: nil) }
let(:tracker) { double(entity: entity) }
let(:context) { BulkImports::Pipeline::Context.new(tracker) }
let_it_be(:tracker) { create(:bulk_import_tracker) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
it 'returns project repository url' do
expect(described_class.to_s).to include('httpUrlToRepo')
end
subject(:query) { described_class.new(context: context) }
it 'queries project based on source_full_path' do
expected = { full_path: entity.source_full_path }
it 'has a valid query' do
parsed_query = GraphQL::Query.new(
GitlabSchema,
query.to_s,
variables: query.variables
)
result = GitlabSchema.static_validator.validate(parsed_query)
expect(described_class.variables(context)).to eq(expected)
end
expect(result[:errors]).to be_empty
end
it 'returns project repository url' do
expect(subject.to_s).to include('httpUrlToRepo')
end
it 'queries project based on source_full_path' do
expected = { full_path: tracker.entity.source_full_path }
expect(subject.variables).to eq(expected)
end
end
......@@ -3,56 +3,56 @@
require 'spec_helper'
RSpec.describe BulkImports::Projects::Graphql::GetSnippetRepositoryQuery do
describe 'query repository based on full_path' do
let_it_be(:entity) { create(:bulk_import_entity) }
let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
it 'has a valid query' do
query = GraphQL::Query.new(
GitlabSchema,
described_class.to_s,
variables: described_class.variables(context)
)
result = GitlabSchema.static_validator.validate(query)
expect(result[:errors]).to be_empty
end
let_it_be(:entity) { create(:bulk_import_entity) }
let_it_be(:tracker) { create(:bulk_import_tracker, entity: entity) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
it 'returns snippet httpUrlToRepo' do
expect(described_class.to_s).to include('httpUrlToRepo')
end
subject(:query) { described_class.new(context: context) }
it 'returns snippet createdAt' do
expect(described_class.to_s).to include('createdAt')
end
it 'has a valid query' do
parsed_query = GraphQL::Query.new(
GitlabSchema,
query.to_s,
variables: query.variables
)
result = GitlabSchema.static_validator.validate(parsed_query)
it 'returns snippet title' do
expect(described_class.to_s).to include('title')
end
expect(result[:errors]).to be_empty
end
describe '.variables' do
it 'queries project based on source_full_path and pagination' do
expected = { full_path: entity.source_full_path, cursor: nil, per_page: 500 }
it 'returns snippet httpUrlToRepo' do
expect(subject.to_s).to include('httpUrlToRepo')
end
expect(described_class.variables(context)).to eq(expected)
end
it 'returns snippet createdAt' do
expect(subject.to_s).to include('createdAt')
end
it 'returns snippet title' do
expect(subject.to_s).to include('title')
end
describe '.variables' do
it 'queries project based on source_full_path and pagination' do
expected = { full_path: entity.source_full_path, cursor: nil, per_page: 500 }
expect(subject.variables).to eq(expected)
end
end
describe '.data_path' do
it '.data_path returns data path' do
expected = %w[data project snippets nodes]
describe '.data_path' do
it '.data_path returns data path' do
expected = %w[data project snippets nodes]
expect(described_class.data_path).to eq(expected)
end
expect(subject.data_path).to eq(expected)
end
end
describe '.page_info_path' do
it '.page_info_path returns pagination information path' do
expected = %w[data project snippets page_info]
describe '.page_info_path' do
it '.page_info_path returns pagination information path' do
expected = %w[data project snippets page_info]
expect(described_class.page_info_path).to eq(expected)
end
expect(subject.page_info_path).to eq(expected)
end
end
end
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