Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
cb48c93abf
commit
a70e2c0418
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Refactor ee/spec/features/* to fix SaveBang Cop
|
||||||
|
merge_request: 38289
|
||||||
|
author: Rajendra Kadam
|
||||||
|
type: fixed
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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] = ''
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue