Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1cbefa896f
commit
be3c4986f5
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlCollapsibleListbox, GlDrawer, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { GlAlert, GlButton, GlCollapsibleListbox, GlDrawer, GlTooltipDirective } from '@gitlab/ui';
|
||||
import UserAvatar from '~/vue_shared/components/users_table/user_avatar.vue';
|
||||
import {
|
||||
ACCESS_LEVEL_DEFAULT_STRING,
|
||||
|
|
@ -8,13 +8,15 @@ import {
|
|||
} from '~/organizations/shared/constants';
|
||||
import { getContentWrapperHeight } from '~/lib/utils/dom_utils';
|
||||
import { DRAWER_Z_INDEX } from '~/lib/utils/constants';
|
||||
import { s__ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import organizationUserUpdateMutation from '~/organizations/users/graphql/mutations/organization_user_update.mutation.graphql';
|
||||
import { createAlert } from '~/alert';
|
||||
|
||||
export default {
|
||||
name: 'UserDetailsDrawer',
|
||||
components: {
|
||||
GlAlert,
|
||||
GlButton,
|
||||
GlCollapsibleListbox,
|
||||
GlDrawer,
|
||||
UserAvatar,
|
||||
|
|
@ -27,10 +29,15 @@ export default {
|
|||
title: s__('Organization|Organization user details'),
|
||||
roleListboxLabel: s__('Organization|Organization role'),
|
||||
disabledRoleListboxTooltipText: s__('Organization|Organizations must have at least one owner.'),
|
||||
removeSelfAsOwnerWarning: s__(
|
||||
'Organization|If you proceed with this change you will lose your owner permissions for this organization, including access to this page.',
|
||||
),
|
||||
errorMessage: s__(
|
||||
'Organization|An error occurred updating the organization role. Please try again.',
|
||||
),
|
||||
successMessage: s__('Organization|Organization role was updated successfully.'),
|
||||
save: __('Save'),
|
||||
cancel: __('Cancel'),
|
||||
},
|
||||
roleListboxItems: [
|
||||
{
|
||||
|
|
@ -60,12 +67,21 @@ export default {
|
|||
drawerHeaderHeight() {
|
||||
return getContentWrapperHeight();
|
||||
},
|
||||
isChangingRole() {
|
||||
return this.initialAccessLevel !== this.selectedAccessLevel;
|
||||
},
|
||||
roleListboxDisabled() {
|
||||
return this.user?.isLastOwner;
|
||||
},
|
||||
roleListboxTooltip() {
|
||||
return this.roleListboxDisabled ? this.$options.i18n.disabledRoleListboxTooltipText : null;
|
||||
},
|
||||
showRemoveSelfAsOwnerWarning() {
|
||||
const isUserCurrentUser = this.user?.id === window.gon?.current_user_id;
|
||||
const isOwnerRoleSelected = this.selectedAccessLevel === ACCESS_LEVEL_OWNER_STRING;
|
||||
|
||||
return isUserCurrentUser && !isOwnerRoleSelected && this.isChangingRole;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
user(value) {
|
||||
|
|
@ -83,7 +99,7 @@ export default {
|
|||
this.$toast.show(this.$options.i18n.successMessage);
|
||||
this.$emit('role-change');
|
||||
},
|
||||
async onRoleSelect() {
|
||||
async save() {
|
||||
this.setLoading(true);
|
||||
|
||||
try {
|
||||
|
|
@ -113,6 +129,9 @@ export default {
|
|||
this.setLoading(false);
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
this.selectedAccessLevel = this.initialAccessLevel;
|
||||
},
|
||||
close() {
|
||||
this.$emit('close');
|
||||
},
|
||||
|
|
@ -135,9 +154,8 @@ export default {
|
|||
</template>
|
||||
<template #default>
|
||||
<div>
|
||||
<user-avatar :user="user" :admin-user-path="paths.adminUser" />
|
||||
</div>
|
||||
<div>
|
||||
<user-avatar class="gl-mt-3" :user="user" :admin-user-path="paths.adminUser" />
|
||||
<hr />
|
||||
<h5>{{ $options.i18n.roleListboxLabel }}</h5>
|
||||
<div
|
||||
v-gl-tooltip="{ disabled: !roleListboxTooltip, title: roleListboxTooltip }"
|
||||
|
|
@ -151,10 +169,26 @@ export default {
|
|||
:disabled="roleListboxDisabled"
|
||||
:items="$options.roleListboxItems"
|
||||
:loading="loading"
|
||||
@select="onRoleSelect"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<div
|
||||
v-if="isChangingRole"
|
||||
class="gl-flex gl-flex-col gl-gap-4"
|
||||
data-testid="user-details-drawer-footer"
|
||||
>
|
||||
<gl-alert v-if="showRemoveSelfAsOwnerWarning" variant="warning" :dismissible="false">{{
|
||||
$options.i18n.removeSelfAsOwnerWarning
|
||||
}}</gl-alert>
|
||||
<div class="gl-flex gl-gap-3">
|
||||
<gl-button variant="confirm" :disabled="loading" @click="save">{{
|
||||
$options.i18n.save
|
||||
}}</gl-button>
|
||||
<gl-button :disabled="loading" @click="cancel">{{ $options.i18n.cancel }}</gl-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</gl-drawer>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="gl-flex gl-gap-2" @click.prevent>
|
||||
<div class="gl-flex gl-gap-2 gl-self-start" @click.prevent>
|
||||
<toggle-snoozed-status
|
||||
v-if="glFeatures.todosSnoozing"
|
||||
:todo="todo"
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ module Namespaces
|
|||
|
||||
def by_feature_availability(items)
|
||||
items = items.with_issues_available_for_user(current_user) if params[:with_issues_enabled].present?
|
||||
items = items.with_namespace_domain_pages if params[:with_namespace_domain_pages].present?
|
||||
if params[:with_merge_requests_enabled].present?
|
||||
items = items.with_merge_requests_available_for_user(current_user)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ class PersonalAccessTokensFinder
|
|||
tokens = by_revoked_state(tokens)
|
||||
tokens = by_created_before(tokens)
|
||||
tokens = by_created_after(tokens)
|
||||
tokens = by_expires_before(tokens)
|
||||
tokens = by_expires_after(tokens)
|
||||
tokens = by_last_used_before(tokens)
|
||||
tokens = by_last_used_after(tokens)
|
||||
tokens = by_search(tokens)
|
||||
|
|
@ -113,6 +115,18 @@ class PersonalAccessTokensFinder
|
|||
tokens.created_after(params[:created_after])
|
||||
end
|
||||
|
||||
def by_expires_before(tokens)
|
||||
return tokens unless params[:expires_before]
|
||||
|
||||
tokens.expires_before(params[:expires_before])
|
||||
end
|
||||
|
||||
def by_expires_after(tokens)
|
||||
return tokens unless params[:expires_after]
|
||||
|
||||
tokens.expires_after(params[:expires_after])
|
||||
end
|
||||
|
||||
def by_last_used_before(tokens)
|
||||
return tokens unless params[:last_used_before]
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@ module Resolvers
|
|||
required: false,
|
||||
description: "Return only projects with merge requests enabled."
|
||||
|
||||
argument :with_namespace_domain_pages, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: "Return only projects that use the namespace domain for pages projects."
|
||||
|
||||
type Types::ProjectType, null: true
|
||||
|
||||
def resolve(args)
|
||||
|
|
@ -83,7 +87,8 @@ module Resolvers
|
|||
search: args[:search],
|
||||
ids: parse_gids(args[:ids]),
|
||||
with_issues_enabled: args[:with_issues_enabled],
|
||||
with_merge_requests_enabled: args[:with_merge_requests_enabled]
|
||||
with_merge_requests_enabled: args[:with_merge_requests_enabled],
|
||||
with_namespace_domain_pages: args[:with_namespace_domain_pages]
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Integrations
|
||||
module Base
|
||||
module Assembla
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
def title
|
||||
'Assembla'
|
||||
end
|
||||
|
||||
def description
|
||||
_('Manage projects.')
|
||||
end
|
||||
|
||||
def to_param
|
||||
'assembla'
|
||||
end
|
||||
|
||||
def supported_events
|
||||
%w[push]
|
||||
end
|
||||
end
|
||||
|
||||
included do
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
field :token,
|
||||
type: :password,
|
||||
description: -> { s_('The authentication token.') },
|
||||
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
|
||||
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
|
||||
placeholder: '',
|
||||
required: true
|
||||
|
||||
field :subdomain,
|
||||
description: -> { s_('The subdomain setting.') },
|
||||
exposes_secrets: true,
|
||||
placeholder: ''
|
||||
end
|
||||
|
||||
def execute(data)
|
||||
return unless supported_events.include?(data[:object_kind])
|
||||
|
||||
url = "https://atlas.assembla.com/spaces/#{URI.encode_www_form_component(subdomain)}/github_tool?secret_key=#{URI.encode_www_form_component(token)}"
|
||||
body = { payload: data }
|
||||
|
||||
Gitlab::HTTP.post(
|
||||
url,
|
||||
body: Gitlab::Json.dump(body),
|
||||
headers: { 'Content-Type' => 'application/json' }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,44 +2,6 @@
|
|||
|
||||
module Integrations
|
||||
class Assembla < Integration
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
field :token,
|
||||
type: :password,
|
||||
description: -> { s_('The authentication token.') },
|
||||
non_empty_password_title: -> { s_('ProjectService|Enter new token') },
|
||||
non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
|
||||
placeholder: '',
|
||||
required: true
|
||||
|
||||
field :subdomain,
|
||||
description: -> { s_('The subdomain setting.') },
|
||||
exposes_secrets: true,
|
||||
placeholder: ''
|
||||
|
||||
def self.title
|
||||
'Assembla'
|
||||
end
|
||||
|
||||
def self.description
|
||||
_('Manage projects.')
|
||||
end
|
||||
|
||||
def self.to_param
|
||||
'assembla'
|
||||
end
|
||||
|
||||
def self.supported_events
|
||||
%w[push]
|
||||
end
|
||||
|
||||
def execute(data)
|
||||
return unless supported_events.include?(data[:object_kind])
|
||||
|
||||
url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}"
|
||||
body = { payload: data }
|
||||
|
||||
Gitlab::HTTP.post(url, body: Gitlab::Json.dump(body), headers: { 'Content-Type' => 'application/json' })
|
||||
end
|
||||
include Integrations::Base::Assembla
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Integrations
|
||||
module Instance
|
||||
class Assembla < Integration
|
||||
include Integrations::Base::Assembla
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -27,14 +27,8 @@ module Network
|
|||
# https://gitlab.com/gitlab-org/gitlab-foss/issues/58013
|
||||
Gitlab::GitalyClient.allow_n_plus_1_calls do
|
||||
# Decorate with app/model/network/commit.rb
|
||||
if Feature.enabled?(:use_list_commits_rpc_network_graph, @project, type: :gitlab_com_derisk)
|
||||
list_commits(count_to_display_commit_in_center).map do |commit|
|
||||
Network::Commit.new(commit)
|
||||
end
|
||||
else
|
||||
find_commits(count_to_display_commit_in_center).map do |commit|
|
||||
Network::Commit.new(commit)
|
||||
end
|
||||
list_commits(count_to_display_commit_in_center).map do |commit|
|
||||
Network::Commit.new(commit)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -76,11 +70,7 @@ module Network
|
|||
offset = -1
|
||||
skip = 0
|
||||
while offset == -1
|
||||
tmp_commits = if Feature.enabled?(:use_list_commits_rpc_network_graph, @project, type: :gitlab_com_derisk)
|
||||
list_commits(skip)
|
||||
else
|
||||
find_commits(skip)
|
||||
end
|
||||
tmp_commits = list_commits(skip)
|
||||
|
||||
if tmp_commits.present?
|
||||
index = tmp_commits.index do |c|
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ class PersonalAccessToken < ApplicationRecord
|
|||
scope :expiring_and_not_notified, ->(date) { where(["revoked = false AND expire_notification_delivered = false AND seven_days_notification_sent_at IS NULL AND expires_at >= CURRENT_DATE AND expires_at <= ?", date]) }
|
||||
scope :expired_today_and_not_notified, -> { where(["revoked = false AND expires_at = CURRENT_DATE AND after_expiry_notification_delivered = false"]) }
|
||||
scope :expired_before, ->(date) { expired.where(arel_table[:expires_at].lt(date)) }
|
||||
scope :expires_before, ->(date) { where(arel_table[:expires_at].lteq(date)) }
|
||||
scope :expires_after, ->(date) { where(arel_table[:expires_at].gteq(date)) }
|
||||
scope :inactive, -> { where("revoked = true OR expires_at < CURRENT_DATE") }
|
||||
scope :last_used_before_or_unused, ->(date) { where("personal_access_tokens.created_at < :date AND (last_used_at < :date OR last_used_at IS NULL)", date: date) }
|
||||
scope :with_impersonation, -> { where(impersonation: true) }
|
||||
|
|
|
|||
|
|
@ -817,6 +817,11 @@ class Project < ApplicationRecord
|
|||
.where(project_pages_metadata: { project_id: nil })
|
||||
end
|
||||
|
||||
scope :with_namespace_domain_pages, -> do
|
||||
joins(:project_setting)
|
||||
.where(project_setting: { pages_unique_domain_enabled: false })
|
||||
end
|
||||
|
||||
scope :with_api_commit_entity_associations, -> {
|
||||
preload(:project_feature, :route, namespace: [:route, :owner])
|
||||
}
|
||||
|
|
@ -2358,6 +2363,10 @@ class Project < ApplicationRecord
|
|||
!(pages_metadatum&.onboarding_complete || pages_deployed?)
|
||||
end
|
||||
|
||||
def pages_unique_domain_enabled?
|
||||
project_setting.pages_unique_domain_enabled
|
||||
end
|
||||
|
||||
def remove_private_deploy_keys
|
||||
exclude_keys_linked_to_other_projects = <<-SQL
|
||||
NOT EXISTS (
|
||||
|
|
@ -3353,7 +3362,7 @@ class Project < ApplicationRecord
|
|||
end
|
||||
|
||||
def pages_domain_present?(domain_url)
|
||||
pages_url == domain_url || pages_domains.exists?(domain: domain_url)
|
||||
pages_url == domain_url || pages_domains.any? { |domain| domain.url == domain_url }
|
||||
end
|
||||
|
||||
# overridden in EE
|
||||
|
|
|
|||
|
|
@ -16,5 +16,6 @@
|
|||
#js-storage-usage-app{ data: { project_path: @project.full_path } }
|
||||
= render_if_exists 'projects/usage_quotas/transfer_tab_content'
|
||||
= render_if_exists 'shared/usage_quotas/tabs_content/observability'
|
||||
= render_if_exists 'projects/usage_quotas/pages'
|
||||
|
||||
= render 'shared/usage_quotas/index'
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
name: use_list_commits_rpc_network_graph
|
||||
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/386449
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/164081
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/484681
|
||||
milestone: '17.4'
|
||||
group: group::source code
|
||||
type: gitlab_com_derisk
|
||||
default_enabled: false
|
||||
|
|
@ -1287,8 +1287,8 @@ production: &base
|
|||
# private_key_file: /home/git/gitlab/config/topology-service-key.pem
|
||||
|
||||
cell:
|
||||
# id: 1
|
||||
# name: cell-1
|
||||
# id: null
|
||||
# name: null
|
||||
# skip_sequence_alteration: false
|
||||
|
||||
gitlab_kas:
|
||||
|
|
|
|||
|
|
@ -1054,8 +1054,8 @@ Settings.topology_service['private_key_file'] ||= '/home/git/gitlab/config/topol
|
|||
# Cells
|
||||
#
|
||||
Settings['cell'] ||= {}
|
||||
Settings.cell['id'] ||= 1
|
||||
Settings.cell['name'] ||= "cell-#{Settings.cell['id']}"
|
||||
Settings.cell['id'] ||= nil
|
||||
Settings.cell['name'] ||= nil
|
||||
Settings.cell['skip_sequence_alteration'] ||= false
|
||||
|
||||
#
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
return if Gitlab::Utils.to_boolean(ENV['SKIP_CELL_CONFIG_VALIDATION'], default: false)
|
||||
|
||||
ValidationError = Class.new(StandardError)
|
||||
|
||||
if Gitlab.config.cell.id.present? && !Gitlab.config.topology_service.enabled
|
||||
raise ValidationError, "Topology Service is not configured, but Cell ID is set"
|
||||
end
|
||||
|
||||
if Gitlab.config.topology_service.enabled && Gitlab.config.cell.id.blank?
|
||||
raise ValidationError, "Topology Service is enabled, but Cell ID is not set"
|
||||
end
|
||||
|
|
@ -179,16 +179,13 @@ Settings = GitlabSettings.load(file, Rails.env) do
|
|||
[[Gitlab::SidekiqConfig::WorkerMatcher::WILDCARD_MATCH, 'default']]
|
||||
end
|
||||
|
||||
# This method dictates whether the GitLab instance is part of a cells cluster
|
||||
def topology_service_enabled?
|
||||
topology_service && topology_service.respond_to?(:enabled) && topology_service.enabled
|
||||
end
|
||||
|
||||
def has_configured_cell?
|
||||
cell && cell.respond_to?(:name) && cell.name.present?
|
||||
end
|
||||
|
||||
def skip_sequence_alteration?
|
||||
has_configured_cell? && cell.respond_to?(:skip_sequence_alteration) && cell.skip_sequence_alteration
|
||||
cell.respond_to?(:skip_sequence_alteration) && cell.skip_sequence_alteration
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
table_name: instance_integrations
|
||||
classes:
|
||||
- Integrations::Instance::Integration
|
||||
- Integrations::Instance::Assembla
|
||||
feature_categories:
|
||||
- integrations
|
||||
description: Support 3rd party instance-wide integrations
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UpdateWorkspacesDevfilePathNullable < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.8'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
change_column_null :workspaces, :devfile_path, true
|
||||
end
|
||||
|
||||
def down
|
||||
change_column_null :workspaces, :devfile_path, false
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RenameWorkspacesDevfileRefToProjectRef < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.8'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
rename_column_concurrently :workspaces, :devfile_ref, :project_ref
|
||||
end
|
||||
|
||||
def down
|
||||
undo_rename_column_concurrently :workspaces, :devfile_ref, :project_ref
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
|
||||
# for more information on how to write migrations for GitLab.
|
||||
|
||||
class AddWorkflowDefinitionToWorkflows < Gitlab::Database::Migration[2.2]
|
||||
disable_ddl_transaction!
|
||||
|
||||
milestone '17.8'
|
||||
|
||||
def up
|
||||
with_lock_retries do
|
||||
add_column :duo_workflows_workflows, :workflow_definition, :text, default: 'software_development',
|
||||
null: false
|
||||
end
|
||||
|
||||
add_text_limit :duo_workflows_workflows, :workflow_definition, 255
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
remove_column :duo_workflows_workflows, :workflow_definition
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanupWorkspacesDevfileRefRename < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.8'
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
cleanup_concurrent_column_rename :workspaces, :devfile_ref, :project_ref
|
||||
end
|
||||
|
||||
def down
|
||||
undo_cleanup_concurrent_column_rename :workspaces, :devfile_ref, :project_ref
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
2ab125f0a48dbece1a6e98b698f2f44734f07976e34909a502f7cfbb937d9c55
|
||||
|
|
@ -0,0 +1 @@
|
|||
3de77a23770fa6eb0bfff996189e1f3b9748299fed8d01c0929423eba4570978
|
||||
|
|
@ -0,0 +1 @@
|
|||
32e2325b1442e77a35af0544cdc9a6ea7fab37d4f9185264923dcdc574da758e
|
||||
|
|
@ -0,0 +1 @@
|
|||
d037f2d4a30977b304f4df8da3374043d5e1fd15ddb7b066cf168c848148d825
|
||||
|
|
@ -12142,7 +12142,9 @@ CREATE TABLE duo_workflows_workflows (
|
|||
status smallint DEFAULT 0 NOT NULL,
|
||||
goal text,
|
||||
agent_privileges smallint[] DEFAULT '{1,2}'::smallint[] NOT NULL,
|
||||
CONSTRAINT check_5aedde451d CHECK ((char_length(goal) <= 4096))
|
||||
workflow_definition text DEFAULT 'software_development'::text NOT NULL,
|
||||
CONSTRAINT check_5aedde451d CHECK ((char_length(goal) <= 4096)),
|
||||
CONSTRAINT check_ec723e2a1a CHECK ((char_length(workflow_definition) <= 255))
|
||||
);
|
||||
|
||||
CREATE SEQUENCE duo_workflows_workflows_id_seq
|
||||
|
|
@ -22773,8 +22775,7 @@ CREATE TABLE workspaces (
|
|||
namespace text NOT NULL,
|
||||
desired_state text NOT NULL,
|
||||
actual_state text NOT NULL,
|
||||
devfile_ref text NOT NULL,
|
||||
devfile_path text NOT NULL,
|
||||
devfile_path text,
|
||||
devfile text,
|
||||
processed_devfile text,
|
||||
url text,
|
||||
|
|
@ -22785,15 +22786,17 @@ CREATE TABLE workspaces (
|
|||
url_query_string text,
|
||||
workspaces_agent_config_version integer NOT NULL,
|
||||
desired_config_generator_version integer,
|
||||
project_ref text,
|
||||
CONSTRAINT check_15543fb0fa CHECK ((char_length(name) <= 64)),
|
||||
CONSTRAINT check_157d5f955c CHECK ((char_length(namespace) <= 64)),
|
||||
CONSTRAINT check_2b401b0034 CHECK ((char_length(deployment_resource_version) <= 64)),
|
||||
CONSTRAINT check_35e31ca320 CHECK ((desired_config_generator_version IS NOT NULL)),
|
||||
CONSTRAINT check_72fee08424 CHECK ((char_length(project_ref) <= 256)),
|
||||
CONSTRAINT check_77d1a2ff50 CHECK ((char_length(processed_devfile) <= 65535)),
|
||||
CONSTRAINT check_8a0ab61b6b CHECK ((char_length(url_query_string) <= 256)),
|
||||
CONSTRAINT check_8e363ee3ad CHECK ((char_length(devfile_ref) <= 256)),
|
||||
CONSTRAINT check_8e4db5ffc2 CHECK ((char_length(actual_state) <= 32)),
|
||||
CONSTRAINT check_9e42558c35 CHECK ((char_length(url) <= 1024)),
|
||||
CONSTRAINT check_a758efdc89 CHECK ((project_ref IS NOT NULL)),
|
||||
CONSTRAINT check_b70eddcbc1 CHECK ((char_length(desired_state) <= 32)),
|
||||
CONSTRAINT check_dc58d56169 CHECK ((char_length(devfile_path) <= 2048)),
|
||||
CONSTRAINT check_eb32879a3d CHECK ((char_length(devfile) <= 65535)),
|
||||
|
|
|
|||
|
|
@ -671,3 +671,50 @@ Gitlab::HTTP.get(primary.internal_uri, allow_local_requests: true, limit: 10)
|
|||
|
||||
Make sure that the value of `internal_uri` is correct in the output above.
|
||||
If the URL of the primary site is incorrect, double-check it in `/etc/gitlab/gitlab.rb`, and in **Admin > Geo > Sites**.
|
||||
|
||||
### Excessive database IO from Geo metrics collection
|
||||
|
||||
If you're experiencing high database load due to frequent Geo metrics collection, you can reduce the frequency of the `geo_metrics_update_worker` job. This adjustment can help alleviate database strain in large GitLab instances where metrics collection significantly impacts database performance.
|
||||
|
||||
Increasing the interval means that your Geo metrics are updated less frequently. This results in metrics being out-of-date for longer periods of time, which may impact your ability to monitor Geo replication in real-time. If metrics are out-of-date for more than 10 minutes, the site is arbitrarily marked as "Unhealthy" in the Admin Area.
|
||||
|
||||
The following example sets the job to run every 30 minutes. Adjust the cron schedule based on your needs.
|
||||
|
||||
::Tabs
|
||||
|
||||
:::TabTitle Linux package (Omnibus)
|
||||
|
||||
1. Add or modify the following setting in `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['geo_metrics_update_worker_cron'] = "*/30 * * * *"
|
||||
```
|
||||
|
||||
1. Reconfigure GitLab:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl reconfigure
|
||||
```
|
||||
|
||||
:::TabTitle Self-compiled (source)
|
||||
|
||||
1. Edit `/home/git/gitlab/config/gitlab.yml`:
|
||||
|
||||
```yaml
|
||||
production: &base
|
||||
ee_cron_jobs:
|
||||
geo_metrics_update_worker:
|
||||
cron: "*/30 * * * *"
|
||||
```
|
||||
|
||||
1. Save the file and restart GitLab:
|
||||
|
||||
```shell
|
||||
# For systems running systemd
|
||||
sudo systemctl restart gitlab.target
|
||||
|
||||
# For systems running SysV init
|
||||
sudo service gitlab restart
|
||||
```
|
||||
|
||||
::EndTabs
|
||||
|
|
|
|||
|
|
@ -11728,11 +11728,12 @@ Input type: `WorkspaceCreateInput`
|
|||
| <a id="mutationworkspacecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationworkspacecreateclusteragentid"></a>`clusterAgentId` | [`ClustersAgentID!`](#clustersagentid) | GlobalID of the cluster agent the created workspace will be associated with. |
|
||||
| <a id="mutationworkspacecreatedesiredstate"></a>`desiredState` | [`String!`](#string) | Desired state of the created workspace. |
|
||||
| <a id="mutationworkspacecreatedevfilepath"></a>`devfilePath` | [`String!`](#string) | Project repo git path containing the devfile used to configure the workspace. |
|
||||
| <a id="mutationworkspacecreatedevfileref"></a>`devfileRef` | [`String!`](#string) | Project repo git ref containing the devfile used to configure the workspace. |
|
||||
| <a id="mutationworkspacecreatedevfilepath"></a>`devfilePath` | [`String`](#string) | Project path containing the devfile used to configure the workspace. If not provided, the GitLab default devfile is used. |
|
||||
| <a id="mutationworkspacecreatedevfileref"></a>`devfileRef` **{warning-solid}** | [`String`](#string) | **Deprecated:** Argument is renamed to project_ref. Deprecated in GitLab 17.8. |
|
||||
| <a id="mutationworkspacecreateeditor"></a>`editor` **{warning-solid}** | [`String`](#string) | **Deprecated:** Argument is not used. Deprecated in GitLab 17.5. |
|
||||
| <a id="mutationworkspacecreatemaxhoursbeforetermination"></a>`maxHoursBeforeTermination` | [`Int!`](#int) | Maximum hours the workspace can exist before it is automatically terminated. |
|
||||
| <a id="mutationworkspacecreateprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | ID of the project that will provide the Devfile for the created workspace. |
|
||||
| <a id="mutationworkspacecreateprojectref"></a>`projectRef` | [`String`](#string) | Project repo git ref. |
|
||||
| <a id="mutationworkspacecreatevariables"></a>`variables` | [`[WorkspaceVariableInput!]`](#workspacevariableinput) | Variables to inject into the workspace. |
|
||||
|
||||
#### Fields
|
||||
|
|
@ -23650,6 +23651,7 @@ A Duo Workflow.
|
|||
| <a id="duoworkflowprojectid"></a>`projectId` | [`ProjectID!`](#projectid) | ID of the project. |
|
||||
| <a id="duoworkflowupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the workflow was last updated. |
|
||||
| <a id="duoworkflowuserid"></a>`userId` | [`UserID!`](#userid) | ID of the user. |
|
||||
| <a id="duoworkflowworkflowdefinition"></a>`workflowDefinition` | [`String`](#string) | Duo Workflow type based on its capabilities. |
|
||||
|
||||
### `DuoWorkflowEnablement`
|
||||
|
||||
|
|
@ -23687,6 +23689,7 @@ Events that describe the history and progress of a Duo Workflow.
|
|||
| <a id="duoworkfloweventmetadata"></a>`metadata` | [`JsonString`](#jsonstring) | Metadata associated with the event. |
|
||||
| <a id="duoworkfloweventparenttimestamp"></a>`parentTimestamp` | [`Time`](#time) | Time of the parent event. |
|
||||
| <a id="duoworkfloweventtimestamp"></a>`timestamp` | [`Time`](#time) | Time of the event. |
|
||||
| <a id="duoworkfloweventworkflowdefinition"></a>`workflowDefinition` | [`String`](#string) | Duo Workflow type based on its capabilities. |
|
||||
| <a id="duoworkfloweventworkflowgoal"></a>`workflowGoal` | [`String`](#string) | Goal of the workflow. |
|
||||
| <a id="duoworkfloweventworkflowstatus"></a>`workflowStatus` | [`DuoWorkflowStatus`](#duoworkflowstatus) | Status of the workflow. |
|
||||
|
||||
|
|
@ -26110,6 +26113,7 @@ four standard [pagination arguments](#pagination-arguments):
|
|||
| <a id="groupprojectssort"></a>`sort` | [`NamespaceProjectSort`](#namespaceprojectsort) | Sort projects by the criteria. |
|
||||
| <a id="groupprojectswithissuesenabled"></a>`withIssuesEnabled` | [`Boolean`](#boolean) | Return only projects with issues enabled. |
|
||||
| <a id="groupprojectswithmergerequestsenabled"></a>`withMergeRequestsEnabled` | [`Boolean`](#boolean) | Return only projects with merge requests enabled. |
|
||||
| <a id="groupprojectswithnamespacedomainpages"></a>`withNamespaceDomainPages` | [`Boolean`](#boolean) | Return only projects that use the namespace domain for pages projects. |
|
||||
|
||||
##### `Group.releases`
|
||||
|
||||
|
|
@ -30352,6 +30356,7 @@ four standard [pagination arguments](#pagination-arguments):
|
|||
| <a id="namespaceprojectssort"></a>`sort` | [`NamespaceProjectSort`](#namespaceprojectsort) | Sort projects by the criteria. |
|
||||
| <a id="namespaceprojectswithissuesenabled"></a>`withIssuesEnabled` | [`Boolean`](#boolean) | Return only projects with issues enabled. |
|
||||
| <a id="namespaceprojectswithmergerequestsenabled"></a>`withMergeRequestsEnabled` | [`Boolean`](#boolean) | Return only projects with merge requests enabled. |
|
||||
| <a id="namespaceprojectswithnamespacedomainpages"></a>`withNamespaceDomainPages` | [`Boolean`](#boolean) | Return only projects that use the namespace domain for pages projects. |
|
||||
|
||||
##### `Namespace.remoteDevelopmentClusterAgents`
|
||||
|
||||
|
|
@ -38361,9 +38366,9 @@ Represents a remote development workspace.
|
|||
| <a id="workspacedesiredstate"></a>`desiredState` | [`String!`](#string) | Desired state of the workspace. |
|
||||
| <a id="workspacedesiredstateupdatedat"></a>`desiredStateUpdatedAt` | [`Time!`](#time) | Timestamp of the last update to the desired state. |
|
||||
| <a id="workspacedevfile"></a>`devfile` | [`String!`](#string) | Source YAML of the devfile used to configure the workspace. |
|
||||
| <a id="workspacedevfilepath"></a>`devfilePath` | [`String!`](#string) | Path to the devfile used to configure the workspace. |
|
||||
| <a id="workspacedevfileref"></a>`devfileRef` | [`String!`](#string) | Git reference that contains the devfile used to configure the workspace. |
|
||||
| <a id="workspacedevfileweburl"></a>`devfileWebUrl` | [`String!`](#string) | Web URL of the devfile used to configure the workspace. |
|
||||
| <a id="workspacedevfilepath"></a>`devfilePath` | [`String`](#string) | Path to the devfile used to configure the workspace. |
|
||||
| <a id="workspacedevfileref"></a>`devfileRef` **{warning-solid}** | [`String!`](#string) | **Deprecated** in GitLab 17.8. Field is renamed to project_ref. |
|
||||
| <a id="workspacedevfileweburl"></a>`devfileWebUrl` **{warning-solid}** | [`String`](#string) | **Deprecated** in GitLab 17.8. Field is not used. |
|
||||
| <a id="workspaceeditor"></a>`editor` **{warning-solid}** | [`String!`](#string) | **Deprecated** in GitLab 17.5. Field is not used. |
|
||||
| <a id="workspaceforceincludeallresources"></a>`forceIncludeAllResources` **{warning-solid}** | [`Boolean!`](#boolean) | **Introduced** in GitLab 17.6. **Status**: Experiment. Forces all resources to be included for the workspaceduring the next reconciliation with the agent. |
|
||||
| <a id="workspaceid"></a>`id` | [`RemoteDevelopmentWorkspaceID!`](#remotedevelopmentworkspaceid) | Global ID of the workspace. |
|
||||
|
|
@ -38372,6 +38377,7 @@ Represents a remote development workspace.
|
|||
| <a id="workspacenamespace"></a>`namespace` | [`String!`](#string) | Namespace of the workspace in Kubernetes. |
|
||||
| <a id="workspaceprocesseddevfile"></a>`processedDevfile` | [`String!`](#string) | Processed YAML of the devfile used to configure the workspace. |
|
||||
| <a id="workspaceprojectid"></a>`projectId` | [`ID!`](#id) | ID of the project that contains the devfile for the workspace. |
|
||||
| <a id="workspaceprojectref"></a>`projectRef` | [`String!`](#string) | Git reference that contains the devfile used to configure the workspace, and that will be cloned into the workspace. |
|
||||
| <a id="workspacerespondedtoagentat"></a>`respondedToAgentAt` | [`Time`](#time) | Timestamp of the last response sent to the GitLab agent for Kubernetes for the workspace. |
|
||||
| <a id="workspaceupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of the last update to any mutable workspace property. |
|
||||
| <a id="workspaceurl"></a>`url` | [`String!`](#string) | URL of the workspace. |
|
||||
|
|
|
|||
|
|
@ -25,101 +25,101 @@ Any dependencies are noted in the `Description` column for each permission.
|
|||
|
||||
## Admin
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`read_admin_dashboard`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171581) | Read-only access to admin dashboard | GitLab [17.6](https://gitlab.com/gitlab-org/gitlab/-/issues/501549) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Read-only access to admin dashboard | Read-only access to admin dashboard | [`read_admin_dashboard`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171581) | Instance | GitLab [17.6](https://gitlab.com/gitlab-org/gitlab/-/issues/501549) |
|
||||
|
||||
## Code review workflow
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`manage_merge_request_settings`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151586) | Configure merge request settings at the group or project level. Group actions include managing merge checks and approval settings. Project actions include managing MR configurations, approval rules and settings, and branch targets. In order to enable Suggested reviewers, the "Manage project access tokens" custom permission needs to be enabled. | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/443235) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage merge request approvals and settings | Configure merge request settings at the group or project level. Group actions include managing merge checks and approval settings. Project actions include managing MR configurations, approval rules and settings, and branch targets. In order to enable Suggested reviewers, the "Manage project access tokens" custom permission needs to be enabled. | [`manage_merge_request_settings`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151586) | Group,<br> Project | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/443235) |
|
||||
|
||||
## Compliance management
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_compliance_framework`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144183) | Create, read, update, and delete compliance frameworks. Users with this permission can also assign a compliance framework label to a project, and set the default framework of a group. | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/411502) | | |
|
||||
| [`read_compliance_dashboard`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175066) | Read compliance capabilities including adherence, violations, and frameworks for groups and projects. | GitLab [17.7](https://gitlab.com/gitlab-org/gitlab/-/issues/465324) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage and assign compliance frameworks | Create, read, update, and delete compliance frameworks. Users with this permission can also assign a compliance framework label to a project, and set the default framework of a group. | [`admin_compliance_framework`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144183) | Group,<br> Project | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/411502) |
|
||||
| Read compliance dashboard | Read compliance capabilities including adherence, violations, and frameworks for groups and projects. | [`read_compliance_dashboard`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175066) | Group,<br> Project | GitLab [17.7](https://gitlab.com/gitlab-org/gitlab/-/issues/465324) |
|
||||
|
||||
## Continuous delivery
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`manage_deploy_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151677) | Manage deploy tokens at the group or project level. | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/448843) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage deploy tokens | Manage deploy tokens at the group or project level. | [`manage_deploy_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151677) | Group,<br> Project | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/448843) |
|
||||
|
||||
## Groups and projects
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_group_member`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131914) | Add or remove users in a group, and assign roles to users. When assigning a role, users with this custom permission must select a role that has the same or fewer permissions as the default role used as the base for their custom role. | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/17364) | | |
|
||||
| [`archive_project`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134998) | Allows archiving of projects. | GitLab [16.6](https://gitlab.com/gitlab-org/gitlab/-/issues/425957) | | |
|
||||
| [`remove_group`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145166) | Ability to delete or restore a group. This ability does not allow deleting top-level groups. Review the Retention period settings to prevent accidental deletion. | GitLab [16.10](https://gitlab.com/gitlab-org/gitlab/-/issues/425962) | | |
|
||||
| [`remove_project`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139696) | Allows deletion of projects. | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/425959) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage group members | Add or remove users in a group, and assign roles to users. When assigning a role, users with this custom permission must select a role that has the same or fewer permissions as the default role used as the base for their custom role. | [`admin_group_member`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131914) | Group | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/17364) |
|
||||
| Archive project | Allows archiving of projects. | [`archive_project`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134998) | Project | GitLab [16.6](https://gitlab.com/gitlab-org/gitlab/-/issues/425957) |
|
||||
| Delete group | Ability to delete or restore a group. This ability does not allow deleting top-level groups. Review the Retention period settings to prevent accidental deletion. | [`remove_group`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145166) | Group | GitLab [16.10](https://gitlab.com/gitlab-org/gitlab/-/issues/425962) |
|
||||
| Delete project | Allows deletion of projects. | [`remove_project`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139696) | Project | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/425959) |
|
||||
|
||||
## Infrastructure as code
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_terraform_state`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140759) | Execute terraform commands, lock/unlock terraform state files, and remove file versions. | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/421789) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage Terraform state | Execute terraform commands, lock/unlock terraform state files, and remove file versions. | [`admin_terraform_state`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140759) | Project | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/421789) |
|
||||
|
||||
## Integrations
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_integrations`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154601) | Create, read, update, and delete integrations with external applications. | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/460522) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage integrations | Create, read, update, and delete integrations with external applications. | [`admin_integrations`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154601) | Group,<br> Project | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/460522) |
|
||||
|
||||
## Runner
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_runners`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151825) | Create, view, edit, and delete group or project Runners. Includes configuring Runner settings. | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/442851) | | |
|
||||
| [`read_runners`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156798) | Allows read-only access to group or project runners, including the runner fleet dashboard. | GitLab [17.2](https://gitlab.com/gitlab-org/gitlab/-/issues/468202) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage runners | Create, view, edit, and delete group or project Runners. Includes configuring Runner settings. | [`admin_runners`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151825) | Group,<br> Project | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/442851) |
|
||||
| View runners | Allows read-only access to group or project runners, including the runner fleet dashboard. | [`read_runners`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156798) | Group,<br> Project | GitLab [17.2](https://gitlab.com/gitlab-org/gitlab/-/issues/468202) |
|
||||
|
||||
## Secrets management
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_cicd_variables`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143369) | Create, read, update, and delete CI/CD variables. | GitLab [16.10](https://gitlab.com/gitlab-org/gitlab/-/issues/437947) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage CI/CD variables | Create, read, update, and delete CI/CD variables. | [`admin_cicd_variables`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143369) | Group,<br> Project | GitLab [16.10](https://gitlab.com/gitlab-org/gitlab/-/issues/437947) |
|
||||
|
||||
## Security policy management
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`manage_security_policy_link`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148371) | Allows linking security policy projects. | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/440226) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Link to a security policy project | Allows linking security policy projects. | [`manage_security_policy_link`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148371) | Group,<br> Project | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/440226) |
|
||||
|
||||
## Source code management
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_merge_request`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128302) | Allows approval of merge requests. | GitLab [16.4](https://gitlab.com/gitlab-org/gitlab/-/issues/412708) | | |
|
||||
| [`admin_protected_branch`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162208) | Create, read, update, and delete protected branches for a project. | GitLab [17.4](https://gitlab.com/gitlab-org/gitlab/-/issues/448823) | | |
|
||||
| [`admin_push_rules`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147872) | Configure push rules for repositories at the group or project level. | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/421786) | | |
|
||||
| [`read_code`](https://gitlab.com/gitlab-org/gitlab/-/issues/376180) | Allows read-only access to the source code in the user interface. Does not allow users to edit or download repository archives, clone or pull repositories, view source code in an IDE, or view merge requests for private projects. You can download individual files because read-only access inherently grants the ability to make a local copy of the file. | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/20277) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Approve merge request | Allows approval of merge requests. | [`admin_merge_request`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128302) | Project | GitLab [16.4](https://gitlab.com/gitlab-org/gitlab/-/issues/412708) |
|
||||
| | Create, read, update, and delete protected branches for a project. | [`admin_protected_branch`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162208) | Project | GitLab [17.4](https://gitlab.com/gitlab-org/gitlab/-/issues/448823) |
|
||||
| Manage push rules | Configure push rules for repositories at the group or project level. | [`admin_push_rules`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147872) | Group,<br> Project | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/421786) |
|
||||
| View repository code | Allows read-only access to the source code in the user interface. Does not allow users to edit or download repository archives, clone or pull repositories, view source code in an IDE, or view merge requests for private projects. You can download individual files because read-only access inherently grants the ability to make a local copy of the file. | [`read_code`](https://gitlab.com/gitlab-org/gitlab/-/issues/376180) | Group,<br> Project | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/20277) |
|
||||
|
||||
## System access
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`manage_group_access_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140115) | Create, read, update, and delete group access tokens. When creating a token, users with this custom permission must select a role for that token that has the same or fewer permissions as the default role used as the base for the custom role. | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/428353) | | |
|
||||
| [`manage_project_access_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132342) | Create, read, update, and delete project access tokens. When creating a token, users with this custom permission must select a role for that token that has the same or fewer permissions as the default role used as the base for the custom role. | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/421778) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage group access tokens | Create, read, update, and delete group access tokens. When creating a token, users with this custom permission must select a role for that token that has the same or fewer permissions as the default role used as the base for the custom role. | [`manage_group_access_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140115) | Group | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/428353) |
|
||||
| Manage project access tokens | Create, read, update, and delete project access tokens. When creating a token, users with this custom permission must select a role for that token that has the same or fewer permissions as the default role used as the base for the custom role. | [`manage_project_access_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132342) | Project | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/421778) |
|
||||
|
||||
## Team planning
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`read_crm_contact`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154017) | Read CRM contact. | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/443268) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| View CRM contact | Read CRM contact. | [`read_crm_contact`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154017) | Group | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/443268) |
|
||||
|
||||
## Vulnerability management
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_vulnerability`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121534) | Edit the vulnerability object, including the status and linking an issue. Includes the `read_vulnerability` permission actions. | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/412536) | | |
|
||||
| [`read_dependency`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126247) | Allows read-only access to the dependencies and licenses. | GitLab [16.3](https://gitlab.com/gitlab-org/gitlab/-/issues/415255) | | |
|
||||
| [`read_vulnerability`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120704) | Read vulnerability reports and security dashboards. | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/399119) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage vulnerabilities | Edit the vulnerability object, including the status and linking an issue. Includes the `read_vulnerability` permission actions. | [`admin_vulnerability`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121534) | Group,<br> Project | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/412536) |
|
||||
| View dependency list | Allows read-only access to the dependencies and licenses. | [`read_dependency`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126247) | Group,<br> Project | GitLab [16.3](https://gitlab.com/gitlab-org/gitlab/-/issues/415255) |
|
||||
| View vulnerability reports and dashboards | Read vulnerability reports and security dashboards. | [`read_vulnerability`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120704) | Group,<br> Project | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/399119) |
|
||||
|
||||
## Webhooks
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| [`admin_web_hook`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151551) | Manage webhooks | GitLab [17.0](https://gitlab.com/gitlab-org/quality/triage-ops/-/issues/1373) | | |
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
| Manage web hooks | Manage webhooks | [`admin_web_hook`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151551) | Group,<br> Project | GitLab [17.0](https://gitlab.com/gitlab-org/quality/triage-ops/-/issues/1373) |
|
||||
|
|
|
|||
|
|
@ -75,8 +75,12 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
# https://www.rubydoc.info/stdlib/core/IO:read says:
|
||||
# When this method is called at end of file, it returns nil or "",
|
||||
# depending on length: read, read(nil), and read(0) return "",
|
||||
# read(positive_integer) returns nil.
|
||||
def read(length = nil, outbuf = nil)
|
||||
out = []
|
||||
out = length&.positive? ? nil : []
|
||||
|
||||
length ||= size - tell
|
||||
|
||||
|
|
@ -87,15 +91,16 @@ module Gitlab
|
|||
chunk_bytes = [BUFFER_SIZE - chunk_offset, length].min
|
||||
data_slice = data.byteslice(0, chunk_bytes)
|
||||
|
||||
out ||= []
|
||||
out << data_slice
|
||||
@tell += data_slice.bytesize
|
||||
length -= data_slice.bytesize
|
||||
end
|
||||
|
||||
out = out.join
|
||||
out = out&.join
|
||||
|
||||
# If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
|
||||
if outbuf
|
||||
if outbuf && out
|
||||
outbuf.replace(out)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def enabled?
|
||||
Gitlab.config.topology_service_enabled? && Gitlab.config.has_configured_cell?
|
||||
Gitlab.config.topology_service_enabled?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39520,6 +39520,9 @@ msgstr ""
|
|||
msgid "Organization|Home organization"
|
||||
msgstr ""
|
||||
|
||||
msgid "Organization|If you proceed with this change you will lose your owner permissions for this organization, including access to this page."
|
||||
msgstr ""
|
||||
|
||||
msgid "Organization|Internal - The organization can be accessed by any signed in user except external users."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ RSpec.describe 'Project Network Graph', :js, feature_category: :groups_and_proje
|
|||
before do
|
||||
sign_in(user)
|
||||
|
||||
stub_feature_flags(use_list_commits_rpc_network_graph: false)
|
||||
|
||||
project.repository.create_branch(ref_with_hash, 'master')
|
||||
|
||||
# Stub Graph max_size to speed up test (10 commits vs. 650)
|
||||
|
|
|
|||
|
|
@ -241,6 +241,38 @@ RSpec.describe PersonalAccessTokensFinder, :enable_admin_mode, feature_category:
|
|||
end
|
||||
end
|
||||
|
||||
describe 'by expires before' do
|
||||
where(:by_expires_before, :expected_tokens) do
|
||||
2.days.ago | []
|
||||
29.days.from_now | [:expired, :expired_impersonation]
|
||||
30.days.from_now | ref(:tokens_keys)
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:params) { { expires_before: by_expires_before } }
|
||||
|
||||
it 'returns tokens by expires before' do
|
||||
is_expected.to match_array(tokens.values_at(*expected_tokens))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'by expires after' do
|
||||
where(:by_expires_after, :expected_tokens) do
|
||||
2.days.ago | ref(:tokens_keys)
|
||||
30.days.from_now | [:active, :active_other, :revoked, :active_impersonation, :revoked_impersonation, :bot]
|
||||
31.days.from_now | []
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:params) { { expires_after: by_expires_after } }
|
||||
|
||||
it 'returns tokens by expires after' do
|
||||
is_expected.to match_array(tokens.values_at(*expected_tokens))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'by last used date' do
|
||||
before do
|
||||
PersonalAccessToken.update_all(last_used_at: Time.now)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import Vue from 'vue';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import { GlCollapsibleListbox, GlDrawer } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { GlAlert, GlCollapsibleListbox, GlDrawer } from '@gitlab/ui';
|
||||
import organizationUserUpdateResponseWithErrors from 'test_fixtures/graphql/organizations/organization_user_update.mutation.graphql_with_errors.json';
|
||||
import organizationUserUpdateResponse from 'test_fixtures/graphql/organizations/organization_user_update.mutation.graphql.json';
|
||||
import organizationUserUpdateMutation from '~/organizations/users/graphql/mutations/organization_user_update.mutation.graphql';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { pageInfoMultiplePages } from 'jest/organizations/mock_data';
|
||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
|
||||
import UserDetailsDrawer from '~/organizations/users/components/user_details_drawer.vue';
|
||||
|
|
@ -31,16 +31,24 @@ describe('UserDetailsDrawer', () => {
|
|||
const successfulResponseHandler = jest.fn().mockResolvedValue(organizationUserUpdateResponse);
|
||||
const mockToastShow = jest.fn();
|
||||
|
||||
const findGlAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findGlDrawer = () => wrapper.findComponent(GlDrawer);
|
||||
const findGlCollapsibleListbox = () => wrapper.findComponent(GlCollapsibleListbox);
|
||||
const findUserAvatar = () => wrapper.findComponent(UserAvatar);
|
||||
const findFooter = () => wrapper.findByTestId('user-details-drawer-footer');
|
||||
const findSaveButton = () => wrapper.findByRole('button', { name: 'Save' });
|
||||
const findCancelButton = () => wrapper.findByRole('button', { name: 'Cancel' });
|
||||
|
||||
const selectRole = (value) => findGlCollapsibleListbox().vm.$emit('select', value);
|
||||
|
||||
const createComponent = ({ props = {}, handler = successfulResponseHandler } = {}) => {
|
||||
const createComponent = ({
|
||||
mountFn = shallowMountExtended,
|
||||
props = {},
|
||||
handler = successfulResponseHandler,
|
||||
} = {}) => {
|
||||
mockApollo = createMockApollo([[organizationUserUpdateMutation, handler]]);
|
||||
|
||||
wrapper = shallowMount(UserDetailsDrawer, {
|
||||
wrapper = mountFn(UserDetailsDrawer, {
|
||||
propsData: {
|
||||
user: mockUser,
|
||||
pageInfo: pageInfoMultiplePages,
|
||||
|
|
@ -94,6 +102,12 @@ describe('UserDetailsDrawer', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('does not render footer and action buttons', () => {
|
||||
expect(findFooter().exists()).toBe(false);
|
||||
expect(findSaveButton().exists()).toBe(false);
|
||||
expect(findCancelButton().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('renders role listbox label', () => {
|
||||
expect(findGlDrawer().text()).toContain('Organization role');
|
||||
});
|
||||
|
|
@ -141,89 +155,157 @@ describe('UserDetailsDrawer', () => {
|
|||
});
|
||||
|
||||
describe('when selecting new role', () => {
|
||||
const unselectedRole =
|
||||
mockUser.accessLevel.stringValue === ACCESS_LEVEL_OWNER_STRING
|
||||
? ACCESS_LEVEL_DEFAULT_STRING
|
||||
: ACCESS_LEVEL_OWNER_STRING;
|
||||
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
selectRole(ACCESS_LEVEL_DEFAULT_STRING);
|
||||
createComponent({ mountFn: mountExtended });
|
||||
|
||||
selectRole(unselectedRole);
|
||||
});
|
||||
|
||||
it('calls GraphQL mutation with correct variables', () => {
|
||||
expect(successfulResponseHandler).toHaveBeenCalledWith({
|
||||
input: {
|
||||
id: mockUser.gid,
|
||||
accessLevel: ACCESS_LEVEL_DEFAULT_STRING,
|
||||
},
|
||||
it('renders footer and action buttons', () => {
|
||||
expect(findFooter().exists()).toBe(true);
|
||||
expect(findSaveButton().exists()).toBe(true);
|
||||
expect(findCancelButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not render remove self as owner warning', () => {
|
||||
expect(findGlAlert().exists()).toBe(false);
|
||||
});
|
||||
|
||||
describe('when active user is the current user', () => {
|
||||
beforeEach(() => {
|
||||
window.gon.current_user_id = mockUser.id;
|
||||
});
|
||||
|
||||
describe('when removing self as owner', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
props: {
|
||||
user: {
|
||||
...mockUser,
|
||||
accessLevel: { ...mockUser.accessLevel, stringValue: ACCESS_LEVEL_OWNER_STRING },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
selectRole(ACCESS_LEVEL_DEFAULT_STRING);
|
||||
});
|
||||
|
||||
it('renders remove self as owner warning', () => {
|
||||
expect(findGlAlert().text()).toBe(
|
||||
'If you proceed with this change you will lose your owner permissions for this organization, including access to this page.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('sets listbox to loading', () => {
|
||||
expect(findGlCollapsibleListbox().props('loading')).toBe(true);
|
||||
});
|
||||
|
||||
it('emits loading start event', () => {
|
||||
expect(wrapper.emitted('loading')[0]).toEqual([true]);
|
||||
});
|
||||
|
||||
describe('when role update is successful', () => {
|
||||
describe('when save button is clicked', () => {
|
||||
beforeEach(async () => {
|
||||
await waitForPromises();
|
||||
await findSaveButton().trigger('click');
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
it('shows toast when GraphQL mutation is successful', () => {
|
||||
expect(mockToastShow).toHaveBeenCalledWith('Organization role was updated successfully.');
|
||||
it('calls GraphQL mutation with correct variables', () => {
|
||||
expect(successfulResponseHandler).toHaveBeenCalledWith({
|
||||
input: {
|
||||
id: mockUser.gid,
|
||||
accessLevel: unselectedRole,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('emits role-change event', () => {
|
||||
expect(wrapper.emitted('role-change')).toHaveLength(1);
|
||||
it('sets listbox to loading', () => {
|
||||
expect(findGlCollapsibleListbox().props('loading')).toBe(true);
|
||||
});
|
||||
|
||||
it('emits loading end event', () => {
|
||||
expect(wrapper.emitted('loading')[1]).toEqual([false]);
|
||||
it('emits loading start event', () => {
|
||||
expect(wrapper.emitted('loading')[0]).toEqual([true]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when role update has a validation error', () => {
|
||||
beforeEach(async () => {
|
||||
const errorResponseHandler = jest
|
||||
.fn()
|
||||
.mockResolvedValue(organizationUserUpdateResponseWithErrors);
|
||||
|
||||
createComponent({
|
||||
handler: errorResponseHandler,
|
||||
props: { user: { ...mockUser, accessLevel: ACCESS_LEVEL_OWNER_STRING } },
|
||||
describe('when role update is successful', () => {
|
||||
beforeEach(async () => {
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
selectRole(ACCESS_LEVEL_DEFAULT_STRING);
|
||||
await waitForPromises();
|
||||
it('shows toast when GraphQL mutation is successful', () => {
|
||||
expect(mockToastShow).toHaveBeenCalledWith(
|
||||
'Organization role was updated successfully.',
|
||||
);
|
||||
});
|
||||
|
||||
it('emits role-change event', () => {
|
||||
expect(wrapper.emitted('role-change')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('emits loading end event', () => {
|
||||
expect(wrapper.emitted('loading')[1]).toEqual([false]);
|
||||
});
|
||||
});
|
||||
|
||||
it('creates an alert', () => {
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: 'You cannot change the access of the last owner from the organization',
|
||||
describe('when role update has a validation error', () => {
|
||||
beforeEach(async () => {
|
||||
const errorResponseHandler = jest
|
||||
.fn()
|
||||
.mockResolvedValue(organizationUserUpdateResponseWithErrors);
|
||||
|
||||
createComponent({
|
||||
mountFn: mountExtended,
|
||||
handler: errorResponseHandler,
|
||||
});
|
||||
|
||||
selectRole(unselectedRole);
|
||||
await nextTick();
|
||||
|
||||
await findSaveButton().trigger('click');
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('creates an alert', () => {
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: 'You cannot change the access of the last owner from the organization',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when role update has a network error', () => {
|
||||
const error = new Error();
|
||||
|
||||
beforeEach(async () => {
|
||||
const errorResponseHandler = jest.fn().mockRejectedValue(error);
|
||||
|
||||
createComponent({
|
||||
mountFn: mountExtended,
|
||||
handler: errorResponseHandler,
|
||||
});
|
||||
|
||||
selectRole(unselectedRole);
|
||||
await nextTick();
|
||||
|
||||
await findSaveButton().trigger('click');
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('creates an alert', () => {
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: 'An error occurred updating the organization role. Please try again.',
|
||||
error,
|
||||
captureError: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when role update has a network error', () => {
|
||||
const error = new Error();
|
||||
describe('when cancel button is clicked', () => {
|
||||
it('resets listbox to the initial user role', async () => {
|
||||
await findCancelButton().trigger('click');
|
||||
|
||||
beforeEach(async () => {
|
||||
const errorResponseHandler = jest.fn().mockRejectedValue(error);
|
||||
|
||||
createComponent({
|
||||
handler: errorResponseHandler,
|
||||
props: { user: { ...mockUser, accessLevel: ACCESS_LEVEL_OWNER_STRING } },
|
||||
});
|
||||
|
||||
selectRole(ACCESS_LEVEL_DEFAULT_STRING);
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('creates an alert', () => {
|
||||
expect(createAlert).toHaveBeenCalledWith({
|
||||
message: 'An error occurred updating the organization role. Please try again.',
|
||||
error,
|
||||
captureError: true,
|
||||
});
|
||||
expect(findGlCollapsibleListbox().props('selected')).toBe(
|
||||
mockUser.accessLevel.stringValue,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ RSpec.describe Resolvers::NamespaceProjectsResolver, feature_category: :groups_a
|
|||
sort: nil,
|
||||
ids: nil,
|
||||
with_issues_enabled: nil,
|
||||
with_merge_requests_enabled: nil
|
||||
with_merge_requests_enabled: nil,
|
||||
with_namespace_domain_pages: nil
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -121,6 +122,23 @@ RSpec.describe Resolvers::NamespaceProjectsResolver, feature_category: :groups_a
|
|||
it { is_expected.to contain_exactly(*group_namespaced_projects) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with_namespace_domain_pages' do
|
||||
before do
|
||||
group_namespaced_projects[0...-1].each do |project|
|
||||
project.project_setting.update!(pages_unique_domain_enabled: false)
|
||||
end
|
||||
group_namespaced_projects.last.project_setting.update!(
|
||||
pages_unique_domain_enabled: true,
|
||||
pages_unique_domain: 'foo123.example.com'
|
||||
)
|
||||
end
|
||||
|
||||
let(:args) { default_args.merge(with_namespace_domain_pages: true) }
|
||||
let(:expected_projects) { group_namespaced_projects[0...-1] }
|
||||
|
||||
it { is_expected.to contain_exactly(*expected_projects) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an user namespace' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'validate database config', feature_category: :cell do
|
||||
include StubENV
|
||||
|
||||
let(:rails_configuration) { Rails::Application::Configuration.new(Rails.root) }
|
||||
|
||||
subject(:validate_config) do
|
||||
load Rails.root.join('config/initializers/validate_cell_config.rb')
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Rails.application).to receive(:config).and_return(rails_configuration)
|
||||
end
|
||||
|
||||
shared_examples 'with SKIP_CELL_CONFIG_VALIDATION=true' do
|
||||
before do
|
||||
stub_env('SKIP_CELL_CONFIG_VALIDATION', 'true')
|
||||
end
|
||||
|
||||
it 'does not raise exception' do
|
||||
expect { validate_config }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when topology service is correctly configured' do
|
||||
before do
|
||||
stub_config(cell: { id: 1 }, topology_service: { enabled: true })
|
||||
end
|
||||
|
||||
it 'does not raise exception' do
|
||||
expect { validate_config }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when topology service is not configured' do
|
||||
before do
|
||||
stub_config(cell: { id: nil }, topology_service: { enabled: false })
|
||||
end
|
||||
|
||||
it 'does not raise exception' do
|
||||
expect { validate_config }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when configuration is wrong' do
|
||||
context 'when only cell.id is configured' do
|
||||
before do
|
||||
stub_config(cell: { id: 1 }, topology_service: { enabled: false })
|
||||
end
|
||||
|
||||
it 'does not raise exception' do
|
||||
expect { validate_config }.to raise_error("Topology Service is not configured, but Cell ID is set")
|
||||
end
|
||||
|
||||
it_behaves_like 'with SKIP_CELL_CONFIG_VALIDATION=true'
|
||||
end
|
||||
|
||||
context 'when only topology service is enabled' do
|
||||
before do
|
||||
stub_config(cell: { id: nil }, topology_service: { enabled: true })
|
||||
end
|
||||
|
||||
it 'does not raise exception' do
|
||||
expect { validate_config }.to raise_error("Topology Service is enabled, but Cell ID is not set")
|
||||
end
|
||||
|
||||
it_behaves_like 'with SKIP_CELL_CONFIG_VALIDATION=true'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -148,6 +148,26 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata, feature_category: :job_ar
|
|||
it 'raises error' do
|
||||
expect { find_entries }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
|
||||
end
|
||||
|
||||
context 'when metadata is an HttpIO stream' do
|
||||
let(:tmpfile) { Tempfile.new('test-metadata') }
|
||||
let(:url) { "file://#{tmpfile.path}" }
|
||||
let(:metadata_file_stream) { Gitlab::HttpIO.new(url, 0) }
|
||||
|
||||
before do
|
||||
# Normally file:// URLs are not allowed, but bypass this for the sake of testing
|
||||
# so we don't have to run a Web server.
|
||||
allow(::Gitlab::UrlSanitizer).to receive(:valid?).with(url).and_return(true)
|
||||
end
|
||||
|
||||
after do
|
||||
tmpfile.unlink
|
||||
end
|
||||
|
||||
it 'raises error' do
|
||||
expect { find_entries }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with generated metadata' do
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::HttpIO do
|
||||
RSpec.describe Gitlab::HttpIO, feature_category: :shared do
|
||||
include HttpIOHelpers
|
||||
|
||||
let(:http_io) { described_class.new(url, size) }
|
||||
|
|
@ -122,7 +122,24 @@ RSpec.describe Gitlab::HttpIO do
|
|||
describe '#read' do
|
||||
subject { http_io.read(length) }
|
||||
|
||||
shared_examples 'reads the body' do
|
||||
let(:expected_outbuf) { expected_body || "" }
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to eq(expected_body)
|
||||
end
|
||||
|
||||
it 'reads with outbuf' do
|
||||
buf = +""
|
||||
|
||||
expect(http_io.read(length, buf)).to eq(expected_body)
|
||||
expect(buf).to eq(expected_outbuf)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are no network issue' do
|
||||
let(:expected_body) { file_body }
|
||||
|
||||
before do
|
||||
stub_remote_url_206(url, file_path)
|
||||
end
|
||||
|
|
@ -135,9 +152,7 @@ RSpec.describe Gitlab::HttpIO do
|
|||
set_smaller_buffer_size_than(size)
|
||||
end
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to eq(file_body)
|
||||
end
|
||||
it_behaves_like 'reads the body'
|
||||
end
|
||||
|
||||
context 'when BUFFER_SIZE is larger than file size' do
|
||||
|
|
@ -145,23 +160,20 @@ RSpec.describe Gitlab::HttpIO do
|
|||
set_larger_buffer_size_than(size)
|
||||
end
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to eq(file_body)
|
||||
end
|
||||
it_behaves_like 'reads the body'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when read only first 100 bytes' do
|
||||
let(:length) { 100 }
|
||||
let(:expected_body) { file_body[0, length] }
|
||||
|
||||
context 'when BUFFER_SIZE is smaller than file size' do
|
||||
before do
|
||||
set_smaller_buffer_size_than(size)
|
||||
end
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to eq(file_body[0, length])
|
||||
end
|
||||
it_behaves_like 'reads the body'
|
||||
end
|
||||
|
||||
context 'when BUFFER_SIZE is larger than file size' do
|
||||
|
|
@ -169,23 +181,20 @@ RSpec.describe Gitlab::HttpIO do
|
|||
set_larger_buffer_size_than(size)
|
||||
end
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to eq(file_body[0, length])
|
||||
end
|
||||
it_behaves_like 'reads the body'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when tries to read oversize' do
|
||||
let(:length) { size + 1000 }
|
||||
let(:expected_body) { file_body }
|
||||
|
||||
context 'when BUFFER_SIZE is smaller than file size' do
|
||||
before do
|
||||
set_smaller_buffer_size_than(size)
|
||||
end
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to eq(file_body)
|
||||
end
|
||||
it_behaves_like 'reads the body'
|
||||
end
|
||||
|
||||
context 'when BUFFER_SIZE is larger than file size' do
|
||||
|
|
@ -193,23 +202,20 @@ RSpec.describe Gitlab::HttpIO do
|
|||
set_larger_buffer_size_than(size)
|
||||
end
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to eq(file_body)
|
||||
end
|
||||
it_behaves_like 'reads the body'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when tries to read 0 bytes' do
|
||||
let(:length) { 0 }
|
||||
let(:expected_body) { "" }
|
||||
|
||||
context 'when BUFFER_SIZE is smaller than file size' do
|
||||
before do
|
||||
set_smaller_buffer_size_than(size)
|
||||
end
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to be_empty
|
||||
end
|
||||
it_behaves_like 'reads the body'
|
||||
end
|
||||
|
||||
context 'when BUFFER_SIZE is larger than file size' do
|
||||
|
|
@ -217,14 +223,30 @@ RSpec.describe Gitlab::HttpIO do
|
|||
set_larger_buffer_size_than(size)
|
||||
end
|
||||
|
||||
it 'reads a trace' do
|
||||
is_expected.to be_empty
|
||||
end
|
||||
it_behaves_like 'reads the body'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is anetwork issue' do
|
||||
context 'when current pos is at end of the file' do
|
||||
before do
|
||||
http_io.seek(size, IO::SEEK_SET)
|
||||
end
|
||||
|
||||
it 'returns nil when attempting to read a byte' do
|
||||
expect(http_io.read(1)).to be_nil
|
||||
end
|
||||
|
||||
it 'returns "" when attempting to read 0 bytes' do
|
||||
expect(http_io.read(0)).to eq("")
|
||||
end
|
||||
|
||||
it 'returns "" when attempting to read' do
|
||||
expect(http_io.read).to eq("")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a network issue' do
|
||||
let(:length) { nil }
|
||||
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -12,13 +12,6 @@ RSpec.describe Gitlab::TopologyServiceClient::BaseService, feature_category: :ce
|
|||
|
||||
expect { base_service }.to raise_error(NotImplementedError)
|
||||
end
|
||||
|
||||
it 'raises an error when no cell is configured' do
|
||||
allow(Gitlab.config.topology_service).to receive(:enabled).and_return(true)
|
||||
expect(Gitlab.config.cell).to receive(:name).once.and_return(nil)
|
||||
|
||||
expect { base_service }.to raise_error(NotImplementedError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,13 +22,6 @@ RSpec.describe Gitlab::TopologyServiceClient::CellService, feature_category: :ce
|
|||
|
||||
expect { cell_service }.to raise_error(NotImplementedError)
|
||||
end
|
||||
|
||||
it 'raises an error when no cell is configured' do
|
||||
allow(Gitlab.config.topology_service).to receive(:enabled).and_return(true)
|
||||
expect(Gitlab.config.cell).to receive(:name).once.and_return(nil)
|
||||
|
||||
expect { cell_service }.to raise_error(NotImplementedError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when topology service is enabled' do
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ RSpec.describe CapWorkspacesMaxTerminationToOneYear, feature_category: :workspac
|
|||
desired_state: 'Terminated',
|
||||
actual_state: 'Terminated',
|
||||
editor: 'vs-code',
|
||||
devfile_ref: 'devfile-ref',
|
||||
project_ref: 'devfile-ref',
|
||||
devfile_path: 'devfile-path',
|
||||
devfile: 'devfile',
|
||||
processed_devfile: 'processed_dev_file',
|
||||
|
|
@ -68,7 +68,7 @@ RSpec.describe CapWorkspacesMaxTerminationToOneYear, feature_category: :workspac
|
|||
desired_state: 'Running',
|
||||
actual_state: 'Running',
|
||||
editor: 'vs-code',
|
||||
devfile_ref: 'devfile-ref',
|
||||
project_ref: 'devfile-ref',
|
||||
devfile_path: 'devfile-path',
|
||||
devfile: 'devfile',
|
||||
processed_devfile: 'processed_dev_file',
|
||||
|
|
|
|||
|
|
@ -3,49 +3,5 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Integrations::Assembla, feature_category: :integrations do
|
||||
include StubRequests
|
||||
|
||||
it_behaves_like Integrations::ResetSecretFields do
|
||||
let(:integration) { described_class.new }
|
||||
end
|
||||
|
||||
describe 'Validations' do
|
||||
context 'when active' do
|
||||
before do
|
||||
subject.active = true
|
||||
end
|
||||
|
||||
it { is_expected.to validate_presence_of :token }
|
||||
end
|
||||
|
||||
context 'when inactive' do
|
||||
it { is_expected.not_to validate_presence_of :token }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#execute" do
|
||||
let_it_be(:user) { build(:user) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
|
||||
let(:assembla_integration) { described_class.new }
|
||||
let(:sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
|
||||
let(:api_url) { 'https://atlas.assembla.com/spaces/project_name/github_tool?secret_key=verySecret' }
|
||||
|
||||
before do
|
||||
allow(assembla_integration).to receive_messages(
|
||||
project_id: project.id,
|
||||
project: project,
|
||||
token: 'verySecret',
|
||||
subdomain: 'project_name'
|
||||
)
|
||||
stub_full_request(api_url, method: :post)
|
||||
end
|
||||
|
||||
it "calls Assembla API" do
|
||||
assembla_integration.execute(sample_data)
|
||||
expect(WebMock).to have_requested(:post, stubbed_hostname(api_url)).with(
|
||||
body: /#{sample_data[:before]}.*#{sample_data[:after]}.*#{project.path}/
|
||||
).once
|
||||
end
|
||||
end
|
||||
it_behaves_like Integrations::Base::Assembla
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Integrations::Instance::Assembla, feature_category: :integrations do
|
||||
it_behaves_like Integrations::Base::Assembla
|
||||
end
|
||||
|
|
@ -52,46 +52,26 @@ RSpec.describe Network::Graph, feature_category: :source_code_management do
|
|||
describe '#commits' do
|
||||
let(:graph) { described_class.new(project, 'refs/heads/master', project.repository.commit, nil) }
|
||||
|
||||
context 'when use_list_commits_rpc_network_graph FF is enabled' do
|
||||
let(:opts) do
|
||||
{
|
||||
revisions: %w[--tags --branches],
|
||||
pagination_params: { limit: 650 },
|
||||
reverse: false,
|
||||
order: :date,
|
||||
ref: 'refs/heads/master',
|
||||
skip: 0
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_feature_flags(use_list_commits_rpc_network_graph: true)
|
||||
end
|
||||
|
||||
it 'only fetches the commits once using `list_all`', :request_store do
|
||||
expect(Gitlab::Git::Commit).to receive(:list_all)
|
||||
.with(project.repository.raw_repository, opts)
|
||||
.once
|
||||
.and_call_original
|
||||
|
||||
graph
|
||||
end
|
||||
|
||||
it_behaves_like 'a collection of commits'
|
||||
let(:opts) do
|
||||
{
|
||||
revisions: %w[--tags --branches],
|
||||
pagination_params: { limit: 650 },
|
||||
reverse: false,
|
||||
order: :date,
|
||||
ref: 'refs/heads/master',
|
||||
skip: 0
|
||||
}
|
||||
end
|
||||
|
||||
context 'when use_list_commits_rpc_network_graph FF is disabled' do
|
||||
before do
|
||||
stub_feature_flags(use_list_commits_rpc_network_graph: false)
|
||||
end
|
||||
it 'only fetches the commits once using `list_all`', :request_store do
|
||||
expect(Gitlab::Git::Commit).to receive(:list_all)
|
||||
.with(project.repository.raw_repository, opts)
|
||||
.once
|
||||
.and_call_original
|
||||
|
||||
it 'only fetches the commits once using `find_all`', :request_store do
|
||||
expect(Gitlab::Git::Commit).to receive(:find_all).once.and_call_original
|
||||
|
||||
graph
|
||||
end
|
||||
|
||||
it_behaves_like 'a collection of commits'
|
||||
graph
|
||||
end
|
||||
|
||||
it_behaves_like 'a collection of commits'
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -120,6 +120,25 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'expires scopes', :time_freeze do
|
||||
let!(:expires_last_month_token) { create(:personal_access_token, expires_at: 1.month.ago) }
|
||||
let!(:expires_next_month_token) { create(:personal_access_token, expires_at: 1.month.from_now) }
|
||||
let!(:expires_two_months_token) { create(:personal_access_token, expires_at: 2.months.from_now) }
|
||||
|
||||
describe '.expires_before' do
|
||||
it 'finds tokens that expire before or on date' do
|
||||
expect(described_class.expires_before(1.month.ago)).to contain_exactly(expires_last_month_token)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.expires_after' do
|
||||
it 'finds tokens that expires after or on date' do
|
||||
expect(described_class.expires_after(1.month.from_now.beginning_of_hour))
|
||||
.to contain_exactly(expires_next_month_token, expires_two_months_token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.last_used_before' do
|
||||
context 'last_used_*' do
|
||||
let_it_be(:date) { DateTime.new(2022, 01, 01) }
|
||||
|
|
|
|||
|
|
@ -3116,6 +3116,28 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
end
|
||||
end
|
||||
|
||||
describe '#pages_unique_domain_enabled?' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
subject { project.pages_unique_domain_enabled? }
|
||||
|
||||
context 'if unique domain is enabled' do
|
||||
before do
|
||||
project.project_setting.update!(pages_unique_domain_enabled: true, pages_unique_domain: 'foo123.example.com')
|
||||
end
|
||||
|
||||
it { is_expected.to be(true) }
|
||||
end
|
||||
|
||||
context 'if unique domain is disabled' do
|
||||
before do
|
||||
project.project_setting.update!(pages_unique_domain_enabled: false)
|
||||
end
|
||||
|
||||
it { is_expected.to be(false) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#default_branch_protected?' do
|
||||
let_it_be(:namespace) { create(:namespace) }
|
||||
let_it_be(:project) { create(:project, namespace: namespace) }
|
||||
|
|
@ -9840,4 +9862,32 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
expect(project.uploads_sharding_key).to eq(namespace_id: namespace.id)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#pages_domain_present?' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
before do
|
||||
allow(project).to receive(:pages_url).and_return('https://example.com')
|
||||
end
|
||||
|
||||
context 'when the domain matches pages_url' do
|
||||
it 'returns true' do
|
||||
expect(project.pages_domain_present?('https://example.com')).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the domain exists in pages_domains' do
|
||||
let!(:pages_domain) { create(:pages_domain, project: project, domain: 'custom.com') }
|
||||
|
||||
it 'returns true' do
|
||||
expect(project.pages_domain_present?('https://custom.com')).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the domain does not match pages_url or pages_domains' do
|
||||
it 'returns false' do
|
||||
expect(project.pages_domain_present?('https://unknown.com')).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -110,7 +110,8 @@ RSpec.describe API::Pages, feature_category: :pages do
|
|||
|
||||
context 'and updates pages primary domain' do
|
||||
let(:domain) { 'my.domain.com' }
|
||||
let(:params) { { pages_primary_domain: domain } }
|
||||
let(:pages_primary_domain_url) { 'https://my.domain.com' }
|
||||
let(:params) { { pages_primary_domain: pages_primary_domain_url } }
|
||||
|
||||
before do
|
||||
create(:pages_domain, project: project, domain: domain)
|
||||
|
|
@ -120,7 +121,7 @@ RSpec.describe API::Pages, feature_category: :pages do
|
|||
patch api(path, admin, admin_mode: true), params: params
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(json_response['pages_primary_domain']).to eq(domain)
|
||||
expect(json_response['pages_primary_domain']).to eq(pages_primary_domain_url)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples Integrations::Base::Assembla do
|
||||
include StubRequests
|
||||
|
||||
it_behaves_like Integrations::ResetSecretFields do
|
||||
let(:integration) { described_class.new }
|
||||
end
|
||||
|
||||
describe 'Validations' do
|
||||
context 'when active' do
|
||||
before do
|
||||
subject.active = true
|
||||
end
|
||||
|
||||
it { is_expected.to validate_presence_of :token }
|
||||
end
|
||||
|
||||
context 'when inactive' do
|
||||
it { is_expected.not_to validate_presence_of :token }
|
||||
end
|
||||
end
|
||||
|
||||
describe "#execute" do
|
||||
let_it_be(:user) { build(:user) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
|
||||
let(:assembla_integration) { described_class.new }
|
||||
let(:sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
|
||||
let(:api_url) { 'https://atlas.assembla.com/spaces/project_name/github_tool?secret_key=verySecret' }
|
||||
|
||||
it "calls Assembla API" do
|
||||
allow(assembla_integration).to receive_messages(
|
||||
project_id: project.id,
|
||||
project: project,
|
||||
token: 'verySecret',
|
||||
subdomain: 'project_name'
|
||||
)
|
||||
|
||||
stub_full_request(api_url, method: :post).with(body: { payload: sample_data })
|
||||
|
||||
assembla_integration.execute(sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, stubbed_hostname(api_url)).with(
|
||||
body: /#{sample_data[:before]}.*#{sample_data[:after]}.*#{project.path}/
|
||||
).once
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -156,7 +156,6 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout, feature_categor
|
|||
|
||||
before do
|
||||
allow(Settings).to receive(:topology_service_enabled?).and_return(topology_service_enabled)
|
||||
allow(Settings).to receive(:has_configured_cell?).and_return(configured_cell)
|
||||
allow(Settings).to receive(:skip_sequence_alteration?).and_return(skip_sequence_alteration)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,13 @@
|
|||
<% return unless ability[:feature_flag] %>
|
||||
<% "`#{ability[:feature_flag]}`" %>
|
||||
<% end %>
|
||||
<% def scope(ability) %>
|
||||
<% scopes = [] %>
|
||||
<% scopes << 'Instance' if ability[:admin_ability]%>
|
||||
<% scopes << 'Group' if ability[:group_ability]%>
|
||||
<% scopes << 'Project' if ability[:project_ability]%>
|
||||
<% return scopes.join(',<br> ') %>
|
||||
<% end %>
|
||||
---
|
||||
stage: Software Supply Chain Security
|
||||
group: Authorization
|
||||
|
|
@ -46,9 +53,9 @@ Any dependencies are noted in the `Description` column for each permission.
|
|||
|
||||
## <%= "#{humanize(category)}" %>
|
||||
|
||||
| Name | Description | Introduced | Feature flag | Enabled |
|
||||
|:-----|:------------|:-----------|:-------------|:--------|
|
||||
| Permission | Description | API Attribute | Scope | Introduced |
|
||||
|:-----------|:------------|:--------------|:------|:-----------|
|
||||
<% abilities.each do |name, ability| %>
|
||||
| <%= "[`#{name}`](#{ability[:introduced_by_mr]})" %> | <%= ability[:description] %> | GitLab <%= "[#{ability[:milestone]}](#{ability[:introduced_by_issue]})" %> | <%= feature_flag(ability) %> | <%= enabled_link(ability) %> |
|
||||
| <%= ability[:title] %> | <%= ability[:description] %> | <%= "[`#{name}`](#{ability[:introduced_by_mr]})" %> | <%= scope(ability) %> | GitLab <%= "[#{ability[:milestone]}](#{ability[:introduced_by_issue]})" %> |
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
|||
Loading…
Reference in New Issue