Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
5431dbfffc
commit
2578890510
|
|
@ -198,7 +198,7 @@ export default {
|
||||||
<gl-badge
|
<gl-badge
|
||||||
v-if="isInternalNote"
|
v-if="isInternalNote"
|
||||||
v-gl-tooltip:tooltipcontainer.bottom
|
v-gl-tooltip:tooltipcontainer.bottom
|
||||||
data-testid="internalNoteIndicator"
|
data-testid="internal-note-indicator"
|
||||||
variant="warning"
|
variant="warning"
|
||||||
size="sm"
|
size="sm"
|
||||||
class="gl-ml-2"
|
class="gl-ml-2"
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ module IssuableCollectionsAction
|
||||||
|
|
||||||
def finder_options
|
def finder_options
|
||||||
issue_types = Issue::TYPES_FOR_LIST
|
issue_types = Issue::TYPES_FOR_LIST
|
||||||
issue_types = issue_types.excluding('task') unless Feature.enabled?(:work_items)
|
|
||||||
|
|
||||||
super.merge(
|
super.merge(
|
||||||
non_archived: true,
|
non_archived: true,
|
||||||
|
|
|
||||||
|
|
@ -405,7 +405,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||||
options = super
|
options = super
|
||||||
|
|
||||||
options[:issue_types] = Issue::TYPES_FOR_LIST
|
options[:issue_types] = Issue::TYPES_FOR_LIST
|
||||||
options[:issue_types] = options[:issue_types].excluding('task') unless project.work_items_feature_flag_enabled?
|
|
||||||
|
|
||||||
if service_desk?
|
if service_desk?
|
||||||
options.reject! { |key| key == 'author_username' || key == 'author_id' }
|
options.reject! { |key| key == 'author_username' || key == 'author_id' }
|
||||||
|
|
@ -432,7 +431,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||||
def create_vulnerability_issue_feedback(issue); end
|
def create_vulnerability_issue_feedback(issue); end
|
||||||
|
|
||||||
def redirect_if_task
|
def redirect_if_task
|
||||||
return render_404 if issue.task? && !project.work_items_feature_flag_enabled?
|
|
||||||
return unless issue.task?
|
return unless issue.task?
|
||||||
|
|
||||||
if Feature.enabled?(:use_iid_in_work_items_path, project.group)
|
if Feature.enabled?(:use_iid_in_work_items_path, project.group)
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,4 @@ class Projects::WorkItemsController < Projects::ApplicationController
|
||||||
|
|
||||||
feature_category :team_planning
|
feature_category :team_planning
|
||||||
urgency :low
|
urgency :low
|
||||||
|
|
||||||
def index
|
|
||||||
render_404 unless project&.work_items_feature_flag_enabled?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ module Mutations
|
||||||
include FindsProject
|
include FindsProject
|
||||||
include Mutations::WorkItems::Widgetable
|
include Mutations::WorkItems::Widgetable
|
||||||
|
|
||||||
description "Creates a work item. Available only when feature flag `work_items` is enabled."
|
description "Creates a work item."
|
||||||
|
|
||||||
authorize :create_work_item
|
authorize :create_work_item
|
||||||
|
|
||||||
|
|
@ -42,10 +42,6 @@ module Mutations
|
||||||
def resolve(project_path:, **attributes)
|
def resolve(project_path:, **attributes)
|
||||||
project = authorized_find!(project_path)
|
project = authorized_find!(project_path)
|
||||||
|
|
||||||
unless project.work_items_feature_flag_enabled?
|
|
||||||
return { errors: ['`work_items` feature flag disabled for this project'] }
|
|
||||||
end
|
|
||||||
|
|
||||||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||||
params = global_id_compatibility_params(attributes).merge(author_id: current_user.id)
|
params = global_id_compatibility_params(attributes).merge(author_id: current_user.id)
|
||||||
type = ::WorkItems::Type.find(attributes[:work_item_type_id])
|
type = ::WorkItems::Type.find(attributes[:work_item_type_id])
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@ module Mutations
|
||||||
|
|
||||||
include Mutations::SpamProtection
|
include Mutations::SpamProtection
|
||||||
|
|
||||||
description "Creates a work item from a task in another work item's description." \
|
description "Creates a work item from a task in another work item's description."
|
||||||
" Available only when feature flag `work_items` is enabled."
|
|
||||||
|
|
||||||
authorize :update_work_item
|
authorize :update_work_item
|
||||||
|
|
||||||
|
|
@ -31,10 +30,6 @@ module Mutations
|
||||||
def resolve(id:, work_item_data:)
|
def resolve(id:, work_item_data:)
|
||||||
work_item = authorized_find!(id: id)
|
work_item = authorized_find!(id: id)
|
||||||
|
|
||||||
unless work_item.project.work_items_feature_flag_enabled?
|
|
||||||
return { errors: ['`work_items` feature flag disabled for this project'] }
|
|
||||||
end
|
|
||||||
|
|
||||||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||||
|
|
||||||
result = ::WorkItems::CreateFromTaskService.new(
|
result = ::WorkItems::CreateFromTaskService.new(
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ module Mutations
|
||||||
module WorkItems
|
module WorkItems
|
||||||
class Delete < BaseMutation
|
class Delete < BaseMutation
|
||||||
graphql_name 'WorkItemDelete'
|
graphql_name 'WorkItemDelete'
|
||||||
description "Deletes a work item." \
|
description "Deletes a work item."
|
||||||
" Available only when feature flag `work_items` is enabled."
|
|
||||||
|
|
||||||
authorize :delete_work_item
|
authorize :delete_work_item
|
||||||
|
|
||||||
|
|
@ -20,10 +19,6 @@ module Mutations
|
||||||
def resolve(id:)
|
def resolve(id:)
|
||||||
work_item = authorized_find!(id: id)
|
work_item = authorized_find!(id: id)
|
||||||
|
|
||||||
unless work_item.project.work_items_feature_flag_enabled?
|
|
||||||
return { errors: ['`work_items` feature flag disabled for this project'] }
|
|
||||||
end
|
|
||||||
|
|
||||||
result = ::WorkItems::DeleteService.new(
|
result = ::WorkItems::DeleteService.new(
|
||||||
project: work_item.project,
|
project: work_item.project,
|
||||||
current_user: current_user
|
current_user: current_user
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ module Mutations
|
||||||
class DeleteTask < BaseMutation
|
class DeleteTask < BaseMutation
|
||||||
graphql_name 'WorkItemDeleteTask'
|
graphql_name 'WorkItemDeleteTask'
|
||||||
|
|
||||||
description "Deletes a task in a work item's description." \
|
description "Deletes a task in a work item's description."
|
||||||
' Available only when feature flag `work_items` is enabled.'
|
|
||||||
|
|
||||||
authorize :update_work_item
|
authorize :update_work_item
|
||||||
|
|
||||||
|
|
@ -29,10 +28,6 @@ module Mutations
|
||||||
work_item = authorized_find!(id: id)
|
work_item = authorized_find!(id: id)
|
||||||
task_data[:task] = authorized_find_task!(task_data[:id])
|
task_data[:task] = authorized_find_task!(task_data[:id])
|
||||||
|
|
||||||
unless work_item.project.work_items_feature_flag_enabled?
|
|
||||||
return { errors: ['`work_items` feature flag disabled for this project'] }
|
|
||||||
end
|
|
||||||
|
|
||||||
result = ::WorkItems::DeleteTaskService.new(
|
result = ::WorkItems::DeleteTaskService.new(
|
||||||
work_item: work_item,
|
work_item: work_item,
|
||||||
current_user: current_user,
|
current_user: current_user,
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ module Mutations
|
||||||
module WorkItems
|
module WorkItems
|
||||||
class Update < BaseMutation
|
class Update < BaseMutation
|
||||||
graphql_name 'WorkItemUpdate'
|
graphql_name 'WorkItemUpdate'
|
||||||
description "Updates a work item by Global ID." \
|
description "Updates a work item by Global ID."
|
||||||
" Available only when feature flag `work_items` is enabled."
|
|
||||||
|
|
||||||
include Mutations::SpamProtection
|
include Mutations::SpamProtection
|
||||||
include Mutations::WorkItems::UpdateArguments
|
include Mutations::WorkItems::UpdateArguments
|
||||||
|
|
@ -20,10 +19,6 @@ module Mutations
|
||||||
def resolve(id:, **attributes)
|
def resolve(id:, **attributes)
|
||||||
work_item = authorized_find!(id: id)
|
work_item = authorized_find!(id: id)
|
||||||
|
|
||||||
unless work_item.project.work_items_feature_flag_enabled?
|
|
||||||
return { errors: ['`work_items` feature flag disabled for this project'] }
|
|
||||||
end
|
|
||||||
|
|
||||||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||||
widget_params = extract_widget_params!(work_item.work_item_type, attributes)
|
widget_params = extract_widget_params!(work_item.work_item_type, attributes)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ module Mutations
|
||||||
module WorkItems
|
module WorkItems
|
||||||
class UpdateTask < BaseMutation
|
class UpdateTask < BaseMutation
|
||||||
graphql_name 'WorkItemUpdateTask'
|
graphql_name 'WorkItemUpdateTask'
|
||||||
description "Updates a work item's task by Global ID." \
|
description "Updates a work item's task by Global ID."
|
||||||
" Available only when feature flag `work_items` is enabled."
|
|
||||||
|
|
||||||
include Mutations::SpamProtection
|
include Mutations::SpamProtection
|
||||||
|
|
||||||
|
|
@ -30,10 +29,6 @@ module Mutations
|
||||||
work_item = authorized_find!(id: id)
|
work_item = authorized_find!(id: id)
|
||||||
task = authorized_find_task!(task_data_hash[:id])
|
task = authorized_find_task!(task_data_hash[:id])
|
||||||
|
|
||||||
unless work_item.project.work_items_feature_flag_enabled?
|
|
||||||
return { errors: ['`work_items` feature flag disabled for this project'] }
|
|
||||||
end
|
|
||||||
|
|
||||||
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
spam_params = ::Spam::SpamParams.new_from_request(request: context[:request])
|
||||||
|
|
||||||
::WorkItems::UpdateService.new(
|
::WorkItems::UpdateService.new(
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,7 @@ module Resolvers
|
||||||
argument :id, ::Types::GlobalIDType[::WorkItem], required: true, description: 'Global ID of the work item.'
|
argument :id, ::Types::GlobalIDType[::WorkItem], required: true, description: 'Global ID of the work item.'
|
||||||
|
|
||||||
def resolve(id:)
|
def resolve(id:)
|
||||||
work_item = authorized_find!(id: id)
|
authorized_find!(id: id)
|
||||||
return unless work_item.project.work_items_feature_flag_enabled?
|
|
||||||
|
|
||||||
work_item
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@ module Resolvers
|
||||||
' Argument is experimental and can be removed in the future without notice.'
|
' Argument is experimental and can be removed in the future without notice.'
|
||||||
|
|
||||||
def resolve(taskable: nil)
|
def resolve(taskable: nil)
|
||||||
return unless feature_flag_enabled_for_parent?(object)
|
|
||||||
|
|
||||||
# This will require a finder in the future when groups/projects get their work item types
|
# This will require a finder in the future when groups/projects get their work item types
|
||||||
# All groups/projects use the default types for now
|
# All groups/projects use the default types for now
|
||||||
base_scope = ::WorkItems::Type.default
|
base_scope = ::WorkItems::Type.default
|
||||||
|
|
@ -20,14 +18,6 @@ module Resolvers
|
||||||
|
|
||||||
base_scope.order_by_name_asc
|
base_scope.order_by_name_asc
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def feature_flag_enabled_for_parent?(parent)
|
|
||||||
return false unless parent.is_a?(::Project) || parent.is_a?(::Group)
|
|
||||||
|
|
||||||
parent.work_items_feature_flag_enabled?
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ module Resolvers
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
def resolve_with_lookahead(**args)
|
def resolve_with_lookahead(**args)
|
||||||
return WorkItem.none if resource_parent.nil? || !resource_parent.work_items_feature_flag_enabled?
|
return WorkItem.none if resource_parent.nil?
|
||||||
|
|
||||||
finder = ::WorkItems::WorkItemsFinder.new(current_user, prepare_finder_params(args))
|
finder = ::WorkItems::WorkItemsFinder.new(current_user, prepare_finder_params(args))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -231,9 +231,7 @@ module Types
|
||||||
|
|
||||||
field :work_item_types, Types::WorkItems::TypeType.connection_type,
|
field :work_item_types, Types::WorkItems::TypeType.connection_type,
|
||||||
resolver: Resolvers::WorkItems::TypesResolver,
|
resolver: Resolvers::WorkItems::TypesResolver,
|
||||||
description: 'Work item types available to the group.' \
|
description: 'Work item types available to the group.'
|
||||||
' Returns `null` if `work_items` feature flag is disabled.' \
|
|
||||||
' This flag is disabled by default, because the feature is experimental and is subject to change without notice.'
|
|
||||||
|
|
||||||
def label(title:)
|
def label(title:)
|
||||||
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
|
BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ module Types
|
||||||
end
|
end
|
||||||
|
|
||||||
value 'TASK', value: 'task',
|
value 'TASK', value: 'task',
|
||||||
description: 'Task issue type. Available only when feature flag `work_items` is enabled.',
|
description: 'Task issue type.',
|
||||||
alpha: { milestone: '15.2' }
|
alpha: { milestone: '15.2' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -513,9 +513,7 @@ module Types
|
||||||
|
|
||||||
field :work_item_types, Types::WorkItems::TypeType.connection_type,
|
field :work_item_types, Types::WorkItems::TypeType.connection_type,
|
||||||
resolver: Resolvers::WorkItems::TypesResolver,
|
resolver: Resolvers::WorkItems::TypesResolver,
|
||||||
description: 'Work item types available to the project.' \
|
description: 'Work item types available to the project.'
|
||||||
' Returns `null` if `work_items` feature flag is disabled.' \
|
|
||||||
' This flag is disabled by default, because the feature is experimental and is subject to change without notice.'
|
|
||||||
|
|
||||||
field :timelog_categories, Types::TimeTracking::TimelogCategoryType.connection_type,
|
field :timelog_categories, Types::TimeTracking::TimelogCategoryType.connection_type,
|
||||||
null: true,
|
null: true,
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ module Types
|
||||||
null: true,
|
null: true,
|
||||||
resolver: Resolvers::WorkItemResolver,
|
resolver: Resolvers::WorkItemResolver,
|
||||||
alpha: { milestone: '15.1' },
|
alpha: { milestone: '15.1' },
|
||||||
description: 'Find a work item. Returns `null` if `work_items` feature flag is disabled.'
|
description: 'Find a work item.'
|
||||||
|
|
||||||
field :merge_request, Types::MergeRequestType,
|
field :merge_request, Types::MergeRequestType,
|
||||||
null: true,
|
null: true,
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ module Routing
|
||||||
private
|
private
|
||||||
|
|
||||||
def use_work_items_path?(issue)
|
def use_work_items_path?(issue)
|
||||||
issue.issue_type == 'task' && issue.project.work_items_feature_flag_enabled?
|
issue.issue_type == 'task'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,16 @@ class Appearance < ApplicationRecord
|
||||||
include ObjectStorage::BackgroundMove
|
include ObjectStorage::BackgroundMove
|
||||||
include WithUploads
|
include WithUploads
|
||||||
|
|
||||||
|
attribute :title, default: ''
|
||||||
|
attribute :description, default: ''
|
||||||
|
attribute :new_project_guidelines, default: ''
|
||||||
|
attribute :profile_image_guidelines, default: ''
|
||||||
|
attribute :header_message, default: ''
|
||||||
|
attribute :footer_message, default: ''
|
||||||
|
attribute :message_background_color, default: '#E75E40'
|
||||||
|
attribute :message_font_color, default: '#FFFFFF'
|
||||||
|
attribute :email_header_and_footer_enabled, default: false
|
||||||
|
|
||||||
cache_markdown_field :description
|
cache_markdown_field :description
|
||||||
cache_markdown_field :new_project_guidelines
|
cache_markdown_field :new_project_guidelines
|
||||||
cache_markdown_field :profile_image_guidelines
|
cache_markdown_field :profile_image_guidelines
|
||||||
|
|
@ -20,16 +30,6 @@ class Appearance < ApplicationRecord
|
||||||
|
|
||||||
validate :single_appearance_row, on: :create
|
validate :single_appearance_row, on: :create
|
||||||
|
|
||||||
default_value_for :title, ''
|
|
||||||
default_value_for :description, ''
|
|
||||||
default_value_for :new_project_guidelines, ''
|
|
||||||
default_value_for :profile_image_guidelines, ''
|
|
||||||
default_value_for :header_message, ''
|
|
||||||
default_value_for :footer_message, ''
|
|
||||||
default_value_for :message_background_color, '#E75E40'
|
|
||||||
default_value_for :message_font_color, '#FFFFFF'
|
|
||||||
default_value_for :email_header_and_footer_enabled, false
|
|
||||||
|
|
||||||
mount_uploader :logo, AttachmentUploader
|
mount_uploader :logo, AttachmentUploader
|
||||||
mount_uploader :header_logo, AttachmentUploader
|
mount_uploader :header_logo, AttachmentUploader
|
||||||
mount_uploader :favicon, FaviconUploader
|
mount_uploader :favicon, FaviconUploader
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,9 @@ class ApplicationSetting < ApplicationRecord
|
||||||
cache_markdown_field :shared_runners_text, pipeline: :plain_markdown
|
cache_markdown_field :shared_runners_text, pipeline: :plain_markdown
|
||||||
cache_markdown_field :after_sign_up_text
|
cache_markdown_field :after_sign_up_text
|
||||||
|
|
||||||
default_value_for :id, 1
|
attribute :id, default: 1
|
||||||
default_value_for :repository_storages_weighted, {}
|
attribute :repository_storages_weighted, default: -> { {} }
|
||||||
default_value_for :kroki_formats, {}
|
attribute :kroki_formats, default: -> { {} }
|
||||||
|
|
||||||
chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds
|
chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ class BroadcastMessage < ApplicationRecord
|
||||||
validates :color, allow_blank: true, color: true
|
validates :color, allow_blank: true, color: true
|
||||||
validates :font, allow_blank: true, color: true
|
validates :font, allow_blank: true, color: true
|
||||||
|
|
||||||
default_value_for :color, '#E75E40'
|
attribute :color, default: '#E75E40'
|
||||||
default_value_for :font, '#FFFFFF'
|
attribute :font, default: '#FFFFFF'
|
||||||
|
|
||||||
CACHE_KEY = 'broadcast_message_current_json'
|
CACHE_KEY = 'broadcast_message_current_json'
|
||||||
BANNER_CACHE_KEY = 'broadcast_message_current_banner_json'
|
BANNER_CACHE_KEY = 'broadcast_message_current_banner_json'
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ module TtlExpirable
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
included do
|
included do
|
||||||
|
attribute :read_at, default: -> { Time.zone.now }
|
||||||
validates :status, presence: true
|
validates :status, presence: true
|
||||||
default_value_for :read_at, Time.zone.now
|
|
||||||
|
|
||||||
enum status: { default: 0, pending_destruction: 1, processing: 2, error: 3 }
|
enum status: { default: 0, pending_destruction: 1, processing: 2, error: 3 }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
class DependencyProxy::GroupSetting < ApplicationRecord
|
class DependencyProxy::GroupSetting < ApplicationRecord
|
||||||
belongs_to :group
|
belongs_to :group
|
||||||
|
|
||||||
validates :group, presence: true
|
attribute :enabled, default: true
|
||||||
|
|
||||||
default_value_for :enabled, true
|
validates :group, presence: true
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3006,7 +3006,7 @@ class Project < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def work_items_create_from_markdown_feature_flag_enabled?
|
def work_items_create_from_markdown_feature_flag_enabled?
|
||||||
work_items_feature_flag_enabled? && (group&.work_items_create_from_markdown_feature_flag_enabled? || Feature.enabled?(:work_items_create_from_markdown))
|
group&.work_items_create_from_markdown_feature_flag_enabled? || Feature.enabled?(:work_items_create_from_markdown)
|
||||||
end
|
end
|
||||||
|
|
||||||
def enqueue_record_project_target_platforms
|
def enqueue_record_project_target_platforms
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,10 @@ module WorkItems
|
||||||
module Widgets
|
module Widgets
|
||||||
class Hierarchy < Base
|
class Hierarchy < Base
|
||||||
def parent
|
def parent
|
||||||
return unless work_item.project.work_items_feature_flag_enabled?
|
|
||||||
|
|
||||||
work_item.work_item_parent
|
work_item.work_item_parent
|
||||||
end
|
end
|
||||||
|
|
||||||
def children
|
def children
|
||||||
return WorkItem.none unless work_item.project.work_items_feature_flag_enabled?
|
|
||||||
|
|
||||||
work_item.work_item_children
|
work_item.work_item_children
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -195,8 +195,6 @@ class ProjectPolicy < BasePolicy
|
||||||
with_scope :subject
|
with_scope :subject
|
||||||
condition(:packages_disabled) { !@subject.packages_enabled }
|
condition(:packages_disabled) { !@subject.packages_enabled }
|
||||||
|
|
||||||
condition(:work_items_enabled, scope: :subject) { project&.work_items_feature_flag_enabled? }
|
|
||||||
|
|
||||||
features = %w[
|
features = %w[
|
||||||
merge_requests
|
merge_requests
|
||||||
issues
|
issues
|
||||||
|
|
@ -304,7 +302,7 @@ class ProjectPolicy < BasePolicy
|
||||||
|
|
||||||
rule { can?(:create_issue) }.enable :create_work_item
|
rule { can?(:create_issue) }.enable :create_work_item
|
||||||
|
|
||||||
rule { can?(:create_issue) & work_items_enabled }.enable :create_task
|
rule { can?(:create_issue) }.enable :create_task
|
||||||
|
|
||||||
# These abilities are not allowed to admins that are not members of the project,
|
# These abilities are not allowed to admins that are not members of the project,
|
||||||
# that's why they are defined separately.
|
# that's why they are defined separately.
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ value_type: number
|
||||||
status: active
|
status: active
|
||||||
time_frame: all
|
time_frame: all
|
||||||
data_source: database
|
data_source: database
|
||||||
instrumentation_class: DistinctCountProjectsWithExpirationPolicyDisabledMetric
|
instrumentation_class: DistinctCountProjectsWithExpirationPolicyMetric
|
||||||
|
options:
|
||||||
|
enabled: false
|
||||||
distribution:
|
distribution:
|
||||||
- ee
|
- ee
|
||||||
- ce
|
- ce
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
table_name: audit_events_streaming_event_type_filters
|
||||||
|
classes:
|
||||||
|
- AuditEvents::Streaming::EventTypeFilter
|
||||||
|
feature_categories:
|
||||||
|
- audit_events
|
||||||
|
description: Represents a event type filter for audit event streaming
|
||||||
|
introduced_by_url:
|
||||||
|
milestone: '15.6'
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class CreateAuditEventsStreamingEventTypeFilters < Gitlab::Database::Migration[2.0]
|
||||||
|
UNIQ_INDEX_NAME = 'unique_streaming_event_type_filters_destination_id'
|
||||||
|
|
||||||
|
def change
|
||||||
|
create_table :audit_events_streaming_event_type_filters do |t|
|
||||||
|
t.timestamps_with_timezone null: false
|
||||||
|
t.references :external_audit_event_destination,
|
||||||
|
null: false,
|
||||||
|
index: false,
|
||||||
|
foreign_key: { to_table: 'audit_events_external_audit_event_destinations', on_delete: :cascade }
|
||||||
|
t.text :audit_event_type, null: false, limit: 255
|
||||||
|
|
||||||
|
t.index [:external_audit_event_destination_id, :audit_event_type], unique: true, name: UNIQ_INDEX_NAME
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
84b89419404d26f7d2783a3adf1fa7b7d89417d6533b393ae6e0de40a31e299a
|
||||||
|
|
@ -11858,6 +11858,24 @@ CREATE SEQUENCE audit_events_id_seq
|
||||||
|
|
||||||
ALTER SEQUENCE audit_events_id_seq OWNED BY audit_events.id;
|
ALTER SEQUENCE audit_events_id_seq OWNED BY audit_events.id;
|
||||||
|
|
||||||
|
CREATE TABLE audit_events_streaming_event_type_filters (
|
||||||
|
id bigint NOT NULL,
|
||||||
|
created_at timestamp with time zone NOT NULL,
|
||||||
|
updated_at timestamp with time zone NOT NULL,
|
||||||
|
external_audit_event_destination_id bigint NOT NULL,
|
||||||
|
audit_event_type text NOT NULL,
|
||||||
|
CONSTRAINT check_d20c8e5a51 CHECK ((char_length(audit_event_type) <= 255))
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE SEQUENCE audit_events_streaming_event_type_filters_id_seq
|
||||||
|
START WITH 1
|
||||||
|
INCREMENT BY 1
|
||||||
|
NO MINVALUE
|
||||||
|
NO MAXVALUE
|
||||||
|
CACHE 1;
|
||||||
|
|
||||||
|
ALTER SEQUENCE audit_events_streaming_event_type_filters_id_seq OWNED BY audit_events_streaming_event_type_filters.id;
|
||||||
|
|
||||||
CREATE TABLE audit_events_streaming_headers (
|
CREATE TABLE audit_events_streaming_headers (
|
||||||
id bigint NOT NULL,
|
id bigint NOT NULL,
|
||||||
created_at timestamp with time zone NOT NULL,
|
created_at timestamp with time zone NOT NULL,
|
||||||
|
|
@ -23397,6 +23415,8 @@ ALTER TABLE ONLY audit_events ALTER COLUMN id SET DEFAULT nextval('audit_events_
|
||||||
|
|
||||||
ALTER TABLE ONLY audit_events_external_audit_event_destinations ALTER COLUMN id SET DEFAULT nextval('audit_events_external_audit_event_destinations_id_seq'::regclass);
|
ALTER TABLE ONLY audit_events_external_audit_event_destinations ALTER COLUMN id SET DEFAULT nextval('audit_events_external_audit_event_destinations_id_seq'::regclass);
|
||||||
|
|
||||||
|
ALTER TABLE ONLY audit_events_streaming_event_type_filters ALTER COLUMN id SET DEFAULT nextval('audit_events_streaming_event_type_filters_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY audit_events_streaming_headers ALTER COLUMN id SET DEFAULT nextval('audit_events_streaming_headers_id_seq'::regclass);
|
ALTER TABLE ONLY audit_events_streaming_headers ALTER COLUMN id SET DEFAULT nextval('audit_events_streaming_headers_id_seq'::regclass);
|
||||||
|
|
||||||
ALTER TABLE ONLY authentication_events ALTER COLUMN id SET DEFAULT nextval('authentication_events_id_seq'::regclass);
|
ALTER TABLE ONLY authentication_events ALTER COLUMN id SET DEFAULT nextval('authentication_events_id_seq'::regclass);
|
||||||
|
|
@ -25066,6 +25086,9 @@ ALTER TABLE ONLY audit_events_external_audit_event_destinations
|
||||||
ALTER TABLE ONLY audit_events
|
ALTER TABLE ONLY audit_events
|
||||||
ADD CONSTRAINT audit_events_pkey PRIMARY KEY (id, created_at);
|
ADD CONSTRAINT audit_events_pkey PRIMARY KEY (id, created_at);
|
||||||
|
|
||||||
|
ALTER TABLE ONLY audit_events_streaming_event_type_filters
|
||||||
|
ADD CONSTRAINT audit_events_streaming_event_type_filters_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
ALTER TABLE ONLY audit_events_streaming_headers
|
ALTER TABLE ONLY audit_events_streaming_headers
|
||||||
ADD CONSTRAINT audit_events_streaming_headers_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT audit_events_streaming_headers_pkey PRIMARY KEY (id);
|
||||||
|
|
||||||
|
|
@ -31162,6 +31185,8 @@ CREATE UNIQUE INDEX unique_merge_request_metrics_by_merge_request_id ON merge_re
|
||||||
|
|
||||||
CREATE UNIQUE INDEX unique_projects_on_name_namespace_id ON projects USING btree (name, namespace_id);
|
CREATE UNIQUE INDEX unique_projects_on_name_namespace_id ON projects USING btree (name, namespace_id);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX unique_streaming_event_type_filters_destination_id ON audit_events_streaming_event_type_filters USING btree (external_audit_event_destination_id, audit_event_type);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX unique_vuln_merge_request_link_vuln_id_and_mr_id ON vulnerability_merge_request_links USING btree (vulnerability_id, merge_request_id);
|
CREATE UNIQUE INDEX unique_vuln_merge_request_link_vuln_id_and_mr_id ON vulnerability_merge_request_links USING btree (vulnerability_id, merge_request_id);
|
||||||
|
|
||||||
CREATE INDEX user_follow_users_followee_id_idx ON user_follow_users USING btree (followee_id);
|
CREATE INDEX user_follow_users_followee_id_idx ON user_follow_users USING btree (followee_id);
|
||||||
|
|
@ -34949,6 +34974,9 @@ ALTER TABLE ONLY dast_site_tokens
|
||||||
ALTER TABLE ONLY group_deploy_keys_groups
|
ALTER TABLE ONLY group_deploy_keys_groups
|
||||||
ADD CONSTRAINT fk_rails_e87145115d FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_e87145115d FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
ALTER TABLE ONLY audit_events_streaming_event_type_filters
|
||||||
|
ADD CONSTRAINT fk_rails_e8bd011129 FOREIGN KEY (external_audit_event_destination_id) REFERENCES audit_events_external_audit_event_destinations(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY description_versions
|
ALTER TABLE ONLY description_versions
|
||||||
ADD CONSTRAINT fk_rails_e8f4caf9c7 FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
|
ADD CONSTRAINT fk_rails_e8f4caf9c7 FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
---
|
|
||||||
# Suggestion: gitlab.Admin
|
|
||||||
#
|
|
||||||
# Checks for "admin" and recommends using the full word instead. "Admin Area" is OK.
|
|
||||||
#
|
|
||||||
# For a list of all options, see https://vale.sh/docs/topics/styles/
|
|
||||||
extends: substitution
|
|
||||||
message: 'Verify this use of the word "admin". Can it be updated to "administration", "administrator", "administer", or "Admin Area"?'
|
|
||||||
link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html
|
|
||||||
level: suggestion
|
|
||||||
ignorecase: false
|
|
||||||
swap:
|
|
||||||
'[Aa]dmin ?\w*': '(?:Admin( Area| Mode)?|[Aa]dminist(ration|rator|rators|er|rative|ering|ered))'
|
|
||||||
|
|
@ -595,7 +595,7 @@ Returns [`Vulnerability`](#vulnerability).
|
||||||
|
|
||||||
### `Query.workItem`
|
### `Query.workItem`
|
||||||
|
|
||||||
Find a work item. Returns `null` if `work_items` feature flag is disabled.
|
Find a work item.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
**Introduced** in 15.1.
|
**Introduced** in 15.1.
|
||||||
|
|
@ -5777,7 +5777,7 @@ Input type: `VulnerabilityRevertToDetectedInput`
|
||||||
|
|
||||||
### `Mutation.workItemCreate`
|
### `Mutation.workItemCreate`
|
||||||
|
|
||||||
Creates a work item. Available only when feature flag `work_items` is enabled.
|
Creates a work item.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
**Introduced** in 15.1.
|
**Introduced** in 15.1.
|
||||||
|
|
@ -5808,7 +5808,7 @@ Input type: `WorkItemCreateInput`
|
||||||
|
|
||||||
### `Mutation.workItemCreateFromTask`
|
### `Mutation.workItemCreateFromTask`
|
||||||
|
|
||||||
Creates a work item from a task in another work item's description. Available only when feature flag `work_items` is enabled.
|
Creates a work item from a task in another work item's description.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
**Introduced** in 15.1.
|
**Introduced** in 15.1.
|
||||||
|
|
@ -5835,7 +5835,7 @@ Input type: `WorkItemCreateFromTaskInput`
|
||||||
|
|
||||||
### `Mutation.workItemDelete`
|
### `Mutation.workItemDelete`
|
||||||
|
|
||||||
Deletes a work item. Available only when feature flag `work_items` is enabled.
|
Deletes a work item.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
**Introduced** in 15.1.
|
**Introduced** in 15.1.
|
||||||
|
|
@ -5860,7 +5860,7 @@ Input type: `WorkItemDeleteInput`
|
||||||
|
|
||||||
### `Mutation.workItemDeleteTask`
|
### `Mutation.workItemDeleteTask`
|
||||||
|
|
||||||
Deletes a task in a work item's description. Available only when feature flag `work_items` is enabled.
|
Deletes a task in a work item's description.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
**Introduced** in 15.1.
|
**Introduced** in 15.1.
|
||||||
|
|
@ -5887,7 +5887,7 @@ Input type: `WorkItemDeleteTaskInput`
|
||||||
|
|
||||||
### `Mutation.workItemUpdate`
|
### `Mutation.workItemUpdate`
|
||||||
|
|
||||||
Updates a work item by Global ID. Available only when feature flag `work_items` is enabled.
|
Updates a work item by Global ID.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
**Introduced** in 15.1.
|
**Introduced** in 15.1.
|
||||||
|
|
@ -5924,7 +5924,7 @@ Input type: `WorkItemUpdateInput`
|
||||||
|
|
||||||
### `Mutation.workItemUpdateTask`
|
### `Mutation.workItemUpdateTask`
|
||||||
|
|
||||||
Updates a work item's task by Global ID. Available only when feature flag `work_items` is enabled.
|
Updates a work item's task by Global ID.
|
||||||
|
|
||||||
WARNING:
|
WARNING:
|
||||||
**Introduced** in 15.1.
|
**Introduced** in 15.1.
|
||||||
|
|
@ -13585,7 +13585,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
|
||||||
|
|
||||||
##### `Group.workItemTypes`
|
##### `Group.workItemTypes`
|
||||||
|
|
||||||
Work item types available to the group. Returns `null` if `work_items` feature flag is disabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
Work item types available to the group.
|
||||||
|
|
||||||
Returns [`WorkItemTypeConnection`](#workitemtypeconnection).
|
Returns [`WorkItemTypeConnection`](#workitemtypeconnection).
|
||||||
|
|
||||||
|
|
@ -17521,7 +17521,7 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
|
||||||
|
|
||||||
##### `Project.workItemTypes`
|
##### `Project.workItemTypes`
|
||||||
|
|
||||||
Work item types available to the project. Returns `null` if `work_items` feature flag is disabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
|
Work item types available to the project.
|
||||||
|
|
||||||
Returns [`WorkItemTypeConnection`](#workitemtypeconnection).
|
Returns [`WorkItemTypeConnection`](#workitemtypeconnection).
|
||||||
|
|
||||||
|
|
@ -21052,7 +21052,7 @@ Issue type.
|
||||||
| <a id="issuetypeincident"></a>`INCIDENT` | Incident issue type. |
|
| <a id="issuetypeincident"></a>`INCIDENT` | Incident issue type. |
|
||||||
| <a id="issuetypeissue"></a>`ISSUE` | Issue issue type. |
|
| <a id="issuetypeissue"></a>`ISSUE` | Issue issue type. |
|
||||||
| <a id="issuetyperequirement"></a>`REQUIREMENT` | Requirement issue type. |
|
| <a id="issuetyperequirement"></a>`REQUIREMENT` | Requirement issue type. |
|
||||||
| <a id="issuetypetask"></a>`TASK` **{warning-solid}** | **Introduced** in 15.2. This feature is in Alpha. It can be changed or removed at any time. Task issue type. Available only when feature flag `work_items` is enabled. |
|
| <a id="issuetypetask"></a>`TASK` **{warning-solid}** | **Introduced** in 15.2. This feature is in Alpha. It can be changed or removed at any time. Task issue type. |
|
||||||
| <a id="issuetypetest_case"></a>`TEST_CASE` | Test Case issue type. |
|
| <a id="issuetypetest_case"></a>`TEST_CASE` | Test Case issue type. |
|
||||||
|
|
||||||
### `IterationSearchableField`
|
### `IterationSearchableField`
|
||||||
|
|
|
||||||
|
|
@ -120,24 +120,25 @@ To do so:
|
||||||
|
|
||||||
### Simulate a SaaS instance
|
### Simulate a SaaS instance
|
||||||
|
|
||||||
If you're developing locally and need your instance to act like the SaaS version of the product,
|
If you're developing locally and need your instance to simulate the SaaS (GitLab.com)
|
||||||
you can simulate SaaS by exporting an environment variable:
|
version of the product:
|
||||||
|
|
||||||
```shell
|
1. Export this environment variable:
|
||||||
export GITLAB_SIMULATE_SAAS=1
|
|
||||||
```
|
|
||||||
|
|
||||||
There are many ways to pass an environment variable to your local GitLab instance.
|
```shell
|
||||||
For example, you can create a `env.runit` file in the root of your GDK with the above snippet.
|
export GITLAB_SIMULATE_SAAS=1
|
||||||
|
```
|
||||||
|
|
||||||
#### Enable plans per namespace
|
There are many ways to pass an environment variable to your local GitLab instance.
|
||||||
|
For example, you can create an `env.runit` file in the root of your GDK with the above snippet.
|
||||||
|
|
||||||
To enable plans per namespace turn on the `Allow use of licensed EE features` option from the settings page.
|
1. Enable **Allow use of licensed EE features** to make licensed EE features available to projects
|
||||||
This will make licensed EE features available to projects only if the project namespace's plan includes the feature
|
only if the project namespace's plan includes the feature.
|
||||||
or if the project is public. To enable it:
|
|
||||||
|
|
||||||
1. If you are developing locally, follow the steps in [Simulate a SaaS instance](#simulate-a-saas-instance) to make the option available.
|
1. Visit **Admin > Settings > General**.
|
||||||
1. Visit Admin > Settings > General > "Account and limit" and enable "Allow use of licensed EE features".
|
1. Expand **Account and limit**.
|
||||||
|
1. Select the **Allow use of licensed EE features** checkbox.
|
||||||
|
1. Click **Save changes**.
|
||||||
|
|
||||||
### Run CI pipelines in a FOSS context
|
### Run CI pipelines in a FOSS context
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ We have built a domain-specific language (DSL) to define the metrics instrumenta
|
||||||
You can use database metrics to track data kept in the database, for example, a count of issues that exist on a given instance.
|
You can use database metrics to track data kept in the database, for example, a count of issues that exist on a given instance.
|
||||||
|
|
||||||
- `operation`: Operations for the given `relation`, one of `count`, `distinct_count`, `sum`, and `average`.
|
- `operation`: Operations for the given `relation`, one of `count`, `distinct_count`, `sum`, and `average`.
|
||||||
- `relation`: `ActiveRecord::Relation` for the objects we want to perform the `operation`.
|
- `relation`: Assigns lambda that returns the `ActiveRecord::Relation` for the objects we want to perform the `operation`. The assigned lambda can accept up to one parameter. The parameter is hashed and stored under the `options` key in the metric definition.
|
||||||
- `start`: Specifies the start value of the batch counting, by default is `relation.minimum(:id)`.
|
- `start`: Specifies the start value of the batch counting, by default is `relation.minimum(:id)`.
|
||||||
- `finish`: Specifies the end value of the batch counting, by default is `relation.maximum(:id)`.
|
- `finish`: Specifies the end value of the batch counting, by default is `relation.maximum(:id)`.
|
||||||
- `cache_start_and_finish_as`: Specifies the cache key for `start` and `finish` values and sets up caching them. Use this call when `start` and `finish` are expensive queries that should be reused between different metric calculations.
|
- `cache_start_and_finish_as`: Specifies the cache key for `start` and `finish` values and sets up caching them. Use this call when `start` and `finish` are expensive queries that should be reused between different metric calculations.
|
||||||
|
|
@ -55,10 +55,10 @@ module Gitlab
|
||||||
module Usage
|
module Usage
|
||||||
module Metrics
|
module Metrics
|
||||||
module Instrumentations
|
module Instrumentations
|
||||||
class CountBoardsMetric < DatabaseMetric
|
class CountIssuesMetric < DatabaseMetric
|
||||||
operation :count
|
operation :count
|
||||||
|
|
||||||
relation { Board }
|
relation ->(options) { Issue.where(confidential: options[:confidential]) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||||
# This specific template is located at:
|
# This specific template is located at:
|
||||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST-On-Demand-API-Scan.gitlab-ci.yml
|
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST-On-Demand-API-Scan.gitlab-ci.yml
|
||||||
|
# NOTE: This template is intended for internal GitLab use only and likely will not work properly
|
||||||
|
# in any other project. Do not include it in your pipeline configuration.
|
||||||
|
# For information on how to set up and use DAST, visit https://docs.gitlab.com/ee/user/application_security/dast/
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||||
# This specific template is located at:
|
# This specific template is located at:
|
||||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml
|
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml
|
||||||
|
# NOTE: This template is intended for internal GitLab use only and likely will not work properly
|
||||||
|
# in any other project. Do not include it in your pipeline configuration.
|
||||||
|
# For information on how to set up and use DAST, visit https://docs.gitlab.com/ee/user/application_security/dast/
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||||
# This specific template is located at:
|
# This specific template is located at:
|
||||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST-Runner-Validation.gitlab-ci.yml
|
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST-Runner-Validation.gitlab-ci.yml
|
||||||
|
# NOTE: This template is intended for internal GitLab use only and likely will not work properly
|
||||||
|
# in any other project. Do not include it in your pipeline configuration.
|
||||||
|
# For information on how to set up and use DAST, visit https://docs.gitlab.com/ee/user/application_security/dast/
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ atlassian_identities: :gitlab_main
|
||||||
audit_events_external_audit_event_destinations: :gitlab_main
|
audit_events_external_audit_event_destinations: :gitlab_main
|
||||||
audit_events: :gitlab_main
|
audit_events: :gitlab_main
|
||||||
audit_events_streaming_headers: :gitlab_main
|
audit_events_streaming_headers: :gitlab_main
|
||||||
|
audit_events_streaming_event_type_filters: :gitlab_main
|
||||||
authentication_events: :gitlab_main
|
authentication_events: :gitlab_main
|
||||||
award_emoji: :gitlab_main
|
award_emoji: :gitlab_main
|
||||||
aws_roles: :gitlab_main
|
aws_roles: :gitlab_main
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ module Gitlab
|
||||||
@metric_finish = block
|
@metric_finish = block
|
||||||
end
|
end
|
||||||
|
|
||||||
def relation(&block)
|
def relation(relation_proc = nil, &block)
|
||||||
return @metric_relation&.call unless block
|
return unless relation_proc || block
|
||||||
|
|
||||||
@metric_relation = block
|
@metric_relation = (relation_proc || block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def metric_options(&block)
|
def metric_options(&block)
|
||||||
|
|
@ -106,7 +106,11 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def relation
|
def relation
|
||||||
self.class.metric_relation.call.where(time_constraints)
|
if self.class.metric_relation.arity == 1
|
||||||
|
self.class.metric_relation.call(options)
|
||||||
|
else
|
||||||
|
self.class.metric_relation.call
|
||||||
|
end.where(time_constraints)
|
||||||
end
|
end
|
||||||
|
|
||||||
def time_constraints
|
def time_constraints
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ module Gitlab
|
||||||
module Usage
|
module Usage
|
||||||
module Metrics
|
module Metrics
|
||||||
module Instrumentations
|
module Instrumentations
|
||||||
class DistinctCountProjectsWithExpirationPolicyDisabledMetric < DatabaseMetric
|
class DistinctCountProjectsWithExpirationPolicyMetric < DatabaseMetric
|
||||||
operation :distinct_count, column: :project_id
|
operation :distinct_count, column: :project_id
|
||||||
|
|
||||||
start { Project.minimum(:id) }
|
start { Project.minimum(:id) }
|
||||||
|
|
@ -12,7 +12,7 @@ module Gitlab
|
||||||
|
|
||||||
cache_start_and_finish_as :project_id
|
cache_start_and_finish_as :project_id
|
||||||
|
|
||||||
relation { ::ContainerExpirationPolicy.where(enabled: false) }
|
relation ->(options) { ::ContainerExpirationPolicy.where(enabled: options[:enabled]) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -41,20 +41,6 @@ RSpec.describe DashboardController do
|
||||||
|
|
||||||
expect(assigns[:issues].map(&:id)).to include(task.id)
|
expect(assigns[:issues].map(&:id)).to include(task.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not include tasks in issue list' do
|
|
||||||
task = create(:work_item, :task, project: project, author: user)
|
|
||||||
|
|
||||||
get :issues, params: { author_id: user.id }
|
|
||||||
|
|
||||||
expect(assigns[:issues].map(&:id)).not_to include(task.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET merge requests' do
|
describe 'GET merge requests' do
|
||||||
|
|
|
||||||
|
|
@ -168,94 +168,56 @@ RSpec.describe Projects::IssuesController do
|
||||||
|
|
||||||
let_it_be(:task) { create(:issue, :task, project: project) }
|
let_it_be(:task) { create(:issue, :task, project: project) }
|
||||||
|
|
||||||
context 'when work_items feature flag is enabled' do
|
shared_examples 'redirects to show work item page' do
|
||||||
shared_examples 'redirects to show work item page' do
|
context 'when use_iid_in_work_items_path feature flag is disabled' do
|
||||||
context 'when use_iid_in_work_items_path feature flag is disabled' do
|
before do
|
||||||
before do
|
stub_feature_flags(use_iid_in_work_items_path: false)
|
||||||
stub_feature_flags(use_iid_in_work_items_path: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'redirects to work item page' do
|
|
||||||
make_request
|
|
||||||
|
|
||||||
expect(response).to redirect_to(project_work_items_path(project, task.id, query))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'redirects to work item page using iid' do
|
it 'redirects to work item page' do
|
||||||
make_request
|
make_request
|
||||||
|
|
||||||
expect(response).to redirect_to(project_work_items_path(project, task.iid, query.merge(iid_path: true)))
|
expect(response).to redirect_to(project_work_items_path(project, task.id, query))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'show action' do
|
it 'redirects to work item page using iid' do
|
||||||
let(:query) { { query: 'any' } }
|
make_request
|
||||||
|
|
||||||
it_behaves_like 'redirects to show work item page' do
|
expect(response).to redirect_to(project_work_items_path(project, task.iid, query.merge(iid_path: true)))
|
||||||
subject(:make_request) do
|
|
||||||
get :show, params: { namespace_id: project.namespace, project_id: project, id: task.iid, **query }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'edit action' do
|
context 'show action' do
|
||||||
let(:query) { { query: 'any' } }
|
let(:query) { { query: 'any' } }
|
||||||
|
|
||||||
it_behaves_like 'redirects to show work item page' do
|
it_behaves_like 'redirects to show work item page' do
|
||||||
subject(:make_request) do
|
subject(:make_request) do
|
||||||
get :edit, params: { namespace_id: project.namespace, project_id: project, id: task.iid, **query }
|
get :show, params: { namespace_id: project.namespace, project_id: project, id: task.iid, **query }
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'update action' do
|
|
||||||
it_behaves_like 'redirects to show work item page' do
|
|
||||||
subject(:make_request) do
|
|
||||||
put :update, params: {
|
|
||||||
namespace_id: project.namespace,
|
|
||||||
project_id: project,
|
|
||||||
id: task.iid,
|
|
||||||
issue: { title: 'New title' }
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items feature flag is disabled' do
|
context 'edit action' do
|
||||||
before do
|
let(:query) { { query: 'any' } }
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'renders 404' do
|
it_behaves_like 'redirects to show work item page' do
|
||||||
it 'renders 404 for show action' do
|
subject(:make_request) do
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
get :edit, params: { namespace_id: project.namespace, project_id: project, id: task.iid, **query }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'show action' do
|
context 'update action' do
|
||||||
before do
|
it_behaves_like 'redirects to show work item page' do
|
||||||
get :show, params: { namespace_id: project.namespace, project_id: project, id: task.iid }
|
subject(:make_request) do
|
||||||
|
put :update, params: {
|
||||||
|
namespace_id: project.namespace,
|
||||||
|
project_id: project,
|
||||||
|
id: task.iid,
|
||||||
|
issue: { title: 'New title' }
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'renders 404'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'edit action' do
|
|
||||||
before do
|
|
||||||
get :edit, params: { namespace_id: project.namespace, project_id: project, id: task.iid }
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'renders 404'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'update action' do
|
|
||||||
before do
|
|
||||||
put :update, params: { namespace_id: project.namespace, project_id: project, id: task.iid, issue: { title: 'New title' } }
|
|
||||||
end
|
|
||||||
|
|
||||||
it_behaves_like 'renders 404'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -1728,19 +1690,6 @@ RSpec.describe Projects::IssuesController do
|
||||||
expect(response).to redirect_to(project_issues_path(project))
|
expect(response).to redirect_to(project_issues_path(project))
|
||||||
expect(controller).to set_flash[:notice].to match(/\AYour CSV export has started/i)
|
expect(controller).to set_flash[:notice].to match(/\AYour CSV export has started/i)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not include tasks in CSV export' do
|
|
||||||
expect(IssuableExportCsvWorker).to receive(:perform_async)
|
|
||||||
.with(:issue, viewer.id, project.id, hash_including('issue_types' => Issue::TYPES_FOR_LIST.excluding('task')))
|
|
||||||
|
|
||||||
request_csv
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when not logged in' do
|
context 'when not logged in' do
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
RSpec.describe "Confidential notes on issues", :js do
|
||||||
|
it_behaves_like 'confidential notes on issuables' do
|
||||||
|
let_it_be(:issuable_parent) { create(:project) }
|
||||||
|
let_it_be(:issuable) { create(:issue, project: issuable_parent) }
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
||||||
|
let(:issuable_path) { project_issue_path(issuable_parent, issuable) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -5,9 +5,9 @@ require "spec_helper"
|
||||||
RSpec.describe "User comments on issue", :js do
|
RSpec.describe "User comments on issue", :js do
|
||||||
include Spec::Support::Helpers::Features::NotesHelpers
|
include Spec::Support::Helpers::Features::NotesHelpers
|
||||||
|
|
||||||
let(:project) { create(:project_empty_repo, :public) }
|
let_it_be(:project) { create(:project, :public) }
|
||||||
let(:issue) { create(:issue, project: project) }
|
let_it_be(:issue) { create(:issue, project: project) }
|
||||||
let(:user) { create(:user) }
|
let_it_be(:user) { create(:user) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
project.add_guest(user)
|
project.add_guest(user)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ describe('NoteHeader component', () => {
|
||||||
const findActionText = () => wrapper.findComponent({ ref: 'actionText' });
|
const findActionText = () => wrapper.findComponent({ ref: 'actionText' });
|
||||||
const findTimestampLink = () => wrapper.findComponent({ ref: 'noteTimestampLink' });
|
const findTimestampLink = () => wrapper.findComponent({ ref: 'noteTimestampLink' });
|
||||||
const findTimestamp = () => wrapper.findComponent({ ref: 'noteTimestamp' });
|
const findTimestamp = () => wrapper.findComponent({ ref: 'noteTimestamp' });
|
||||||
const findInternalNoteIndicator = () => wrapper.findByTestId('internalNoteIndicator');
|
const findInternalNoteIndicator = () => wrapper.findByTestId('internal-note-indicator');
|
||||||
const findSpinner = () => wrapper.findComponent({ ref: 'spinner' });
|
const findSpinner = () => wrapper.findComponent({ ref: 'spinner' });
|
||||||
|
|
||||||
const statusHtml =
|
const statusHtml =
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,6 @@ RSpec.describe Resolvers::WorkItemResolver do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { is_expected.to be_nil }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,6 @@ RSpec.describe Resolvers::WorkItems::TypesResolver do
|
||||||
expect(result.to_a).to contain_exactly(WorkItems::Type.default_by_type(:task))
|
expect(result.to_a).to contain_exactly(WorkItems::Type.default_by_type(:task))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns nil' do
|
|
||||||
expect(result).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#resolve' do
|
describe '#resolve' do
|
||||||
|
|
@ -53,15 +43,5 @@ RSpec.describe Resolvers::WorkItems::TypesResolver do
|
||||||
|
|
||||||
it_behaves_like 'a work item type resolver'
|
it_behaves_like 'a work item type resolver'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when parent is not a group or project' do
|
|
||||||
let(:object) { 'not a project/group' }
|
|
||||||
|
|
||||||
it 'returns nil because of feature flag check' do
|
|
||||||
result = resolve(described_class, obj: object, args: {})
|
|
||||||
|
|
||||||
expect(result).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -72,18 +72,6 @@ RSpec.describe Gitlab::UrlBuilder do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns an issue path for an issue of type task' do
|
|
||||||
task = create(:issue, :task)
|
|
||||||
|
|
||||||
expect(subject.build(task, only_path: true)).to eq("/#{task.project.full_path}/-/issues/#{task.iid}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when passing a compare' do
|
context 'when passing a compare' do
|
||||||
# NOTE: The Compare requires an actual repository, which isn't available
|
# NOTE: The Compare requires an actual repository, which isn't available
|
||||||
# with the `build_stubbed` strategy used by the table tests above
|
# with the `build_stubbed` strategy used by the table tests above
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
|
||||||
described_class.tap do |metric_class|
|
described_class.tap do |metric_class|
|
||||||
metric_class.relation { Issue }
|
metric_class.relation { Issue }
|
||||||
metric_class.operation :count
|
metric_class.operation :count
|
||||||
metric_class.start { metric_class.relation.minimum(:id) }
|
metric_class.start { Issue.minimum(:id) }
|
||||||
metric_class.finish { metric_class.relation.maximum(:id) }
|
metric_class.finish { Issue.maximum(:id) }
|
||||||
end.new(time_frame: 'all')
|
end.new(time_frame: 'all')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -41,8 +41,8 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
|
||||||
described_class.tap do |metric_class|
|
described_class.tap do |metric_class|
|
||||||
metric_class.relation { Issue }
|
metric_class.relation { Issue }
|
||||||
metric_class.operation :count
|
metric_class.operation :count
|
||||||
metric_class.start { metric_class.relation.minimum(:id) }
|
metric_class.start { Issue.minimum(:id) }
|
||||||
metric_class.finish { metric_class.relation.maximum(:id) }
|
metric_class.finish { Issue.maximum(:id) }
|
||||||
metric_class.metric_options { { batch_size: 12345 } }
|
metric_class.metric_options { { batch_size: 12345 } }
|
||||||
end.new(time_frame: 'all')
|
end.new(time_frame: 'all')
|
||||||
end
|
end
|
||||||
|
|
@ -103,8 +103,8 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
|
||||||
described_class.tap do |metric_class|
|
described_class.tap do |metric_class|
|
||||||
metric_class.relation { Issue }
|
metric_class.relation { Issue }
|
||||||
metric_class.operation :count
|
metric_class.operation :count
|
||||||
metric_class.start { metric_class.relation.minimum(:id) }
|
metric_class.start { Issue.minimum(:id) }
|
||||||
metric_class.finish { metric_class.relation.maximum(:id) }
|
metric_class.finish { Issue.maximum(:id) }
|
||||||
metric_class.cache_start_and_finish_as :special_issue_count
|
metric_class.cache_start_and_finish_as :special_issue_count
|
||||||
end.new(time_frame: 'all')
|
end.new(time_frame: 'all')
|
||||||
end
|
end
|
||||||
|
|
@ -126,8 +126,8 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
|
||||||
described_class.tap do |metric_class|
|
described_class.tap do |metric_class|
|
||||||
metric_class.relation { Issue }
|
metric_class.relation { Issue }
|
||||||
metric_class.operation(:estimate_batch_distinct_count)
|
metric_class.operation(:estimate_batch_distinct_count)
|
||||||
metric_class.start { metric_class.relation.minimum(:id) }
|
metric_class.start { Issue.minimum(:id) }
|
||||||
metric_class.finish { metric_class.relation.maximum(:id) }
|
metric_class.finish { Issue.maximum(:id) }
|
||||||
end.new(time_frame: 'all')
|
end.new(time_frame: 'all')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -144,8 +144,8 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
|
||||||
metric_class.operation(:estimate_batch_distinct_count) do |result|
|
metric_class.operation(:estimate_batch_distinct_count) do |result|
|
||||||
result.foo
|
result.foo
|
||||||
end
|
end
|
||||||
metric_class.start { metric_class.relation.minimum(:id) }
|
metric_class.start { Issue.minimum(:id) }
|
||||||
metric_class.finish { metric_class.relation.maximum(:id) }
|
metric_class.finish { Issue.maximum(:id) }
|
||||||
end.new(time_frame: 'all')
|
end.new(time_frame: 'all')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -192,6 +192,22 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DatabaseMetric do
|
||||||
expect(subject.value).to eq(1)
|
expect(subject.value).to eq(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with additional parameters passed via options' do
|
||||||
|
subject do
|
||||||
|
described_class.tap do |metric_class|
|
||||||
|
metric_class.relation ->(options) { Issue.where(confidential: options[:confidential]) }
|
||||||
|
metric_class.operation :count
|
||||||
|
end.new(time_frame: '28d', options: { confidential: true })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'calculates a correct result' do
|
||||||
|
create(:issue, last_edited_at: 5.days.ago, confidential: true)
|
||||||
|
create(:issue, last_edited_at: 5.days.ago, confidential: false)
|
||||||
|
|
||||||
|
expect(subject.value).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with unimplemented operation method used' do
|
context 'with unimplemented operation method used' do
|
||||||
|
|
|
||||||
|
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DistinctCountProjectsWithExpirationPolicyDisabledMetric do
|
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::DistinctCountProjectsWithExpirationPolicyMetric do
|
||||||
before_all do
|
before_all do
|
||||||
create(:container_expiration_policy, enabled: false)
|
create(:container_expiration_policy, enabled: false)
|
||||||
create(:container_expiration_policy, enabled: false, created_at: 29.days.ago)
|
create(:container_expiration_policy, enabled: false, created_at: 29.days.ago)
|
||||||
create(:container_expiration_policy, enabled: true)
|
create(:container_expiration_policy, enabled: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'a correct instrumented metric value', { time_frame: '28d' } do
|
it_behaves_like 'a correct instrumented metric value', { time_frame: '28d', options: { enabled: false } } do
|
||||||
let(:expected_value) { 1 }
|
let(:expected_value) { 1 }
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'a correct instrumented metric value', { time_frame: 'all' } do
|
it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', options: { enabled: false } } do
|
||||||
let(:expected_value) { 2 }
|
let(:expected_value) { 2 }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -10,6 +10,20 @@ RSpec.describe Appearance do
|
||||||
|
|
||||||
it { is_expected.to have_many(:uploads) }
|
it { is_expected.to have_many(:uploads) }
|
||||||
|
|
||||||
|
describe 'default values' do
|
||||||
|
subject(:appearance) { described_class.new }
|
||||||
|
|
||||||
|
it { expect(appearance.title).to eq('') }
|
||||||
|
it { expect(appearance.description).to eq('') }
|
||||||
|
it { expect(appearance.new_project_guidelines).to eq('') }
|
||||||
|
it { expect(appearance.profile_image_guidelines).to eq('') }
|
||||||
|
it { expect(appearance.header_message).to eq('') }
|
||||||
|
it { expect(appearance.footer_message).to eq('') }
|
||||||
|
it { expect(appearance.message_background_color).to eq('#E75E40') }
|
||||||
|
it { expect(appearance.message_font_color).to eq('#FFFFFF') }
|
||||||
|
it { expect(appearance.email_header_and_footer_enabled).to eq(false) }
|
||||||
|
end
|
||||||
|
|
||||||
describe '#single_appearance_row' do
|
describe '#single_appearance_row' do
|
||||||
it 'adds an error when more than 1 row exists' do
|
it 'adds an error when more than 1 row exists' do
|
||||||
create(:appearance)
|
create(:appearance)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,14 @@ RSpec.describe ApplicationSetting do
|
||||||
it { expect(setting.uuid).to be_present }
|
it { expect(setting.uuid).to be_present }
|
||||||
it { expect(setting).to have_db_column(:auto_devops_enabled) }
|
it { expect(setting).to have_db_column(:auto_devops_enabled) }
|
||||||
|
|
||||||
|
describe 'default values' do
|
||||||
|
subject(:setting) { described_class.new }
|
||||||
|
|
||||||
|
it { expect(setting.id).to eq(1) }
|
||||||
|
it { expect(setting.repository_storages_weighted).to eq({}) }
|
||||||
|
it { expect(setting.kroki_formats).to eq({}) }
|
||||||
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
describe 'validations' do
|
||||||
let(:http) { 'http://example.com' }
|
let(:http) { 'http://example.com' }
|
||||||
let(:https) { 'https://example.com' }
|
let(:https) { 'https://example.com' }
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,13 @@ RSpec.describe BroadcastMessage do
|
||||||
it { is_expected.to validate_inclusion_of(:target_access_levels).in_array(described_class::ALLOWED_TARGET_ACCESS_LEVELS) }
|
it { is_expected.to validate_inclusion_of(:target_access_levels).in_array(described_class::ALLOWED_TARGET_ACCESS_LEVELS) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'default values' do
|
||||||
|
subject(:message) { described_class.new }
|
||||||
|
|
||||||
|
it { expect(message.color).to eq('#E75E40') }
|
||||||
|
it { expect(message.font).to eq('#FFFFFF') }
|
||||||
|
end
|
||||||
|
|
||||||
shared_examples 'time constrainted' do |broadcast_type|
|
shared_examples 'time constrainted' do |broadcast_type|
|
||||||
it 'returns message if time match' do
|
it 'returns message if time match' do
|
||||||
message = create(:broadcast_message, broadcast_type: broadcast_type)
|
message = create(:broadcast_message, broadcast_type: broadcast_type)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,11 @@ RSpec.describe DependencyProxy::GroupSetting, type: :model do
|
||||||
it { is_expected.to belong_to(:group) }
|
it { is_expected.to belong_to(:group) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'default values' do
|
||||||
|
it { is_expected.to be_enabled }
|
||||||
|
it { expect(described_class.new(enabled: false)).not_to be_enabled }
|
||||||
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
describe 'validations' do
|
||||||
it { is_expected.to validate_presence_of(:group) }
|
it { is_expected.to validate_presence_of(:group) }
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -26,22 +26,6 @@ RSpec.describe WorkItems::Widgets::Hierarchy do
|
||||||
subject { described_class.new(parent_link.work_item).parent }
|
subject { described_class.new(parent_link.work_item).parent }
|
||||||
|
|
||||||
it { is_expected.to eq(parent_link.work_item_parent) }
|
it { is_expected.to eq(parent_link.work_item_parent) }
|
||||||
|
|
||||||
context 'when work_items flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { is_expected.to be_nil }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when work_items flag is enabled for the parent group' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: group)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { is_expected.to eq(parent_link.work_item_parent) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#children' do
|
describe '#children' do
|
||||||
|
|
@ -51,21 +35,5 @@ RSpec.describe WorkItems::Widgets::Hierarchy do
|
||||||
subject { described_class.new(work_item_parent).children }
|
subject { described_class.new(work_item_parent).children }
|
||||||
|
|
||||||
it { is_expected.to contain_exactly(parent_link1.work_item, parent_link2.work_item) }
|
it { is_expected.to contain_exactly(parent_link1.work_item, parent_link2.work_item) }
|
||||||
|
|
||||||
context 'when work_items flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { is_expected.to be_empty }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when work_items flag is enabled for the parent group' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: group)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { is_expected.to contain_exactly(parent_link1.work_item, parent_link2.work_item) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -629,17 +629,7 @@ RSpec.describe ProjectPolicy do
|
||||||
context 'when user is member of the project' do
|
context 'when user is member of the project' do
|
||||||
let(:current_user) { developer }
|
let(:current_user) { developer }
|
||||||
|
|
||||||
context 'when work_items feature flag is enabled' do
|
it { expect_allowed(:create_task) }
|
||||||
it { expect_allowed(:create_task) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it { expect_disallowed(:create_task) }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,32 +26,20 @@ RSpec.describe IssuePresenter do
|
||||||
context 'when issue type is task' do
|
context 'when issue type is task' do
|
||||||
let(:presented_issue) { task }
|
let(:presented_issue) { task }
|
||||||
|
|
||||||
context 'when work_items feature flag is enabled' do
|
context 'when use_iid_in_work_items_path feature flag is disabled' do
|
||||||
context 'when use_iid_in_work_items_path feature flag is disabled' do
|
before do
|
||||||
before do
|
stub_feature_flags(use_iid_in_work_items_path: false)
|
||||||
stub_feature_flags(use_iid_in_work_items_path: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns a work item url for the task' do
|
|
||||||
expect(presenter.web_url).to eq(project_work_items_url(project, work_items_path: presented_issue.id))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a work item url using iid for the task' do
|
it 'returns a work item url for the task' do
|
||||||
expect(presenter.web_url).to eq(
|
expect(presenter.web_url).to eq(project_work_items_url(project, work_items_path: presented_issue.id))
|
||||||
project_work_items_url(project, work_items_path: presented_issue.iid, iid_path: true)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items feature flag is disabled' do
|
it 'returns a work item url using iid for the task' do
|
||||||
before do
|
expect(presenter.web_url).to eq(
|
||||||
stub_feature_flags(work_items: false)
|
project_work_items_url(project, work_items_path: presented_issue.iid, iid_path: true)
|
||||||
end
|
)
|
||||||
|
|
||||||
it 'returns an issue url for the task' do
|
|
||||||
expect(presenter.web_url).to eq("http://localhost/#{group.name}/#{project.name}/-/issues/#{presented_issue.iid}")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -78,32 +66,20 @@ RSpec.describe IssuePresenter do
|
||||||
context 'when issue type is task' do
|
context 'when issue type is task' do
|
||||||
let(:presented_issue) { task }
|
let(:presented_issue) { task }
|
||||||
|
|
||||||
context 'when work_items feature flag is enabled' do
|
context 'when use_iid_in_work_items_path feature flag is disabled' do
|
||||||
context 'when use_iid_in_work_items_path feature flag is disabled' do
|
before do
|
||||||
before do
|
stub_feature_flags(use_iid_in_work_items_path: false)
|
||||||
stub_feature_flags(use_iid_in_work_items_path: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns a work item path for the task' do
|
|
||||||
expect(presenter.issue_path).to eq(project_work_items_path(project, work_items_path: presented_issue.id))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a work item path using iid for the task' do
|
it 'returns a work item path for the task' do
|
||||||
expect(presenter.issue_path).to eq(
|
expect(presenter.issue_path).to eq(project_work_items_path(project, work_items_path: presented_issue.id))
|
||||||
project_work_items_path(project, work_items_path: presented_issue.iid, iid_path: true)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items feature flag is disabled' do
|
it 'returns a work item path using iid for the task' do
|
||||||
before do
|
expect(presenter.issue_path).to eq(
|
||||||
stub_feature_flags(work_items: false)
|
project_work_items_path(project, work_items_path: presented_issue.iid, iid_path: true)
|
||||||
end
|
)
|
||||||
|
|
||||||
it 'returns an issue path for the task' do
|
|
||||||
expect(presenter.issue_path).to eq("/#{group.name}/#{project.name}/-/issues/#{presented_issue.iid}")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -57,15 +57,4 @@ RSpec.describe 'getting a list of work item types for a group' do
|
||||||
expect(graphql_data).to eq('group' => nil)
|
expect(graphql_data).to eq('group' => nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
post_graphql(query, current_user: current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns null' do
|
|
||||||
expect(graphql_data.dig('group', 'workItemTypes')).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -58,34 +58,15 @@ RSpec.describe 'Create an issue' do
|
||||||
input['type'] = 'TASK'
|
input['type'] = 'TASK'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items feature flag is disabled' do
|
it 'creates an issue with TASK type' do
|
||||||
before do
|
expect do
|
||||||
stub_feature_flags(work_items: false)
|
post_graphql_mutation(mutation, current_user: current_user)
|
||||||
end
|
end.to change(Issue, :count).by(1)
|
||||||
|
|
||||||
it 'creates an issue with the default ISSUE type' do
|
created_issue = Issue.last
|
||||||
expect do
|
|
||||||
post_graphql_mutation(mutation, current_user: current_user)
|
|
||||||
end.to change(Issue, :count).by(1)
|
|
||||||
|
|
||||||
created_issue = Issue.last
|
expect(created_issue.work_item_type.base_type).to eq('task')
|
||||||
|
expect(created_issue.issue_type).to eq('task')
|
||||||
expect(created_issue.work_item_type.base_type).to eq('issue')
|
|
||||||
expect(created_issue.issue_type).to eq('issue')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when work_items feature flag is enabled' do
|
|
||||||
it 'creates an issue with TASK type' do
|
|
||||||
expect do
|
|
||||||
post_graphql_mutation(mutation, current_user: current_user)
|
|
||||||
end.to change(Issue, :count).by(1)
|
|
||||||
|
|
||||||
created_issue = Issue.last
|
|
||||||
|
|
||||||
expect(created_issue.work_item_type.base_type).to eq('task')
|
|
||||||
expect(created_issue.issue_type).to eq('task')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,19 +71,5 @@ RSpec.describe "Create a work item from a task in a work item's description" do
|
||||||
it_behaves_like 'has spam protection' do
|
it_behaves_like 'has spam protection' do
|
||||||
let(:mutation_class) { ::Mutations::WorkItems::CreateFromTask }
|
let(:mutation_class) { ::Mutations::WorkItems::CreateFromTask }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does nothing and returns and error' do
|
|
||||||
expect do
|
|
||||||
post_graphql_mutation(mutation, current_user: current_user)
|
|
||||||
end.to not_change(WorkItem, :count)
|
|
||||||
|
|
||||||
expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -215,19 +215,5 @@ RSpec.describe 'Create a work item' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not create the work item and returns an error' do
|
|
||||||
expect do
|
|
||||||
post_graphql_mutation(mutation, current_user: current_user)
|
|
||||||
end.to not_change(WorkItem, :count)
|
|
||||||
|
|
||||||
expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -31,19 +31,5 @@ RSpec.describe 'Delete a work item' do
|
||||||
expect(response).to have_gitlab_http_status(:success)
|
expect(response).to have_gitlab_http_status(:success)
|
||||||
expect(mutation_response['project']).to include('id' => work_item.project.to_global_id.to_s)
|
expect(mutation_response['project']).to include('id' => work_item.project.to_global_id.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not delete the work item' do
|
|
||||||
expect do
|
|
||||||
post_graphql_mutation(mutation, current_user: current_user)
|
|
||||||
end.to not_change(WorkItem, :count)
|
|
||||||
|
|
||||||
expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -75,19 +75,5 @@ RSpec.describe "Delete a task in a work item's description" do
|
||||||
expect(mutation_response['errors']).to contain_exactly('Stale work item. Check lock version')
|
expect(mutation_response['errors']).to contain_exactly('Stale work item. Check lock version')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does nothing and returns and error' do
|
|
||||||
expect do
|
|
||||||
post_graphql_mutation(mutation, current_user: current_user)
|
|
||||||
end.to not_change(WorkItem, :count)
|
|
||||||
|
|
||||||
expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -644,20 +644,5 @@ RSpec.describe 'Update a work item' do
|
||||||
it_behaves_like 'a mutation that returns top-level errors',
|
it_behaves_like 'a mutation that returns top-level errors',
|
||||||
errors: ["Following widget keys are not supported by some_test_case_name type: [:hierarchy_widget]"]
|
errors: ["Following widget keys are not supported by some_test_case_name type: [:hierarchy_widget]"]
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not update the work item and returns and error' do
|
|
||||||
expect do
|
|
||||||
post_graphql_mutation(mutation, current_user: current_user)
|
|
||||||
work_item.reload
|
|
||||||
end.to not_change(work_item, :title)
|
|
||||||
|
|
||||||
expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -74,22 +74,6 @@ RSpec.describe 'Update a work item task' do
|
||||||
it_behaves_like 'has spam protection' do
|
it_behaves_like 'has spam protection' do
|
||||||
let(:mutation_class) { ::Mutations::WorkItems::UpdateTask }
|
let(:mutation_class) { ::Mutations::WorkItems::UpdateTask }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not update the task item and returns and error' do
|
|
||||||
expect do
|
|
||||||
post_graphql_mutation(mutation, current_user: current_user)
|
|
||||||
work_item.reload
|
|
||||||
task.reload
|
|
||||||
end.to not_change(task, :title)
|
|
||||||
|
|
||||||
expect(mutation_response['errors']).to contain_exactly('`work_items` feature flag disabled for this project')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user does not have permissions to update a work item' do
|
context 'when user does not have permissions to update a work item' do
|
||||||
|
|
|
||||||
|
|
@ -57,15 +57,4 @@ RSpec.describe 'getting a list of work item types for a project' do
|
||||||
expect(graphql_data).to eq('project' => nil)
|
expect(graphql_data).to eq('project' => nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
post_graphql(query, current_user: current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns null' do
|
|
||||||
expect(graphql_data.dig('project', 'workItemTypes')).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -130,18 +130,6 @@ RSpec.describe 'getting a work item list for a project' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when work_items flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns an empty list' do
|
|
||||||
post_graphql(query)
|
|
||||||
|
|
||||||
expect(items_data).to eq([])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns only items visible to user' do
|
it 'returns only items visible to user' do
|
||||||
post_graphql(query, current_user: current_user)
|
post_graphql(query, current_user: current_user)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -357,16 +357,4 @@ RSpec.describe 'Query.work_item(id)' do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns nil' do
|
|
||||||
post_graphql(query)
|
|
||||||
|
|
||||||
expect(work_item_data).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -15,24 +15,10 @@ RSpec.describe 'Work Items' do
|
||||||
sign_in(developer)
|
sign_in(developer)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the work_items feature flag is enabled' do
|
it 'renders index' do
|
||||||
it 'renders index' do
|
get project_work_items_url(work_item.project, work_items_path: work_item.id)
|
||||||
get project_work_items_url(work_item.project, work_items_path: work_item.id)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the work_items feature flag is disabled' do
|
|
||||||
before do
|
|
||||||
stub_feature_flags(work_items: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns 404' do
|
|
||||||
get project_work_items_url(work_item.project, work_items_path: work_item.id)
|
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ RSpec.shared_examples 'a correct instrumented metric value' do |params|
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
if described_class.respond_to?(:relation) && described_class.relation.respond_to?(:connection)
|
if metric.respond_to?(:relation, true) && metric.send(:relation).respond_to?(:connection)
|
||||||
allow(described_class.relation.connection).to receive(:transaction_open?).and_return(false)
|
allow(metric.send(:relation).connection).to receive(:transaction_open?).and_return(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
RSpec.shared_examples 'confidential notes on issuables' do
|
||||||
|
include Spec::Support::Helpers::Features::NotesHelpers
|
||||||
|
|
||||||
|
context 'when user does not have permissions' do
|
||||||
|
it 'does not show confidential note checkbox' do
|
||||||
|
issuable_parent.add_guest(user)
|
||||||
|
sign_in(user)
|
||||||
|
visit(issuable_path)
|
||||||
|
|
||||||
|
page.within('.new-note') do
|
||||||
|
expect(page).not_to have_selector('[data-testid="internal-note-checkbox"]')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user has permissions' do
|
||||||
|
it 'creates confidential note' do
|
||||||
|
issuable_parent.add_reporter(user)
|
||||||
|
sign_in(user)
|
||||||
|
visit(issuable_path)
|
||||||
|
|
||||||
|
find('[data-testid="internal-note-checkbox"]').click
|
||||||
|
add_note('Confidential note')
|
||||||
|
|
||||||
|
page.within('.note-header') do
|
||||||
|
expect(page).to have_selector('[data-testid="internal-note-indicator"]')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -7,6 +7,11 @@ RSpec.shared_examples 'ttl_expirable' do
|
||||||
|
|
||||||
it_behaves_like 'having unique enum values'
|
it_behaves_like 'having unique enum values'
|
||||||
|
|
||||||
|
describe 'default values', :freeze_time do
|
||||||
|
it { expect(described_class.new.read_at).to be_like_time(Time.zone.now) }
|
||||||
|
it { expect(described_class.new(read_at: 1.day.ago).read_at).to be_like_time(1.day.ago) }
|
||||||
|
end
|
||||||
|
|
||||||
describe 'validations' do
|
describe 'validations' do
|
||||||
it { is_expected.to validate_presence_of(:status) }
|
it { is_expected.to validate_presence_of(:status) }
|
||||||
end
|
end
|
||||||
|
|
@ -38,7 +43,7 @@ RSpec.shared_examples 'ttl_expirable' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#read', :freeze_time do
|
describe '#read!', :freeze_time do
|
||||||
let_it_be(:old_read_at) { 1.day.ago }
|
let_it_be(:old_read_at) { 1.day.ago }
|
||||||
let_it_be(:item1) { create(class_symbol, read_at: old_read_at) }
|
let_it_be(:item1) { create(class_symbol, read_at: old_read_at) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ func TestUploadHandlerSendingToExternalStorageAndSupportRequestTimeout(t *testin
|
||||||
RemoteObject: api.RemoteObject{
|
RemoteObject: api.RemoteObject{
|
||||||
StoreURL: storeServer.URL + "/url/put",
|
StoreURL: storeServer.URL + "/url/put",
|
||||||
ID: "store-id",
|
ID: "store-id",
|
||||||
Timeout: 0.001,
|
Timeout: 0.1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue