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 "~/runner/graphql/list/all_runners_connection.fragment.graphql"
|
||||||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
|
||||||
|
|
||||||
query getAllRunners(
|
query getAllRunners(
|
||||||
$before: String
|
$before: String
|
||||||
|
@ -25,13 +24,6 @@ query getAllRunners(
|
||||||
search: $search
|
search: $search
|
||||||
sort: $sort
|
sort: $sort
|
||||||
) {
|
) {
|
||||||
nodes {
|
...AllRunnersConnection
|
||||||
...ListItem
|
|
||||||
adminUrl
|
|
||||||
editAdminUrl
|
|
||||||
}
|
|
||||||
pageInfo {
|
|
||||||
...PageInfo
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches]
|
||||||
before_action :check_search_rate_limit!, only: [:users, :projects]
|
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 :users, [:users, :user]
|
||||||
feature_category :projects, [:projects]
|
feature_category :projects, [:projects]
|
||||||
|
@ -61,7 +60,9 @@ class AutocompleteController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def deploy_keys_with_owners
|
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(
|
render json: DeployKeys::BasicDeployKeySerializer.new.represent(
|
||||||
deploy_keys, { with_owner: true, user: current_user }
|
deploy_keys, { with_owner: true, user: current_user }
|
||||||
|
@ -70,10 +71,6 @@ class AutocompleteController < ApplicationController
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def authorize_admin_project
|
|
||||||
render_403 unless Ability.allowed?(current_user, :admin_project, project)
|
|
||||||
end
|
|
||||||
|
|
||||||
def project
|
def project
|
||||||
@project ||= Autocomplete::ProjectFinder
|
@project ||= Autocomplete::ProjectFinder
|
||||||
.new(current_user, params)
|
.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]
|
"about how to reduce your storage.")).html_safe % text_args[:p2]
|
||||||
else
|
else
|
||||||
html_escape_once(s_("UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. " \
|
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} " \
|
"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}" \
|
||||||
"about how to reduce your storage.")).html_safe % text_args[:p2]
|
)).html_safe % text_args[:p2]
|
||||||
end
|
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}" \
|
"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],
|
"View the %{rollout_link_start}rollout schedule for this change%{link_end}.")).html_safe % text_args[:p1],
|
||||||
text_paragraph_2: text_paragraph_2,
|
text_paragraph_2: text_paragraph_2,
|
||||||
|
@ -92,8 +92,7 @@ module StorageHelper
|
||||||
storage_enforcement_date: root_ancestor.storage_enforcement_date,
|
storage_enforcement_date: root_ancestor.storage_enforcement_date,
|
||||||
namespace_name: root_ancestor.name,
|
namespace_name: root_ancestor.name,
|
||||||
extra_message: extra_message,
|
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: 'namespace-storage-limit-enforcement-schedule') },
|
||||||
rollout_link_start: '<a href="%{url}" >'.html_safe % { url: help_page_path('user/usage_quotas', anchor: 'tbd') },
|
|
||||||
link_end: "</a>".html_safe
|
link_end: "</a>".html_safe
|
||||||
}.merge(strong_tags),
|
}.merge(strong_tags),
|
||||||
p2: {
|
p2: {
|
||||||
|
@ -102,7 +101,7 @@ module StorageHelper
|
||||||
link_end: "</a>".html_safe
|
link_end: "</a>".html_safe
|
||||||
}.merge(strong_tags),
|
}.merge(strong_tags),
|
||||||
p3: {
|
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
|
link_end: "</a>".html_safe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ class Issue < ApplicationRecord
|
||||||
validates :project, presence: true
|
validates :project, presence: true
|
||||||
validates :issue_type, presence: true
|
validates :issue_type, presence: true
|
||||||
validates :namespace, presence: true, if: -> { project.present? }
|
validates :namespace, presence: true, if: -> { project.present? }
|
||||||
|
validates :work_item_type, presence: true
|
||||||
|
|
||||||
validate :due_date_after_start_date
|
validate :due_date_after_start_date
|
||||||
validate :parent_link_confidentiality
|
validate :parent_link_confidentiality
|
||||||
|
@ -204,7 +205,7 @@ class Issue < ApplicationRecord
|
||||||
scope :with_null_relative_position, -> { where(relative_position: nil) }
|
scope :with_null_relative_position, -> { where(relative_position: nil) }
|
||||||
scope :with_non_null_relative_position, -> { where.not(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_commit :expire_etag_cache, unless: :importing?
|
||||||
after_save :ensure_metrics, unless: :importing?
|
after_save :ensure_metrics, unless: :importing?
|
||||||
|
@ -732,6 +733,12 @@ class Issue < ApplicationRecord
|
||||||
def ensure_namespace_id
|
def ensure_namespace_id
|
||||||
self.namespace = project.project_namespace if project
|
self.namespace = project.project_namespace if project
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
Issue.prepend_mod_with('Issue')
|
Issue.prepend_mod_with('Issue')
|
||||||
|
|
|
@ -59,6 +59,7 @@ class Member < ApplicationRecord
|
||||||
},
|
},
|
||||||
if: :project_bot?
|
if: :project_bot?
|
||||||
validate :access_level_inclusion
|
validate :access_level_inclusion
|
||||||
|
validate :validate_member_role_access_level
|
||||||
|
|
||||||
scope :with_invited_user_state, -> do
|
scope :with_invited_user_state, -> do
|
||||||
joins('LEFT JOIN users as invited_user ON invited_user.email = members.invite_email')
|
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")
|
errors.add(:access_level, "is not included in the list")
|
||||||
end
|
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
|
def send_invite
|
||||||
# override in subclass
|
# override in subclass
|
||||||
end
|
end
|
||||||
|
|
|
@ -669,6 +669,7 @@ class Project < ApplicationRecord
|
||||||
scope :imported_from, -> (type) { where(import_type: type) }
|
scope :imported_from, -> (type) { where(import_type: type) }
|
||||||
scope :imported, -> { where.not(import_type: nil) }
|
scope :imported, -> { where.not(import_type: nil) }
|
||||||
scope :with_enabled_error_tracking, -> { joins(:error_tracking_setting).where(project_error_tracking_settings: { enabled: true }) }
|
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
|
scope :with_service_desk_key, -> (key) do
|
||||||
# project_key is not indexed for now
|
# project_key is not indexed for now
|
||||||
|
|
|
@ -36,7 +36,7 @@ module Groups
|
||||||
update_crm_objects(was_root_group)
|
update_crm_objects(was_root_group)
|
||||||
end
|
end
|
||||||
|
|
||||||
post_update_hooks(@updated_project_ids)
|
post_update_hooks(@updated_project_ids, old_root_ancestor_id)
|
||||||
propagate_integrations
|
propagate_integrations
|
||||||
update_pending_builds
|
update_pending_builds
|
||||||
|
|
||||||
|
@ -44,9 +44,10 @@ module Groups
|
||||||
end
|
end
|
||||||
|
|
||||||
# Overridden in EE
|
# 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_project_authorizations
|
||||||
refresh_descendant_groups if @new_parent_group
|
refresh_descendant_groups if @new_parent_group
|
||||||
|
publish_event(old_root_ancestor_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_allowed_transfer
|
def ensure_allowed_transfer
|
||||||
|
@ -266,6 +267,18 @@ module Groups
|
||||||
|
|
||||||
CustomerRelations::IssueContact.delete_for_group(@group)
|
CustomerRelations::IssueContact.delete_for_group(@group)
|
||||||
end
|
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
|
||||||
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'] ||= 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']['cron'] ||= "0 0 * * *"
|
||||||
Settings.cron_jobs['licenses_reset_submit_license_usage_data_banner']['job_class'] = 'Licenses::ResetSubmitLicenseUsageDataBannerWorker'
|
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
|
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_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 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);
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
stage: Plan
|
||||||
group: Source Code
|
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
|
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)**
|
# Markdown API **(FREE)**
|
||||||
|
|
||||||
|
Convert Markdown content to HTML.
|
||||||
|
|
||||||
Available only in APIv4.
|
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
|
## Render an arbitrary Markdown document
|
||||||
|
|
||||||
```plaintext
|
```plaintext
|
||||||
|
@ -18,10 +32,12 @@ POST /markdown
|
||||||
| --------- | ------- | ------------- | ------------------------------------------ |
|
| --------- | ------- | ------------- | ------------------------------------------ |
|
||||||
| `text` | string | yes | The Markdown text to render |
|
| `text` | string | yes | The Markdown text to render |
|
||||||
| `gfm` | boolean | no | Render text using GitLab Flavored Markdown. Default is `false` |
|
| `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
|
```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:
|
Response example:
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
stage: Data Stores
|
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
|
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
|
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
|
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.
|
- Creating and editing the structure of tables.
|
||||||
- Inserting and formatting code blocks with syntax highlighting.
|
- 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).
|
- 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
|
### Use the Content Editor
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
module API
|
module API
|
||||||
class Markdown < ::API::Base
|
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
|
params do
|
||||||
requires :text, type: String, desc: "The markdown text to render"
|
requires :text, type: String, desc: "The markdown text to render"
|
||||||
|
|
|
@ -41934,7 +41934,7 @@ msgstr ""
|
||||||
msgid "UsageQuota|Dependency proxy"
|
msgid "UsageQuota|Dependency proxy"
|
||||||
msgstr ""
|
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 ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "UsageQuota|File attachments and smaller design graphics."
|
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. "
|
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
|
||||||
msgstr ""
|
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 ""
|
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."
|
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:"
|
msgid "repository:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "role's base access level does not match the access level of the membership"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "satisfied"
|
msgid "satisfied"
|
||||||
msgstr ""
|
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();
|
createComponent();
|
||||||
findReplyPlaceholder().vm.$emit('focus');
|
findReplyPlaceholder().vm.$emit('focus');
|
||||||
|
|
||||||
expect(wrapper.emitted('open-form')).toBeTruthy();
|
expect(wrapper.emitted('open-form')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when user is not logged in', () => {
|
describe('when user is not logged in', () => {
|
||||||
|
|
|
@ -239,7 +239,7 @@ describe('DiffRow', () => {
|
||||||
const coverage = wrapper.find('.line-coverage.right-side');
|
const coverage = wrapper.find('.line-coverage.right-side');
|
||||||
|
|
||||||
expect(coverage.attributes('title')).toContain('Test coverage: 5 hits');
|
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', () => {
|
it('for lines without coverage', () => {
|
||||||
|
@ -248,7 +248,7 @@ describe('DiffRow', () => {
|
||||||
const coverage = wrapper.find('.line-coverage.right-side');
|
const coverage = wrapper.find('.line-coverage.right-side');
|
||||||
|
|
||||||
expect(coverage.attributes('title')).toContain('No test coverage');
|
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', () => {
|
it('for unknown lines', () => {
|
||||||
|
@ -256,9 +256,9 @@ describe('DiffRow', () => {
|
||||||
wrapper = createWrapper({ props, state: { coverageFiles } });
|
wrapper = createWrapper({ props, state: { coverageFiles } });
|
||||||
const coverage = wrapper.find('.line-coverage.right-side');
|
const coverage = wrapper.find('.line-coverage.right-side');
|
||||||
|
|
||||||
expect(coverage.attributes('title')).toBeFalsy();
|
expect(coverage.attributes('title')).toBeUndefined();
|
||||||
expect(coverage.classes('coverage')).toBeFalsy();
|
expect(coverage.classes('coverage')).toBe(false);
|
||||||
expect(coverage.classes('no-coverage')).toBeFalsy();
|
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)
|
used_storage = helper.storage_counter(free_group.root_storage_statistics&.storage_size || 0)
|
||||||
|
|
||||||
expect(helper.storage_enforcement_banner_info(free_group)).to eql({
|
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_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. 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_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-and-transfer-limits-on-gitlab-saas-free-tier\" >FAQ</a> for more information.",
|
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',
|
variant: 'warning',
|
||||||
namespace_id: free_group.id,
|
namespace_id: free_group.id,
|
||||||
callouts_feature_name: 'storage_enforcement_banner_second_enforcement_threshold',
|
callouts_feature_name: 'storage_enforcement_banner_second_enforcement_threshold',
|
||||||
|
@ -116,7 +116,7 @@ RSpec.describe StorageHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns a hash with the correct storage size text' do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -141,7 +141,8 @@ RSpec.describe StorageHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the enforcement info' do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -222,6 +222,61 @@ RSpec.describe Issue do
|
||||||
end
|
end
|
||||||
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
|
describe '#record_create_action' do
|
||||||
it 'records the creation action after saving' do
|
it 'records the creation action after saving' do
|
||||||
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_created_action)
|
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
|
# TODO: Remove when NOT NULL constraint is added to the relationship
|
||||||
describe '#work_item_type' do
|
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
|
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
|
expect(issue.work_item_type_id).to be_nil
|
||||||
|
|
|
@ -167,6 +167,36 @@ RSpec.describe Member do
|
||||||
end
|
end
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
describe 'Scopes & finders' do
|
describe 'Scopes & finders' do
|
||||||
|
|
|
@ -5,9 +5,11 @@ require "spec_helper"
|
||||||
RSpec.describe API::Markdown do
|
RSpec.describe API::Markdown do
|
||||||
describe "POST /markdown" do
|
describe "POST /markdown" do
|
||||||
let(:user) {} # No-op. It gets overwritten in the contexts below.
|
let(:user) {} # No-op. It gets overwritten in the contexts below.
|
||||||
|
let(:disable_authenticate_markdown_api) { false }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
stub_commonmark_sourcepos_disabled
|
stub_commonmark_sourcepos_disabled
|
||||||
|
stub_feature_flags(authenticate_markdown_api: false) if disable_authenticate_markdown_api
|
||||||
|
|
||||||
post api("/markdown", user), params: params
|
post api("/markdown", user), params: params
|
||||||
end
|
end
|
||||||
|
@ -21,25 +23,51 @@ RSpec.describe API::Markdown do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples "404 Project Not Found" do
|
shared_examples '404 Project Not Found' do
|
||||||
it "responses with 404 Not Found" do
|
it 'responds with 404 Not Found' do
|
||||||
expect(response).to have_gitlab_http_status(:not_found)
|
expect(response).to have_gitlab_http_status(:not_found)
|
||||||
expect(response.headers["Content-Type"]).to eq("application/json")
|
expect(response.headers["Content-Type"]).to eq("application/json")
|
||||||
expect(json_response).to be_a(Hash)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when arguments are invalid" do
|
shared_examples '400 Bad Request' do
|
||||||
context "when text is missing" 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) { {} }
|
let(:params) { {} }
|
||||||
|
|
||||||
it "responses with 400 Bad Request" do
|
it_behaves_like '400 Bad Request'
|
||||||
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
|
end
|
||||||
|
|
||||||
context "when project is not found" do
|
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(:project) { create(:project) }
|
||||||
let_it_be(:issue) { create(:issue, project: 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(: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}" }
|
let(:text) { ":tada: Hello world! :100: #{issue.to_reference}" }
|
||||||
|
|
||||||
|
@ -132,13 +161,12 @@ RSpec.describe API::Markdown do
|
||||||
|
|
||||||
context 'when not logged in' do
|
context 'when not logged in' do
|
||||||
let(:user) {}
|
let(:user) {}
|
||||||
|
let(:disable_authenticate_markdown_api) { true }
|
||||||
|
|
||||||
it_behaves_like 'user without proper access'
|
it_behaves_like 'user without proper access'
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when logged in as user without access' do
|
context 'when logged in as user without access' do
|
||||||
let(:user) { create(:user) }
|
|
||||||
|
|
||||||
it_behaves_like 'user without proper access'
|
it_behaves_like 'user without proper access'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -175,8 +203,9 @@ RSpec.describe API::Markdown do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when not logged in' do
|
context 'when not logged in and authenticate_markdown_api turned off' do
|
||||||
let(:user) {}
|
let(:user) {}
|
||||||
|
let(:disable_authenticate_markdown_api) { true }
|
||||||
|
|
||||||
it_behaves_like 'user without proper access'
|
it_behaves_like 'user without proper access'
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,6 +22,18 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
||||||
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
|
let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
|
||||||
let(:transfer_service) { described_class.new(group, 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
|
context 'handling packages' do
|
||||||
let_it_be(:group) { create(:group, :public) }
|
let_it_be(:group) { create(:group, :public) }
|
||||||
let_it_be(:new_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) }
|
expect { transfer_service.execute(root_group) }
|
||||||
.not_to change { CustomerRelations::IssueContact.count }
|
.not_to change { CustomerRelations::IssueContact.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||||
|
let(:target) { root_group }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'moving down' do
|
context 'moving down' do
|
||||||
|
@ -904,6 +920,10 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
||||||
expect { transfer_service.execute(another_subgroup) }
|
expect { transfer_service.execute(another_subgroup) }
|
||||||
.not_to change { CustomerRelations::IssueContact.count }
|
.not_to change { CustomerRelations::IssueContact.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||||
|
let(:target) { another_subgroup }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'moving sideways' do
|
context 'moving sideways' do
|
||||||
|
@ -913,6 +933,10 @@ RSpec.describe Groups::TransferService, :sidekiq_inline do
|
||||||
expect { transfer_service.execute(another_subgroup) }
|
expect { transfer_service.execute(another_subgroup) }
|
||||||
.not_to change { CustomerRelations::IssueContact.count }
|
.not_to change { CustomerRelations::IssueContact.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||||
|
let(:target) { another_subgroup }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'moving to new root group' do
|
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) }
|
expect { transfer_service.execute(new_parent_group) }
|
||||||
.not_to change { CustomerRelations::IssueContact.count }
|
.not_to change { CustomerRelations::IssueContact.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||||
|
let(:target) { new_parent_group }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'moving to a subgroup within a new root group' do
|
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) }
|
expect { transfer_service.execute(subgroup_in_new_parent_group) }
|
||||||
.not_to change { CustomerRelations::IssueContact.count }
|
.not_to change { CustomerRelations::IssueContact.count }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it_behaves_like 'publishes a GroupTransferedEvent' do
|
||||||
|
let(:target) { subgroup_in_new_parent_group }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with permission on the subgroup' do
|
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.")
|
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
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,9 @@ RSpec.shared_context 'with API::Markdown Snapshot shared context' do |glfm_speci
|
||||||
# rubocop:enable Layout/LineLength
|
# rubocop:enable Layout/LineLength
|
||||||
include ApiHelpers
|
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|
|
markdown_examples, html_examples = %w[markdown.yml html.yml].map do |file_name|
|
||||||
yaml = File.read("#{glfm_specification_dir}/example_snapshots/#{file_name}")
|
yaml = File.read("#{glfm_specification_dir}/example_snapshots/#{file_name}")
|
||||||
YAML.safe_load(yaml, symbolize_names: true, aliases: true)
|
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) }
|
let(:normalizations) { normalizations_by_example_name.dig(name, :html, :static, :snapshot) }
|
||||||
|
|
||||||
it "verifies conversion of GLFM to HTML", :unlimited_max_formatted_output_length do
|
it "verifies conversion of GLFM to HTML", :unlimited_max_formatted_output_length do
|
||||||
api_url = api "/markdown"
|
|
||||||
|
|
||||||
# noinspection RubyResolve
|
# noinspection RubyResolve
|
||||||
normalized_html = normalize_html(html, normalizations)
|
normalized_html = normalize_html(html, normalizations)
|
||||||
|
|
||||||
|
|
|
@ -382,6 +382,7 @@ RSpec.describe 'Every Sidekiq worker' do
|
||||||
'ProjectScheduleBulkRepositoryShardMovesWorker' => 3,
|
'ProjectScheduleBulkRepositoryShardMovesWorker' => 3,
|
||||||
'ProjectTemplateExportWorker' => false,
|
'ProjectTemplateExportWorker' => false,
|
||||||
'ProjectUpdateRepositoryStorageWorker' => 3,
|
'ProjectUpdateRepositoryStorageWorker' => 3,
|
||||||
|
'Projects::DisableLegacyOpenSourceLicenseForInactiveProjectsWorker' => 3,
|
||||||
'Projects::GitGarbageCollectWorker' => false,
|
'Projects::GitGarbageCollectWorker' => false,
|
||||||
'Projects::InactiveProjectsDeletionNotificationWorker' => 3,
|
'Projects::InactiveProjectsDeletionNotificationWorker' => 3,
|
||||||
'Projects::PostCreationWorker' => 3,
|
'Projects::PostCreationWorker' => 3,
|
||||||
|
|
|
@ -63,6 +63,7 @@ module Tooling
|
||||||
%r{\A((ee|jh)/)?app/views/} => [:frontend, :backend],
|
%r{\A((ee|jh)/)?app/views/} => [:frontend, :backend],
|
||||||
%r{\A((ee|jh)/)?public/} => :frontend,
|
%r{\A((ee|jh)/)?public/} => :frontend,
|
||||||
%r{\A((ee|jh)/)?spec/(javascripts|frontend|frontend_integration)/} => :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)/)?vendor/assets/} => :frontend,
|
||||||
%r{\A((ee|jh)/)?scripts/frontend/} => :frontend,
|
%r{\A((ee|jh)/)?scripts/frontend/} => :frontend,
|
||||||
%r{(\A|/)(
|
%r{(\A|/)(
|
||||||
|
@ -117,6 +118,7 @@ module Tooling
|
||||||
%r{\Alib/gitlab/ci/templates} => :ci_template,
|
%r{\Alib/gitlab/ci/templates} => :ci_template,
|
||||||
|
|
||||||
%r{\A((ee|jh)/)?spec/features/} => :test,
|
%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_examples/features/} => :test,
|
||||||
%r{\A((ee|jh)/)?spec/support/shared_contexts/features/} => :test,
|
%r{\A((ee|jh)/)?spec/support/shared_contexts/features/} => :test,
|
||||||
%r{\A((ee|jh)/)?spec/support/helpers/features/} => :test,
|
%r{\A((ee|jh)/)?spec/support/helpers/features/} => :test,
|
||||||
|
|
Loading…
Reference in New Issue