Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-08-03 12:09:47 +00:00
parent cb48c93abf
commit a70e2c0418
16 changed files with 474 additions and 55 deletions

View File

@ -336,6 +336,15 @@ Graphql/AuthorizeTypes:
- 'spec/**/*.rb' - 'spec/**/*.rb'
- 'ee/spec/**/*.rb' - 'ee/spec/**/*.rb'
Graphql/JSONType:
Enabled: true
Include:
- 'app/graphql/types/**/*'
- 'ee/app/graphql/types/**/*'
Exclude:
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
RSpec/EnvAssignment: RSpec/EnvAssignment:
Enable: true Enable: true
Include: Include:

View File

@ -1249,24 +1249,6 @@ Rails/SaveBang:
Exclude: Exclude:
- 'ee/spec/controllers/projects/merge_requests_controller_spec.rb' - 'ee/spec/controllers/projects/merge_requests_controller_spec.rb'
- 'ee/spec/controllers/subscriptions_controller_spec.rb' - 'ee/spec/controllers/subscriptions_controller_spec.rb'
- 'ee/spec/features/admin/admin_users_spec.rb'
- 'ee/spec/features/admin/geo/admin_geo_nodes_spec.rb'
- 'ee/spec/features/admin/licenses/admin_views_license_spec.rb'
- 'ee/spec/features/boards/scoped_issue_board_spec.rb'
- 'ee/spec/features/ci_shared_runner_warnings_spec.rb'
- 'ee/spec/features/dashboards/operations_spec.rb'
- 'ee/spec/features/issues/gfm_autocomplete_ee_spec.rb'
- 'ee/spec/features/merge_request/user_approves_spec.rb'
- 'ee/spec/features/merge_requests/user_views_all_merge_requests_spec.rb'
- 'ee/spec/features/projects/members/invite_group_and_members_spec.rb'
- 'ee/spec/features/projects/merge_requests/user_approves_merge_request_spec.rb'
- 'ee/spec/features/projects/mirror_spec.rb'
- 'ee/spec/features/projects/new_project_spec.rb'
- 'ee/spec/features/projects/settings/user_manages_approval_settings_spec.rb'
- 'ee/spec/features/projects/settings/user_manages_members_spec.rb'
- 'ee/spec/features/search/elastic/global_search_spec.rb'
- 'ee/spec/features/security/project/internal_access_spec.rb'
- 'ee/spec/features/security/project/public_access_spec.rb'
- 'ee/spec/finders/epics_finder_spec.rb' - 'ee/spec/finders/epics_finder_spec.rb'
- 'ee/spec/finders/security/vulnerabilities_finder_spec.rb' - 'ee/spec/finders/security/vulnerabilities_finder_spec.rb'
- 'ee/spec/frontend/fixtures/analytics.rb' - 'ee/spec/frontend/fixtures/analytics.rb'

View File

@ -71,7 +71,7 @@ module Types
description: 'Number of events of this alert', description: 'Number of events of this alert',
method: :events method: :events
field :details, field :details, # rubocop:disable Graphql/JSONType
GraphQL::Types::JSON, GraphQL::Types::JSON,
null: true, null: true,
description: 'Alert details' description: 'Alert details'

View File

@ -56,7 +56,7 @@ class Service < ApplicationRecord
validates :instance, uniqueness: { scope: :type }, if: -> { instance? } validates :instance, uniqueness: { scope: :type }, if: -> { instance? }
validate :validate_is_instance_or_template validate :validate_is_instance_or_template
scope :issue_trackers, -> { where(category: 'issue_tracker') } scope :external_issue_trackers, -> { where(category: 'issue_tracker').active }
scope :external_wikis, -> { where(type: 'ExternalWikiService').active } scope :external_wikis, -> { where(type: 'ExternalWikiService').active }
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
scope :by_type, -> (type) { where(type: type) } scope :by_type, -> (type) { where(type: type) }
@ -76,7 +76,6 @@ class Service < ApplicationRecord
scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) } scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
scope :deployment_hooks, -> { where(deployment_events: true, active: true) } scope :deployment_hooks, -> { where(deployment_events: true, active: true) }
scope :alert_hooks, -> { where(alert_events: true, active: true) } scope :alert_hooks, -> { where(alert_events: true, active: true) }
scope :external_issue_trackers, -> { issue_trackers.active }
scope :deployment, -> { where(category: 'deployment') } scope :deployment, -> { where(category: 'deployment') }
default_value_for :category, 'common' default_value_for :category, 'common'

View File

@ -0,0 +1,5 @@
---
title: Refactor ee/spec/features/* to fix SaveBang Cop
merge_request: 38289
author: Rajendra Kadam
type: fixed

View File

@ -325,9 +325,11 @@ Example response:
"design_repositories_failed_count": nil, "design_repositories_failed_count": nil,
"design_repositories_synced_in_percentage": "0.00%", "design_repositories_synced_in_percentage": "0.00%",
"projects_count": 41, "projects_count": 41,
"repositories_count": 41,
"repositories_failed_count": nil, "repositories_failed_count": nil,
"repositories_synced_count": nil, "repositories_synced_count": nil,
"repositories_synced_in_percentage": "0.00%", "repositories_synced_in_percentage": "0.00%",
"wikis_count": 41,
"wikis_failed_count": nil, "wikis_failed_count": nil,
"wikis_synced_count": nil, "wikis_synced_count": nil,
"wikis_synced_in_percentage": "0.00%", "wikis_synced_in_percentage": "0.00%",
@ -402,9 +404,11 @@ Example response:
"design_repositories_failed_count": nil, "design_repositories_failed_count": nil,
"design_repositories_synced_in_percentage": "0.00%", "design_repositories_synced_in_percentage": "0.00%",
"projects_count": 41, "projects_count": 41,
"repositories_count": 41,
"repositories_failed_count": 1, "repositories_failed_count": 1,
"repositories_synced_count": 40, "repositories_synced_count": 40,
"repositories_synced_in_percentage": "97.56%", "repositories_synced_in_percentage": "97.56%",
"wikis_count": 41,
"wikis_failed_count": 0, "wikis_failed_count": 0,
"wikis_synced_count": 41, "wikis_synced_count": 41,
"wikis_synced_in_percentage": "100.00%", "wikis_synced_in_percentage": "100.00%",
@ -448,9 +452,6 @@ Example response:
] ]
``` ```
NOTE: **Note:**
In GitLab 12.0, deprecated fields `wikis_count` and `repositories_count` were removed. Use `projects_count` instead.
## Retrieve status about a specific Geo node ## Retrieve status about a specific Geo node
```plaintext ```plaintext
@ -495,9 +496,11 @@ Example response:
"design_repositories_failed_count": nil, "design_repositories_failed_count": nil,
"design_repositories_synced_in_percentage": "0.00%", "design_repositories_synced_in_percentage": "0.00%",
"projects_count": 41, "projects_count": 41,
"repositories_count": 41,
"repositories_failed_count": 1, "repositories_failed_count": 1,
"repositories_synced_count": 40, "repositories_synced_count": 40,
"repositories_synced_in_percentage": "97.56%", "repositories_synced_in_percentage": "97.56%",
"wikis_count": 41,
"wikis_failed_count": 0, "wikis_failed_count": 0,
"wikis_synced_count": 41, "wikis_synced_count": 41,
"wikis_synced_in_percentage": "100.00%", "wikis_synced_in_percentage": "100.00%",
@ -517,9 +520,6 @@ Example response:
Note: The `health_status` parameter can only be in an "Healthy" or "Unhealthy" state, while the `health` parameter can be empty, "Healthy", or contain the actual error message. Note: The `health_status` parameter can only be in an "Healthy" or "Unhealthy" state, while the `health` parameter can be empty, "Healthy", or contain the actual error message.
NOTE: **Note:**
In GitLab 12.0, deprecated fields `wikis_count` and `repositories_count` were removed. Use `projects_count` instead.
## Retrieve project sync or verification failures that occurred on the current node ## Retrieve project sync or verification failures that occurred on the current node
This only works on a secondary node. This only works on a secondary node.

View File

@ -1964,7 +1964,12 @@ input CreateIterationInput {
""" """
The target group for the iteration The target group for the iteration
""" """
groupPath: ID! groupPath: ID
"""
The target project for the iteration
"""
projectPath: ID
""" """
The start date of the iteration The start date of the iteration
@ -9714,6 +9719,68 @@ type Project {
""" """
issuesEnabled: Boolean issuesEnabled: Boolean
"""
Find iterations
"""
iterations(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
List items within a time frame where items.end_date is between startDate and
endDate parameters (startDate parameter must be present)
"""
endDate: Time
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
The ID of the Iteration to look up
"""
id: ID
"""
The internal ID of the Iteration to look up
"""
iid: ID
"""
Whether to include ancestor iterations. Defaults to true
"""
includeAncestors: Boolean
"""
Returns the last _n_ elements from the list.
"""
last: Int
"""
List items within a time frame where items.start_date is between startDate
and endDate parameters (endDate parameter must be present)
"""
startDate: Time
"""
Filter iterations by state
"""
state: IterationState
"""
Fuzzy search by title
"""
title: String
): IterationConnection
""" """
Status of Jira import background job of the project Status of Jira import background job of the project
""" """

View File

@ -5202,13 +5202,19 @@
"name": "groupPath", "name": "groupPath",
"description": "The target group for the iteration", "description": "The target group for the iteration",
"type": { "type": {
"kind": "NON_NULL", "kind": "SCALAR",
"name": null, "name": "ID",
"ofType": { "ofType": null
"kind": "SCALAR", },
"name": "ID", "defaultValue": null
"ofType": null },
} {
"name": "projectPath",
"description": "The target project for the iteration",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}, },
"defaultValue": null "defaultValue": null
}, },
@ -28960,6 +28966,129 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "iterations",
"description": "Find iterations",
"args": [
{
"name": "startDate",
"description": "List items within a time frame where items.start_date is between startDate and endDate parameters (endDate parameter must be present)",
"type": {
"kind": "SCALAR",
"name": "Time",
"ofType": null
},
"defaultValue": null
},
{
"name": "endDate",
"description": "List items within a time frame where items.end_date is between startDate and endDate parameters (startDate parameter must be present)",
"type": {
"kind": "SCALAR",
"name": "Time",
"ofType": null
},
"defaultValue": null
},
{
"name": "state",
"description": "Filter iterations by state",
"type": {
"kind": "ENUM",
"name": "IterationState",
"ofType": null
},
"defaultValue": null
},
{
"name": "title",
"description": "Fuzzy search by title",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "id",
"description": "The ID of the Iteration to look up",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null
},
{
"name": "iid",
"description": "The internal ID of the Iteration to look up",
"type": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null
},
{
"name": "includeAncestors",
"description": "Whether to include ancestor iterations. Defaults to true",
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "IterationConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "jiraImportStatus", "name": "jiraImportStatus",
"description": "Status of Jira import background job of the project", "description": "Status of Jira import background job of the project",

View File

@ -9,6 +9,11 @@ You can only restore a backup to **exactly the same version and type (CE/EE)**
of GitLab on which it was created. The best way to migrate your repositories of GitLab on which it was created. The best way to migrate your repositories
from one server to another is through backup restore. from one server to another is through backup restore.
CAUTION: **Warning:**
GitLab will not backup items that are not stored on the
filesystem. If using [object storage](../administration/object_storage.md),
remember to enable backups with your object storage provider if desired.
## Requirements ## Requirements
In order to be able to backup and restore, you need two essential tools In order to be able to backup and restore, you need two essential tools

View File

@ -271,6 +271,7 @@ The following issue metadata will be copied to the epic:
- Upvotes/downvotes. - Upvotes/downvotes.
- Participants. - Participants.
- Group labels that the issue already has. - Group labels that the issue already has.
- Parent epic. **(ULTIMATE)**
## Manage multi-level child epics **(ULTIMATE)** ## Manage multi-level child epics **(ULTIMATE)**

View File

@ -10,6 +10,23 @@ module API
requires :ref, type: String, desc: 'The name of branch, tag or commit' requires :ref, type: String, desc: 'The name of branch, tag or commit'
end end
params :create_file_params do
optional :files, type: Array, desc: 'An array of files' do
requires :file_path, type: String, file_path: true, allow_blank: false, desc: 'The path of a snippet file'
requires :content, type: String, allow_blank: false, desc: 'The content of a snippet file'
end
optional :content, type: String, allow_blank: false, desc: 'The content of a snippet'
given :content do
requires :file_name, type: String, desc: 'The name of a snippet file'
end
mutually_exclusive :files, :content
exactly_one_of :files, :content
end
def content_for(snippet) def content_for(snippet)
if snippet.empty_repo? if snippet.empty_repo?
env['api.format'] = :txt env['api.format'] = :txt
@ -35,5 +52,12 @@ module API
send_git_blob(repo, blob) send_git_blob(repo, blob)
end end
end end
def process_file_args(args)
args[:snippet_actions] = args.delete(:files)&.map do |file|
file[:action] = :create
file.symbolize_keys
end
end
end end
end end

View File

@ -66,18 +66,23 @@ module API
end end
params do params do
requires :title, type: String, allow_blank: false, desc: 'The title of a snippet' requires :title, type: String, allow_blank: false, desc: 'The title of a snippet'
requires :file_name, type: String, desc: 'The name of a snippet file'
requires :content, type: String, allow_blank: false, desc: 'The content of a snippet'
optional :description, type: String, desc: 'The description of a snippet' optional :description, type: String, desc: 'The description of a snippet'
optional :visibility, type: String, optional :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values, values: Gitlab::VisibilityLevel.string_values,
default: 'internal', default: 'internal',
desc: 'The visibility of the snippet' desc: 'The visibility of the snippet'
use :create_file_params
end end
post do post do
authorize! :create_snippet authorize! :create_snippet
attrs = declared_params(include_missing: false).merge(request: request, api: true) attrs = declared_params(include_missing: false).tap do |create_args|
create_args[:request] = request
create_args[:api] = true
process_file_args(create_args)
end
service_response = ::Snippets::CreateService.new(nil, current_user, attrs).execute service_response = ::Snippets::CreateService.new(nil, current_user, attrs).execute
snippet = service_response.payload[:snippet] snippet = service_response.payload[:snippet]

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
# This cop checks for use of GraphQL::Types::JSON types in GraphQL fields
# and arguments.
#
# @example
#
# # bad
# class AwfulClass
# field :some_field, GraphQL::Types::JSON
# end
#
# # good
# class GreatClass
# field :some_field, GraphQL::STRING_TYPE
# end
module RuboCop
module Cop
module Graphql
class JSONType < RuboCop::Cop::Cop
MSG = 'Avoid using GraphQL::Types::JSON. See: ' \
'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#json'.freeze
def_node_matcher :has_json_type?, <<~PATTERN
(send nil? {:field :argument}
(sym _)
(const
(const
(const nil? :GraphQL) :Types) :JSON)
(...)?)
PATTERN
def on_send(node)
add_offense(node, location: :expression) if has_json_type?(node)
end
end
end
end
end

View File

@ -217,7 +217,7 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
it_behaves_like 'all pipelines' it_behaves_like 'all pipelines'
it 'includes custom filters', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/233077' do it 'includes custom filters' do
aggregate_failures 'UploadLinkFilter' do aggregate_failures 'UploadLinkFilter' do
expect(doc).to parse_upload_links expect(doc).to parse_upload_links
end end
@ -282,7 +282,7 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
it_behaves_like 'all pipelines' it_behaves_like 'all pipelines'
it 'includes custom filters', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/233077' do it 'includes custom filters' do
aggregate_failures 'UploadLinkFilter' do aggregate_failures 'UploadLinkFilter' do
expect(doc).to parse_upload_links expect(doc).to parse_upload_links
end end

View File

@ -229,13 +229,15 @@ RSpec.describe API::Snippets do
let(:base_params) do let(:base_params) do
{ {
title: 'Test Title', title: 'Test Title',
file_name: 'test.rb',
description: 'test description', description: 'test description',
content: 'puts "hello world"',
visibility: 'public' visibility: 'public'
} }
end end
let(:params) { base_params.merge(extra_params) } let(:file_path) { 'file_1.rb' }
let(:file_content) { 'puts "hello world"' }
let(:params) { base_params.merge(file_params, extra_params) }
let(:file_params) { { files: [{ file_path: file_path, content: file_content }] } }
let(:extra_params) { {} } let(:extra_params) { {} }
subject { post api("/snippets/", user), params: params } subject { post api("/snippets/", user), params: params }
@ -251,7 +253,7 @@ RSpec.describe API::Snippets do
expect(response).to have_gitlab_http_status(:created) expect(response).to have_gitlab_http_status(:created)
expect(json_response['title']).to eq(params[:title]) expect(json_response['title']).to eq(params[:title])
expect(json_response['description']).to eq(params[:description]) expect(json_response['description']).to eq(params[:description])
expect(json_response['file_name']).to eq(params[:file_name]) expect(json_response['file_name']).to eq(file_path)
expect(json_response['files']).to eq(snippet.blobs.map { |blob| snippet_blob_file(blob) }) expect(json_response['files']).to eq(snippet.blobs.map { |blob| snippet_blob_file(blob) })
expect(json_response['visibility']).to eq(params[:visibility]) expect(json_response['visibility']).to eq(params[:visibility])
end end
@ -265,9 +267,90 @@ RSpec.describe API::Snippets do
it 'commit the files to the repository' do it 'commit the files to the repository' do
subject subject
blob = snippet.repository.blob_at('master', params[:file_name]) blob = snippet.repository.blob_at('master', file_path)
expect(blob.data).to eq params[:content] expect(blob.data).to eq file_content
end
end
context 'with files parameter' do
using RSpec::Parameterized::TableSyntax
where(:path, :content, :status, :error) do
'.gitattributes' | 'file content' | :created | nil
'valid/path/file.rb' | 'file content' | :created | nil
'.gitattributes' | nil | :bad_request | 'files[0][content] is empty'
'.gitattributes' | '' | :bad_request | 'files[0][content] is empty'
'' | 'file content' | :bad_request | 'files[0][file_path] is empty'
nil | 'file content' | :bad_request | 'files[0][file_path] should be a valid file path, files[0][file_path] is empty'
'../../etc/passwd' | 'file content' | :bad_request | 'files[0][file_path] should be a valid file path'
end
with_them do
let(:file_path) { path }
let(:file_content) { content }
before do
subject
end
it 'responds correctly' do
expect(response).to have_gitlab_http_status(status)
expect(json_response['error']).to eq(error)
end
end
it 'returns 400 if both files and content are provided' do
params[:file_name] = 'foo.rb'
params[:content] = 'bar'
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq 'files, content are mutually exclusive'
end
it 'returns 400 when neither files or content are provided' do
params.delete(:files)
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq 'files, content are missing, exactly one parameter must be provided'
end
context 'with multiple files' do
let(:file_params) do
{
files: [
{ file_path: 'file_1.rb', content: 'puts "hello world"' },
{ file_path: 'file_2.rb', content: 'puts "hello world 2"' }
]
}
end
it_behaves_like 'snippet creation'
end
end
context 'without files parameter' do
let(:file_params) { { file_name: 'testing.rb', content: 'snippet content' } }
it 'allows file_name and content parameters' do
subject
expect(response).to have_gitlab_http_status(:created)
end
it 'returns 400 if file_name and content are not both provided' do
params.delete(:file_name)
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq 'file_name is missing'
end end
end end
@ -305,15 +388,6 @@ RSpec.describe API::Snippets do
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
end end
it 'returns 400 if content is blank' do
params[:content] = ''
subject
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq 'content is empty'
end
it 'returns 400 if title is blank' do it 'returns 400 if title is blank' do
params[:title] = '' params[:title] = ''

View File

@ -0,0 +1,79 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/graphql/json_type'
RSpec.describe RuboCop::Cop::Graphql::JSONType, type: :rubocop do
include CopHelper
subject(:cop) { described_class.new }
context 'fields' do
it 'adds an offense when GraphQL::Types::JSON is used' do
inspect_source(<<~RUBY.strip)
class MyType
field :some_field, GraphQL::Types::JSON
end
RUBY
expect(cop.offenses.size).to eq(1)
end
it 'adds an offense when GraphQL::Types::JSON is used with other keywords' do
inspect_source(<<~RUBY.strip)
class MyType
field :some_field, GraphQL::Types::JSON, null: true, description: 'My description'
end
RUBY
expect(cop.offenses.size).to eq(1)
end
it 'does not add an offense for other types' do
expect_no_offenses(<<~RUBY.strip)
class MyType
field :some_field, GraphQL::STRING_TYPE
end
RUBY
end
end
context 'arguments' do
it 'adds an offense when GraphQL::Types::JSON is used' do
inspect_source(<<~RUBY.strip)
class MyType
argument :some_arg, GraphQL::Types::JSON
end
RUBY
expect(cop.offenses.size).to eq(1)
end
it 'adds an offense when GraphQL::Types::JSON is used with other keywords' do
inspect_source(<<~RUBY.strip)
class MyType
argument :some_arg, GraphQL::Types::JSON, null: true, description: 'My description'
end
RUBY
expect(cop.offenses.size).to eq(1)
end
it 'does not add an offense for other types' do
expect_no_offenses(<<~RUBY.strip)
class MyType
argument :some_arg, GraphQL::STRING_TYPE
end
RUBY
end
end
it 'does not add an offense for uses outside of field or argument' do
expect_no_offenses(<<~RUBY.strip)
class MyType
foo :some_field, GraphQL::Types::JSON
end
RUBY
end
end