Commit 049ae0ca authored by Jacques Erasmus's avatar Jacques Erasmus

Merge branch 'selhorn-tooltip-update' into 'master'

Changed tooltip to gl-tooltip for "Lock" on issues

See merge request gitlab-org/gitlab!44660
parents 9cf281c5 35014fa3
......@@ -21,12 +21,10 @@ https://docs.gitlab.com/ee/development/documentation/index.html#move-or-rename-a
a link to the new location.
- [ ] Make sure internal links pointing to the document in question are not broken.
- [ ] Search and replace any links referring to old docs in GitLab Rails app,
specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ce/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
specifically under the `app/views/` and `ee/app/views` (for GitLab EE) directories.
- [ ] Make sure to add [`redirect_from`](https://docs.gitlab.com/ee/development/documentation/index.html#redirections-for-pages-with-disqus-comments)
to the new document if there are any Disqus comments on the old document thread.
- [ ] Update the link in `features.yml` (if applicable)
- [ ] If working on CE and the `ee-compat-check` jobs fails, submit an MR to EE
with the changes as well (https://docs.gitlab.com/ce/development/documentation/index.html#cherry-picking-from-ce-to-ee).
- [ ] Ping one of the technical writers for review.
- [ ] Assign one of the technical writers for review.
/label ~documentation
......@@ -362,6 +362,15 @@ Graphql/AuthorizeTypes:
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
Graphql/GIDExpectedType:
Enabled: true
Include:
- 'app/graphql/**/*'
- 'ee/app/graphql/**/*'
Exclude:
- 'spec/**/*.rb'
- 'ee/spec/**/*.rb'
Graphql/JSONType:
Enabled: true
Include:
......
......@@ -73,7 +73,7 @@ sensitive as to how you word things. Use Emoji to express your feelings (heart,
star, smile, etc.). Some good tips about code reviews can be found in our
[Code Review Guidelines].
[Code Review Guidelines]: https://docs.gitlab.com/ce/development/code_review.html
[Code Review Guidelines]: https://docs.gitlab.com/ee/development/code_review.html
## Feature flags
......@@ -217,5 +217,5 @@ rebase with master to see if that solves the issue.
[team]: https://about.gitlab.com/team/
[done]: https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#definition-of-done
[automatic_ce_ee_merge]: https://docs.gitlab.com/ce/development/automatic_ce_ee_merge.html
[ee_features]: https://docs.gitlab.com/ce/development/ee_features.html
[automatic_ce_ee_merge]: https://docs.gitlab.com/ee/development/automatic_ce_ee_merge.html
[ee_features]: https://docs.gitlab.com/ee/development/ee_features.html
......@@ -376,7 +376,7 @@ const Api = {
},
commitMultiple(id, data) {
// see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
// see https://docs.gitlab.com/ee/api/commits.html#create-a-commit-with-multiple-files-and-actions
const url = Api.buildUrl(Api.commitsPath).replace(':id', encodeURIComponent(id));
return axios.post(url, JSON.stringify(data), {
headers: {
......
......@@ -360,7 +360,7 @@ export default {
>
<template #link="{ content }">
<gl-link
href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html"
href="https://docs.gitlab.com/ee/user/project/integrations/prometheus.html"
target="_blank"
>{{ content }}</gl-link
>
......
......@@ -116,6 +116,7 @@ export default {
:placeholder="placeholder"
:value="text"
class="note-textarea ide-commit-message-textarea"
data-qa-selector="ide_commit_message_field"
dir="auto"
name="commit-message"
@scroll="handleScroll"
......
import createFlash from '~/flash';
import { BLOB_EDITOR_ERROR } from '~/blob_edit/constants';
import EditorLite from '~/editor/editor_lite';
export default class CILintEditor {
constructor() {
const monacoEnabled = window?.gon?.features?.monacoCi;
this.clearYml = document.querySelector('.clear-yml');
this.clearYml.addEventListener('click', this.clear.bind(this));
return monacoEnabled ? this.initEditorLite() : this.initAce();
return this.initEditorLite();
}
clear() {
......@@ -15,34 +13,20 @@ export default class CILintEditor {
}
initEditorLite() {
import(/* webpackChunkName: 'monaco_editor_lite' */ '~/editor/editor_lite')
.then(({ default: EditorLite }) => {
const editorEl = document.getElementById('editor');
const fileContentEl = document.getElementById('content');
const form = document.querySelector('.js-ci-lint-form');
const editorEl = document.getElementById('editor');
const fileContentEl = document.getElementById('content');
const form = document.querySelector('.js-ci-lint-form');
const rootEditor = new EditorLite();
const rootEditor = new EditorLite();
this.editor = rootEditor.createInstance({
el: editorEl,
blobPath: '.gitlab-ci.yml',
blobContent: editorEl.innerText,
});
form.addEventListener('submit', () => {
fileContentEl.value = this.editor.getValue();
});
})
.catch(() => createFlash({ message: BLOB_EDITOR_ERROR }));
}
initAce() {
this.editor = window.ace.edit('ci-editor');
this.textarea = document.getElementById('content');
this.editor = rootEditor.createInstance({
el: editorEl,
blobPath: '.gitlab-ci.yml',
blobContent: editorEl.innerText,
});
this.editor.getSession().setMode('ace/mode/yaml');
this.editor.on('input', () => {
this.textarea.value = this.editor.getSession().getValue();
form.addEventListener('submit', () => {
fileContentEl.value = this.editor.getValue();
});
}
}
......@@ -471,12 +471,6 @@ $mr-widget-min-height: 69px;
flex: 1;
}
.issuable-meta {
.author-link {
display: inline-block;
}
}
.merge-request-title {
margin-bottom: 2px;
......
......@@ -28,7 +28,10 @@
height: 14px;
width: 14px;
vertical-align: middle;
fill: $gl-text-color-secondary;
&:not(.text-warning) {
fill: $gl-text-color-secondary;
}
}
.sprite {
......
......@@ -11,8 +11,8 @@ class Dashboard::LabelsController < Dashboard::ApplicationController
def labels
finder_params = { project_ids: projects.select(:id) }
labels = LabelsFinder.new(current_user, finder_params).execute
GlobalLabel.build_collection(labels)
LabelsFinder.new(current_user, finder_params).execute
.select('DISTINCT ON (labels.title) labels.*')
end
end
......@@ -25,7 +25,7 @@ module Ci
attr_reader :current_user, :pipeline, :project, :params, :type
def init_collection
if Feature.enabled?(:ci_jobs_finder_refactor)
if Feature.enabled?(:ci_jobs_finder_refactor, default_enabled: true)
pipeline_jobs || project_jobs || all_jobs
else
project ? project_builds : all_jobs
......@@ -59,7 +59,7 @@ module Ci
end
def filter_by_scope(builds)
if Feature.enabled?(:ci_jobs_finder_refactor)
if Feature.enabled?(:ci_jobs_finder_refactor, default_enabled: true)
return filter_by_statuses!(params[:scope], builds) if params[:scope].is_a?(Array)
end
......
......@@ -102,7 +102,7 @@ class IssuableFinder
items = filter_items(items)
# Let's see if we have to negate anything
items = filter_negated_items(items)
items = filter_negated_items(items) if should_filter_negated_args?
# This has to be last as we use a CTE as an optimization fence
# for counts by passing the force_cte param and passing the
......@@ -134,13 +134,15 @@ class IssuableFinder
by_my_reaction_emoji(items)
end
# Negates all params found in `negatable_params`
def filter_negated_items(items)
return items unless Feature.enabled?(:not_issuable_queries, params.group || params.project, default_enabled: true)
def should_filter_negated_args?
return false unless Feature.enabled?(:not_issuable_queries, params.group || params.project, default_enabled: true)
# API endpoints send in `nil` values so we test if there are any non-nil
return items unless not_params.present? && not_params.values.any?
not_params.present? && not_params.values.any?
end
# Negates all params found in `negatable_params`
def filter_negated_items(items)
items = by_negated_author(items)
items = by_negated_assignee(items)
items = by_negated_label(items)
......
# frozen_string_literal: true
module Mutations
module Issues
module CommonMutationArguments
extend ActiveSupport::Concern
included do
argument :description, GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::IssueType, :description)
argument :due_date, GraphQL::Types::ISO8601Date,
required: false,
description: copy_field_description(Types::IssueType, :due_date)
argument :confidential, GraphQL::BOOLEAN_TYPE,
required: false,
description: copy_field_description(Types::IssueType, :confidential)
argument :locked, GraphQL::BOOLEAN_TYPE,
as: :discussion_locked,
required: false,
description: copy_field_description(Types::IssueType, :discussion_locked)
end
end
end
end
Mutations::Issues::CommonMutationArguments.prepend_if_ee('::EE::Mutations::Issues::CommonMutationArguments')
# frozen_string_literal: true
module Mutations
module Issues
class Create < BaseMutation
include ResolvesProject
graphql_name 'CreateIssue'
authorize :create_issue
include CommonMutationArguments
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: 'Project full path the issue is associated with'
argument :iid, GraphQL::INT_TYPE,
required: false,
description: 'The IID (internal ID) of a project issue. Only admins and project owners can modify'
argument :title, GraphQL::STRING_TYPE,
required: true,
description: copy_field_description(Types::IssueType, :title)
argument :milestone_id, ::Types::GlobalIDType[::Milestone],
required: false,
description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null'
argument :labels, [GraphQL::STRING_TYPE],
required: false,
description: copy_field_description(Types::IssueType, :labels)
argument :label_ids, [::Types::GlobalIDType[::Label]],
required: false,
description: 'The IDs of labels to be added to the issue'
argument :created_at, Types::TimeType,
required: false,
description: 'Timestamp when the issue was created. Available only for admins and project owners'
argument :merge_request_to_resolve_discussions_of, ::Types::GlobalIDType[::MergeRequest],
required: false,
description: 'The IID of a merge request for which to resolve discussions'
argument :discussion_to_resolve, GraphQL::STRING_TYPE,
required: false,
description: 'The ID of a discussion to resolve. Also pass `merge_request_to_resolve_discussions_of`'
argument :assignee_ids, [::Types::GlobalIDType[::User]],
required: false,
description: 'The array of user IDs to assign to the issue'
field :issue,
Types::IssueType,
null: true,
description: 'The issue after mutation'
def ready?(**args)
if args.slice(*mutually_exclusive_label_args).size > 1
arg_str = mutually_exclusive_label_args.map { |x| x.to_s.camelize(:lower) }.join(' or ')
raise Gitlab::Graphql::Errors::ArgumentError, "one and only one of #{arg_str} is required."
end
if args[:discussion_to_resolve].present? && args[:merge_request_to_resolve_discussions_of].blank?
raise Gitlab::Graphql::Errors::ArgumentError,
'to resolve a discussion please also provide `merge_request_to_resolve_discussions_of` parameter'
end
super
end
def resolve(project_path:, **attributes)
project = authorized_find!(full_path: project_path)
params = build_create_issue_params(attributes.merge(author_id: current_user.id))
issue = ::Issues::CreateService.new(project, current_user, params).execute
if issue.spam?
issue.errors.add(:base, 'Spam detected.')
end
{
issue: issue.valid? ? issue : nil,
errors: errors_on_object(issue)
}
end
private
def build_create_issue_params(params)
params[:milestone_id] &&= params[:milestone_id]&.model_id
params[:assignee_ids] &&= params[:assignee_ids].map { |assignee_id| assignee_id&.model_id }
params[:label_ids] &&= params[:label_ids].map { |label_id| label_id&.model_id }
params
end
def mutually_exclusive_label_args
[:labels, :label_ids]
end
def find_object(full_path:)
resolve_project(full_path: full_path)
end
end
end
end
Mutations::Issues::Create.prepend_if_ee('::EE::Mutations::Issues::Create')
......@@ -5,49 +5,26 @@ module Mutations
class Update < Base
graphql_name 'UpdateIssue'
argument :title,
GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::IssueType, :title)
include CommonMutationArguments
argument :description,
GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::IssueType, :description)
argument :due_date,
Types::TimeType,
required: false,
description: copy_field_description(Types::IssueType, :due_date)
argument :confidential,
GraphQL::BOOLEAN_TYPE,
required: false,
description: copy_field_description(Types::IssueType, :confidential)
argument :locked,
GraphQL::BOOLEAN_TYPE,
as: :discussion_locked,
argument :title, GraphQL::STRING_TYPE,
required: false,
description: copy_field_description(Types::IssueType, :discussion_locked)
description: copy_field_description(Types::IssueType, :title)
argument :add_label_ids,
[GraphQL::ID_TYPE],
argument :milestone_id, GraphQL::ID_TYPE,
required: false,
description: 'The IDs of labels to be added to the issue.'
description: 'The ID of the milestone to assign to the issue. On update milestone will be removed if set to null'
argument :remove_label_ids,
[GraphQL::ID_TYPE],
argument :add_label_ids, [GraphQL::ID_TYPE],
required: false,
description: 'The IDs of labels to be removed from the issue.'
description: 'The IDs of labels to be added to the issue'
argument :milestone_id,
GraphQL::ID_TYPE,
argument :remove_label_ids, [GraphQL::ID_TYPE],
required: false,
description: 'The ID of the milestone to be assigned, milestone will be removed if set to null.'
description: 'The IDs of labels to be removed from the issue'
argument :state_event, Types::IssueStateEventEnum,
description: 'Close or reopen an issue.',
description: 'Close or reopen an issue',
required: false
def resolve(project_path:, iid:, **args)
......
......@@ -17,7 +17,7 @@ module Mutations
discussion_id = nil
if args[:discussion_id]
discussion = GitlabSchema.object_from_id(args[:discussion_id])
discussion = GitlabSchema.object_from_id(args[:discussion_id], expected_type: ::Discussion)
authorize_discussion!(discussion)
discussion_id = discussion.id
......
......@@ -30,7 +30,7 @@ module Types
# most recent `Version` for an issue
Gitlab::SafeRequestStore.fetch([request_cache_base_key, 'stateful_version', object.issue_id, version_gid]) do
if version_gid
GitlabSchema.object_from_id(version_gid)&.sync
GitlabSchema.object_from_id(version_gid, expected_type: ::DesignManagement::Version)&.sync
else
object.issue.design_versions.most_recent
end
......
......@@ -23,6 +23,7 @@ module Types
mount_mutation Mutations::Branches::Create, calls_gitaly: true
mount_mutation Mutations::Commits::Create, calls_gitaly: true
mount_mutation Mutations::Discussions::ToggleResolve
mount_mutation Mutations::Issues::Create
mount_mutation Mutations::Issues::SetAssignees
mount_mutation Mutations::Issues::SetConfidential
mount_mutation Mutations::Issues::SetLocked
......
# frozen_string_literal: true
class GlobalLabel
include Presentable
attr_accessor :title, :labels
alias_attribute :name, :title
delegate :color, :text_color, :description, :scoped_label?, to: :@first_label
def for_display
@first_label
end
def self.build_collection(labels)
labels = labels.group_by(&:title)
labels.map do |title, labels|
new(title, labels)
end
end
def initialize(title, labels)
@title = title
@labels = labels
@first_label = labels.find { |lbl| lbl.description.present? } || labels.first
end
def present(attributes)
super(attributes.merge(presenter_class: ::LabelPresenter))
end
end
......@@ -3,7 +3,6 @@
class Packages::Event < ApplicationRecord
belongs_to :package, optional: true
# FIXME: Remove debian: 9 from here when it's added to the types in package.rb model
EVENT_SCOPES = ::Packages::Package.package_types.merge(container: 1000, tag: 1001).freeze
enum event_scope: EVENT_SCOPES
......
......@@ -106,14 +106,26 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
add_special_file_path(file_name: 'LICENSE')
end
def add_license_ide_path
ide_edit_path(project, default_branch_or_master, 'LICENSE')
end
def add_changelog_path
add_special_file_path(file_name: 'CHANGELOG')
end
def add_changelog_ide_path
ide_edit_path(project, default_branch_or_master, 'CHANGELOG')
end
def add_contribution_guide_path
add_special_file_path(file_name: 'CONTRIBUTING.md', commit_message: 'Add CONTRIBUTING')
end
def add_contribution_guide_ide_path
ide_edit_path(project, default_branch_or_master, 'CONTRIBUTING.md')
end
def add_ci_yml_path
add_special_file_path(file_name: ci_config_path_or_default)
end
......@@ -122,6 +134,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
add_special_file_path(file_name: 'README.md')
end
def add_readme_ide_path
ide_edit_path(project, default_branch_or_master, 'README.md')
end
def license_short_name
license = repository.license
license&.nickname || license&.name || 'LICENSE'
......@@ -218,9 +234,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def new_file_anchor_data
if current_user && can_current_user_push_to_default_branch?
new_file_path = empty_repo? ? ide_edit_path(project, default_branch_or_master) : project_new_blob_path(project, default_branch_or_master)
AnchorData.new(false,
statistic_icon + _('New file'),
project_new_blob_path(project, default_branch_or_master),
new_file_path,
'missing')
end
end
......@@ -229,7 +247,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
if current_user && can_current_user_push_to_default_branch? && repository.readme.nil?
AnchorData.new(false,
statistic_icon + _('Add README'),
add_readme_path)
empty_repo? ? add_readme_ide_path : add_readme_path)
elsif repository.readme
AnchorData.new(false,
statistic_icon('doc-text') + _('README'),
......@@ -243,7 +261,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
if current_user && can_current_user_push_to_default_branch? && repository.changelog.blank?
AnchorData.new(false,
statistic_icon + _('Add CHANGELOG'),
add_changelog_path)
empty_repo? ? add_changelog_ide_path : add_changelog_path)
elsif repository.changelog.present?
AnchorData.new(false,
statistic_icon('doc-text') + _('CHANGELOG'),
......@@ -264,7 +282,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
if current_user && can_current_user_push_to_default_branch?
AnchorData.new(false,
content_tag(:span, statistic_icon + _('Add LICENSE'), class: 'add-license-link d-flex'),
add_license_path)
empty_repo? ? add_license_ide_path : add_license_path)
else
AnchorData.new(false,
icon + content_tag(:span, _('No license. All rights reserved'), class: 'project-stat-value'),
......@@ -277,7 +295,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
if current_user && can_current_user_push_to_default_branch? && repository.contribution_guide.blank?
AnchorData.new(false,
statistic_icon + _('Add CONTRIBUTING'),
add_contribution_guide_path)
empty_repo? ? add_contribution_guide_ide_path : add_contribution_guide_path)
elsif repository.contribution_guide.present?
AnchorData.new(false,
statistic_icon('doc-text') + _('CONTRIBUTING'),
......
......@@ -133,7 +133,7 @@ class BuildDetailsEntity < JobEntity
def callout_message
return super unless build.failure_reason.to_sym == :missing_dependency_failure
docs_url = "https://docs.gitlab.com/ce/ci/yaml/README.html#dependencies"
docs_url = "https://docs.gitlab.com/ee/ci/yaml/README.html#dependencies"
[
failure_message.html_safe,
......
# frozen_string_literal: true
class LabelEntity < Grape::Entity
expose :id, if: ->(label, _) { !label.is_a?(GlobalLabel) }
expose :id
expose :title
expose :color
expose :description
expose :group_id
expose :project_id, if: ->(label, _) { !label.is_a?(GlobalLabel) }
expose :project_id
expose :template
expose :text_color
expose :created_at
......
......@@ -34,6 +34,18 @@ module Issues
private
def filter_params(merge_request)
super
moved_issue = params.delete(:moved_issue)
# Setting created_at, updated_at and iid is allowed only for admins and owners or
# when moving an issue as we preserve the original issue attributes except id and iid.
params.delete(:iid) unless current_user.can?(:set_issue_iid, project)
params.delete(:created_at) unless moved_issue || current_user.can?(:set_issue_created_at, project)
params.delete(:updated_at) unless moved_issue || current_user.can?(:set_issue_updated_at, project)
end
def create_assignee_note(issue, old_assignees)
SystemNoteService.change_issuable_assignees(
issue, issue.project, current_user, old_assignees)
......
......@@ -52,7 +52,8 @@ module Issues
iid: nil,
project: target_project,
author: original_entity.author,
assignee_ids: original_entity.assignee_ids
assignee_ids: original_entity.assignee_ids,
moved_issue: true
}
new_params = original_entity.serializable_hash.symbolize_keys.merge(new_params)
......
......@@ -32,7 +32,7 @@
= hidden_field_tag 'new_parent_group_id'
%ul
- side_effects_link_start = '<a href="https://docs.gitlab.com/ce/user/project/index.html#redirects-when-changing-repository-paths" target="_blank">'
- side_effects_link_start = '<a href="https://docs.gitlab.com/ee/user/project/index.html#redirects-when-changing-repository-paths" target="_blank">'
- warning_text = s_("GroupSettings|Be careful. Changing a group's parent can have unintended %{side_effects_link_start}side effects%{side_effects_link_end}.") % { side_effects_link_start: side_effects_link_start, side_effects_link_end:'</a>' }
%li= warning_text.html_safe
%li= s_('GroupSettings|You can only transfer the group to a group you manage.')
......
......@@ -22,7 +22,7 @@
- if ref
- if job.ref
.icon-container.gl-display-inline-block
= job.tag? ? icon('tag') : sprite_icon('fork', css_class: 'sprite')
= job.tag? ? sprite_icon('label', css_class: 'sprite') : sprite_icon('fork', css_class: 'sprite')
= link_to job.ref, project_ref_path(job.project, job.ref), class: "ref-name"
- else
.light= _('none')
......@@ -33,10 +33,12 @@
= link_to job.short_sha, project_commit_path(job.project, job.sha), class: "commit-sha mr-0"
- if job.stuck?
= icon('warning', class: 'text-warning has-tooltip', title: _('Job is stuck. Check runners.'))
%span.has-tooltip{ title: _('Job is stuck. Check runners.') }
= sprite_icon('warning', css_class: 'text-warning!')
- if retried
= icon('refresh', class: 'text-warning has-tooltip', title: _('Job was retried'))
%span.has-tooltip{ title: _('Job was retried') }
= sprite_icon('retry', css_class: 'text-warning')
.label-container
- if job.tags.any?
......@@ -87,7 +89,7 @@
- if job.finished_at
%p.finished-at
= icon("calendar")
= sprite_icon("calendar")
%span= time_ago_with_tooltip(job.finished_at)
%td.coverage
......
- page_title _("CI Lint")
- page_description _("Validate your GitLab CI configuration file")
- unless Feature.enabled?(:monaco_ci, default_enabled: true)
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
%h2.pt-3.pb-3= _("Validate your GitLab CI configuration")
......@@ -17,12 +14,9 @@
.file-holder
.js-file-title.file-title.clearfix
= _("Contents of .gitlab-ci.yml")
- if Feature.enabled?(:monaco_ci, default_enabled: true)
.file-editor.code
.js-edit-mode-pane.qa-editor#editor{ data: { 'editor-loading': true } }<
%pre.editor-loading-content= params[:content]
- else
#ci-editor.ci-editor= @content
.file-editor.code
.js-edit-mode-pane.qa-editor#editor{ data: { 'editor-loading': true } }<
%pre.editor-loading-content= params[:content]
= text_area_tag(:content, @content, class: 'hidden form-control span1', rows: 7, require: true)
.col-sm-12
.float-left.gl-mt-3
......
......@@ -53,7 +53,7 @@
= link_to merge_request_path(merge_request), class: "has-tooltip", title: _('Cannot be merged automatically') do
= sprite_icon('warning-solid')
- if merge_request.assignees.any?
%li.gl-display-flex
%li.gl-display-flex.gl-align-items-center
= render 'shared/issuable/assignees', project: merge_request.project, issuable: merge_request
- if Feature.enabled?(:merge_request_reviewers, @project) && merge_request.reviewers.any?
%li.gl-display-flex.issuable-reviewers
......
- current_route_path = request.fullpath.match(/-\/tree\/[^\/]+\/(.+$)/).to_a[1]
- add_page_startup_graphql_call('repository/path_last_commit', { projectPath: @project.full_path, ref: current_ref, currentRoutePath: current_route_path })
- add_page_startup_graphql_call('repository/path_last_commit', { projectPath: @project.full_path, ref: current_ref, path: current_route_path })
- breadcrumb_title _("Repository")
- @content_class = "limit-container-width" unless fluid_layout
......
---
title: Add GraphQL mutation to create an issue
merge_request: 43735
author:
type: added
---
title: Align badge with avatar in MR List
merge_request: 44671
author:
type: fixed
---
title: Use Web IDE to create new files in empty repos
merge_request: 44287
author:
type: added
---
title: Replace fa icons in CI build table
merge_request: 45123
author:
type: changed
---
title: Fixed incorrect parameter in GraphQL startup call
merge_request: 45115
author:
type: fixed
......@@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36622
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/245183
group: group::continuous integration
type: development
default_enabled: false
default_enabled: true
---
name: monaco_ci
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23666
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/249137
group: group::editor
type: development
default_enabled: true
......@@ -26,18 +26,18 @@ PATTERNS = %w[
gl-deprecated-dropdown-divider
gl-deprecated-dropdown-header
gl-deprecated-dropdown-item
graphql_pagination
has-tooltip
has_tooltip
initDeprecatedJQueryDropdown
loading-button
pagination-button
v-popover
v-tooltip
with_tooltip
].freeze
BLOCKING_PATTERNS = %w[
pagination-button
graphql_pagination
].freeze
def get_added_lines(files)
......
......@@ -2,7 +2,7 @@
description: 'Learn how to install, configure, update, and maintain your GitLab instance.'
---
# Administrator Docs **(CORE ONLY)**
# Administrator documentation **(CORE ONLY)**
Learn how to administer your self-managed GitLab instance.
......@@ -12,18 +12,16 @@ GitLab has two product distributions available through [different subscriptions]
- The open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab).
You can [install either GitLab CE or GitLab EE](https://about.gitlab.com/install/ce-or-ee/).
However, the features you'll have access to depend on the subscription you choose
(Core, Starter, Premium, or Ultimate).
However, the features you have access to depend on your chosen [subscription](https://about.gitlab.com/pricing/).
NOTE: **Note:**
GitLab Community Edition installations only have access to Core features.
GitLab Community Edition installations have access only to Core features.
GitLab.com is administered by GitLab, Inc., therefore, only GitLab team members have
access to its admin configurations. If you're a GitLab.com user, please check the
[user documentation](../user/index.md).
Non-administrator users can't access GitLab administration tools and settings.
NOTE: **Note:**
Non-administrator users don’t have access to GitLab administration tools and settings.
GitLab.com is administered by GitLab, Inc., and only GitLab team members have
access to its administration tools and settings. Users of GitLab.com should
instead refer to the [User documentation](../user/index.md) for GitLab
configuration and usage documentation.
## Installing and maintaining GitLab
......
......@@ -64,18 +64,17 @@ To set up GitLab and its components to accommodate up to 2,000 users:
## Configure the external load balancer
NOTE: **Note:**
This architecture has been tested and validated with [HAProxy](https://www.haproxy.org/).
Although you can use a load balancer with a similar set of features, GitLab
hasn't validated other load balancers.
In an active/active GitLab configuration, you'll need a load balancer to route
traffic to the application servers. The specifics for which load balancer to
use or its exact configuration is out of scope for the GitLab documentation.
If you're managing multi-node systems (including GitLab) you'll probably
already have a load balancer of choice. Some examples including HAProxy
(open-source), F5 Big-IP LTM, and Citrix Net Scaler. This documentation
includes the ports and protocols for use with GitLab.
traffic to the application servers. The specifics on which load balancer to use
or its exact configuration is beyond the scope of GitLab documentation. We hope
that if you're managing multi-node systems like GitLab, you already have a load
balancer of choice. Some load balancer examples include HAProxy (open-source),
F5 Big-IP LTM, and Citrix Net Scaler. This documentation outline the ports and
protocols needed for use with GitLab.
This architecture has been tested and validated with [HAProxy](https://www.haproxy.org/)
as the load balancer. Although other load balancers with similar feature sets
could also be used, those load balancers have not been validated.
The next question is how you will handle SSL in your environment. There are
several different options:
......@@ -489,11 +488,10 @@ Name. If you are addressing the Gitaly server by its IP address, you must add it
as a Subject Alternative Name to the certificate.
[gRPC does not support using an IP address as Common Name in a certificate](https://github.com/grpc/grpc/issues/2691).
NOTE: **Note:**
It is possible to configure Gitaly servers with both an
unencrypted listening address `listen_addr` and an encrypted listening
address `tls_listen_addr` at the same time. This allows you to do a
gradual transition from unencrypted to encrypted traffic, if necessary.
It's possible to configure Gitaly servers with both an unencrypted listening
address (`listen_addr`) and an encrypted listening address (`tls_listen_addr`)
at the same time. This allows you to do a gradual transition from unencrypted to
encrypted traffic, if necessary.
To configure Gitaly with TLS:
......@@ -537,14 +535,14 @@ To configure Gitaly with TLS:
## Configure GitLab Rails
NOTE: **Note:**
In our architectures we run each GitLab Rails node using the Puma webserver
and have its number of workers set to 90% of available CPUs along with four threads. For
nodes that are running Rails with other components the worker value should be reduced
accordingly where we've found 50% achieves a good balance but this is dependent
on workload.
This section describes how to configure the GitLab application (Rails) component.
In our architecture, we run each GitLab Rails node using the Puma webserver, and
have its number of workers set to 90% of available CPUs, with four threads. For
nodes running Rails with other components, the worker value should be reduced
accordingly. We've determined that a worker value of 50% achieves a good balance,
but this is dependent on workload.
On each node perform the following:
1. If you're [using NFS](#configure-nfs-optional):
......@@ -572,10 +570,10 @@ On each node perform the following:
mkdir -p /var/opt/gitlab/.ssh /var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/git-data
```
1. Download/install Omnibus GitLab using **steps 1 and 2** from
1. Download and install Omnibus GitLab using **steps 1 and 2** from
[GitLab downloads](https://about.gitlab.com/install/). Do not complete other
steps on the download page.
1. Create/edit `/etc/gitlab/gitlab.rb` and use the following configuration.
1. Create or edit `/etc/gitlab/gitlab.rb` and use the following configuration.
To maintain uniformity of links across nodes, the `external_url`
on the application server should point to the external URL that users will use
to access GitLab. This would be the URL of the [load balancer](#configure-the-external-load-balancer)
......@@ -671,12 +669,10 @@ On each node perform the following:
[Gitaly node](#configure-gitaly) and
[reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
NOTE: **Note:**
When you specify `https` in the `external_url`, as in the example
above, GitLab assumes you have SSL certificates in `/etc/gitlab/ssl/`. If
certificates are not present, NGINX will fail to start. See the
[NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
for more information.
When you specify `https` in the `external_url`, as in the previous example,
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
certificates aren't present, NGINX will fail to start. For more information, see
the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
### GitLab Rails post-configuration
......@@ -688,12 +684,11 @@ for more information.
sudo gitlab-rake gitlab:db:configure
```
NOTE: **Note:**
If you encounter a `rake aborted!` error stating that PgBouncer is failing to connect to
PostgreSQL it may be that your PgBouncer node's IP address is missing from
PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb` on your database nodes. See
[PgBouncer error `ERROR: pgbouncer cannot connect to server`](troubleshooting.md#pgbouncer-error-error-pgbouncer-cannot-connect-to-server)
in the Troubleshooting section before proceeding.
If you encounter a `rake aborted!` error message stating that PgBouncer is
failing to connect to PostgreSQL, it may be that your PgBouncer node's IP
address is missing from PostgreSQL's `trust_auth_cidr_addresses` in `gitlab.rb`
on your database nodes. Before proceeding, see
[PgBouncer error `ERROR: pgbouncer cannot connect to server`](troubleshooting.md#pgbouncer-error-error-pgbouncer-cannot-connect-to-server).
1. [Configure fast lookup of authorized SSH keys in the database](../operations/fast_ssh_key_lookup.md).
......@@ -893,16 +888,13 @@ functioning backups is encountered.
## Configure Advanced Search **(STARTER ONLY)**
NOTE: **Note:**
Elasticsearch cluster design and requirements are dependent on your specific data.
For recommended best practices on how to set up your Elasticsearch cluster
alongside your instance, read how to
[choose the optimal cluster configuration](../../integration/elasticsearch.md#guidance-on-choosing-optimal-cluster-configuration).
You can leverage Elasticsearch and enable Advanced Search for faster, more
advanced code search across your entire GitLab instance.
You can leverage Elasticsearch and [enable Advanced Search](../../integration/elasticsearch.md)
for faster, more advanced code search across your entire GitLab instance.
[Learn how to set it up.](../../integration/elasticsearch.md)
Elasticsearch cluster design and requirements are dependent on your specific
data. For recommended best practices about how to set up your Elasticsearch
cluster alongside your instance, read how to
[choose the optimal cluster configuration](../../integration/elasticsearch.md#guidance-on-choosing-optimal-cluster-configuration).
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
......
......@@ -3422,6 +3422,111 @@ type CreateImageDiffNotePayload {
note: Note
}
"""
Autogenerated input type of CreateIssue
"""
input CreateIssueInput {
"""
The array of user IDs to assign to the issue
"""
assigneeIds: [UserID!]
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Indicates the issue is confidential
"""
confidential: Boolean
"""
Timestamp when the issue was created. Available only for admins and project owners
"""
createdAt: Time
"""
Description of the issue
"""
description: String
"""
The ID of a discussion to resolve. Also pass `merge_request_to_resolve_discussions_of`
"""
discussionToResolve: String
"""
Due date of the issue
"""
dueDate: ISO8601Date
"""
The ID of an epic to associate the issue with
"""
epicId: EpicID
"""
The IID (internal ID) of a project issue. Only admins and project owners can modify
"""
iid: Int
"""
The IDs of labels to be added to the issue
"""
labelIds: [LabelID!]
"""
Labels of the issue
"""
labels: [String!]
"""
Indicates discussion is locked on the issue
"""
locked: Boolean
"""
The IID of a merge request for which to resolve discussions
"""
mergeRequestToResolveDiscussionsOf: MergeRequestID
"""
The ID of the milestone to assign to the issue. On update milestone will be removed if set to null
"""
milestoneId: MilestoneID
"""
Project full path the issue is associated with
"""
projectPath: ID!
"""
Title of the issue
"""
title: String!
}
"""
Autogenerated return type of CreateIssue
"""
type CreateIssuePayload {
"""
A unique identifier for the client performing the mutation.
"""
clientMutationId: String
"""
Errors encountered during execution of the mutation.
"""
errors: [String!]!
"""
The issue after mutation
"""
issue: Issue
}
"""
Autogenerated input type of CreateIteration
"""
......@@ -11188,6 +11293,11 @@ type MergeRequestEdge {
node: MergeRequest
}
"""
Identifier of MergeRequest
"""
scalar MergeRequestID
"""
Check permissions for the current user on a merge request
"""
......@@ -11964,6 +12074,7 @@ type Mutation {
createDiffNote(input: CreateDiffNoteInput!): CreateDiffNotePayload
createEpic(input: CreateEpicInput!): CreateEpicPayload
createImageDiffNote(input: CreateImageDiffNoteInput!): CreateImageDiffNotePayload
createIssue(input: CreateIssueInput!): CreateIssuePayload
createIteration(input: CreateIterationInput!): CreateIterationPayload
createNote(input: CreateNoteInput!): CreateNotePayload
createRequirement(input: CreateRequirementInput!): CreateRequirementPayload
......@@ -19452,7 +19563,7 @@ Autogenerated input type of UpdateIssue
"""
input UpdateIssueInput {
"""
The IDs of labels to be added to the issue.
The IDs of labels to be added to the issue
"""
addLabelIds: [ID!]
......@@ -19474,18 +19585,13 @@ input UpdateIssueInput {
"""
Due date of the issue
"""
dueDate: Time
dueDate: ISO8601Date
"""
The ID of the parent epic. NULL when removing the association
"""
epicId: ID
"""
The desired health status
"""
healthStatus: HealthStatus
"""
The IID of the issue to mutate
"""
......@@ -19497,7 +19603,7 @@ input UpdateIssueInput {
locked: Boolean
"""
The ID of the milestone to be assigned, milestone will be removed if set to null.
The ID of the milestone to assign to the issue. On update milestone will be removed if set to null
"""
milestoneId: ID
......@@ -19507,12 +19613,12 @@ input UpdateIssueInput {
projectPath: ID!
"""
The IDs of labels to be removed from the issue.
The IDs of labels to be removed from the issue
"""
removeLabelIds: [ID!]
"""
Close or reopen an issue.
Close or reopen an issue
"""
stateEvent: IssueStateEvent
......
......@@ -537,6 +537,16 @@ Autogenerated return type of CreateImageDiffNote.
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `note` | Note | The note after mutation |
### CreateIssuePayload
Autogenerated return type of CreateIssue.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
| `issue` | Issue | The issue after mutation |
### CreateIterationPayload
Autogenerated return type of CreateIteration.
......
......@@ -324,7 +324,6 @@ There are three main considerations on the logic built for the nav:
- `https://docs.gitlab.com/ee/`
- `https://docs.gitlab.com/omnibus/`
- `https://docs.gitlab.com/runner/`
- `https://docs.gitlab.com/debug/`
- `https://docs.gitlab.com/*`
- [EE-only](#ee-only-docs): documentation only available in `/ee/`, not on `/ce/`, e.g.:
- `https://docs.gitlab.com/ee/user/group/epics/`
......@@ -342,8 +341,8 @@ all the nav links to other pages:
<% dir = @item.identifier.to_s[%r{(?<=/)[^/]+}] %>
```
For instance, for `https://docs.gitlab.com/ce/user/index.html`,
`dir` == `ce`, and for `https://docs.gitlab.com/omnibus/README.html`,
For instance, for `https://docs.gitlab.com/ee/user/index.html`,
`dir` == `ee`, and for `https://docs.gitlab.com/omnibus/README.html`,
`dir` == `omnibus`.
#### Default URL
......
......@@ -1892,7 +1892,7 @@ for the changes to take effect.
If the document you are editing resides in a place other than the GitLab CE/EE
`doc/` directory, instead of the relative link, use the full path:
`https://docs.gitlab.com/ce/administration/restart_gitlab.html`. Replace
`https://docs.gitlab.com/ee/administration/restart_gitlab.html`. Replace
`reconfigure` with `restart` where appropriate.
### Installation guide
......
......@@ -619,7 +619,7 @@ Ensure you comply with the [Changelog entries guide](../changelog.md).
### 8. Ask for a Product Analytics Review
On GitLab.com, we have DangerBot setup to monitor Product Analytics related files and DangerBot will recommend a Product Analytics review. Mention `@gitlab-org/growth/product-analytics/engineers` in your MR for a review.
On GitLab.com, we have DangerBot setup to monitor Product Analytics related files and DangerBot will recommend a Product Analytics review. Mention `@gitlab-org/growth/product_analytics/engineers` in your MR for a review.
### 9. Verify your metric
......
# GitLab Docker images
This content has been moved to [our documentation site](https://docs.gitlab.com/ce/install/docker.html).
This content has been moved to [our documentation site](https://docs.gitlab.com/ee/install/docker.html).
......@@ -75,5 +75,18 @@ module EE
items.in_iterations(params.iterations)
end
end
override :filter_negated_items
def filter_negated_items(items)
items = by_negated_epic(items)
super(items)
end
def by_negated_epic(items)
return items unless not_params[:epic_id].present?
items.not_in_epics(not_params[:epic_id].to_i)
end
end
end
# frozen_string_literal: true
module EE
module Mutations
module Issues
module CommonMutationArguments
extend ActiveSupport::Concern
included do
argument :health_status,
::Types::HealthStatusEnum,
required: false,
description: 'The desired health status'
argument :weight, GraphQL::INT_TYPE,
required: false,
description: 'The weight of the issue'
end
end
end
end
end
# frozen_string_literal: true
module EE
module Mutations
module Issues
module Create
extend ActiveSupport::Concern
prepended do
argument :epic_id, ::Types::GlobalIDType[::Epic],
required: false,
description: 'The ID of an epic to associate the issue with'
end
private
def create_issue_params(params)
params[:epic_id] &&= params[:epic_id]&.model_id
super(params)
end
end
end
end
end
......@@ -7,12 +7,7 @@ module EE
extend ActiveSupport::Concern
prepended do
argument :health_status,
::Types::HealthStatusEnum,
required: false,
description: 'The desired health status'
argument :epic_id,
GraphQL::ID_TYPE,
argument :epic_id, GraphQL::ID_TYPE,
required: false,
description: 'The ID of the parent epic. NULL when removing the association'
end
......
......@@ -26,6 +26,7 @@ module EE
scope :no_epic, -> { left_outer_joins(:epic_issue).where(epic_issues: { epic_id: nil }) }
scope :any_epic, -> { joins(:epic_issue) }
scope :in_epics, ->(epics) { joins(:epic_issue).where(epic_issues: { epic_id: epics }) }
scope :not_in_epics, ->(epics) { left_outer_joins(:epic_issue).where('epic_issues.epic_id NOT IN (?) OR epic_issues.epic_id IS NULL', epics) }
scope :no_iteration, -> { where(sprint_id: nil) }
scope :any_iteration, -> { where.not(sprint_id: nil) }
scope :in_iterations, ->(iterations) { where(sprint_id: iterations) }
......
---
title: Fix issue filtering by negated epic parameter
merge_request: 44719
author:
type: fixed
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Filter issues by epic', :js do
include FilteredSearchHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) }
let_it_be(:issue1) { create(:issue, project: project) }
let_it_be(:issue2) { create(:issue, project: project) }
let_it_be(:issue3) { create(:issue, project: project) }
let_it_be(:issue4) { create(:issue, project: project) }
let_it_be(:epic1) { create(:epic) }
let_it_be(:epic2) { create(:epic) }
let_it_be(:epic_issue1) { create(:epic_issue, issue: issue1, epic: epic1) }
let_it_be(:epic_issue2) { create(:epic_issue, issue: issue2, epic: epic2) }
let_it_be(:epic_issue3) { create(:epic_issue, issue: issue3, epic: epic2) }
let(:js_dropdown) { '#js-dropdown-epic' }
before do
stub_licensed_features(epics: true)
stub_feature_flags(vue_issuables_list: false)
project.add_developer(user)
sign_in(user)
visit project_issues_path(project)
end
it 'filter issues by epic' do
input_filtered_search("epic:=&#{epic1.id}")
expect_issues_list_count(1)
end
it 'filter issues not in the epic' do
input_filtered_search("epic:!=&#{epic1.id}")
expect_issues_list_count(3)
end
end
......@@ -121,6 +121,14 @@ RSpec.describe IssuesFinder do
expect(issues).to contain_exactly(issue_1, issue_2, issue_subepic)
end
end
context 'filter issues not in the epic' do
let(:params) { { not: { epic_id: epic_1.id } } }
it 'returns issues not assigned to the epic' do
expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, issue_2, issue_subepic)
end
end
end
context 'filter by iteration' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Issues::Create do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:assignee1) { create(:user) }
let_it_be(:assignee2) { create(:user) }
let(:expected_attributes) do
{
title: 'new title',
description: 'new description',
confidential: true,
due_date: Date.tomorrow,
discussion_locked: true,
weight: 10
}
end
let(:mutation_params) do
{
project_path: project.full_path,
assignee_ids: [assignee1.to_global_id, assignee2.to_global_id],
health_status: Issue.health_statuses[:at_risk]
}.merge(expected_attributes)
end
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutated_issue) { subject[:issue] }
specify { expect(described_class).to require_graphql_authorizations(:create_issue) }
describe '#resolve' do
before do
project.add_guest(assignee1)
project.add_guest(assignee2)
stub_licensed_features(issuable_health_status: true)
end
subject { mutation.resolve(mutation_params) }
context 'when user can create issues' do
before do
project.add_developer(user)
end
it 'creates issue with correct EE values' do
expect(mutated_issue).to have_attributes(expected_attributes)
expect(mutated_issue.assignees.pluck(:id)).to eq([assignee1.id, assignee2.id])
expect(mutated_issue.health_status).to eq('at_risk')
end
end
end
end
......@@ -16,7 +16,15 @@ RSpec.describe Mutations::Issues::Update do
let_it_be(:epic) { create(:epic, group: group) }
let(:epic_id) { epic.to_global_id.to_s }
let(:params) { { project_path: project.full_path, iid: issue.iid, epic_id: epic_id } }
let(:params) do
{
project_path: project.full_path,
iid: issue.iid,
epic_id: epic_id,
weight: 10
}
end
let(:mutated_issue) { subject[:issue] }
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
......@@ -53,6 +61,7 @@ RSpec.describe Mutations::Issues::Update do
it 'returns the updated issue' do
expect(mutated_issue.epic).to eq(epic)
expect(mutated_issue.weight).to eq(10)
end
end
......
......@@ -132,6 +132,13 @@ RSpec.describe Issue do
end
end
describe '.not_in_epics' do
it 'returns only issues not in selected epics' do
expect(described_class.count).to eq 3
expect(described_class.not_in_epics([epic1])).to match_array([epic_issue2.issue, issue_no_epic])
end
end
describe '.distinct_epic_ids' do
it 'returns distinct epic ids' do
expect(described_class.distinct_epic_ids.map(&:epic_id)).to match_array([epic1.id, epic2.id])
......
......@@ -128,7 +128,7 @@ module API
pipeline = user_project.all_pipelines.find(params[:pipeline_id])
if Feature.enabled?(:ci_jobs_finder_refactor)
if Feature.enabled?(:ci_jobs_finder_refactor, default_enabled: true)
builds = ::Ci::JobsFinder
.new(current_user: current_user, pipeline: pipeline, params: params)
.execute
......@@ -157,7 +157,7 @@ module API
pipeline = user_project.all_pipelines.find(params[:pipeline_id])
if Feature.enabled?(:ci_jobs_finder_refactor)
if Feature.enabled?(:ci_jobs_finder_refactor, default_enabled: true)
bridges = ::Ci::JobsFinder
.new(current_user: current_user, pipeline: pipeline, params: params, type: ::Ci::Bridge)
.execute
......
......@@ -231,9 +231,6 @@ module API
authorize! :create_issue, user_project
params.delete(:created_at) unless current_user.can?(:set_issue_created_at, user_project)
params.delete(:iid) unless current_user.can?(:set_issue_iid, user_project)
issue_params = declared_params(include_missing: false)
issue_params[:system_note_timestamp] = params[:created_at]
......@@ -279,8 +276,6 @@ module API
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
authorize! :update_issue, issue
# Setting updated_at is allowed only for admins and owners
params.delete(:updated_at) unless current_user.can?(:set_issue_updated_at, user_project)
issue.system_note_timestamp = params[:updated_at]
update_params = declared_params(include_missing: false).merge(request: request, api: true)
......
# see https://docs.gitlab.com/ce/ci/yaml/README.html for all available options
# see https://docs.gitlab.com/ee/ci/yaml/README.html for all available options
# you can delete this line if you're not using Docker
image: busybox:latest
......
# Based on openjdk:8, already includes lein
image: clojure:lein-2.7.0
# If you need to configure a database, add a `services` section here
# See https://docs.gitlab.com/ce/ci/services/postgres.html
# See https://docs.gitlab.com/ee/ci/services/postgres.html
# Make sure you configure the connection as well
before_script:
......
# Template project: https://gitlab.com/pages/jekyll
# Docs: https://docs.gitlab.com/ce/pages/
# Docs: https://docs.gitlab.com/ee/pages/
image: ruby:2.6
variables:
......
......@@ -44,7 +44,6 @@ module Gitlab
# Initialize gon.features with any flags that should be
# made globally available to the frontend
push_frontend_feature_flag(:monaco_blobs, default_enabled: true)
push_frontend_feature_flag(:monaco_ci, default_enabled: true)
push_frontend_feature_flag(:webperf_experiment, default_enabled: false)
push_frontend_feature_flag(:snippets_binary_blob, default_enabled: false)
push_frontend_feature_flag(:usage_data_api, default_enabled: false)
......
......@@ -48,7 +48,7 @@ module Gitlab
*Documentation*
For more information about GitLab chatops, refer to its
documentation: https://docs.gitlab.com/ce/ci/chatops/README.html.
documentation: https://docs.gitlab.com/ee/ci/chatops/README.html.
MESSAGE
message
......
......@@ -3,7 +3,7 @@
# Gitlab::VisibilityLevelChecker verifies that:
# - Current @project.visibility_level is not restricted
# - Override visibility param is not restricted
# - @see https://docs.gitlab.com/ce/api/project_import_export.html#import-a-file
# - @see https://docs.gitlab.com/ee/api/project_import_export.html#import-a-file
#
# @param current_user [User] Current user object to verify visibility level against
# @param project [Project] Current project that is being created/imported
......
......@@ -73,6 +73,10 @@ module QA
element :project_path_content
end
view 'app/assets/javascripts/ide/components/commit_sidebar/message_field.vue' do
element :ide_commit_message_field
end
def has_file?(file_name)
within_element(:file_list) do
page.has_content? file_name
......@@ -83,6 +87,10 @@ module QA
has_element?(:project_path_content, project_path: project_path)
end
def go_to_project
click_element(:project_path_content, Page::Project::Show)
end
def create_new_file_from_template(file_name, template)
click_element(:new_file, Page::Component::WebIDE::Modal::CreateNewFile)
......@@ -115,7 +123,7 @@ module QA
find_element(:commit_sha_content).text
end
def commit_changes(open_merge_request: false)
def commit_changes(commit_message = nil, open_merge_request: false)
# Clicking :begin_commit_button switches from the
# edit to the commit view
click_element(:begin_commit_button)
......@@ -133,6 +141,10 @@ module QA
has_element?(:commit_button)
end
if commit_message
fill_element(:ide_commit_message_field, commit_message)
end
if open_merge_request
click_element(:commit_button, Page::MergeRequest::New)
else
......
......@@ -27,11 +27,14 @@ module QA
Page::Project::Show.perform(&:create_first_new_file!)
Page::File::Form.perform do |form|
form.add_name(@name)
form.add_content(@content)
form.add_commit_message(@commit_message)
form.commit_changes
Page::Project::WebIDE::Edit.perform do |ide|
ide.add_file(@name, @content)
ide.commit_changes(@commit_message)
ide.go_to_project
end
Page::Project::Show.perform do |project|
project.click_file(@name)
end
end
......
......@@ -18,7 +18,6 @@ module QA
file.commit_message = commit_message_for_create
end
expect(page).to have_content('The file has been successfully created.')
expect(page).to have_content(file_name)
expect(page).to have_content(file_content)
expect(page).to have_content(commit_message_for_create)
......
......@@ -10,7 +10,6 @@ module QA
end
end
let(:web_ide_url) { current_url + '-/ide/project/' + project.path_with_namespace }
let(:file_name) { 'the very first file.txt' }
before do
......@@ -18,10 +17,8 @@ module QA
end
it "creates the first file in an empty project via Web IDE", testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/847' do
# In the first iteration, the test opens Web IDE by modifying the URL to address past regressions.
# Once the Web IDE button is introduced for empty projects, the test will be modified to go through UI.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/27915 and https://gitlab.com/gitlab-org/gitlab/-/issues/27535.
page.visit(web_ide_url)
project.visit!
Page::Project::Show.perform(&:create_first_new_file!)
Page::Project::WebIDE::Edit.perform do |ide|
ide.create_first_file(file_name)
......
# frozen_string_literal: true
module RuboCop
module Cop
module Graphql
class GIDExpectedType < RuboCop::Cop::Cop
MSG = 'Add an expected_type parameter to #object_from_id calls if possible.'
def_node_search :id_from_object?, <<~PATTERN
(send ... :object_from_id (...))
PATTERN
def on_send(node)
return unless id_from_object?(node)
add_offense(node)
end
end
end
end
end
......@@ -9,7 +9,7 @@ module RuboCop
include MigrationHelpers
MSG = 'Creating a table with more than one foreign key at once violates our migration style guide. ' \
'For more details check the https://docs.gitlab.com/ce/development/migration_style_guide.html#examples'
'For more details check the https://docs.gitlab.com/ee/development/migration_style_guide.html#examples'
def_node_matcher :create_table_with_block?, <<~PATTERN
(block
......
# Make sure to update the docs if this file moves. Docs URL: https://docs.gitlab.com/ce/development/migration_style_guide.html#when-to-use-the-helper-method
# Make sure to update the docs if this file moves. Docs URL: https://docs.gitlab.com/ee/development/migration_style_guide.html#when-to-use-the-helper-method
Migration/UpdateLargeTable:
Enabled: true
HighTrafficTables: &high_traffic_tables # size in GB (>= 10 GB on GitLab.com as of 02/2020) and/or number of records
......
......@@ -3,27 +3,32 @@
require 'spec_helper'
RSpec.describe Dashboard::LabelsController do
let(:project) { create(:project) }
let(:user) { create(:user) }
let!(:label) { create(:label, project: project) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:project_2) { create(:project) }
let_it_be(:label) { create(:label, project: project, title: 'some_label') }
let_it_be(:label_with_same_title) { create(:label, project: project_2, title: 'some_label') }
let_it_be(:unrelated_label) { create(:label, project: create(:project, :public)) }
before_all do
project.add_reporter(user)
project_2.add_reporter(user)
end
before do
sign_in(user)
project.add_reporter(user)
end
describe "#index" do
let!(:unrelated_label) { create(:label, project: create(:project, :public)) }
subject { get :index, format: :json }
it 'returns global labels for projects the user has a relationship with' do
it 'returns labels with unique titles for projects the user has a relationship with' do
subject
expect(json_response).to be_kind_of(Array)
expect(json_response.size).to eq(1)
expect(json_response[0]["id"]).to be_nil
expect(json_response[0]["title"]).to eq(label.title)
expect(json_response[0]['title']).to eq(label.title)
end
it_behaves_like 'disabled when using an external authorization service'
......
......@@ -11,7 +11,7 @@ RSpec.describe 'Database schema' do
let(:columns_name_with_jsonb) { retrieve_columns_name_with_jsonb }
# List of columns historically missing a FK, don't add more columns
# See: https://docs.gitlab.com/ce/development/foreign_keys.html#naming-foreign-keys
# See: https://docs.gitlab.com/ee/development/foreign_keys.html#naming-foreign-keys
IGNORED_FK_COLUMNS = {
abuse_reports: %w[reporter_id user_id],
application_settings: %w[performance_bar_allowed_group_id slack_app_id snowplow_app_id eks_account_id eks_access_key_id],
......
......@@ -2,7 +2,9 @@
require 'spec_helper'
RSpec.describe 'User creates blob in new project', :js do
RSpec.describe 'User creates new blob', :js do
include WebIdeSpecHelpers
let(:user) { create(:user) }
let(:project) { create(:project, :empty_repo) }
......@@ -12,16 +14,19 @@ RSpec.describe 'User creates blob in new project', :js do
visit project_path(project)
end
it 'allows the user to add a new file' do
it 'allows the user to add a new file in Web IDE' do
click_link 'New file'
execute_script("monaco.editor.getModels()[0].setValue('Hello world')")
wait_for_requests
ide_create_new_file('dummy-file', content: "Hello world\n")
fill_in(:file_name, with: 'dummy-file')
ide_commit
click_button('Commit changes')
click_button('Commit')
expect(page).to have_content('The file has been successfully created')
expect(page).to have_content('All changes are committed')
expect(project.repository.blob_at('master', 'dummy-file').data).to eql("Hello world\n")
end
end
......
......@@ -8,117 +8,88 @@ RSpec.describe 'CI Lint', :js do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
shared_examples 'correct ci linting process' do
describe 'YAML parsing' do
shared_examples 'validates the YAML' do
before do
stub_feature_flags(ci_lint_vue: false)
click_on 'Validate'
end
let(:content_selector) { '.content .view-lines' }
context 'YAML is correct' do
let(:yaml_content) do
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
end
before do
stub_feature_flags(ci_lint_vue: false)
project.add_developer(user)
sign_in(user)
it 'parses Yaml and displays the jobs' do
expect(page).to have_content('Status: syntax is correct')
visit project_ci_lint_path(project)
editor_set_value(yaml_content)
within "table" do
aggregate_failures do
expect(page).to have_content('Job - rspec')
expect(page).to have_content('Job - spinach')
expect(page).to have_content('Deploy Job - staging')
expect(page).to have_content('Deploy Job - production')
end
end
end
end
wait_for('YAML content') do
find(content_selector).text.present?
end
end
context 'YAML is incorrect' do
let(:yaml_content) { 'value: cannot have :' }
describe 'YAML parsing' do
shared_examples 'validates the YAML' do
before do
stub_feature_flags(ci_lint_vue: false)
click_on 'Validate'
end
it 'displays information about an error' do
expect(page).to have_content('Status: syntax is incorrect')
expect(page).to have_selector(content_selector, text: yaml_content)
end
context 'YAML is correct' do
let(:yaml_content) do
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
end
end
it_behaves_like 'validates the YAML'
it 'parses Yaml and displays the jobs' do
expect(page).to have_content('Status: syntax is correct')
context 'when Dry Run is checked' do
before do
check 'Simulate a pipeline created for the default branch'
within "table" do
aggregate_failures do
expect(page).to have_content('Job - rspec')
expect(page).to have_content('Job - spinach')
expect(page).to have_content('Deploy Job - staging')
expect(page).to have_content('Deploy Job - production')
end
end
end
it_behaves_like 'validates the YAML'
end
describe 'YAML revalidate' do
let(:yaml_content) { 'my yaml content' }
context 'YAML is incorrect' do
let(:yaml_content) { 'value: cannot have :' }
it 'loads previous YAML content after validation' do
expect(page).to have_field('content', with: 'my yaml content', visible: false, type: 'textarea')
it 'displays information about an error' do
expect(page).to have_content('Status: syntax is incorrect')
expect(page).to have_selector(content_selector, text: yaml_content)
end
end
end
describe 'YAML clearing' do
it_behaves_like 'validates the YAML'
context 'when Dry Run is checked' do
before do
click_on 'Clear'
check 'Simulate a pipeline created for the default branch'
end
context 'YAML is present' do
let(:yaml_content) do
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
end
it 'YAML content is cleared' do
expect(page).to have_field('content', with: '', visible: false, type: 'textarea')
end
end
it_behaves_like 'validates the YAML'
end
end
context 'with ACE editor' do
it_behaves_like 'correct ci linting process' do
let(:content_selector) { '.ace_content' }
describe 'YAML revalidate' do
let(:yaml_content) { 'my yaml content' }
before do
stub_feature_flags(monaco_ci: false)
stub_feature_flags(ci_lint_vue: false)
project.add_developer(user)
sign_in(user)
visit project_ci_lint_path(project)
find('#ci-editor')
execute_script("ace.edit('ci-editor').setValue(#{yaml_content.to_json});")
# Ace editor updates a hidden textarea and it happens asynchronously
wait_for('YAML content') do
find(content_selector).text.present?
end
it 'loads previous YAML content after validation' do
expect(page).to have_field('content', with: 'my yaml content', visible: false, type: 'textarea')
end
end
end
context 'with Editor Lite' do
it_behaves_like 'correct ci linting process' do
let(:content_selector) { '.content .view-lines' }
before do
stub_feature_flags(monaco_ci: true)
stub_feature_flags(ci_lint_vue: false)
project.add_developer(user)
sign_in(user)
describe 'YAML clearing' do
before do
click_on 'Clear'
end
visit project_ci_lint_path(project)
editor_set_value(yaml_content)
context 'YAML is present' do
let(:yaml_content) do
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
end
wait_for('YAML content') do
find(content_selector).text.present?
end
it 'YAML content is cleared' do
expect(page).to have_field('content', with: '', visible: false, type: 'textarea')
end
end
end
......
......@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Projects > Files > Project owner sees a link to create a license file in empty project', :js do
include WebIdeSpecHelpers
let(:project) { create(:project_empty_repo) }
let(:project_maintainer) { project.owner }
......@@ -10,36 +12,35 @@ RSpec.describe 'Projects > Files > Project owner sees a link to create a license
sign_in(project_maintainer)
end
it 'project maintainer creates a license file from a template' do
it 'allows project maintainer creates a license file from a template in Web IDE' do
visit project_path(project)
click_on 'Add LICENSE'
expect(page).to have_content('New file')
expect(current_path).to eq(
project_new_blob_path(project, 'master'))
expect(find('#file_name').value).to eq('LICENSE')
expect(page).to have_selector('.license-selector')
expect(current_path).to eq("/-/ide/project/#{project.full_path}/edit/master/-/LICENSE")
expect(page).to have_selector('.qa-file-templates-bar')
select_template('MIT License')
file_content = first('.file-editor')
expect(file_content).to have_content('MIT License')
expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
expect(ide_editor_value).to have_content('MIT License')
expect(ide_editor_value).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
ide_commit
click_button('Commit')
expect(current_path).to eq("/-/ide/project/#{project.full_path}/tree/master/-/")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
click_button 'Commit changes'
expect(page).to have_content('All changes are committed')
expect(current_path).to eq(
project_blob_path(project, 'master/LICENSE'))
expect(page).to have_content('MIT License')
expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
license_file = project.repository.blob_at('master', 'LICENSE').data
expect(license_file).to have_content('MIT License')
expect(license_file).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end
def select_template(template)
page.within('.js-license-selector-wrap') do
click_button 'Apply a template'
click_link template
wait_for_requests
end
click_button 'Choose a template...'
click_button template
wait_for_requests
end
end
......@@ -46,21 +46,21 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
end
it '"New file" button linked to new file page' do
it '"New file" button linked to IDE new file page' do
page.within('.project-buttons') do
expect(page).to have_link('New file', href: project_new_blob_path(project, project.default_branch || 'master'))
expect(page).to have_link('New file', href: presenter.ide_edit_path(project, project.default_branch || 'master'))
end
end
it '"Add README" button linked to new file populated for a README' do
it '"Add README" button linked to IDE new file populated for a README' do
page.within('.project-buttons') do
expect(page).to have_link('Add README', href: presenter.add_readme_path)
expect(page).to have_link('Add README', href: presenter.add_readme_ide_path)
end
end
it '"Add license" button linked to new file populated for a license' do
it '"Add license" button linked to IDE new file populated for a license' do
page.within('.project-buttons') do
expect(page).to have_link('Add LICENSE', href: presenter.add_license_path)
expect(page).to have_link('Add LICENSE', href: presenter.add_license_ide_path)
end
end
......@@ -74,9 +74,9 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
end
it '"New file" button linked to new file page' do
it '"New file" button linked to IDE new file page' do
page.within('.project-buttons') do
expect(page).to have_link('New file', href: project_new_blob_path(project, 'example_branch'))
expect(page).to have_link('New file', href: presenter.ide_edit_path(project, 'example_branch'))
end
end
end
......@@ -144,7 +144,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
expect(project.repository.readme).not_to be_nil
page.within('.project-buttons') do
expect(page).not_to have_link('Add README', href: presenter.add_readme_path)
expect(page).not_to have_link('Add README', href: presenter.add_readme_ide_path)
expect(page).to have_link('README', href: presenter.readme_path)
end
end
......@@ -164,7 +164,7 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
end
context 'when the project does not have a README' do
it 'shows the "Add README" button' do
it 'shows the single file editor "Add README" button' do
allow(project.repository).to receive(:readme).and_return(nil)
visit project_path(project)
......
......@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'Developer views tags' do
include RepoHelpers
let(:user) { create(:user) }
let(:group) { create(:group) }
......@@ -15,10 +17,13 @@ RSpec.describe 'Developer views tags' do
let(:project) { create(:project_empty_repo, namespace: group) }
before do
visit project_path(project)
click_on 'Add README'
fill_in :commit_message, with: 'Add a README file', visible: true
click_button 'Commit changes'
project.repository.create_file(
user,
'README.md',
'Example readme',
message: 'Add README',
branch_name: 'master')
visit project_tags_path(project)
end
......
......@@ -94,7 +94,7 @@ exports[`Applications Prometheus application shows the correct description 1`] =
Prometheus is an open-source monitoring system with
<a
class="gl-link"
href="https://docs.gitlab.com/ce/user/project/integrations/prometheus.html"
href="https://docs.gitlab.com/ee/user/project/integrations/prometheus.html"
rel="noopener noreferrer"
target="_blank"
>
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Issues::Create do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:assignee1) { create(:user) }
let_it_be(:assignee2) { create(:user) }
let_it_be(:project_label1) { create(:label, project: project) }
let_it_be(:project_label2) { create(:label, project: project) }
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:new_label1) { FFaker::Lorem.word }
let_it_be(:new_label2) { new_label1 + 'Extra' }
let(:expected_attributes) do
{
title: 'new title',
description: 'new description',
confidential: true,
due_date: Date.tomorrow,
discussion_locked: true
}
end
let(:mutation_params) do
{
project_path: project.full_path,
milestone_id: milestone.to_global_id,
labels: [project_label1.title, project_label2.title, new_label1, new_label2],
assignee_ids: [assignee1.to_global_id, assignee2.to_global_id]
}.merge(expected_attributes)
end
let(:special_params) do
{
iid: non_existing_record_id,
created_at: 2.days.ago
}
end
let(:mutation) { described_class.new(object: nil, context: { current_user: user }, field: nil) }
let(:mutated_issue) { subject[:issue] }
specify { expect(described_class).to require_graphql_authorizations(:create_issue) }
describe '#resolve' do
before do
stub_licensed_features(multiple_issue_assignees: false, issue_weights: false)
project.add_guest(assignee1)
project.add_guest(assignee2)
end
subject { mutation.resolve(mutation_params) }
context 'when the user does not have permission to create an issue' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when the user can create an issue' do
context 'when creating an issue a developer' do
before do
project.add_developer(user)
end
it 'creates issue with correct values' do
expect(mutated_issue).to have_attributes(expected_attributes)
expect(mutated_issue.milestone_id).to eq(milestone.id)
expect(mutated_issue.labels.pluck(:title)).to eq([project_label1.title, project_label2.title, new_label1, new_label2])
expect(mutated_issue.assignees.pluck(:id)).to eq([assignee1.id])
end
context 'when passing in label_ids' do
before do
mutation_params.delete(:labels)
mutation_params.merge!(label_ids: [project_label1.to_global_id, project_label2.to_global_id])
end
it 'creates issue with correct values' do
expect(mutated_issue.labels.pluck(:title)).to eq([project_label1.title, project_label2.title])
end
end
context 'when trying to create issue with restricted params' do
before do
mutation_params.merge!(special_params)
end
it 'ignores the special params' do
expect(mutated_issue).not_to be_like_time(special_params[:created_at])
expect(mutated_issue.iid).not_to eq(special_params[:iid])
end
end
end
context 'when creating an issue as owner' do
let_it_be(:user) { project.owner }
before do
mutation_params.merge!(special_params)
end
it 'sets the special params' do
expect(mutated_issue.created_at).to be_like_time(special_params[:created_at])
expect(mutated_issue.iid).to eq(special_params[:iid])
end
end
end
end
describe "#ready?" do
context 'when passing in both labels and label_ids' do
before do
mutation_params.merge!(label_ids: [project_label1.to_global_id, project_label2.to_global_id])
end
it 'raises exception when mutually exclusive params are given' do
expect { mutation.ready?(mutation_params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /one and only one of/)
end
end
context 'when passing only `discussion_to_resolve` param' do
before do
mutation_params.merge!(discussion_to_resolve: 'abc')
end
it 'raises exception when mutually exclusive params are given' do
expect { mutation.ready?(mutation_params) }
.to raise_error(Gitlab::Graphql::Errors::ArgumentError, /to resolve a discussion please also provide `merge_request_to_resolve_discussions_of` parameter/)
end
end
context 'when passing only `merge_request_to_resolve_discussions_of` param' do
before do
mutation_params.merge!(merge_request_to_resolve_discussions_of: 'abc')
end
it 'raises exception when mutually exclusive params are given' do
expect { mutation.ready?(mutation_params) }.not_to raise_error
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Create an issue' do
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:assignee1) { create(:user) }
let_it_be(:assignee2) { create(:user) }
let_it_be(:project_label1) { create(:label, project: project) }
let_it_be(:project_label2) { create(:label, project: project) }
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:new_label1) { FFaker::Lorem.word }
let_it_be(:new_label2) { FFaker::Lorem.word }
let(:input) do
{
'title' => 'new title',
'description' => 'new description',
'confidential' => true,
'dueDate' => Date.tomorrow.strftime('%Y-%m-%d')
}
end
let(:mutation) { graphql_mutation(:createIssue, input.merge('projectPath' => project.full_path, 'locked' => true)) }
let(:mutation_response) { graphql_mutation_response(:create_issue) }
context 'the user is not allowed to create an issue' do
it_behaves_like 'a mutation that returns a top-level access error'
end
context 'when user has permissions to create an issue' do
before do
project.add_developer(current_user)
end
it 'updates the issue' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['issue']).to include(input)
expect(mutation_response['issue']).to include('discussionLocked' => true)
end
end
end
......@@ -10,13 +10,15 @@ RSpec.describe 'Update of an existing issue' do
let_it_be(:issue) { create(:issue, project: project) }
let(:input) do
{
project_path: project.full_path,
iid: issue.iid.to_s,
locked: true
'iid' => issue.iid.to_s,
'title' => 'new title',
'description' => 'new description',
'confidential' => true,
'dueDate' => Date.tomorrow.strftime('%Y-%m-%d')
}
end
let(:mutation) { graphql_mutation(:update_issue, input) }
let(:mutation) { graphql_mutation(:update_issue, input.merge(project_path: project.full_path, locked: true)) }
let(:mutation_response) { graphql_mutation_response(:update_issue) }
context 'the user is not allowed to update issue' do
......@@ -32,9 +34,8 @@ RSpec.describe 'Update of an existing issue' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response['issue']).to include(
'discussionLocked' => true
)
expect(mutation_response['issue']).to include(input)
expect(mutation_response['issue']).to include('discussionLocked' => true)
end
end
end
# frozen_string_literal: true
require 'fast_spec_helper'
require 'rubocop'
require_relative '../../../../rubocop/cop/graphql/gid_expected_type'
RSpec.describe RuboCop::Cop::Graphql::GIDExpectedType, type: :rubocop do
include CopHelper
subject(:cop) { described_class.new }
it 'adds an offense when there is no expected_type parameter' do
inspect_source(<<~TYPE)
GitlabSchema.object_from_id(received_id)
TYPE
expect(cop.offenses.size).to eq 1
end
it 'does not add an offense for calls that have an expected_type parameter' do
expect_no_offenses(<<~TYPE.strip)
GitlabSchema.object_from_id("some_id", expected_type: SomeClass)
TYPE
end
end
......@@ -83,7 +83,7 @@ RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys, type: :ruboc
context 'with more than one foreign keys' do
let(:offense) do
'Creating a table with more than one foreign key at once violates our migration style guide. ' \
'For more details check the https://docs.gitlab.com/ce/development/migration_style_guide.html#examples'
'For more details check the https://docs.gitlab.com/ee/development/migration_style_guide.html#examples'
end
shared_examples 'target to high traffic table' do |dsl_method, table_name|
......
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