Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
34facbbce9
commit
30f9120ba6
|
@ -1,5 +1,4 @@
|
|||
#import "ee_else_ce/runner/graphql/list/list_item.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "~/runner/graphql/list/all_runners_connection.fragment.graphql"
|
||||
|
||||
query getAllRunners(
|
||||
$before: String
|
||||
|
@ -25,13 +24,6 @@ query getAllRunners(
|
|||
search: $search
|
||||
sort: $sort
|
||||
) {
|
||||
nodes {
|
||||
...ListItem
|
||||
adminUrl
|
||||
editAdminUrl
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
}
|
||||
...AllRunnersConnection
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
#import "ee_else_ce/runner/graphql/list/list_item.fragment.graphql"
|
||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
|
||||
fragment AllRunnersConnection on CiRunnerConnection {
|
||||
nodes {
|
||||
...ListItem
|
||||
adminUrl
|
||||
editAdminUrl
|
||||
}
|
||||
pageInfo {
|
||||
...PageInfo
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ class AutocompleteController < ApplicationController
|
|||
|
||||
skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches]
|
||||
before_action :check_search_rate_limit!, only: [:users, :projects]
|
||||
before_action :authorize_admin_project, only: :deploy_keys_with_owners
|
||||
|
||||
feature_category :users, [:users, :user]
|
||||
feature_category :projects, [:projects]
|
||||
|
@ -61,7 +60,9 @@ class AutocompleteController < ApplicationController
|
|||
end
|
||||
|
||||
def deploy_keys_with_owners
|
||||
deploy_keys = DeployKey.with_write_access_for_project(project)
|
||||
deploy_keys = Autocomplete::DeployKeysWithWriteAccessFinder
|
||||
.new(current_user, project)
|
||||
.execute
|
||||
|
||||
render json: DeployKeys::BasicDeployKeySerializer.new.represent(
|
||||
deploy_keys, { with_owner: true, user: current_user }
|
||||
|
@ -70,10 +71,6 @@ class AutocompleteController < ApplicationController
|
|||
|
||||
private
|
||||
|
||||
def authorize_admin_project
|
||||
render_403 unless Ability.allowed?(current_user, :admin_project, project)
|
||||
end
|
||||
|
||||
def project
|
||||
@project ||= Autocomplete::ProjectFinder
|
||||
.new(current_user, params)
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Groups
|
||||
class GroupTransferedEvent < ::Gitlab::EventStore::Event
|
||||
def schema
|
||||
{
|
||||
'type' => 'object',
|
||||
'properties' => {
|
||||
'group_id' => { 'type' => 'integer' },
|
||||
'old_root_namespace_id' => { 'type' => 'integer' },
|
||||
'new_root_namespace_id' => { 'type' => 'integer' }
|
||||
},
|
||||
'required' => %w[group_id old_root_namespace_id new_root_namespace_id]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Autocomplete
|
||||
# Finder for retrieving deploy keys to use for autocomplete data sources.
|
||||
class DeployKeysWithWriteAccessFinder
|
||||
attr_reader :current_user, :project
|
||||
|
||||
def initialize(current_user, project)
|
||||
@current_user = current_user
|
||||
@project = project
|
||||
end
|
||||
|
||||
def execute
|
||||
return DeployKey.none if project.nil?
|
||||
|
||||
raise Gitlab::Access::AccessDeniedError unless current_user.can?(:admin_project, project)
|
||||
|
||||
project.deploy_keys.merge(DeployKeysProject.with_write_access)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -37,12 +37,12 @@ module StorageHelper
|
|||
"about how to reduce your storage.")).html_safe % text_args[:p2]
|
||||
else
|
||||
html_escape_once(s_("UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. " \
|
||||
"View and manage your usage from %{strong_start}Group settings > Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} " \
|
||||
"about how to reduce your storage.")).html_safe % text_args[:p2]
|
||||
"Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings > Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}" \
|
||||
)).html_safe % text_args[:p2]
|
||||
end
|
||||
|
||||
{
|
||||
text_paragraph_1: html_escape_once(s_("UsageQuota|Effective %{storage_enforcement_date}, %{announcement_link_start}namespace storage limits will apply%{link_end} " \
|
||||
text_paragraph_1: html_escape_once(s_("UsageQuota|Effective %{storage_enforcement_date}, namespace storage limits will apply " \
|
||||
"to the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}" \
|
||||
"View the %{rollout_link_start}rollout schedule for this change%{link_end}.")).html_safe % text_args[:p1],
|
||||
text_paragraph_2: text_paragraph_2,
|
||||
|
@ -92,8 +92,7 @@ module StorageHelper
|
|||
storage_enforcement_date: root_ancestor.storage_enforcement_date,
|
||||
namespace_name: root_ancestor.name,
|
||||
extra_message: extra_message,
|
||||
announcement_link_start: '<a href="%{url}" >'.html_safe % { url: "#{Gitlab::Saas.community_forum_url}/t/gitlab-introduces-storage-and-transfer-limits-for-users-on-saas/69883" },
|
||||
rollout_link_start: '<a href="%{url}" >'.html_safe % { url: help_page_path('user/usage_quotas', anchor: 'tbd') },
|
||||
rollout_link_start: '<a href="%{url}" >'.html_safe % { url: help_page_path('user/usage_quotas', anchor: 'namespace-storage-limit-enforcement-schedule') },
|
||||
link_end: "</a>".html_safe
|
||||
}.merge(strong_tags),
|
||||
p2: {
|
||||
|
@ -102,7 +101,7 @@ module StorageHelper
|
|||
link_end: "</a>".html_safe
|
||||
}.merge(strong_tags),
|
||||
p3: {
|
||||
faq_link_start: '<a href="%{url}" >'.html_safe % { url: "#{Gitlab::Saas.about_pricing_url}faq-efficient-free-tier/#storage-and-transfer-limits-on-gitlab-saas-free-tier" },
|
||||
faq_link_start: '<a href="%{url}" >'.html_safe % { url: "#{Gitlab::Saas.about_pricing_url}faq-efficient-free-tier/#storage-limits-on-gitlab-saas-free-tier" },
|
||||
link_end: "</a>".html_safe
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ class Issue < ApplicationRecord
|
|||
validates :project, presence: true
|
||||
validates :issue_type, presence: true
|
||||
validates :namespace, presence: true, if: -> { project.present? }
|
||||
validates :work_item_type, presence: true
|
||||
|
||||
validate :due_date_after_start_date
|
||||
validate :parent_link_confidentiality
|
||||
|
@ -204,7 +205,7 @@ class Issue < ApplicationRecord
|
|||
scope :with_null_relative_position, -> { where(relative_position: nil) }
|
||||
scope :with_non_null_relative_position, -> { where.not(relative_position: nil) }
|
||||
|
||||
before_validation :ensure_namespace_id
|
||||
before_validation :ensure_namespace_id, :ensure_work_item_type
|
||||
|
||||
after_commit :expire_etag_cache, unless: :importing?
|
||||
after_save :ensure_metrics, unless: :importing?
|
||||
|
@ -732,6 +733,12 @@ class Issue < ApplicationRecord
|
|||
def ensure_namespace_id
|
||||
self.namespace = project.project_namespace if project
|
||||
end
|
||||
|
||||
def ensure_work_item_type
|
||||
return if work_item_type_id.present? || work_item_type_id_change&.last.present?
|
||||
|
||||
self.work_item_type = WorkItems::Type.default_by_type(issue_type)
|
||||
end
|
||||
end
|
||||
|
||||
Issue.prepend_mod_with('Issue')
|
||||
|
|
|
@ -59,6 +59,7 @@ class Member < ApplicationRecord
|
|||
},
|
||||
if: :project_bot?
|
||||
validate :access_level_inclusion
|
||||
validate :validate_member_role_access_level
|
||||
|
||||
scope :with_invited_user_state, -> do
|
||||
joins('LEFT JOIN users as invited_user ON invited_user.email = members.invite_email')
|
||||
|
@ -429,6 +430,14 @@ class Member < ApplicationRecord
|
|||
errors.add(:access_level, "is not included in the list")
|
||||
end
|
||||
|
||||
def validate_member_role_access_level
|
||||
return unless member_role_id
|
||||
|
||||
if access_level != member_role.base_access_level
|
||||
errors.add(:member_role_id, _("role's base access level does not match the access level of the membership"))
|
||||
end
|
||||
end
|
||||
|
||||
def send_invite
|
||||
# override in subclass
|
||||
end
|
||||
|
|
|
@ -669,6 +669,7 @@ class Project < ApplicationRecord
|
|||
scope :imported_from, -> (type) { where(import_type: type) }
|
||||
scope :imported, -> { where.not(import_type: nil) }
|
||||
scope :with_enabled_error_tracking, -> { joins(:error_tracking_setting).where(project_error_tracking_settings: { enabled: true }) }
|
||||
scope :last_activity_before, -> (time) { where('projects.last_activity_at < ?', time) }
|
||||
|
||||
scope :with_service_desk_key, -> (key) do
|
||||
# project_key is not indexed for now
|
||||
|
|
|
@ -36,7 +36,7 @@ module Groups
|
|||
update_crm_objects(was_root_group)
|
||||
end
|
||||
|
||||
post_update_hooks(@updated_project_ids)
|
||||
post_update_hooks(@updated_project_ids, old_root_ancestor_id)
|
||||
propagate_integrations
|
||||
update_pending_builds
|
||||
|
||||
|
@ -44,9 +44,10 @@ module Groups
|
|||
end
|
||||
|
||||
# Overridden in EE
|
||||
def post_update_hooks(updated_project_ids)
|
||||
def post_update_hooks(updated_project_ids, old_root_ancestor_id)
|
||||
refresh_project_authorizations
|
||||
refresh_descendant_groups if @new_parent_group
|
||||
publish_event(old_root_ancestor_id)
|
||||
end
|
||||
|
||||
def ensure_allowed_transfer
|
||||
|
@ -266,6 +267,18 @@ module Groups
|
|||
|
||||
CustomerRelations::IssueContact.delete_for_group(@group)
|
||||
end
|
||||
|
||||
def publish_event(old_root_ancestor_id)
|
||||
event = ::Groups::GroupTransferedEvent.new(
|
||||
data: {
|
||||
group_id: group.id,
|
||||
old_root_namespace_id: old_root_ancestor_id,
|
||||
new_root_namespace_id: group.root_ancestor.id
|
||||
}
|
||||
)
|
||||
|
||||
Gitlab::EventStore.publish(event)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: authenticate_markdown_api
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93727
|
||||
rollout_issue_url:
|
||||
milestone: '15.3'
|
||||
type: ops
|
||||
group: group::project management
|
||||
default_enabled: true
|
|
@ -779,6 +779,11 @@ Gitlab.ee do
|
|||
Settings.cron_jobs['licenses_reset_submit_license_usage_data_banner'] ||= Settingslogic.new({})
|
||||
Settings.cron_jobs['licenses_reset_submit_license_usage_data_banner']['cron'] ||= "0 0 * * *"
|
||||
Settings.cron_jobs['licenses_reset_submit_license_usage_data_banner']['job_class'] = 'Licenses::ResetSubmitLicenseUsageDataBannerWorker'
|
||||
Gitlab.com do
|
||||
Settings.cron_jobs['disable_legacy_open_source_license_for_inactive_projects'] ||= Settingslogic.new({})
|
||||
Settings.cron_jobs['disable_legacy_open_source_license_for_inactive_projects']['cron'] ||= "30 5 * * 0"
|
||||
Settings.cron_jobs['disable_legacy_open_source_license_for_inactive_projects']['job_class'] = 'Projects::DisableLegacyOpenSourceLicenseForInactiveProjectsWorker'
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddPartialLegacyOpenSourceLicenseAvailableIndex < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_project_settings_on_legacy_open_source_license_available'
|
||||
|
||||
def up
|
||||
add_concurrent_index :project_settings,
|
||||
%i[legacy_open_source_license_available],
|
||||
where: "legacy_open_source_license_available = TRUE",
|
||||
name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name(:project_settings, INDEX_NAME)
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
0e8b193943aa02c8b700c06110725fd643378cf79715d1398238abc407639c67
|
|
@ -29484,6 +29484,8 @@ CREATE UNIQUE INDEX index_project_repository_states_on_project_id ON project_rep
|
|||
|
||||
CREATE INDEX index_project_repository_storage_moves_on_project_id ON project_repository_storage_moves USING btree (project_id);
|
||||
|
||||
CREATE INDEX index_project_settings_on_legacy_open_source_license_available ON project_settings USING btree (legacy_open_source_license_available) WHERE (legacy_open_source_license_available = true);
|
||||
|
||||
CREATE INDEX index_project_settings_on_project_id_partially ON project_settings USING btree (project_id) WHERE (has_vulnerabilities IS TRUE);
|
||||
|
||||
CREATE UNIQUE INDEX index_project_settings_on_push_rule_id ON project_settings USING btree (push_rule_id);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Memory
|
||||
group: Application Performance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Memory
|
||||
group: Application Performance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Memory
|
||||
group: Application Performance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Memory
|
||||
group: Application Performance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
---
|
||||
stage: Create
|
||||
group: Source Code
|
||||
stage: Plan
|
||||
group: Project Management
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
# Markdown API **(FREE)**
|
||||
|
||||
Convert Markdown content to HTML.
|
||||
|
||||
Available only in APIv4.
|
||||
|
||||
## Required authentication
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93727) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `authenticate_markdown_api`. Enabled by default.
|
||||
|
||||
FLAG:
|
||||
On self-managed GitLab, by default this feature is enabled and authentication is required.
|
||||
To remove the requirement to authenticate, ask an administrator to
|
||||
[disable the feature flag](../administration/feature_flags.md) named `authenticate_markdown_api`.
|
||||
On GitLab.com, this feature is available.
|
||||
|
||||
All API calls to the Markdown API must be [authenticated](index.md#authentication).
|
||||
|
||||
## Render an arbitrary Markdown document
|
||||
|
||||
```plaintext
|
||||
|
@ -18,10 +32,12 @@ POST /markdown
|
|||
| --------- | ------- | ------------- | ------------------------------------------ |
|
||||
| `text` | string | yes | The Markdown text to render |
|
||||
| `gfm` | boolean | no | Render text using GitLab Flavored Markdown. Default is `false` |
|
||||
| `project` | string | no | Use `project` as a context when creating references using GitLab Flavored Markdown. [Authentication](index.md#authentication) is required if a project is not public. |
|
||||
| `project` | string | no | Use `project` as a context when creating references using GitLab Flavored Markdown |
|
||||
|
||||
```shell
|
||||
curl --header Content-Type:application/json --data '{"text":"Hello world! :tada:", "gfm":true, "project":"group_example/project_example"}' "https://gitlab.example.com/api/v4/markdown"
|
||||
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
|
||||
--header "Content-Type:application/json" \
|
||||
--data '{"text":"Hello world! :tada:", "gfm":true, "project":"group_example/project_example"}' "https://gitlab.example.com/api/v4/markdown"
|
||||
```
|
||||
|
||||
Response example:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Memory
|
||||
group: Application Performance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Data Stores
|
||||
group: Memory
|
||||
group: Application Performance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -341,6 +341,7 @@ Support includes:
|
|||
- Creating and editing the structure of tables.
|
||||
- Inserting and formatting code blocks with syntax highlighting.
|
||||
- Live preview of Mermaid, PlantUML, and Kroki diagrams ([Introduced]<https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86701> in GitLab 15.2).
|
||||
- Real-time visualization of table of contents.
|
||||
|
||||
### Use the Content Editor
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
module API
|
||||
class Markdown < ::API::Base
|
||||
feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
|
||||
before { authenticate! if Feature.enabled?(:authenticate_markdown_api, type: :ops) }
|
||||
|
||||
feature_category :team_planning
|
||||
|
||||
params do
|
||||
requires :text, type: String, desc: "The markdown text to render"
|
||||
|
|
|
@ -41934,7 +41934,7 @@ msgstr ""
|
|||
msgid "UsageQuota|Dependency proxy"
|
||||
msgstr ""
|
||||
|
||||
msgid "UsageQuota|Effective %{storage_enforcement_date}, %{announcement_link_start}namespace storage limits will apply%{link_end} to the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}View the %{rollout_link_start}rollout schedule for this change%{link_end}."
|
||||
msgid "UsageQuota|Effective %{storage_enforcement_date}, namespace storage limits will apply to the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}View the %{rollout_link_start}rollout schedule for this change%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "UsageQuota|File attachments and smaller design graphics."
|
||||
|
@ -42045,7 +42045,7 @@ msgstr ""
|
|||
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
|
||||
msgstr ""
|
||||
|
||||
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}Group settings > Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
|
||||
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings > Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
|
||||
msgstr ""
|
||||
|
||||
msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}User settings > Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
|
||||
|
@ -47170,6 +47170,9 @@ msgstr ""
|
|||
msgid "repository:"
|
||||
msgstr ""
|
||||
|
||||
msgid "role's base access level does not match the access level of the membership"
|
||||
msgstr ""
|
||||
|
||||
msgid "satisfied"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
factory :member_role do
|
||||
namespace { association(:group) }
|
||||
base_access_level { Gitlab::Access::DEVELOPER }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,53 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Autocomplete::DeployKeysWithWriteAccessFinder do
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:finder) { described_class.new(user, project) }
|
||||
|
||||
describe '#execute' do
|
||||
subject(:execute) { finder.execute }
|
||||
|
||||
context 'when project is missing' do
|
||||
let(:project) { nil }
|
||||
|
||||
it 'returns an empty ActiveRecord::Relation' do
|
||||
expect(execute).to eq(DeployKey.none)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project is present' do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
context 'and current user cannot admin project' do
|
||||
it 'raises Gitlab::Access::AccessDeniedError' do
|
||||
expect { execute }.to raise_error(Gitlab::Access::AccessDeniedError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'and current user can admin project' do
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
context 'when deploy key does not have write access to project' do
|
||||
let(:deploy_key_project) { create(:deploy_keys_project, project: project) }
|
||||
|
||||
it 'returns an empty ActiveRecord::Relation' do
|
||||
expect(execute).to eq(DeployKey.none)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when deploy key has write access to project' do
|
||||
let(:deploy_key_project) { create(:deploy_keys_project, :write_access, project: project) }
|
||||
|
||||
it 'returns the deploy keys' do
|
||||
expect(execute).to match_array([deploy_key_project.deploy_key])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -351,7 +351,7 @@ describe('Design discussions component', () => {
|
|||
createComponent();
|
||||
findReplyPlaceholder().vm.$emit('focus');
|
||||
|
||||
expect(wrapper.emitted('open-form')).toBeTruthy();
|
||||
expect(wrapper.emitted('open-form')).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe('when user is not logged in', () => {
|
||||
|
|
|
@ -239,7 +239,7 @@ describe('DiffRow', () => {
|
|||
const coverage = wrapper.find('.line-coverage.right-side');
|
||||
|
||||
expect(coverage.attributes('title')).toContain('Test coverage: 5 hits');
|
||||
expect(coverage.classes('coverage')).toBeTruthy();
|
||||
expect(coverage.classes('coverage')).toBe(true);
|
||||
});
|
||||
|
||||
it('for lines without coverage', () => {
|
||||
|
@ -248,7 +248,7 @@ describe('DiffRow', () => {
|
|||
const coverage = wrapper.find('.line-coverage.right-side');
|
||||
|
||||
expect(coverage.attributes('title')).toContain('No test coverage');
|
||||
expect(coverage.classes('no-coverage')).toBeTruthy();
|
||||
expect(coverage.classes('no-coverage')).toBe(true);
|
||||
});
|
||||
|
||||
it('for unknown lines', () => {
|
||||
|
@ -256,9 +256,9 @@ describe('DiffRow', () => {
|
|||
wrapper = createWrapper({ props, state: { coverageFiles } });
|
||||
const coverage = wrapper.find('.line-coverage.right-side');
|
||||
|
||||
expect(coverage.attributes('title')).toBeFalsy();
|
||||
expect(coverage.classes('coverage')).toBeFalsy();
|
||||
expect(coverage.classes('no-coverage')).toBeFalsy();
|
||||
expect(coverage.attributes('title')).toBeUndefined();
|
||||
expect(coverage.classes('coverage')).toBe(false);
|
||||
expect(coverage.classes('no-coverage')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -100,9 +100,9 @@ RSpec.describe StorageHelper do
|
|||
used_storage = helper.storage_counter(free_group.root_storage_statistics&.storage_size || 0)
|
||||
|
||||
expect(helper.storage_enforcement_banner_info(free_group)).to eql({
|
||||
text_paragraph_1: "Effective #{storage_enforcement_date}, <a href=\"https://forum.gitlab.com/t/gitlab-introduces-storage-and-transfer-limits-for-users-on-saas/69883\" >namespace storage limits will apply</a> to the <strong>#{free_group.name}</strong> namespace. View the <a href=\"/help/user/usage_quotas#tbd\" >rollout schedule for this change</a>.",
|
||||
text_paragraph_2: "The namespace is currently using <strong>#{used_storage}</strong> of namespace storage. View and manage your usage from <strong>Group settings > Usage quotas</strong>. <a href=\"/help/user/usage_quotas#manage-your-storage-usage\" >Learn more</a> about how to reduce your storage.",
|
||||
text_paragraph_3: "See our <a href=\"https://about.gitlab.com/pricing/faq-efficient-free-tier/#storage-and-transfer-limits-on-gitlab-saas-free-tier\" >FAQ</a> for more information.",
|
||||
text_paragraph_1: "Effective #{storage_enforcement_date}, namespace storage limits will apply to the <strong>#{free_group.name}</strong> namespace. View the <a href=\"/help/user/usage_quotas#namespace-storage-limit-enforcement-schedule\" >rollout schedule for this change</a>.",
|
||||
text_paragraph_2: "The namespace is currently using <strong>#{used_storage}</strong> of namespace storage. Group owners can view namespace storage usage and purchase more from <strong>Group settings > Usage quotas</strong>. <a href=\"/help/user/usage_quotas#manage-your-storage-usage\" >Learn more.</a>",
|
||||
text_paragraph_3: "See our <a href=\"https://about.gitlab.com/pricing/faq-efficient-free-tier/#storage-limits-on-gitlab-saas-free-tier\" >FAQ</a> for more information.",
|
||||
variant: 'warning',
|
||||
namespace_id: free_group.id,
|
||||
callouts_feature_name: 'storage_enforcement_banner_second_enforcement_threshold',
|
||||
|
@ -116,7 +116,7 @@ RSpec.describe StorageHelper do
|
|||
end
|
||||
|
||||
it 'returns a hash with the correct storage size text' do
|
||||
expect(helper.storage_enforcement_banner_info(free_group)[:text_paragraph_2]).to eql("The namespace is currently using <strong>100 KB</strong> of namespace storage. View and manage your usage from <strong>Group settings > Usage quotas</strong>. <a href=\"/help/user/usage_quotas#manage-your-storage-usage\" >Learn more</a> about how to reduce your storage.")
|
||||
expect(helper.storage_enforcement_banner_info(free_group)[:text_paragraph_2]).to eql("The namespace is currently using <strong>100 KB</strong> of namespace storage. Group owners can view namespace storage usage and purchase more from <strong>Group settings > Usage quotas</strong>. <a href=\"/help/user/usage_quotas#manage-your-storage-usage\" >Learn more.</a>")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -141,7 +141,8 @@ RSpec.describe StorageHelper do
|
|||
end
|
||||
|
||||
it 'returns the enforcement info' do
|
||||
expect(helper.storage_enforcement_banner_info(free_group)[:text_paragraph_1]).to include("Effective #{Date.current}, <a href=\"https://forum.gitlab.com/t/gitlab-introduces-storage-and-transfer-limits-for-users-on-saas/69883\" >namespace storage limits will apply</a>")
|
||||
puts helper.storage_enforcement_banner_info(free_group)[:text_paragraph_1]
|
||||
expect(helper.storage_enforcement_banner_info(free_group)[:text_paragraph_1]).to include("Effective #{Date.current}, namespace storage limits will apply")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -222,6 +222,61 @@ RSpec.describe Issue do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#ensure_work_item_type' do
|
||||
let_it_be(:issue_type) { create(:work_item_type, :issue, :default) }
|
||||
let_it_be(:task_type) { create(:work_item_type, :issue, :default) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
context 'when a type was already set' do
|
||||
let_it_be(:issue, refind: true) { create(:issue, project: project) }
|
||||
|
||||
it 'does not fetch a work item type from the DB' do
|
||||
expect(issue.work_item_type_id).to eq(issue_type.id)
|
||||
expect(WorkItems::Type).not_to receive(:default_by_type)
|
||||
|
||||
expect(issue).to be_valid
|
||||
end
|
||||
|
||||
it 'does not fetch a work item type from the DB when updating the type' do
|
||||
expect(issue.work_item_type_id).to eq(issue_type.id)
|
||||
expect(WorkItems::Type).not_to receive(:default_by_type)
|
||||
|
||||
issue.update!(work_item_type: task_type, issue_type: 'task')
|
||||
|
||||
expect(issue.work_item_type_id).to eq(task_type.id)
|
||||
end
|
||||
|
||||
it 'ensures a work item type if updated to nil' do
|
||||
expect(issue.work_item_type_id).to eq(issue_type.id)
|
||||
|
||||
expect do
|
||||
issue.update!(work_item_type: nil)
|
||||
end.to not_change(issue, :work_item_type).from(issue_type)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no type was set' do
|
||||
let_it_be(:issue, refind: true) { build(:issue, project: project, work_item_type: nil).tap { |issue| issue.save!(validate: false) } }
|
||||
|
||||
it 'sets a work item type before validation' do
|
||||
expect(issue.work_item_type_id).to be_nil
|
||||
|
||||
issue.save!
|
||||
|
||||
expect(issue.work_item_type_id).to eq(issue_type.id)
|
||||
end
|
||||
|
||||
it 'does not fetch type from DB if provided during update' do
|
||||
expect(issue.work_item_type_id).to be_nil
|
||||
expect(WorkItems::Type).not_to receive(:default_by_type)
|
||||
|
||||
issue.update!(work_item_type: task_type, issue_type: 'task')
|
||||
|
||||
expect(issue.work_item_type_id).to eq(task_type.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#record_create_action' do
|
||||
it 'records the creation action after saving' do
|
||||
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_created_action)
|
||||
|
@ -387,7 +442,7 @@ RSpec.describe Issue do
|
|||
|
||||
# TODO: Remove when NOT NULL constraint is added to the relationship
|
||||
describe '#work_item_type' do
|
||||
let(:issue) { create(:issue, :incident, project: reusable_project, work_item_type: nil) }
|
||||
let(:issue) { build(:issue, :incident, project: reusable_project, work_item_type: nil).tap { |issue| issue.save!(validate: false) } }
|
||||
|
||||
it 'returns a default type if the legacy issue does not have a work item type associated yet' do
|
||||
expect(issue.work_item_type_id).to be_nil
|
||||
|
|
|
@ -167,6 +167,36 @@ RSpec.describe Member do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'member role access level' do
|
||||
let_it_be(:member) { create(:group_member, access_level: Gitlab::Access::DEVELOPER) }
|
||||
|
||||
context 'no member role is associated' do
|
||||
it 'is valid' do
|
||||
expect(member).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'member role is associated' do
|
||||
let_it_be(:member_role) do
|
||||
create(:member_role, members: [member])
|
||||
end
|
||||
|
||||
context 'member role matches access level' do
|
||||
it 'is valid' do
|
||||
expect(member).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'member role does not match access level' do
|
||||
it 'is invalid' do
|
||||
member_role.base_access_level = Gitlab::Access::MAINTAINER
|
||||
|
||||
expect(member).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Scopes & finders' do
|
||||
|
|
|
@ -5,9 +5,11 @@ require "spec_helper"
|
|||
RSpec.describe API::Markdown do
|
||||
describe "POST /markdown" do
|
||||
let(:user) {} # No-op. It gets overwritten in the contexts below.
|
||||
let(:disable_authenticate_markdown_api) { false }
|
||||
|
||||
before do
|
||||
stub_commonmark_sourcepos_disabled
|
||||
stub_feature_flags(authenticate_markdown_api: false) if disable_authenticate_markdown_api
|
||||
|
||||
post api("/markdown", user), params: params
|
||||
end
|
||||
|
@ -21,25 +23,51 @@ RSpec.describe API::Markdown do
|
|||
end
|
||||
end
|
||||
|
||||
shared_examples "404 Project Not Found" do
|
||||
it "responses with 404 Not Found" do
|
||||
shared_examples '404 Project Not Found' do
|
||||
it 'responds with 404 Not Found' do
|
||||
expect(response).to have_gitlab_http_status(:not_found)
|
||||
expect(response.headers["Content-Type"]).to eq("application/json")
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response["message"]).to eq("404 Project Not Found")
|
||||
expect(json_response['message']).to eq('404 Project Not Found')
|
||||
end
|
||||
end
|
||||
|
||||
context "when arguments are invalid" do
|
||||
context "when text is missing" do
|
||||
shared_examples '400 Bad Request' do
|
||||
it 'responds with 400 Bad Request' do
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(response.headers['Content-Type']).to eq('application/json')
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response['error']).to eq('text is missing')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not logged in' do
|
||||
let(:user) {}
|
||||
let(:params) { {} }
|
||||
|
||||
context 'and authenticate_markdown_api turned on' do
|
||||
it 'responds with 401 Unathorized' do
|
||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||
expect(response.headers['Content-Type']).to eq('application/json')
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response['message']).to eq('401 Unauthorized')
|
||||
end
|
||||
end
|
||||
|
||||
context 'and authenticate_markdown_api turned off' do
|
||||
let(:disable_authenticate_markdown_api) { true }
|
||||
|
||||
it_behaves_like '400 Bad Request'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when arguments are invalid' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
context 'when text is missing' do
|
||||
let(:params) { {} }
|
||||
|
||||
it "responses with 400 Bad Request" do
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
expect(response.headers["Content-Type"]).to eq("application/json")
|
||||
expect(json_response).to be_a(Hash)
|
||||
expect(json_response["error"]).to eq("text is missing")
|
||||
end
|
||||
it_behaves_like '400 Bad Request'
|
||||
end
|
||||
|
||||
context "when project is not found" do
|
||||
|
@ -53,6 +81,7 @@ RSpec.describe API::Markdown do
|
|||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:issue_url) { "http://#{Gitlab.config.gitlab.host}/#{issue.project.namespace.path}/#{issue.project.path}/-/issues/#{issue.iid}" }
|
||||
let(:text) { ":tada: Hello world! :100: #{issue.to_reference}" }
|
||||
|
||||
|
@ -132,13 +161,12 @@ RSpec.describe API::Markdown do
|
|||
|
||||
context 'when not logged in' do
|
||||
let(:user) {}
|
||||
let(:disable_authenticate_markdown_api) { true }
|
||||
|
||||
it_behaves_like 'user without proper access'
|
||||
end
|
||||
|
||||
context 'when logged in as user without access' do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
it_behaves_like 'user without proper access'
|
||||
end
|
||||
|
||||
|
@ -175,8 +203,9 @@ RSpec.describe API::Markdown do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when not logged in' do
|
||||
context 'when not logged in and authenticate_markdown_api turned off' do
|
||||
let(:user) {}
|
||||
let(:disable_authenticate_markdown_api) { true }
|
||||
|
||||
it_behaves_like 'user without proper access'
|
||||
end
|
||||
|
|
|
@ -22,6 +22,18 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
|||
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
|
||||
let(:transfer_service) { described_class.new(group, user) }
|
||||
|
||||
shared_examples 'publishes a GroupTransferedEvent' do
|
||||
it do
|
||||
expect { transfer_service.execute(target) }
|
||||
.to publish_event(Groups::GroupTransferedEvent)
|
||||
.with(
|
||||
group_id: group.id,
|
||||
old_root_namespace_id: group.root_ancestor.id,
|
||||
new_root_namespace_id: target.root_ancestor.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'handling packages' do
|
||||
let_it_be(:group) { create(:group, :public) }
|
||||
let_it_be(:new_group) { create(:group, :public) }
|
||||
|
@ -895,6 +907,10 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
|||
expect { transfer_service.execute(root_group) }
|
||||
.not_to change { CustomerRelations::IssueContact.count }
|
||||
end
|
||||
|
||||
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||
let(:target) { root_group }
|
||||
end
|
||||
end
|
||||
|
||||
context 'moving down' do
|
||||
|
@ -904,6 +920,10 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
|||
expect { transfer_service.execute(another_subgroup) }
|
||||
.not_to change { CustomerRelations::IssueContact.count }
|
||||
end
|
||||
|
||||
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||
let(:target) { another_subgroup }
|
||||
end
|
||||
end
|
||||
|
||||
context 'moving sideways' do
|
||||
|
@ -913,6 +933,10 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
|||
expect { transfer_service.execute(another_subgroup) }
|
||||
.not_to change { CustomerRelations::IssueContact.count }
|
||||
end
|
||||
|
||||
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||
let(:target) { another_subgroup }
|
||||
end
|
||||
end
|
||||
|
||||
context 'moving to new root group' do
|
||||
|
@ -932,6 +956,10 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
|||
expect { transfer_service.execute(new_parent_group) }
|
||||
.not_to change { CustomerRelations::IssueContact.count }
|
||||
end
|
||||
|
||||
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||
let(:target) { new_parent_group }
|
||||
end
|
||||
end
|
||||
|
||||
context 'moving to a subgroup within a new root group' do
|
||||
|
@ -953,6 +981,10 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
|||
expect { transfer_service.execute(subgroup_in_new_parent_group) }
|
||||
.not_to change { CustomerRelations::IssueContact.count }
|
||||
end
|
||||
|
||||
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||
let(:target) { subgroup_in_new_parent_group }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with permission on the subgroup' do
|
||||
|
@ -965,6 +997,11 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
|||
|
||||
expect(transfer_service.error).to eq("Transfer failed: Group contains contacts/organizations and you don't have enough permissions to move them to the new root group.")
|
||||
end
|
||||
|
||||
it 'does not publish a GroupTransferedEvent' do
|
||||
expect { transfer_service.execute(subgroup_in_new_parent_group) }
|
||||
.not_to publish_event(Groups::GroupTransferedEvent)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,9 @@ RSpec.shared_context 'with API::Markdown Snapshot shared context' do |glfm_speci
|
|||
# rubocop:enable Layout/LineLength
|
||||
include ApiHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:api_url) { api('/markdown', user) }
|
||||
|
||||
markdown_examples, html_examples = %w[markdown.yml html.yml].map do |file_name|
|
||||
yaml = File.read("#{glfm_specification_dir}/example_snapshots/#{file_name}")
|
||||
YAML.safe_load(yaml, symbolize_names: true, aliases: true)
|
||||
|
@ -29,8 +32,6 @@ RSpec.shared_context 'with API::Markdown Snapshot shared context' do |glfm_speci
|
|||
let(:normalizations) { normalizations_by_example_name.dig(name, :html, :static, :snapshot) }
|
||||
|
||||
it "verifies conversion of GLFM to HTML", :unlimited_max_formatted_output_length do
|
||||
api_url = api "/markdown"
|
||||
|
||||
# noinspection RubyResolve
|
||||
normalized_html = normalize_html(html, normalizations)
|
||||
|
||||
|
|
|
@ -382,6 +382,7 @@ RSpec.describe 'Every Sidekiq worker' do
|
|||
'ProjectScheduleBulkRepositoryShardMovesWorker' => 3,
|
||||
'ProjectTemplateExportWorker' => false,
|
||||
'ProjectUpdateRepositoryStorageWorker' => 3,
|
||||
'Projects::DisableLegacyOpenSourceLicenseForInactiveProjectsWorker' => 3,
|
||||
'Projects::GitGarbageCollectWorker' => false,
|
||||
'Projects::InactiveProjectsDeletionNotificationWorker' => 3,
|
||||
'Projects::PostCreationWorker' => 3,
|
||||
|
|
|
@ -63,6 +63,7 @@ module Tooling
|
|||
%r{\A((ee|jh)/)?app/views/} => [:frontend, :backend],
|
||||
%r{\A((ee|jh)/)?public/} => :frontend,
|
||||
%r{\A((ee|jh)/)?spec/(javascripts|frontend|frontend_integration)/} => :frontend,
|
||||
%r{\A((ee|jh)/)?spec/contracts/consumer} => :frontend,
|
||||
%r{\A((ee|jh)/)?vendor/assets/} => :frontend,
|
||||
%r{\A((ee|jh)/)?scripts/frontend/} => :frontend,
|
||||
%r{(\A|/)(
|
||||
|
@ -117,6 +118,7 @@ module Tooling
|
|||
%r{\Alib/gitlab/ci/templates} => :ci_template,
|
||||
|
||||
%r{\A((ee|jh)/)?spec/features/} => :test,
|
||||
%r{\A((ee|jh)/)?spec/contracts/} => :test,
|
||||
%r{\A((ee|jh)/)?spec/support/shared_examples/features/} => :test,
|
||||
%r{\A((ee|jh)/)?spec/support/shared_contexts/features/} => :test,
|
||||
%r{\A((ee|jh)/)?spec/support/helpers/features/} => :test,
|
||||
|
|
Loading…
Reference in New Issue