Commit 33cf8eda authored by Bob Van Landuyt's avatar Bob Van Landuyt 👶

Port changes for design management to CE

This ports the changes from
gitlab-ee!10462/
to CE
parent ee4ccd31
...@@ -79,6 +79,7 @@ gem 'rack-cors', '~> 1.0.0', require: 'rack/cors' ...@@ -79,6 +79,7 @@ gem 'rack-cors', '~> 1.0.0', require: 'rack/cors'
# GraphQL API # GraphQL API
gem 'graphql', '~> 1.8.0' gem 'graphql', '~> 1.8.0'
gem 'graphiql-rails', '~> 1.4.10' gem 'graphiql-rails', '~> 1.4.10'
gem 'apollo_upload_server', '~> 2.0.0.beta3'
# Disable strong_params so that Mash does not respond to :permitted? # Disable strong_params so that Mash does not respond to :permitted?
gem 'hashie-forbidden_attributes' gem 'hashie-forbidden_attributes'
......
...@@ -52,6 +52,9 @@ GEM ...@@ -52,6 +52,9 @@ GEM
public_suffix (>= 2.0.2, < 4.0) public_suffix (>= 2.0.2, < 4.0)
aes_key_wrap (1.0.1) aes_key_wrap (1.0.1)
akismet (2.0.0) akismet (2.0.0)
apollo_upload_server (2.0.0.beta.3)
graphql (>= 1.8)
rails (>= 4.2)
arel (8.0.0) arel (8.0.0)
asana (0.8.1) asana (0.8.1)
faraday (~> 0.9) faraday (~> 0.9)
...@@ -988,6 +991,7 @@ DEPENDENCIES ...@@ -988,6 +991,7 @@ DEPENDENCIES
acts-as-taggable-on (~> 6.0) acts-as-taggable-on (~> 6.0)
addressable (~> 2.5.2) addressable (~> 2.5.2)
akismet (~> 2.0) akismet (~> 2.0)
apollo_upload_server (~> 2.0.0.beta3)
asana (~> 0.8.1) asana (~> 0.8.1)
asciidoctor (~> 1.5.8) asciidoctor (~> 1.5.8)
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
......
...@@ -44,6 +44,12 @@ module Resolvers ...@@ -44,6 +44,12 @@ module Resolvers
alias_method :project, :object alias_method :project, :object
def resolve(**args) def resolve(**args)
# The project could have been loaded in batch by `BatchLoader`.
# At this point we need the `id` of the project to query for issues, so
# make sure it's loaded and not `nil` before continueing.
project.sync if project.respond_to?(:sync)
return Issue.none if project.nil?
# Will need to be be made group & namespace aware with # Will need to be be made group & namespace aware with
# https://gitlab.com/gitlab-org/gitlab-ce/issues/54520 # https://gitlab.com/gitlab-org/gitlab-ce/issues/54520
args[:project_id] = project.id args[:project_id] = project.id
......
...@@ -1065,6 +1065,19 @@ class Repository ...@@ -1065,6 +1065,19 @@ class Repository
blob.data blob.data
end end
def create_if_not_exists
return if exists?
raw.create_repository
after_create
end
def blobs_metadata(paths, ref = 'HEAD')
references = Array.wrap(paths).map { |path| [ref, path] }
Gitlab::Git::Blob.batch_metadata(raw, references).map { |raw_blob| Blob.decorate(raw_blob) }
end
private private
# TODO Generice finder, later split this on finders by Ref or Oid # TODO Generice finder, later split this on finders by Ref or Oid
......
...@@ -21,8 +21,10 @@ class PostReceive ...@@ -21,8 +21,10 @@ class PostReceive
if repo_type.wiki? if repo_type.wiki?
process_wiki_changes(post_received) process_wiki_changes(post_received)
else elsif repo_type.project?
process_project_changes(post_received) process_project_changes(post_received)
else
# Other repos don't have hooks for now
end end
end end
......
...@@ -118,6 +118,12 @@ module Gitlab ...@@ -118,6 +118,12 @@ module Gitlab
gitaly_repository_client.exists? gitaly_repository_client.exists?
end end
def create_repository
wrapped_gitaly_errors do
gitaly_repository_client.create_repository
end
end
# Returns an Array of branch names # Returns an Array of branch names
# sorted by name ASC # sorted by name ASC
def branch_names def branch_names
......
...@@ -85,7 +85,7 @@ module Gitlab ...@@ -85,7 +85,7 @@ module Gitlab
check_push_access! check_push_access!
end end
::Gitlab::GitAccessResult::Success.new(console_messages: check_for_console_messages(cmd)) success_result(cmd)
end end
def guest_can_download_code? def guest_can_download_code?
...@@ -365,6 +365,10 @@ module Gitlab ...@@ -365,6 +365,10 @@ module Gitlab
protected protected
def success_result(cmd)
::Gitlab::GitAccessResult::Success.new(console_messages: check_for_console_messages(cmd))
end
def changes_list def changes_list
@changes_list ||= Gitlab::ChangesList.new(changes == ANY ? [] : changes) @changes_list ||= Gitlab::ChangesList.new(changes == ANY ? [] : changes)
end end
......
...@@ -4,104 +4,119 @@ describe Resolvers::IssuesResolver do ...@@ -4,104 +4,119 @@ describe Resolvers::IssuesResolver do
include GraphqlHelpers include GraphqlHelpers
let(:current_user) { create(:user) } let(:current_user) { create(:user) }
set(:project) { create(:project) }
set(:issue1) { create(:issue, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago) }
set(:issue2) { create(:issue, project: project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago) }
set(:label1) { create(:label, project: project) }
set(:label2) { create(:label, project: project) }
before do
project.add_developer(current_user)
create(:label_link, label: label1, target: issue1)
create(:label_link, label: label1, target: issue2)
create(:label_link, label: label2, target: issue2)
end
describe '#resolve' do
it 'finds all issues' do
expect(resolve_issues).to contain_exactly(issue1, issue2)
end
it 'filters by state' do context "with a project" do
expect(resolve_issues(state: 'opened')).to contain_exactly(issue1) set(:project) { create(:project) }
expect(resolve_issues(state: 'closed')).to contain_exactly(issue2) set(:issue1) { create(:issue, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago) }
set(:issue2) { create(:issue, project: project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago) }
set(:label1) { create(:label, project: project) }
set(:label2) { create(:label, project: project) }
before do
project.add_developer(current_user)
create(:label_link, label: label1, target: issue1)
create(:label_link, label: label1, target: issue2)
create(:label_link, label: label2, target: issue2)
end end
it 'filters by labels' do describe '#resolve' do
expect(resolve_issues(label_name: [label1.title])).to contain_exactly(issue1, issue2) it 'finds all issues' do
expect(resolve_issues(label_name: [label1.title, label2.title])).to contain_exactly(issue2) expect(resolve_issues).to contain_exactly(issue1, issue2)
end end
describe 'filters by created_at' do it 'filters by state' do
it 'filters by created_before' do expect(resolve_issues(state: 'opened')).to contain_exactly(issue1)
expect(resolve_issues(created_before: 2.hours.ago)).to contain_exactly(issue1) expect(resolve_issues(state: 'closed')).to contain_exactly(issue2)
end end
it 'filters by created_after' do it 'filters by labels' do
expect(resolve_issues(created_after: 2.hours.ago)).to contain_exactly(issue2) expect(resolve_issues(label_name: [label1.title])).to contain_exactly(issue1, issue2)
expect(resolve_issues(label_name: [label1.title, label2.title])).to contain_exactly(issue2)
end end
end
describe 'filters by updated_at' do describe 'filters by created_at' do
it 'filters by updated_before' do it 'filters by created_before' do
expect(resolve_issues(updated_before: 2.hours.ago)).to contain_exactly(issue1) expect(resolve_issues(created_before: 2.hours.ago)).to contain_exactly(issue1)
end
it 'filters by created_after' do
expect(resolve_issues(created_after: 2.hours.ago)).to contain_exactly(issue2)
end
end end
it 'filters by updated_after' do describe 'filters by updated_at' do
expect(resolve_issues(updated_after: 2.hours.ago)).to contain_exactly(issue2) it 'filters by updated_before' do
expect(resolve_issues(updated_before: 2.hours.ago)).to contain_exactly(issue1)
end
it 'filters by updated_after' do
expect(resolve_issues(updated_after: 2.hours.ago)).to contain_exactly(issue2)
end
end end
end
describe 'filters by closed_at' do describe 'filters by closed_at' do
let!(:issue3) { create(:issue, project: project, state: :closed, closed_at: 3.hours.ago) } let!(:issue3) { create(:issue, project: project, state: :closed, closed_at: 3.hours.ago) }
it 'filters by closed_before' do it 'filters by closed_before' do
expect(resolve_issues(closed_before: 2.hours.ago)).to contain_exactly(issue3) expect(resolve_issues(closed_before: 2.hours.ago)).to contain_exactly(issue3)
end
it 'filters by closed_after' do
expect(resolve_issues(closed_after: 2.hours.ago)).to contain_exactly(issue2)
end
end end
it 'filters by closed_after' do it 'searches issues' do
expect(resolve_issues(closed_after: 2.hours.ago)).to contain_exactly(issue2) expect(resolve_issues(search: 'foo')).to contain_exactly(issue2)
end end
end
it 'searches issues' do it 'sort issues' do
expect(resolve_issues(search: 'foo')).to contain_exactly(issue2) expect(resolve_issues(sort: 'created_desc')).to eq [issue2, issue1]
end end
it 'sort issues' do it 'returns issues user can see' do
expect(resolve_issues(sort: 'created_desc')).to eq [issue2, issue1] project.add_guest(current_user)
end
it 'returns issues user can see' do create(:issue, confidential: true)
project.add_guest(current_user)
create(:issue, confidential: true) expect(resolve_issues).to contain_exactly(issue1, issue2)
end
expect(resolve_issues).to contain_exactly(issue1, issue2) it 'finds a specific issue with iid' do
end expect(resolve_issues(iid: issue1.iid)).to contain_exactly(issue1)
end
it 'finds a specific issue with iid' do it 'finds a specific issue with iids' do
expect(resolve_issues(iid: issue1.iid)).to contain_exactly(issue1) expect(resolve_issues(iids: issue1.iid)).to contain_exactly(issue1)
end end
it 'finds a specific issue with iids' do it 'finds multiple issues with iids' do
expect(resolve_issues(iids: issue1.iid)).to contain_exactly(issue1) expect(resolve_issues(iids: [issue1.iid, issue2.iid]))
end .to contain_exactly(issue1, issue2)
end
it 'finds multiple issues with iids' do it 'finds only the issues within the project we are looking at' do
expect(resolve_issues(iids: [issue1.iid, issue2.iid])) another_project = create(:project)
.to contain_exactly(issue1, issue2) iids = [issue1, issue2].map(&:iid)
end
iids.each do |iid|
create(:issue, project: another_project, iid: iid)
end
it 'finds only the issues within the project we are looking at' do expect(resolve_issues(iids: iids)).to contain_exactly(issue1, issue2)
another_project = create(:project) end
iids = [issue1, issue2].map(&:iid) end
end
iids.each do |iid| context "when passing a non existent, batch loaded project" do
create(:issue, project: another_project, iid: iid) let(:project) do
BatchLoader.for("non-existent-path").batch do |_fake_paths, loader, _|
loader.call("non-existent-path", nil)
end end
end
expect(resolve_issues(iids: iids)).to contain_exactly(issue1, issue2) it "returns nil without breaking" do
expect(resolve_issues(iids: ["don't", "break"])).to be_empty
end end
end end
......
...@@ -95,6 +95,12 @@ describe Gitlab::Git::Repository, :seed_helper do ...@@ -95,6 +95,12 @@ describe Gitlab::Git::Repository, :seed_helper do
end end
end end
describe '#create_repository' do
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RepositoryService, :create_repository do
subject { repository.create_repository }
end
end
describe '#branch_names' do describe '#branch_names' do
subject { repository.branch_names } subject { repository.branch_names }
......
...@@ -2487,4 +2487,69 @@ describe Repository do ...@@ -2487,4 +2487,69 @@ describe Repository do
repository.merge_base('master', 'fix') repository.merge_base('master', 'fix')
end end
end end
describe '#create_if_not_exists' do
let(:project) { create(:project) }
let(:repository) { project.repository }
it 'creates the repository if it did not exist' do
expect { repository.create_if_not_exists }.to change { repository.exists? }.from(false).to(true)
end
it 'calls out to the repository client to create a repo' do
expect(repository.raw.gitaly_repository_client).to receive(:create_repository)
repository.create_if_not_exists
end
context 'it does nothing if the repository already existed' do
let(:project) { create(:project, :repository) }
it 'does nothing if the repository already existed' do
expect(repository.raw.gitaly_repository_client).not_to receive(:create_repository)
repository.create_if_not_exists
end
end
context 'when the repository exists but the cache is not up to date' do
let(:project) { create(:project, :repository) }
it 'does not raise errors' do
allow(repository).to receive(:exists?).and_return(false)
expect(repository.raw).to receive(:create_repository).and_call_original
expect { repository.create_if_not_exists }.not_to raise_error
end
end
end
describe "#blobs_metadata" do
set(:project) { create(:project, :repository) }
let(:repository) { project.repository }
def expect_metadata_blob(thing)
expect(thing).to be_a(Blob)
expect(thing.data).to be_empty
end
it "returns blob metadata in batch for HEAD" do
result = repository.blobs_metadata(["bar/branch-test.txt", "README.md", "does/not/exist"])
expect_metadata_blob(result.first)
expect_metadata_blob(result.second)
expect(result.size).to eq(2)
end
it "returns blob metadata for a specified ref" do
result = repository.blobs_metadata(["files/ruby/feature.rb"], "feature")
expect_metadata_blob(result.first)
end
it "performs a single gitaly call", :request_store do
expect { repository.blobs_metadata(["bar/branch-test.txt", "readme.txt", "does/not/exist"]) }
.to change { Gitlab::GitalyClient.get_request_count }.by(1)
end
end
end end
...@@ -61,7 +61,14 @@ module GraphqlHelpers ...@@ -61,7 +61,14 @@ module GraphqlHelpers
def variables_for_mutation(name, input) def variables_for_mutation(name, input)
graphql_input = input.map { |name, value| [GraphqlHelpers.fieldnamerize(name), value] }.to_h graphql_input = input.map { |name, value| [GraphqlHelpers.fieldnamerize(name), value] }.to_h
{ input_variable_name_for_mutation(name) => graphql_input }.to_json result = { input_variable_name_for_mutation(name) => graphql_input }
# Avoid trying to serialize multipart data into JSON
if graphql_input.values.none? { |value| io_value?(value) }
result.to_json
else
result
end
end end
def input_variable_name_for_mutation(mutation_name) def input_variable_name_for_mutation(mutation_name)
...@@ -162,6 +169,10 @@ module GraphqlHelpers ...@@ -162,6 +169,10 @@ module GraphqlHelpers
field.arguments.values.any? { |argument| argument.type.non_null? } field.arguments.values.any? { |argument| argument.type.non_null? }
end end
def io_value?(value)
Array.wrap(value).any? { |v| v.respond_to?(:to_io) }
end
def field_type(field) def field_type(field)
field_type = field.type field_type = field.type
......
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