Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
afaae32aba
commit
a009356633
|
|
@ -714,7 +714,7 @@ lib/gitlab/checks/**
|
|||
/doc/api/boards.md @msedlakjakubowski
|
||||
/doc/api/branches.md @brendan777
|
||||
/doc/api/bulk_imports.md @ashrafkhamis
|
||||
/doc/api/chat.md @fneill
|
||||
/doc/api/chat.md @jglassman1
|
||||
/doc/api/cluster_agents.md @phillipwells
|
||||
/doc/api/code_suggestions.md @jglassman1
|
||||
/doc/api/commits.md @brendan777
|
||||
|
|
@ -1096,7 +1096,7 @@ lib/gitlab/checks/**
|
|||
/doc/user/get_started/get_started_projects.md @emily.sahlani
|
||||
/doc/user/get_started/getting_started_gitlab_duo.md @sselhorn
|
||||
/doc/user/gitlab_duo/ @sselhorn
|
||||
/doc/user/gitlab_duo_chat/ @fneill
|
||||
/doc/user/gitlab_duo_chat/ @jglassman1
|
||||
/doc/user/glql/ @msedlakjakubowski
|
||||
/doc/user/group/access_and_permissions.md @emily.sahlani
|
||||
/doc/user/group/clusters/ @phillipwells
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ Gitlab/DocumentationLinks/Link:
|
|||
- 'ee/app/helpers/projects/learn_gitlab_helper.rb'
|
||||
- 'ee/lib/ee/gitlab/namespace_storage_size_error_message.rb'
|
||||
- 'ee/lib/gitlab/checks/secrets_check.rb'
|
||||
- 'ee/lib/gitlab/llm/embeddings/utils/base_content_parser.rb'
|
||||
- 'ee/spec/helpers/projects/learn_gitlab_helper_spec.rb'
|
||||
- 'ee/spec/lib/gitlab/llm/embeddings/utils/base_content_parser_spec.rb'
|
||||
- 'ee/spec/support/shared_contexts/secrets_check_shared_contexts.rb'
|
||||
- 'lib/backup/tasks/database.rb'
|
||||
- 'lib/system_check/helpers.rb'
|
||||
|
|
|
|||
|
|
@ -531,8 +531,6 @@ Layout/EmptyLineAfterMagicComment:
|
|||
- 'spec/requests/api/group_debian_distributions_spec.rb'
|
||||
- 'spec/requests/api/helm_packages_spec.rb'
|
||||
- 'spec/requests/api/maven_packages_spec.rb'
|
||||
- 'spec/requests/api/nuget_group_packages_spec.rb'
|
||||
- 'spec/requests/api/nuget_project_packages_spec.rb'
|
||||
- 'spec/requests/api/project_debian_distributions_spec.rb'
|
||||
- 'spec/requests/api/pypi_packages_spec.rb'
|
||||
- 'spec/requests/api/rpm_project_packages_spec.rb'
|
||||
|
|
|
|||
|
|
@ -2004,8 +2004,6 @@ Layout/LineLength:
|
|||
- 'lib/api/notes.rb'
|
||||
- 'lib/api/notification_settings.rb'
|
||||
- 'lib/api/npm_project_packages.rb'
|
||||
- 'lib/api/nuget_group_packages.rb'
|
||||
- 'lib/api/nuget_project_packages.rb'
|
||||
- 'lib/api/pages_domains.rb'
|
||||
- 'lib/api/project_clusters.rb'
|
||||
- 'lib/api/project_container_repositories.rb'
|
||||
|
|
@ -3729,7 +3727,6 @@ Layout/LineLength:
|
|||
- 'spec/requests/api/notes_spec.rb'
|
||||
- 'spec/requests/api/notification_settings_spec.rb'
|
||||
- 'spec/requests/api/npm_project_packages_spec.rb'
|
||||
- 'spec/requests/api/nuget_project_packages_spec.rb'
|
||||
- 'spec/requests/api/pages/internal_access_spec.rb'
|
||||
- 'spec/requests/api/pages/private_access_spec.rb'
|
||||
- 'spec/requests/api/pages/public_access_spec.rb'
|
||||
|
|
@ -4096,7 +4093,6 @@ Layout/LineLength:
|
|||
- 'spec/support/shared_contexts/fixtures/analytics_shared_context.rb'
|
||||
- 'spec/support/shared_contexts/graphql/resolvers/runners_resolver_shared_context.rb'
|
||||
- 'spec/support/shared_contexts/lib/gitlab/sidekiq_middleware/server_metrics_shared_context.rb'
|
||||
- 'spec/support/shared_contexts/presenters/nuget_shared_context.rb'
|
||||
- 'spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb'
|
||||
- 'spec/support/shared_contexts/requests/api/go_modules_shared_context.rb'
|
||||
- 'spec/support/shared_contexts/requests/api/npm_packages_shared_context.rb'
|
||||
|
|
@ -4199,7 +4195,6 @@ Layout/LineLength:
|
|||
- 'spec/support/shared_examples/requests/api/notes_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/read_user_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb'
|
||||
|
|
|
|||
|
|
@ -120,7 +120,6 @@ Lint/AssignmentInCondition:
|
|||
- 'ee/lib/ee/gitlab/repo_path.rb'
|
||||
- 'ee/lib/gem_extensions/elasticsearch/model/indexing/instance_methods.rb'
|
||||
- 'ee/lib/gitlab/group_plans_preloader.rb'
|
||||
- 'ee/lib/gitlab/llm/embeddings/utils/base_content_parser.rb'
|
||||
- 'ee/lib/gitlab/path_locks_finder.rb'
|
||||
- 'ee/lib/gitlab/subscription_portal/clients/graphql.rb'
|
||||
- 'ee/lib/system_check/geo/authorized_keys_check.rb'
|
||||
|
|
|
|||
|
|
@ -217,7 +217,6 @@ Performance/StringIdentifierArgument:
|
|||
- 'spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb'
|
||||
|
|
|
|||
|
|
@ -1119,7 +1119,6 @@ RSpec/BeforeAllRoleAssignment:
|
|||
- 'spec/requests/api/maven_packages_spec.rb'
|
||||
- 'spec/requests/api/merge_request_approvals_spec.rb'
|
||||
- 'spec/requests/api/merge_requests_spec.rb'
|
||||
- 'spec/requests/api/nuget_group_packages_spec.rb'
|
||||
- 'spec/requests/api/package_files_spec.rb'
|
||||
- 'spec/requests/api/pages/internal_access_spec.rb'
|
||||
- 'spec/requests/api/pages/private_access_spec.rb'
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ RSpec/ChangeByZero:
|
|||
- 'spec/support/shared_examples/controllers/todos_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/graphql/mutations/timelogs/create_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/models/relative_positioning_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/services/clusters/create_service_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/services/schedule_bulk_repository_shard_moves_shared_examples.rb'
|
||||
|
|
|
|||
|
|
@ -2219,7 +2219,6 @@ RSpec/ContextWording:
|
|||
- 'spec/requests/api/merge_requests_spec.rb'
|
||||
- 'spec/requests/api/notes_spec.rb'
|
||||
- 'spec/requests/api/npm_project_packages_spec.rb'
|
||||
- 'spec/requests/api/nuget_group_packages_spec.rb'
|
||||
- 'spec/requests/api/oauth_tokens_spec.rb'
|
||||
- 'spec/requests/api/package_files_spec.rb'
|
||||
- 'spec/requests/api/pages/internal_access_spec.rb'
|
||||
|
|
@ -2737,7 +2736,6 @@ RSpec/ContextWording:
|
|||
- 'spec/support/shared_examples/requests/api/multiple_and_scoped_issue_boards_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/notes_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/repositories_shared_context.rb'
|
||||
|
|
|
|||
|
|
@ -855,7 +855,6 @@ RSpec/VerifiedDoubles:
|
|||
- 'spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/debian_common_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/rack_attack_shared_examples.rb'
|
||||
|
|
|
|||
|
|
@ -449,7 +449,6 @@ Style/IfUnlessModifier:
|
|||
- 'lib/api/invitations.rb'
|
||||
- 'lib/api/maven_packages.rb'
|
||||
- 'lib/api/merge_requests.rb'
|
||||
- 'lib/api/nuget_project_packages.rb'
|
||||
- 'lib/api/pages_domains.rb'
|
||||
- 'lib/api/project_snippets.rb'
|
||||
- 'lib/api/projects.rb'
|
||||
|
|
|
|||
|
|
@ -1724,7 +1724,6 @@ Style/InlineDisableAnnotation:
|
|||
- 'lib/api/metadata.rb'
|
||||
- 'lib/api/namespaces.rb'
|
||||
- 'lib/api/notes.rb'
|
||||
- 'lib/api/nuget_project_packages.rb'
|
||||
- 'lib/api/pages_domains.rb'
|
||||
- 'lib/api/project_container_repositories.rb'
|
||||
- 'lib/api/project_packages.rb'
|
||||
|
|
@ -2428,7 +2427,6 @@ Style/InlineDisableAnnotation:
|
|||
- 'spec/support/shared_examples/requests/api/graphql/group_and_project_boards_query_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/ml_model_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb'
|
||||
- 'spec/support/shared_examples/requests/api/terraform/modules/v1/packages_shared_examples.rb'
|
||||
|
|
|
|||
|
|
@ -50,4 +50,3 @@ Style/NumericLiteralPrefix:
|
|||
- 'spec/models/personal_access_token_spec.rb'
|
||||
- 'spec/requests/api/personal_access_tokens_spec.rb'
|
||||
- 'spec/support/import_export/export_file_helper.rb'
|
||||
- 'spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb'
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ Style/RedundantReturn:
|
|||
- 'ee/app/workers/ee/repository_check/single_repository_worker.rb'
|
||||
- 'ee/lib/ee/api/entities/billable_member.rb'
|
||||
- 'ee/lib/ee/gitlab/checks/diff_check.rb'
|
||||
- 'lib/api/nuget_project_packages.rb'
|
||||
- 'lib/api/pagination_params.rb'
|
||||
- 'lib/feature/gitaly.rb'
|
||||
- 'lib/gitlab/auth/database/authentication.rb'
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
c9ad6326777be8ab69ddb1cf4b58e91a0fa38c81
|
||||
70e2fc09262d3087f8bca60a680dabc7bb328164
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ const Template = (args, { argTypes }) => ({
|
|||
<template #body>
|
||||
<p><code>#body</code> slot content</p>
|
||||
</template>
|
||||
<template #alert-popover>
|
||||
<div><code>#alert-popover</code> slot content</div>
|
||||
<template #alert-message>
|
||||
<div><code>#alert-message</code> slot content</div>
|
||||
</template>
|
||||
</panels-base>
|
||||
`,
|
||||
|
|
|
|||
|
|
@ -1,29 +1,15 @@
|
|||
<script>
|
||||
import {
|
||||
GlDisclosureDropdown,
|
||||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
GlPopover,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
} from '@gitlab/ui';
|
||||
import { GlPopover, GlDashboardPanel } from '@gitlab/ui';
|
||||
import { alertVariantIconMap } from '@gitlab/ui/src/utils/constants';
|
||||
import uniqueId from 'lodash/uniqueId';
|
||||
import { isObject } from 'lodash';
|
||||
import { VARIANT_DANGER, VARIANT_WARNING, VARIANT_INFO } from '~/alert';
|
||||
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
|
||||
import { PANEL_POPOVER_DELAY } from './constants';
|
||||
|
||||
export default {
|
||||
name: 'PanelsBase',
|
||||
components: {
|
||||
GlDisclosureDropdown,
|
||||
GlLoadingIcon,
|
||||
GlIcon,
|
||||
GlPopover,
|
||||
TooltipOnTruncate,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
GlDashboardPanel,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
|
|
@ -76,28 +62,29 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
popoverId: uniqueId('panel-alert-popover-'),
|
||||
titleTooltipId: uniqueId('title-tooltip-id-'),
|
||||
dropdownOpen: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
alertClasses() {
|
||||
const borderColor = this.showAlertState
|
||||
? this.$options.alertBorderColorMap[this.alertVariant]
|
||||
: '';
|
||||
|
||||
return `gl-border-t-2 gl-border-t-solid ${borderColor}`;
|
||||
borderColor() {
|
||||
return this.showAlertState ? this.$options.alertBorderColorMap[this.alertVariant] : '';
|
||||
},
|
||||
alertIconClasses() {
|
||||
return this.$options.alertIconClassMap[this.alertVariant];
|
||||
return this.showAlertState ? this.$options.alertIconClassMap[this.alertVariant] : '';
|
||||
},
|
||||
alertIcon() {
|
||||
return this.$options.alertVariantIconMap[this.alertVariant] ?? alertVariantIconMap.danger;
|
||||
if (this.showAlertState) {
|
||||
return this.$options.alertVariantIconMap[this.alertVariant] ?? alertVariantIconMap.danger;
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
showAlertPopover() {
|
||||
return this.showAlertState && !this.dropdownOpen;
|
||||
},
|
||||
editingActions() {
|
||||
return this.editing ? this.actions : [];
|
||||
},
|
||||
},
|
||||
PANEL_POPOVER_DELAY,
|
||||
alertVariantIconMap,
|
||||
|
|
@ -115,106 +102,41 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:id="popoverId"
|
||||
class="grid-stack-item-content gl-border gl-h-full !gl-overflow-visible gl-rounded-base gl-bg-white gl-p-4"
|
||||
:class="alertClasses"
|
||||
<gl-dashboard-panel
|
||||
container-class="grid-stack-item-content"
|
||||
:title="title"
|
||||
:title-icon-class="alertIconClasses"
|
||||
:title-icon="alertIcon"
|
||||
:title-popover="tooltip"
|
||||
:loading="loading"
|
||||
:loading-delayed="loadingDelayed"
|
||||
:loading-delayed-text="__('Still loading...')"
|
||||
:actions="editingActions"
|
||||
:actions-toggle-text="__('Actions')"
|
||||
:border-color-class="borderColor"
|
||||
@dropdownOpen="dropdownOpen = true"
|
||||
@dropdownClosed="dropdownOpen = false"
|
||||
>
|
||||
<div class="gl-flex gl-h-full gl-flex-col">
|
||||
<div class="gl-flex gl-items-start gl-justify-between" data-testid="panel-title">
|
||||
<tooltip-on-truncate
|
||||
v-if="title"
|
||||
:title="title"
|
||||
placement="top"
|
||||
boundary="viewport"
|
||||
class="gl-truncate gl-pb-3"
|
||||
>
|
||||
<gl-icon
|
||||
v-if="showAlertState"
|
||||
class="gl-mr-1"
|
||||
:class="alertIconClasses"
|
||||
:name="alertIcon"
|
||||
data-testid="panel-title-alert-icon"
|
||||
/>
|
||||
<strong class="gl-text-subtle">{{ title }}</strong>
|
||||
<template v-if="tooltip && tooltip.description">
|
||||
<gl-icon
|
||||
:id="titleTooltipId"
|
||||
data-testid="panel-title-tooltip-icon"
|
||||
name="information-o"
|
||||
variant="info"
|
||||
/>
|
||||
<gl-popover
|
||||
data-testid="panel-title-popover"
|
||||
boundary="viewport"
|
||||
:target="titleTooltipId"
|
||||
>
|
||||
<gl-sprintf v-if="tooltip.descriptionLink" :message="tooltip.description">
|
||||
<template #link="{ content }">
|
||||
<gl-link :href="tooltip.descriptionLink" class="gl-text-sm">{{
|
||||
content
|
||||
}}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
<template v-else>
|
||||
{{ tooltip.description }}
|
||||
</template>
|
||||
</gl-popover>
|
||||
</template>
|
||||
</tooltip-on-truncate>
|
||||
|
||||
<gl-disclosure-dropdown
|
||||
v-if="editing"
|
||||
:items="actions"
|
||||
icon="ellipsis_v"
|
||||
:toggle-text="__('Actions')"
|
||||
text-sr-only
|
||||
no-caret
|
||||
placement="bottom-end"
|
||||
fluid-width
|
||||
toggle-class="gl-ml-1"
|
||||
category="tertiary"
|
||||
positioning-strategy="fixed"
|
||||
@shown="dropdownOpen = true"
|
||||
@hidden="dropdownOpen = false"
|
||||
>
|
||||
<template #list-item="{ item }">
|
||||
<span> <gl-icon :name="item.icon" /> {{ item.text }}</span>
|
||||
</template>
|
||||
</gl-disclosure-dropdown>
|
||||
</div>
|
||||
<div
|
||||
class="gl-grow gl-overflow-y-auto gl-overflow-x-hidden"
|
||||
:class="{ 'gl-flex gl-flex-wrap gl-content-center gl-text-center': loading }"
|
||||
>
|
||||
<template v-if="loading">
|
||||
<gl-loading-icon size="lg" class="gl-w-full" />
|
||||
<div
|
||||
v-if="loadingDelayed"
|
||||
class="gl-w-full gl-text-subtle"
|
||||
data-testId="panel-loading-delayed-indicator"
|
||||
>
|
||||
{{ __('Still loading...') }}
|
||||
</div>
|
||||
</template>
|
||||
<!-- @slot The panel body to display when not loading. -->
|
||||
<slot v-else name="body"></slot>
|
||||
</div>
|
||||
|
||||
<template #body>
|
||||
<slot name="body"></slot>
|
||||
</template>
|
||||
<template #alert-message="{ panelId }">
|
||||
<gl-popover
|
||||
v-if="showAlertPopover"
|
||||
data-test-id="panel-alert-popover"
|
||||
:aria-describedby="panelId"
|
||||
triggers="hover focus"
|
||||
:title="alertPopoverTitle"
|
||||
:show-close-button="false"
|
||||
placement="top"
|
||||
:css-classes="['gl-max-w-1/2']"
|
||||
:target="popoverId"
|
||||
:target="panelId"
|
||||
:delay="$options.PANEL_POPOVER_DELAY"
|
||||
boundary="viewport"
|
||||
>
|
||||
<!-- @slot The panel error popover body to display when showAlertState is true. -->
|
||||
<slot name="alert-popover"></slot>
|
||||
</gl-popover>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</gl-dashboard-panel>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { GlIcon, GlBadge, GlTooltipDirective } from '@gitlab/ui';
|
|||
import {
|
||||
renderDeleteSuccessToast,
|
||||
deleteParams,
|
||||
} from 'ee_else_ce/vue_shared/components/resource_lists/utils';
|
||||
} from 'ee_else_ce/vue_shared/components/projects_list/utils';
|
||||
import ProjectListItemDescription from 'ee_else_ce/vue_shared/components/projects_list/project_list_item_description.vue';
|
||||
import ProjectListItemActions from 'ee_else_ce/vue_shared/components/projects_list/project_list_item_actions.vue';
|
||||
import ProjectListItemInactiveBadge from 'ee_else_ce/vue_shared/components/projects_list/project_list_item_inactive_badge.vue';
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import toast from '~/vue_shared/plugins/global_toast';
|
||||
import { sprintf, __ } from '~/locale';
|
||||
import { ACTION_EDIT, ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
|
||||
|
||||
export const availableGraphQLProjectActions = ({ userPermissions }) => {
|
||||
|
|
@ -13,3 +15,16 @@ export const availableGraphQLProjectActions = ({ userPermissions }) => {
|
|||
|
||||
return baseActions;
|
||||
};
|
||||
|
||||
export const renderDeleteSuccessToast = (project) => {
|
||||
toast(
|
||||
sprintf(__("Project '%{project_name}' is being deleted."), {
|
||||
project_name: project.name,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export const deleteParams = () => {
|
||||
// Overridden in EE
|
||||
return {};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -89,3 +89,5 @@ module Organizations
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Organizations::OrganizationsController.prepend_mod
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ module ReleasesHelper
|
|||
commit = project.repository.commit(@release.tag)
|
||||
|
||||
deployments.map do |deployment|
|
||||
user = deployment.deployable.user
|
||||
user = deployment.deployable&.user
|
||||
environment = deployment.environment
|
||||
|
||||
{
|
||||
|
|
@ -118,11 +118,15 @@ module ReleasesHelper
|
|||
short_sha: commit.short_id,
|
||||
title: commit.title
|
||||
},
|
||||
triggerer: {
|
||||
name: user&.name,
|
||||
web_url: user ? user_url(user) : nil,
|
||||
avatar_url: user&.avatar_url
|
||||
},
|
||||
|
||||
triggerer: if user
|
||||
{
|
||||
name: user.name,
|
||||
web_url: user_url(user),
|
||||
avatar_url: user.avatar_url
|
||||
}
|
||||
end,
|
||||
|
||||
created_at: deployment.created_at,
|
||||
finished_at: deployment.finished_at
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@
|
|||
module WorkItems
|
||||
module Widgets
|
||||
class Development < Base
|
||||
def self.quick_action_commands
|
||||
[:create_merge_request]
|
||||
end
|
||||
|
||||
def self.quick_action_params
|
||||
[:branch_name]
|
||||
end
|
||||
|
||||
def closing_merge_requests
|
||||
work_item.merge_requests_closing_issues
|
||||
end
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
.row
|
||||
= form.label :merge_after, s_('MergeRequests|Merge can start'), class: 'col-12'
|
||||
.js-merge-request-schedule-input{ data: {
|
||||
merge_after: issuable.merge_schedule&.merge_after&.strftime('%Y-%m-%dT%H:%MZ'),
|
||||
merge_after: issuable.merge_schedule&.merge_after&.strftime('%Y-%m-%dT%H:%M%z'),
|
||||
param_key: issuable.class.model_name.param_key
|
||||
} }
|
||||
%p.gl-text-subtle.col-12= s_('MergeRequests|Requires that merge checks pass.')
|
||||
|
|
|
|||
|
|
@ -51,14 +51,14 @@ The process for configuring TLS support depends on your installation type.
|
|||
sudo cp cert.pem /etc/gitlab/trusted-certs/
|
||||
```
|
||||
|
||||
1. On the Gitaly clients, edit `git_data_dirs` in `/etc/gitlab/gitlab.rb` as follows:
|
||||
1. On the Gitaly clients, edit `gitlab_rails['repositories_storages']` in `/etc/gitlab/gitlab.rb` as follows:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
'default' => { 'gitaly_address' => 'tls://gitaly1.internal:9999' },
|
||||
'storage1' => { 'gitaly_address' => 'tls://gitaly1.internal:9999' },
|
||||
'storage2' => { 'gitaly_address' => 'tls://gitaly2.internal:9999' },
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ Here are common errors and potential causes:
|
|||
- 500 response code
|
||||
- `ActionView::Template::Error (7:permission denied)`
|
||||
- `praefect['configuration'][:auth][:token]` and `gitlab_rails['gitaly_token']` do not match on the GitLab server.
|
||||
- `git_data_dirs` storage configuration is missing on the Sidekiq server.
|
||||
- `gitlab_rails['repositories_storages']` storage configuration is missing on the Sidekiq server.
|
||||
- `Unable to save project. Error: 7:permission denied`
|
||||
- Secret token in `praefect['configuration'][:virtual_storage]` on GitLab server does not match the
|
||||
value in `gitaly['auth_token']` on one or more Gitaly servers.
|
||||
|
|
|
|||
|
|
@ -1448,7 +1448,7 @@ To configure the Praefect nodes, on each one:
|
|||
password: '<praefect_postgresql_password>',
|
||||
},
|
||||
# Praefect Virtual Storage config
|
||||
# Name of storage hash must match storage name in git_data_dirs on GitLab
|
||||
# Name of storage hash must match storage name in gitlab_rails['repositories_storages'] on GitLab
|
||||
# server ('praefect') and in gitaly['configuration'][:storage] on Gitaly nodes ('gitaly-1')
|
||||
virtual_storage: [
|
||||
{
|
||||
|
|
@ -1733,16 +1733,16 @@ To configure Praefect with TLS:
|
|||
sudo cp cert.pem /etc/gitlab/trusted-certs/
|
||||
```
|
||||
|
||||
1. On the Praefect clients (except Gitaly servers), edit `git_data_dirs` in
|
||||
1. On the Praefect clients (except Gitaly servers), edit `gitlab_rails['repositories_storages']` in
|
||||
`/etc/gitlab/gitlab.rb` as follows:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => 'tls://LOAD_BALANCER_SERVER_ADDRESS:3305',
|
||||
"gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
|
||||
|
|
@ -1823,15 +1823,15 @@ To configure the Sidekiq nodes, on each one:
|
|||
]
|
||||
|
||||
# Gitaly Cluster
|
||||
## git_data_dirs get configured for the Praefect virtual storage
|
||||
## Address is Internal Load Balancer for Praefect
|
||||
## Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
## gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
## Address is the Internal Load Balancer for Praefect
|
||||
## Token is the praefect_external_token
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# PostgreSQL
|
||||
gitlab_rails['db_host'] = '10.6.0.40' # internal load balancer IP
|
||||
|
|
@ -1949,15 +1949,15 @@ On each node perform the following:
|
|||
```ruby
|
||||
external_url 'https://gitlab.example.com'
|
||||
|
||||
# git_data_dirs get configured for the Praefect virtual storage
|
||||
# Address is Internal Load Balancer for Praefect
|
||||
# Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
# gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
# Address is the Internal Load Balancer for Praefect
|
||||
# Token is the praefect_external_token
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
## Disable components that will not be on the GitLab application server
|
||||
roles(['application_role'])
|
||||
|
|
@ -2042,15 +2042,15 @@ On each node perform the following:
|
|||
```
|
||||
|
||||
1. If you're using [Gitaly with TLS support](#gitaly-cluster-tls-support), make sure the
|
||||
`git_data_dirs` entry is configured with `tls` instead of `tcp`:
|
||||
`gitlab_rails['repositories_storages']` entry is configured with `tls` instead of `tcp`:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tls://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Copy the cert into `/etc/gitlab/trusted-certs`:
|
||||
|
|
|
|||
|
|
@ -1454,7 +1454,7 @@ To configure the Praefect nodes, on each one:
|
|||
password: '<praefect_postgresql_password>',
|
||||
},
|
||||
# Praefect Virtual Storage config
|
||||
# Name of storage hash must match storage name in git_data_dirs on GitLab
|
||||
# Name of storage hash must match storage name in gitlab_rails['repositories_storages'] on GitLab
|
||||
# server ('praefect') and in gitaly['configuration'][:storage] on Gitaly nodes ('gitaly-1')
|
||||
virtual_storage: [
|
||||
{
|
||||
|
|
@ -1739,16 +1739,16 @@ To configure Praefect with TLS:
|
|||
sudo cp cert.pem /etc/gitlab/trusted-certs/
|
||||
```
|
||||
|
||||
1. On the Praefect clients (except Gitaly servers), edit `git_data_dirs` in
|
||||
1. On the Praefect clients (except Gitaly servers), edit `gitlab_rails['repositories_storages']` in
|
||||
`/etc/gitlab/gitlab.rb` as follows:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => 'tls://LOAD_BALANCER_SERVER_ADDRESS:3305',
|
||||
"gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
|
||||
|
|
@ -1829,15 +1829,15 @@ To configure the Sidekiq nodes, on each one:
|
|||
]
|
||||
|
||||
# Gitaly Cluster
|
||||
## git_data_dirs get configured for the Praefect virtual storage
|
||||
## Address is Internal Load Balancer for Praefect
|
||||
## Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
## gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
## Address is the Internal Load Balancer for Praefect
|
||||
## Token is the praefect_external_token
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# PostgreSQL
|
||||
gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP
|
||||
|
|
@ -1957,15 +1957,15 @@ On each node perform the following:
|
|||
```ruby
|
||||
external_url 'https://gitlab.example.com'
|
||||
|
||||
# git_data_dirs get configured for the Praefect virtual storage
|
||||
# Address is Internal Load Balancer for Praefect
|
||||
# Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
# gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
# Address is the Internal Load Balancer for Praefect
|
||||
# Token is the praefect_external_token
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
## Disable components that will not be on the GitLab application server
|
||||
roles(['application_role'])
|
||||
|
|
@ -2050,15 +2050,15 @@ On each node perform the following:
|
|||
```
|
||||
|
||||
1. If you're using [Gitaly with TLS support](#gitaly-cluster-tls-support), make sure the
|
||||
`git_data_dirs` entry is configured with `tls` instead of `tcp`:
|
||||
`gitlab_rails['repositories_storages']` entry is configured with `tls` instead of `tcp`:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tls://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Copy the cert into `/etc/gitlab/trusted-certs`:
|
||||
|
|
|
|||
|
|
@ -663,11 +663,11 @@ To configure the Sidekiq server, on the server node you want to use for Sidekiq:
|
|||
# of the Gitaly setup
|
||||
gitlab_rails['gitaly_token'] = 'gitalysecret'
|
||||
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
'default' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
|
||||
'storage1' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
|
||||
'storage2' => { 'gitaly_address' => 'tcp://gitaly2.internal:8075' },
|
||||
})
|
||||
}
|
||||
|
||||
## PostgreSQL connection details
|
||||
gitlab_rails['db_adapter'] = 'postgresql'
|
||||
|
|
@ -784,11 +784,11 @@ On each node perform the following:
|
|||
# of the Gitaly setup
|
||||
gitlab_rails['gitaly_token'] = 'gitalysecret'
|
||||
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
'default' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
|
||||
'storage1' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
|
||||
'storage2' => { 'gitaly_address' => 'tcp://gitaly2.internal:8075' },
|
||||
})
|
||||
}
|
||||
|
||||
## Disable components that will not be on the GitLab application server
|
||||
roles(['application_role'])
|
||||
|
|
@ -867,14 +867,14 @@ On each node perform the following:
|
|||
```
|
||||
|
||||
1. If you're using [Gitaly with TLS support](#gitaly-tls-support), make sure the
|
||||
`git_data_dirs` entry is configured with `tls` instead of `tcp`:
|
||||
`gitlab_rails['repositories_storages']` entry is configured with `tls` instead of `tcp`:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
'default' => { 'gitaly_address' => 'tls://gitaly1.internal:9999' },
|
||||
'storage1' => { 'gitaly_address' => 'tls://gitaly1.internal:9999' },
|
||||
'storage2' => { 'gitaly_address' => 'tls://gitaly2.internal:9999' },
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Copy the cert into `/etc/gitlab/trusted-certs`:
|
||||
|
|
|
|||
|
|
@ -1284,7 +1284,7 @@ To configure the Praefect nodes, on each one:
|
|||
password: '<praefect_postgresql_password>',
|
||||
},
|
||||
# Praefect Virtual Storage config
|
||||
# Name of storage hash must match storage name in git_data_dirs on GitLab
|
||||
# Name of storage hash must match storage name in gitlab_rails['repositories_storages'] on the GitLab
|
||||
# server ('praefect') and in gitaly['configuration'][:storage] on Gitaly nodes ('gitaly-1')
|
||||
virtual_storage: [
|
||||
{
|
||||
|
|
@ -1573,16 +1573,16 @@ To configure Praefect with TLS:
|
|||
sudo cp cert.pem /etc/gitlab/trusted-certs/
|
||||
```
|
||||
|
||||
1. On the Praefect clients (except Gitaly servers), edit `git_data_dirs` in
|
||||
1. On the Praefect clients (except Gitaly servers), edit `gitlab_rails['repositories_storages']` in
|
||||
`/etc/gitlab/gitlab.rb` as follows:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => 'tls://LOAD_BALANCER_SERVER_ADDRESS:3305',
|
||||
"gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
|
||||
|
|
@ -1655,15 +1655,15 @@ To configure the Sidekiq nodes, on each one:
|
|||
]
|
||||
|
||||
# Gitaly Cluster
|
||||
## git_data_dirs get configured for the Praefect virtual storage
|
||||
## Address is Internal Load Balancer for Praefect
|
||||
## Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
## repositories_storages gets configured for the Praefect virtual storage
|
||||
## Address is the Internal Load Balancer for Praefect
|
||||
## Token is the praefect_external_token
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# PostgreSQL
|
||||
gitlab_rails['db_host'] = '10.6.0.40' # internal load balancer IP
|
||||
|
|
@ -1790,15 +1790,15 @@ On each node perform the following:
|
|||
```ruby
|
||||
external_url 'https://gitlab.example.com'
|
||||
|
||||
# git_data_dirs get configured for the Praefect virtual storage
|
||||
# Address is Internal Load Balancer for Praefect
|
||||
# Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
# gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
# Address is the Internal Load Balancer for Praefect
|
||||
# Token is the praefect_external_token
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
## Disable components that will not be on the GitLab application server
|
||||
roles(['application_role'])
|
||||
|
|
@ -1901,15 +1901,15 @@ On each node perform the following:
|
|||
```
|
||||
|
||||
1. If you're using [Gitaly with TLS support](#gitaly-cluster-tls-support), make sure the
|
||||
`git_data_dirs` entry is configured with `tls` instead of `tcp`:
|
||||
`gitlab_rails['repositories_storages']` entry is configured with `tls` instead of `tcp`:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tls://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Copy the cert into `/etc/gitlab/trusted-certs`:
|
||||
|
|
|
|||
|
|
@ -1461,7 +1461,7 @@ To configure the Praefect nodes, on each one:
|
|||
password: '<praefect_postgresql_password>',
|
||||
},
|
||||
# Praefect Virtual Storage config
|
||||
# Name of storage hash must match storage name in git_data_dirs on GitLab
|
||||
# Name of storage hash must match storage name in gitlab_rails['repositories_storages'] on GitLab
|
||||
# server ('praefect') and in gitaly['configuration'][:storage] on Gitaly nodes ('gitaly-1')
|
||||
virtual_storage: [
|
||||
{
|
||||
|
|
@ -1746,16 +1746,16 @@ To configure Praefect with TLS:
|
|||
sudo cp cert.pem /etc/gitlab/trusted-certs/
|
||||
```
|
||||
|
||||
1. On the Praefect clients (except Gitaly servers), edit `git_data_dirs` in
|
||||
1. On the Praefect clients (except Gitaly servers), edit `gitlab_rails['repositories_storages']` in
|
||||
`/etc/gitlab/gitlab.rb` as follows:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => 'tls://LOAD_BALANCER_SERVER_ADDRESS:3305',
|
||||
"gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
|
||||
|
|
@ -1837,15 +1837,15 @@ To configure the Sidekiq nodes, on each one:
|
|||
]
|
||||
|
||||
# Gitaly
|
||||
# git_data_dirs get configured for the Praefect virtual storage
|
||||
# gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
# Address is Internal Load Balancer for Praefect
|
||||
# Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# PostgreSQL
|
||||
gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP
|
||||
|
|
@ -1975,15 +1975,15 @@ On each node perform the following:
|
|||
```ruby
|
||||
external_url 'https://gitlab.example.com'
|
||||
|
||||
# git_data_dirs get configured for the Praefect virtual storage
|
||||
# gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
# Address is Internal Load Balancer for Praefect
|
||||
# Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
## Disable components that will not be on the GitLab application server
|
||||
roles(['application_role'])
|
||||
|
|
@ -2069,15 +2069,15 @@ On each node perform the following:
|
|||
```
|
||||
|
||||
1. If you're using [Gitaly with TLS support](#gitaly-cluster-tls-support), make sure the
|
||||
`git_data_dirs` entry is configured with `tls` instead of `tcp`:
|
||||
`gitlab_rails['repositories_storages']` entry is configured with `tls` instead of `tcp`:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tls://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Copy the cert into `/etc/gitlab/trusted-certs`:
|
||||
|
|
|
|||
|
|
@ -1285,7 +1285,7 @@ To configure the Praefect nodes, on each one:
|
|||
password: '<praefect_postgresql_password>',
|
||||
},
|
||||
# Praefect Virtual Storage config
|
||||
# Name of storage hash must match storage name in git_data_dirs on GitLab
|
||||
# Name of storage hash must match storage name in gitlab_rails['repositories_storages'] on GitLab
|
||||
# server ('praefect') and in gitaly['configuration'][:storage] on Gitaly nodes ('gitaly-1')
|
||||
virtual_storage: [
|
||||
{
|
||||
|
|
@ -1570,16 +1570,16 @@ To configure Praefect with TLS:
|
|||
sudo cp cert.pem /etc/gitlab/trusted-certs/
|
||||
```
|
||||
|
||||
1. On the Praefect clients (except Gitaly servers), edit `git_data_dirs` in
|
||||
1. On the Praefect clients (except Gitaly servers), edit `gitlab_rails['repositories_storages']` in
|
||||
`/etc/gitlab/gitlab.rb` as follows:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => 'tls://LOAD_BALANCER_SERVER_ADDRESS:3305',
|
||||
"gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#reconfigure-a-linux-package-installation).
|
||||
|
|
@ -1652,15 +1652,15 @@ To configure the Sidekiq nodes, on each one:
|
|||
]
|
||||
|
||||
# Gitaly Cluster
|
||||
## git_data_dirs get configured for the Praefect virtual storage
|
||||
## Address is Internal Load Balancer for Praefect
|
||||
## Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
## gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
## Address is the Internal Load Balancer for Praefect
|
||||
## Token is the praefect_external_token
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# PostgreSQL
|
||||
gitlab_rails['db_host'] = '10.6.0.40' # internal load balancer IP
|
||||
|
|
@ -1787,15 +1787,15 @@ On each node perform the following:
|
|||
```ruby
|
||||
external_url 'https://gitlab.example.com'
|
||||
|
||||
# git_data_dirs get configured for the Praefect virtual storage
|
||||
# Address is Internal Load Balancer for Praefect
|
||||
# Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
# gitlab_rails['repositories_storages'] gets configured for the Praefect virtual storage
|
||||
# Address is the Internal Load Balancer for Praefect
|
||||
# Token is the praefect_external_token
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
## Disable components that will not be on the GitLab application server
|
||||
roles(['application_role'])
|
||||
|
|
@ -1901,15 +1901,15 @@ On each node perform the following:
|
|||
```
|
||||
|
||||
1. If you're using [Gitaly with TLS support](#gitaly-cluster-tls-support), make sure the
|
||||
`git_data_dirs` entry is configured with `tls` instead of `tcp`:
|
||||
`gitlab_rails['repositories_storages']` entry is configured with `tls` instead of `tcp`:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
gitlab_rails['repositories_storages'] = {
|
||||
"default" => {
|
||||
"gitaly_address" => "tls://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
1. Copy the cert into `/etc/gitlab/trusted-certs`:
|
||||
|
|
|
|||
|
|
@ -191,6 +191,6 @@ To start using Internal Events Tracking, follow these steps:
|
|||
|
||||
### Frontend
|
||||
|
||||
If you are calling `trackRedisHllUserEvent` in the frontend to track the frontend event, you can convert this to Internal events by using mixin, raw JavaScript or data tracking attribute,
|
||||
You can convert `trackRedisHllUserEvent` calls to Internal events by using the mixin, raw JavaScript, or the `data-event-tracking` attribute.
|
||||
|
||||
[Quick start guide](quick_start.md#frontend-tracking) has example for each methods.
|
||||
[Quick start guide](quick_start.md#frontend-tracking) has examples for each method.
|
||||
|
|
|
|||
|
|
@ -56,7 +56,8 @@ module API
|
|||
end
|
||||
|
||||
params do
|
||||
requires :id, types: [Integer, String], desc: 'The group ID or full group path.', regexp: ::API::Concerns::Packages::Nuget::PrivateEndpoints::POSITIVE_INTEGER_REGEX
|
||||
requires :id, types: [Integer, String], desc: 'The group ID or full group path.',
|
||||
regexp: ::API::Concerns::Packages::Nuget::PrivateEndpoints::POSITIVE_INTEGER_REGEX
|
||||
end
|
||||
|
||||
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
|
|
@ -66,8 +67,11 @@ module API
|
|||
end
|
||||
|
||||
authenticate_with do |accept|
|
||||
accept.token_types(:personal_access_token_with_username, :deploy_token_with_username, :job_token_with_username)
|
||||
.sent_through(:http_basic_auth)
|
||||
accept.token_types(
|
||||
:personal_access_token_with_username,
|
||||
:deploy_token_with_username,
|
||||
:job_token_with_username
|
||||
).sent_through(:http_basic_auth)
|
||||
end
|
||||
|
||||
namespace '/nuget' do
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ module API
|
|||
include ::Gitlab::Utils::StrongMemoize
|
||||
|
||||
params :file_params do
|
||||
requires :package, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
|
||||
requires :package, type: ::API::Validations::Types::WorkhorseFile,
|
||||
desc: 'The package file to be published (generated by Multipart middleware)', documentation: { type: 'file' }
|
||||
end
|
||||
|
||||
def project_or_group
|
||||
|
|
@ -82,7 +83,9 @@ module API
|
|||
def legacy_upload_nuget_package_file(symbol_package: false)
|
||||
project = project_or_group
|
||||
authorize_upload!(project)
|
||||
bad_request!('File is too large') if project.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
|
||||
|
||||
bad_request!('File is too large') if project.actual_limits.exceeded?(:nuget_max_file_size,
|
||||
params[:package].size)
|
||||
|
||||
file_params = params.merge(
|
||||
file: params[:package],
|
||||
|
|
@ -95,15 +98,19 @@ module API
|
|||
project, current_user, declared_params.merge(build: current_authenticated_job)
|
||||
).execute(:nuget, name: temp_file_name(symbol_package))
|
||||
|
||||
package_file = ::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job))
|
||||
.execute
|
||||
package_file = ::Packages::CreatePackageFileService
|
||||
.new(package, file_params.merge(build: current_authenticated_job))
|
||||
.execute
|
||||
|
||||
::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
|
||||
::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker -- not newly introduced
|
||||
end
|
||||
|
||||
def upload_nuget_package_file(symbol_package: false)
|
||||
authorize_upload!(project_or_group)
|
||||
bad_request!('File is too large') if project_or_group.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
|
||||
|
||||
if project_or_group.actual_limits.exceeded?(:nuget_max_file_size, params[:package].size)
|
||||
bad_request!('File is too large')
|
||||
end
|
||||
|
||||
file_params = params.merge(
|
||||
file: params[:package],
|
||||
|
|
@ -112,7 +119,8 @@ module API
|
|||
)
|
||||
|
||||
if !symbol_package && nuspec_file_service.success?
|
||||
# Create or update package on the fly if nuspec file is extracted successfully, otherwise fallback to the background job
|
||||
# Create or update package on the fly if nuspec file is extracted successfully,
|
||||
# otherwise fallback to the background job
|
||||
create_or_update_package(file_params)
|
||||
elsif symbol_package || nuspec_file_service.cause.nuspec_extraction_failed?
|
||||
create_temp_package_and_enqueue_worker(file_params, symbol_package)
|
||||
|
|
@ -139,7 +147,7 @@ module API
|
|||
package_file = ::Packages::CreatePackageFileService.new(package, file_params)
|
||||
.execute
|
||||
|
||||
::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker
|
||||
::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker -- not newly introduced
|
||||
end
|
||||
|
||||
def nuspec_file_service
|
||||
|
|
@ -177,7 +185,10 @@ module API
|
|||
)
|
||||
created!
|
||||
rescue ObjectStorage::RemoteStoreError => e
|
||||
Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project_or_group.id })
|
||||
Gitlab::ErrorTracking.track_exception(
|
||||
e,
|
||||
extra: { file_name: params[:file_name], project_id: project_or_group.id }
|
||||
)
|
||||
|
||||
forbidden!
|
||||
end
|
||||
|
|
@ -188,7 +199,10 @@ module API
|
|||
|
||||
def format_filename(package)
|
||||
return "#{params[:package_filename]}.#{params[:format]}" if package.version == params[:package_version]
|
||||
return "#{params[:package_filename].sub(params[:package_version], package.version)}.#{params[:format]}" if package.normalized_nuget_version == params[:package_version]
|
||||
|
||||
return unless package.normalized_nuget_version == params[:package_version]
|
||||
|
||||
"#{params[:package_filename].sub(params[:package_version], package.version)}.#{params[:format]}"
|
||||
end
|
||||
|
||||
def present_odata_entry
|
||||
|
|
@ -219,7 +233,8 @@ module API
|
|||
end
|
||||
|
||||
params do
|
||||
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project', regexp: ::API::Concerns::Packages::Nuget::PrivateEndpoints::POSITIVE_INTEGER_REGEX
|
||||
requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project',
|
||||
regexp: ::API::Concerns::Packages::Nuget::PrivateEndpoints::POSITIVE_INTEGER_REGEX
|
||||
end
|
||||
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
||||
namespace ':id/packages' do
|
||||
|
|
@ -228,8 +243,11 @@ module API
|
|||
end
|
||||
|
||||
authenticate_with do |accept|
|
||||
accept.token_types(:personal_access_token_with_username, :deploy_token_with_username, :job_token_with_username)
|
||||
.sent_through(:http_basic_auth)
|
||||
accept.token_types(
|
||||
:personal_access_token_with_username,
|
||||
:deploy_token_with_username,
|
||||
:job_token_with_username
|
||||
).sent_through(:http_basic_auth)
|
||||
end
|
||||
|
||||
namespace '/nuget' do
|
||||
|
|
@ -237,7 +255,8 @@ module API
|
|||
|
||||
# https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource
|
||||
params do
|
||||
requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'mynugetpkg.1.3.0.17.nupkg' }
|
||||
requires :package_name, type: String, desc: 'The NuGet package name', regexp: API::NO_SLASH_URL_PART_REGEX,
|
||||
documentation: { example: 'mynugetpkg.1.3.0.17.nupkg' }
|
||||
end
|
||||
namespace '/download/*package_name' do
|
||||
after_validation do
|
||||
|
|
@ -270,8 +289,10 @@ module API
|
|||
tags %w[nuget_packages]
|
||||
end
|
||||
params do
|
||||
requires :package_version, type: String, desc: 'The NuGet package version', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: '1.3.0.17' }
|
||||
requires :package_filename, type: String, desc: 'The NuGet package filename', regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'mynugetpkg.1.3.0.17.nupkg' }
|
||||
requires :package_version, type: String, desc: 'The NuGet package version',
|
||||
regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: '1.3.0.17' }
|
||||
requires :package_filename, type: String, desc: 'The NuGet package filename',
|
||||
regexp: API::NO_SLASH_URL_PART_REGEX, documentation: { example: 'mynugetpkg.1.3.0.17.nupkg' }
|
||||
end
|
||||
get '*package_version/*package_filename', format: [:nupkg, :snupkg], urgency: :low do
|
||||
package = find_package
|
||||
|
|
@ -297,8 +318,11 @@ module API
|
|||
# we redefine the `authenticate_with` method by combining the previous
|
||||
# authentication option with the new one.
|
||||
authenticate_with do |accept|
|
||||
accept.token_types(:personal_access_token_with_username, :deploy_token_with_username, :job_token_with_username)
|
||||
.sent_through(:http_basic_auth)
|
||||
accept.token_types(
|
||||
:personal_access_token_with_username,
|
||||
:deploy_token_with_username,
|
||||
:job_token_with_username
|
||||
).sent_through(:http_basic_auth)
|
||||
accept.token_types(:personal_access_token, :deploy_token, :job_token)
|
||||
.sent_through(http_header: API_KEY_HEADER)
|
||||
end
|
||||
|
|
@ -382,8 +406,10 @@ module API
|
|||
tags %w[nuget_packages]
|
||||
end
|
||||
params do
|
||||
requires :package_name, type: String, allow_blank: false, desc: 'The NuGet package name', regexp: Gitlab::Regex.nuget_package_name_regex, documentation: { example: 'mynugetpkg' }
|
||||
requires :package_version, type: String, allow_blank: false, desc: 'The NuGet package version', regexp: Gitlab::Regex.nuget_version_regex, documentation: { example: '1.0.1' }
|
||||
requires :package_name, type: String, allow_blank: false, desc: 'The NuGet package name',
|
||||
regexp: Gitlab::Regex.nuget_package_name_regex, documentation: { example: 'mynugetpkg' }
|
||||
requires :package_version, type: String, allow_blank: false, desc: 'The NuGet package version',
|
||||
regexp: Gitlab::Regex.nuget_version_regex, documentation: { example: '1.0.1' }
|
||||
end
|
||||
delete '*package_name/*package_version', format: false, urgency: :low do
|
||||
authorize_destroy_package!(project_or_group)
|
||||
|
|
@ -391,7 +417,13 @@ module API
|
|||
destroy_conditionally!(find_package) do |package|
|
||||
::Packages::MarkPackageForDestructionService.new(container: package, current_user: current_user).execute
|
||||
|
||||
track_package_event('delete_package', :nuget, category: 'API::NugetPackages', project: package.project, namespace: package.project.namespace)
|
||||
track_package_event(
|
||||
'delete_package',
|
||||
:nuget,
|
||||
category: 'API::NugetPackages',
|
||||
project: package.project,
|
||||
namespace: package.project.namespace
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace :tw do
|
|||
CodeOwnerRule.new('Distribution', '@axil'),
|
||||
CodeOwnerRule.new('Distribution (Charts)', '@axil'),
|
||||
CodeOwnerRule.new('Distribution (Omnibus)', '@eread'),
|
||||
CodeOwnerRule.new('Duo Chat', '@fneill'),
|
||||
CodeOwnerRule.new('Duo Chat', '@jglassman1'),
|
||||
CodeOwnerRule.new('Duo Workflow', '@sselhorn'),
|
||||
CodeOwnerRule.new('Dynamic Analysis', '@phillipwells'),
|
||||
CodeOwnerRule.new('Editor Extensions', '@aqualls'),
|
||||
|
|
|
|||
|
|
@ -43971,6 +43971,9 @@ msgstr ""
|
|||
msgid "Project '%{project_name}' was successfully updated."
|
||||
msgstr ""
|
||||
|
||||
msgid "Project '%{project_name}' will be deleted on %{date}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Project Badges"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
"@gitlab/fonts": "^1.3.0",
|
||||
"@gitlab/query-language-rust": "0.3.2",
|
||||
"@gitlab/svgs": "3.121.0",
|
||||
"@gitlab/ui": "106.2.0",
|
||||
"@gitlab/ui": "106.2.2",
|
||||
"@gitlab/vue-router-vue3": "npm:vue-router@4.1.6",
|
||||
"@gitlab/vuex-vue3": "npm:vuex@4.0.0",
|
||||
"@gitlab/web-ide": "^0.0.1-dev-20250109231656",
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ spec/frontend/ci/pipeline_editor/components/job_assistant_drawer/accordion_items
|
|||
spec/frontend/ci/pipeline_editor/components/lint/ci_lint_warnings_spec.js
|
||||
spec/frontend/ci/pipeline_editor/components/pipeline_editor_tabs_spec.js
|
||||
spec/frontend/ci/pipeline_editor/pipeline_editor_app_spec.js
|
||||
spec/frontend/ci/pipelines_page/components/pipelines_artifacts_spec.js
|
||||
spec/frontend/ci/runner/components/registration/runner_instructions/runner_instructions_modal_spec.js
|
||||
spec/frontend/ci/runner/components/runner_filtered_search_bar_spec.js
|
||||
spec/frontend/ci/runner/components/runner_form_fields_spec.js
|
||||
|
|
|
|||
|
|
@ -1,83 +1,139 @@
|
|||
import { nextTick } from 'vue';
|
||||
import {
|
||||
GlDisclosureDropdown,
|
||||
GlDisclosureDropdownItem,
|
||||
GlLoadingIcon,
|
||||
GlPopover,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
} from '@gitlab/ui';
|
||||
import { GlDashboardPanel, GlPopover } from '@gitlab/ui';
|
||||
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import PanelsBase from '~/vue_shared/components/customizable_dashboard/panels_base.vue';
|
||||
import { VARIANT_DANGER, VARIANT_WARNING, VARIANT_INFO } from '~/alert';
|
||||
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
|
||||
import { PANEL_POPOVER_DELAY } from '~/vue_shared/components/customizable_dashboard/constants';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
|
||||
describe('PanelsBase', () => {
|
||||
/** @type {import('helpers/vue_test_utils_helper').ExtendedWrapper} */
|
||||
let wrapper;
|
||||
|
||||
const createWrapper = ({ props = {}, slots = {}, mountFn = shallowMountExtended } = {}) => {
|
||||
const createWrapper = ({
|
||||
props = {},
|
||||
slots = {},
|
||||
scopedSlots = {},
|
||||
mountFn = shallowMountExtended,
|
||||
} = {}) => {
|
||||
wrapper = mountFn(PanelsBase, {
|
||||
propsData: {
|
||||
...props,
|
||||
},
|
||||
slots,
|
||||
stubs: { GlSprintf },
|
||||
scopedSlots,
|
||||
stubs: {
|
||||
GlPopover: stubComponent(GlPopover, {
|
||||
props: { ...GlPopover.props, delay: {} },
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findPanelTitle = () => wrapper.findComponent(TooltipOnTruncate);
|
||||
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
|
||||
const findLoadingDelayedIndicator = () => wrapper.findByTestId('panel-loading-delayed-indicator');
|
||||
const findPanelTitleTooltipIcon = () => wrapper.findByTestId('panel-title-tooltip-icon');
|
||||
const findPanelTitleAlertIcon = () => wrapper.findByTestId('panel-title-alert-icon');
|
||||
const findPanelTitlePopover = () => wrapper.findByTestId('panel-title-popover');
|
||||
const findPanelTitlePopoverLink = () => findPanelTitlePopover().findComponent(GlLink);
|
||||
const findDashboardPanel = () => wrapper.findComponent(GlDashboardPanel);
|
||||
const findPanelAlertPopover = () => wrapper.findComponent(GlPopover);
|
||||
const findPanelActionsDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
|
||||
const findDropdownItemByText = (text) =>
|
||||
findPanelActionsDropdown()
|
||||
.findAllComponents(GlDisclosureDropdownItem)
|
||||
.filter((w) => w.text() === text)
|
||||
.at(0);
|
||||
|
||||
describe('default behaviour', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper();
|
||||
});
|
||||
|
||||
it('does not render a title', () => {
|
||||
expect(findPanelTitle().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not render a loading icon', () => {
|
||||
expect(findLoadingIcon().exists()).toBe(false);
|
||||
expect(findLoadingDelayedIndicator().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('does not render a disclosure dropdown', () => {
|
||||
expect(findPanelActionsDropdown().exists()).toBe(false);
|
||||
it('sets the default props for the dashboard panel', () => {
|
||||
expect(findDashboardPanel().props()).toStrictEqual({
|
||||
containerClass: 'grid-stack-item-content',
|
||||
borderColorClass: '',
|
||||
title: '',
|
||||
titleIcon: '',
|
||||
titleIconClass: '',
|
||||
titlePopover: {},
|
||||
loading: false,
|
||||
loadingDelayed: false,
|
||||
loadingDelayedText: 'Still loading...',
|
||||
actions: [],
|
||||
actionsToggleText: 'Actions',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render an error popover', () => {
|
||||
expect(findPanelAlertPopover().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render the tooltip icon', () => {
|
||||
expect(findPanelTitleTooltipIcon().exists()).toBe(false);
|
||||
describe('with a title', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
title: 'Panel title',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not set an alert border color', () => {
|
||||
const alertClasses = [
|
||||
'gl-border-t-red-500',
|
||||
'gl-border-t-orange-500',
|
||||
'gl-border-t-blue-500',
|
||||
];
|
||||
it('sets the title prop', () => {
|
||||
expect(findDashboardPanel().props('title')).toBe('Panel title');
|
||||
});
|
||||
});
|
||||
|
||||
alertClasses.forEach((alertClass) => {
|
||||
expect(wrapper.attributes('class')).not.toContain(alertClass);
|
||||
describe('with a tooltip', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
tooltip: {
|
||||
description: 'This is a description',
|
||||
descriptionLink: '#',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('sets the titlePopover prop', () => {
|
||||
expect(findDashboardPanel().props('titlePopover')).toStrictEqual({
|
||||
description: 'This is a description',
|
||||
descriptionLink: '#',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with actions', () => {
|
||||
describe('when not editing', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
actions: [
|
||||
{
|
||||
text: 'Delete',
|
||||
icon: 'remove',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not set the actions prop', () => {
|
||||
expect(findDashboardPanel().props('actions')).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when editing', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
editing: true,
|
||||
actions: [
|
||||
{
|
||||
text: 'Delete',
|
||||
icon: 'remove',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('sets the actions prop', () => {
|
||||
expect(findDashboardPanel().props('actions')).toStrictEqual([
|
||||
{
|
||||
text: 'Delete',
|
||||
icon: 'remove',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -105,201 +161,15 @@ describe('PanelsBase', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('renders a loading icon', () => {
|
||||
expect(findLoadingIcon().exists()).toBe(true);
|
||||
expect(findLoadingDelayedIndicator().exists()).toBe(false);
|
||||
it('sets the dashboard panel loading prop', () => {
|
||||
expect(findDashboardPanel().props('loading')).toBe(true);
|
||||
});
|
||||
|
||||
it('renders the additional "Still loading" indicator if the data source is slow', async () => {
|
||||
await wrapper.setProps({ loadingDelayed: true });
|
||||
await nextTick();
|
||||
|
||||
expect(findLoadingIcon().exists()).toBe(true);
|
||||
expect(findLoadingDelayedIndicator().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when loading with a body slot', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
loading: true,
|
||||
},
|
||||
slots: {
|
||||
body: '<div data-testid="panel-body-slot"></div>',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render the panel body', () => {
|
||||
expect(wrapper.findByTestId('panel-body-slot').exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is a title', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
title: 'Panel Title',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the panel title', () => {
|
||||
expect(findPanelTitle().text()).toBe('Panel Title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is a title with a tooltip', () => {
|
||||
describe('with description and link', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
title: 'Panel Title',
|
||||
tooltip: {
|
||||
description: 'This is just information, %{linkStart}learn more%{linkEnd}',
|
||||
descriptionLink: '/foo',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the panel title tooltip icon with special content', () => {
|
||||
expect(findPanelTitleTooltipIcon().exists()).toBe(true);
|
||||
expect(findPanelTitlePopover().text()).toBe('This is just information, learn more');
|
||||
expect(findPanelTitlePopoverLink().attributes('href')).toBe('/foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('without description link', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
title: 'Panel Title',
|
||||
tooltip: {
|
||||
description: 'This is just information.',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the panel title tooltip icon with description only', () => {
|
||||
expect(findPanelTitleTooltipIcon().exists()).toBe(true);
|
||||
expect(findPanelTitlePopoverLink().exists()).toBe(false);
|
||||
expect(findPanelTitlePopover().text()).toBe('This is just information.');
|
||||
});
|
||||
});
|
||||
|
||||
describe('without description', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
title: 'Panel Title',
|
||||
tooltip: {
|
||||
descriptionLink: '/foo',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render the panel title tooltip icon', () => {
|
||||
expect(findPanelTitleTooltipIcon().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is a title with an error alert', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
title: 'Panel Title',
|
||||
showAlertState: true,
|
||||
alertVariant: VARIANT_DANGER,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the panel title error icon', () => {
|
||||
expect(findPanelTitleAlertIcon().exists()).toBe(true);
|
||||
expect(findPanelTitleAlertIcon().attributes('name')).toBe('error');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when editing and there are actions', () => {
|
||||
const actions = [
|
||||
{
|
||||
icon: 'pencil',
|
||||
text: 'Edit',
|
||||
action: () => {},
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
editing: true,
|
||||
actions,
|
||||
},
|
||||
mountFn: mountExtended,
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the panel actions dropdown', () => {
|
||||
expect(findPanelActionsDropdown().props('items')).toStrictEqual(actions);
|
||||
});
|
||||
|
||||
it('renders the panel action dropdown item and icon', () => {
|
||||
const dropdownItem = findDropdownItemByText(actions[0].text);
|
||||
|
||||
expect(dropdownItem.exists()).toBe(true);
|
||||
expect(dropdownItem.findComponent(GlIcon).props('name')).toBe(actions[0].icon);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is an error alert title and the alert state is true', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
alertPopoverTitle: 'Some error',
|
||||
showAlertState: true,
|
||||
},
|
||||
slots: {
|
||||
'alert-popover': '<div data-testid="alert-popover-slot"></div>',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the error popover', () => {
|
||||
const popover = findPanelAlertPopover();
|
||||
expect(popover.exists()).toBe(true);
|
||||
expect(popover.props('title')).toBe('Some error');
|
||||
|
||||
// TODO: Replace with .props() once GitLab-UI adds all supported props.
|
||||
// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/428
|
||||
expect(popover.vm.$attrs.delay).toStrictEqual(PANEL_POPOVER_DELAY);
|
||||
});
|
||||
|
||||
it('renders the error popover slot', () => {
|
||||
expect(wrapper.findByTestId('alert-popover-slot').exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the editing and error state are true', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper({
|
||||
props: {
|
||||
showAlertState: true,
|
||||
editing: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('hides the error popover when the dropdown is shown', async () => {
|
||||
expect(findPanelAlertPopover().exists()).toBe(true);
|
||||
|
||||
await findPanelActionsDropdown().vm.$emit('shown');
|
||||
|
||||
expect(findPanelAlertPopover().exists()).toBe(false);
|
||||
expect(findDashboardPanel().props('loadingDelayed')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -318,22 +188,55 @@ describe('PanelsBase', () => {
|
|||
showAlertState: true,
|
||||
alertVariant,
|
||||
},
|
||||
slots: {
|
||||
'alert-popover': '<div data-testid="alert-popover-slot"></div>',
|
||||
scopedSlots: {
|
||||
'alert-popover': '<div data-testid="panel-alert-popover-slot">Alert popover</div>',
|
||||
},
|
||||
mountFn: mountExtended,
|
||||
});
|
||||
});
|
||||
|
||||
it('sets the panel colors', () => {
|
||||
['gl-border-t-2', 'gl-border-t-solid', borderColor].forEach((cssClass) => {
|
||||
expect(wrapper.attributes('class')).toContain(cssClass);
|
||||
});
|
||||
expect(findDashboardPanel().props('borderColorClass')).toBe(borderColor);
|
||||
});
|
||||
|
||||
it('sets the alert icon', () => {
|
||||
expect(findPanelTitleAlertIcon().attributes('name')).toBe(iconName);
|
||||
expect(findPanelTitleAlertIcon().attributes('class')).toContain(iconColor);
|
||||
expect(findDashboardPanel().props('titleIcon')).toBe(iconName);
|
||||
expect(findDashboardPanel().props('titleIconClass')).toBe(iconColor);
|
||||
});
|
||||
|
||||
it('renders the alert message slot', () => {
|
||||
expect(findPanelAlertPopover().props()).toMatchObject({
|
||||
title: 'Some error',
|
||||
triggers: 'hover focus',
|
||||
delay: { hide: 500 },
|
||||
showCloseButton: false,
|
||||
placement: 'top',
|
||||
cssClasses: ['gl-max-w-1/2'],
|
||||
target: expect.stringContaining('gl-dashboard-panel-id-'),
|
||||
boundary: 'viewport',
|
||||
});
|
||||
|
||||
expect(findPanelAlertPopover().attributes()).toMatchObject({
|
||||
'aria-describedby': expect.stringContaining('gl-dashboard-panel-id-'),
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the alert popover slot', () => {
|
||||
expect(wrapper.findByTestId('panel-alert-popover-slot').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it.each`
|
||||
eventName | alertPopoverShown
|
||||
${`dropdownOpen`} | ${false}
|
||||
${`dropdownClosed`} | ${true}
|
||||
`(
|
||||
'when the dropdown event $eventName is emitted, the alert popover is $alertPopoverShown',
|
||||
async ({ eventName, alertPopoverShown }) => {
|
||||
findDashboardPanel().vm.$emit(eventName);
|
||||
await nextTick();
|
||||
expect(findPanelAlertPopover().exists()).toBe(alertPopoverShown);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ import {
|
|||
import {
|
||||
renderDeleteSuccessToast,
|
||||
deleteParams,
|
||||
} from 'ee_else_ce/vue_shared/components/resource_lists/utils';
|
||||
} from 'ee_else_ce/vue_shared/components/projects_list/utils';
|
||||
import { deleteProject } from '~/api/projects_api';
|
||||
import { createAlert } from '~/alert';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon/ci_icon.vue';
|
||||
|
|
@ -36,8 +36,8 @@ const MOCK_DELETE_PARAMS = {
|
|||
testParam: true,
|
||||
};
|
||||
|
||||
jest.mock('ee_else_ce/vue_shared/components/resource_lists/utils', () => ({
|
||||
...jest.requireActual('ee_else_ce/vue_shared/components/resource_lists/utils'),
|
||||
jest.mock('ee_else_ce/vue_shared/components/projects_list/utils', () => ({
|
||||
...jest.requireActual('ee_else_ce/vue_shared/components/projects_list/utils'),
|
||||
renderDeleteSuccessToast: jest.fn(),
|
||||
deleteParams: jest.fn(() => MOCK_DELETE_PARAMS),
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,24 @@
|
|||
import { availableGraphQLProjectActions } from '~/vue_shared/components/projects_list/utils';
|
||||
import organizationProjectsGraphQlResponse from 'test_fixtures/graphql/organizations/projects.query.graphql.json';
|
||||
import {
|
||||
availableGraphQLProjectActions,
|
||||
deleteParams,
|
||||
renderDeleteSuccessToast,
|
||||
} from '~/vue_shared/components/projects_list/utils';
|
||||
import { ACTION_EDIT, ACTION_DELETE } from '~/vue_shared/components/list_actions/constants';
|
||||
import { formatGraphQLProjects } from '~/vue_shared/components/projects_list/formatter';
|
||||
import toast from '~/vue_shared/plugins/global_toast';
|
||||
|
||||
describe('Projects list utils', () => {
|
||||
jest.mock('~/vue_shared/plugins/global_toast');
|
||||
|
||||
const {
|
||||
data: {
|
||||
organization: {
|
||||
projects: { nodes: projects },
|
||||
},
|
||||
},
|
||||
} = organizationProjectsGraphQlResponse;
|
||||
|
||||
describe('availableGraphQLProjectActions', () => {
|
||||
describe.each`
|
||||
userPermissions | availableActions
|
||||
${{ viewEditPage: false, removeProject: false }} | ${[]}
|
||||
|
|
@ -14,3 +31,19 @@ describe('Projects list utils', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderDeleteSuccessToast', () => {
|
||||
const [project] = formatGraphQLProjects(projects);
|
||||
|
||||
it('calls toast correctly', () => {
|
||||
renderDeleteSuccessToast(project);
|
||||
|
||||
expect(toast).toHaveBeenCalledWith(`Project '${project.name}' is being deleted.`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteParams', () => {
|
||||
it('returns empty object', () => {
|
||||
expect(deleteParams()).toStrictEqual({});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -166,6 +166,23 @@ RSpec.describe ReleasesHelper, feature_category: :release_orchestration do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when deployable is nil' do
|
||||
let_it_be(:deployment_with_user) do
|
||||
create(:deployment, environment: environment, project: project, sha: project.repository.commit.id,
|
||||
deployable: nil)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(release).to receive(:related_deployments).and_return([deployment_with_user])
|
||||
end
|
||||
|
||||
it 'sets triggerer as nil' do
|
||||
deployment_data = Gitlab::Json.parse(helper.data_for_show_page[:deployments]).first
|
||||
|
||||
expect(deployment_data['triggerer']).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user cannot read deployments' do
|
||||
# rubocop: disable CodeReuse/ActiveRecord -- mock for can? is incorrectly flagged
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -250,7 +250,8 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do
|
|||
:assignees,
|
||||
:labels,
|
||||
:start_and_due_date,
|
||||
:current_user_todos
|
||||
:current_user_todos,
|
||||
:development
|
||||
])
|
||||
end
|
||||
|
||||
|
|
@ -300,6 +301,10 @@ RSpec.describe WorkItem, feature_category: :portfolio_management do
|
|||
it 'returns quick action commands from current user todos widget' do
|
||||
is_expected.to include(:todo, :done)
|
||||
end
|
||||
|
||||
it 'returns quick action commands from development widget' do
|
||||
is_expected.to include(:create_merge_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@ RSpec.describe WorkItems::Widgets::Development, feature_category: :team_planning
|
|||
it { is_expected.to eq(:development) }
|
||||
end
|
||||
|
||||
describe '.quick_action_params' do
|
||||
subject { described_class.quick_action_params }
|
||||
|
||||
it { is_expected.to include(:branch_name) }
|
||||
end
|
||||
|
||||
describe '#type' do
|
||||
subject { described_class.new(build_stubbed(:work_item)).type }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
|
||||
|
|
@ -105,7 +106,7 @@ RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
|
|||
end
|
||||
end
|
||||
|
||||
context 'a group' do
|
||||
context 'for a group' do
|
||||
let(:target) { group }
|
||||
|
||||
it_behaves_like 'handling all endpoints'
|
||||
|
|
@ -171,8 +172,11 @@ RSpec.describe API::NugetGroupPackages, feature_category: :package_registry do
|
|||
|
||||
subject { get api(url), headers: headers }
|
||||
|
||||
before do
|
||||
before_all do
|
||||
subgroup.add_reporter(user)
|
||||
end
|
||||
|
||||
before do
|
||||
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value('private'))
|
||||
subgroup.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value('private'))
|
||||
group.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value('private'))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
|
||||
|
|
@ -111,7 +112,8 @@ RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
|
|||
update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like 'process nuget v2 $metadata service request', params[:user_role], params[:expected_status], params[:member]
|
||||
it_behaves_like 'process nuget v2 $metadata service request', params[:user_role], params[:expected_status],
|
||||
params[:member]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -199,11 +201,18 @@ RSpec.describe API::NugetProjectPackages, feature_category: :package_registry do
|
|||
end
|
||||
|
||||
describe 'GET /api/v4/projects/:id/packages/nuget/download/*package_name/*package_version/*package_filename' do
|
||||
let_it_be(:package) { create(:nuget_package, :with_symbol_package, :with_metadatum, project: project, name: package_name, version: '0.1') }
|
||||
let_it_be(:package) do
|
||||
create(:nuget_package, :with_symbol_package, :with_metadatum, project: project, name: package_name,
|
||||
version: '0.1')
|
||||
end
|
||||
|
||||
let_it_be(:package_version) { package.version }
|
||||
|
||||
let(:format) { 'nupkg' }
|
||||
let(:url) { "/projects/#{target.id}/packages/nuget/download/#{package.name}/#{package_version}/#{package.name}.#{package_version}.#{format}" }
|
||||
let(:url) do
|
||||
"/projects/#{target.id}/packages/nuget/download/" \
|
||||
"#{package.name}/#{package_version}/#{package.name}.#{package_version}.#{format}"
|
||||
end
|
||||
|
||||
subject { get api(url), headers: headers }
|
||||
|
||||
|
|
|
|||
|
|
@ -3,19 +3,18 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
||||
let_it_be_with_reload(:project) { create(:project, :repository) }
|
||||
|
||||
shared_context 'note on noteable' do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:maintainer) { create(:user, maintainer_of: project) }
|
||||
let_it_be(:assignee) { create(:user) }
|
||||
|
||||
before do
|
||||
before_all do
|
||||
project.add_maintainer(assignee)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'note on noteable that supports quick actions' do
|
||||
include_context 'note on noteable'
|
||||
|
||||
before do
|
||||
note.note = note_text
|
||||
end
|
||||
|
|
@ -206,7 +205,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
describe '/confidential' do
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :issue, project: project) }
|
||||
let_it_be(:note_text) { '/confidential' }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
context 'when work item does not have children' do
|
||||
it 'leaves the note empty' do
|
||||
|
|
@ -383,12 +382,12 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
end
|
||||
|
||||
describe '/add_child' do
|
||||
let_it_be(:noteable) { create(:work_item, :objective, project: project) }
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:child) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:second_child) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:note_text) { "/add_child #{child.to_reference}, #{second_child.to_reference}" }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let_it_be(:children) { [child, second_child] }
|
||||
let(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let(:children) { [child, second_child] }
|
||||
|
||||
it_behaves_like 'adds child work items'
|
||||
|
||||
|
|
@ -411,7 +410,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
|
||||
let_it_be_with_reload(:child) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:note_text) { "/remove_child #{child.to_reference}" }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
before do
|
||||
create(:parent_link, work_item_parent: noteable, work_item: child)
|
||||
|
|
@ -453,7 +452,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
|
||||
let_it_be_with_reload(:parent) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:note_text) { "/set_parent #{parent.to_reference}" }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
context 'when using work item reference' do
|
||||
let_it_be(:note_text) { "/set_parent #{project.full_path}#{parent.to_reference}" }
|
||||
|
|
@ -479,7 +478,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
let_it_be_with_reload(:parent) { create(:work_item, :objective, project: project) }
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :objective, project: project) }
|
||||
let_it_be(:note_text) { "/remove_parent" }
|
||||
let_it_be(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let(:note) { create(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
before do
|
||||
create(:parent_link, work_item_parent: parent, work_item: noteable)
|
||||
|
|
@ -511,7 +510,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
context 'when user is not allowed to promote work item' do
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:note_text) { '/promote_to issue' }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
before do
|
||||
project.team.find_member(maintainer.id).destroy!
|
||||
|
|
@ -526,7 +525,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
context 'on a task' do
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :task, project: project) }
|
||||
let_it_be(:note_text) { '/promote_to Issue' }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
it_behaves_like 'promotes work item', from: 'task', to: 'issue'
|
||||
|
||||
|
|
@ -540,7 +539,7 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
context 'on an issue' do
|
||||
let_it_be_with_reload(:noteable) { create(:work_item, :issue, project: project) }
|
||||
let_it_be(:note_text) { '/promote_to Incident' }
|
||||
let_it_be(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
let(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
it_behaves_like 'promotes work item', from: 'issue', to: 'incident'
|
||||
|
||||
|
|
@ -619,6 +618,8 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
end
|
||||
|
||||
describe '#execute' do
|
||||
include_context 'note on noteable'
|
||||
|
||||
let(:service) { described_class.new(project, maintainer) }
|
||||
|
||||
it_behaves_like 'note on noteable that supports quick actions' do
|
||||
|
|
@ -636,9 +637,44 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
let(:note) { build(:note_on_merge_request, project: project, noteable: merge_request) }
|
||||
end
|
||||
|
||||
context 'note on work item that supports quick actions' do
|
||||
include_context 'note on noteable'
|
||||
describe '/create_merge_request' do
|
||||
let(:note) { build(:note, noteable: noteable, project: project, note: note_text) }
|
||||
|
||||
context 'when noteable is a work item' do
|
||||
let_it_be(:noteable) { create(:work_item, project: project) }
|
||||
|
||||
context 'when no branch name is provided' do
|
||||
let(:note_text) { '/create_merge_request' }
|
||||
|
||||
it 'creates a merge request with default branch name', :aggregate_failures do
|
||||
expect { execute(note) }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
expect(MergeRequest.last.source_branch).to eq(noteable.to_branch_name)
|
||||
end
|
||||
|
||||
context 'when work item type does not have the development widget' do
|
||||
let_it_be(:work_item_type) { create(:work_item_type, :non_default) }
|
||||
let_it_be(:noteable) { create(:work_item, project: project, work_item_type: work_item_type) }
|
||||
|
||||
it 'does not create a merge request' do
|
||||
expect { execute(note) }.to not_change { MergeRequest.count }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a branch name is provided' do
|
||||
let(:note_text) { '/create_merge_request test-branch-1' }
|
||||
|
||||
it 'creates a merge request with default branch name', :aggregate_failures do
|
||||
expect { execute(note) }.to change { MergeRequest.count }.by(1)
|
||||
|
||||
expect(MergeRequest.last.source_branch).to eq('test-branch-1')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'note on work item that supports quick actions' do
|
||||
let_it_be(:work_item, reload: true) { create(:work_item, project: project) }
|
||||
|
||||
let(:note) { build(:note_on_work_item, project: project, noteable: work_item) }
|
||||
|
|
@ -720,13 +756,13 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
describe '#apply_updates' do
|
||||
include_context 'note on noteable'
|
||||
|
||||
let_it_be(:issue) { create(:issue, project: project) }
|
||||
let_it_be(:work_item, reload: true) { create(:work_item, :issue, project: project) }
|
||||
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
|
||||
let_it_be(:issue_note) { create(:note_on_issue, project: project, noteable: issue) }
|
||||
let_it_be(:work_item_note) { create(:note, project: project, noteable: work_item) }
|
||||
let_it_be(:mr_note) { create(:note_on_merge_request, project: project, noteable: merge_request) }
|
||||
let_it_be(:commit_note) { create(:note_on_commit, project: project) }
|
||||
let_it_be_with_reload(:issue) { create(:issue, project: project) }
|
||||
let_it_be_with_reload(:work_item) { create(:work_item, :issue, project: project) }
|
||||
let_it_be_with_reload(:merge_request) { create(:merge_request, source_project: project) }
|
||||
let_it_be_with_reload(:issue_note) { create(:note_on_issue, project: project, noteable: issue) }
|
||||
let_it_be_with_reload(:work_item_note) { create(:note, project: project, noteable: work_item) }
|
||||
let_it_be_with_reload(:mr_note) { create(:note_on_merge_request, project: project, noteable: merge_request) }
|
||||
let_it_be_with_reload(:commit_note) { create(:note_on_commit, project: project) }
|
||||
let(:update_params) { {} }
|
||||
|
||||
subject(:apply_updates) { described_class.new(project, maintainer).apply_updates(update_params, note) }
|
||||
|
|
@ -777,8 +813,9 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
create(:parent_link, work_item: task, work_item_parent: work_item)
|
||||
|
||||
expect(apply_updates.success?).to be false
|
||||
expect(apply_updates.message)
|
||||
.to include("All child items must be confidential in order to turn on confidentiality.")
|
||||
expect(apply_updates.message).to include(
|
||||
"A confidential issue must have only confidential children. Make any child items confidential and try again."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -800,9 +837,8 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
|
||||
context 'CE restriction for issue assignees' do
|
||||
describe '/assign' do
|
||||
let(:project) { create(:project) }
|
||||
let(:assignee) { create(:user) }
|
||||
let(:maintainer) { create(:user) }
|
||||
let_it_be(:assignee) { create(:user) }
|
||||
let_it_be(:maintainer) { create(:user) }
|
||||
let(:service) { described_class.new(project, maintainer) }
|
||||
let(:note) { create(:note_on_issue, note: note_text, project: project) }
|
||||
|
||||
|
|
@ -810,12 +846,15 @@ RSpec.describe Notes::QuickActionsService, feature_category: :text_editors do
|
|||
%(/assign @#{assignee.username} @#{maintainer.username}\n")
|
||||
end
|
||||
|
||||
before do
|
||||
stub_licensed_features(multiple_issue_assignees: false)
|
||||
before_all do
|
||||
project.add_maintainer(maintainer)
|
||||
project.add_maintainer(assignee)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_licensed_features(multiple_issue_assignees: false)
|
||||
end
|
||||
|
||||
it 'adds only one assignee from the list' do
|
||||
execute(note)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,10 @@ RSpec.shared_context 'with expected presenters dependency groups' do
|
|||
end
|
||||
|
||||
def create_dependencies_for(package)
|
||||
dependency1 = Packages::Dependency.find_by(name: 'Newtonsoft.Json', version_pattern: '12.0.3') || create(:packages_dependency, name: 'Newtonsoft.Json', version_pattern: '12.0.3')
|
||||
dependency2 = Packages::Dependency.find_by(name: 'Castle.Core', version_pattern: '4.4.1') || create(:packages_dependency, name: 'Castle.Core', version_pattern: '4.4.1')
|
||||
dependency1 = Packages::Dependency.find_by(name: 'Newtonsoft.Json', version_pattern: '12.0.3') ||
|
||||
create(:packages_dependency, name: 'Newtonsoft.Json', version_pattern: '12.0.3')
|
||||
dependency2 = Packages::Dependency.find_by(name: 'Castle.Core', version_pattern: '4.4.1') ||
|
||||
create(:packages_dependency, name: 'Castle.Core', version_pattern: '4.4.1')
|
||||
|
||||
create(:packages_dependency_link, :with_nuget_metadatum, package: package, dependency: dependency1)
|
||||
create(:packages_dependency_link, package: package, dependency: dependency2)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
RSpec.shared_examples 'rejects nuget packages access' do |user_type, status, add_member = true|
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
|
@ -21,7 +21,7 @@ end
|
|||
RSpec.shared_examples 'process nuget service index request' do |user_type, status, add_member = true, v2 = false|
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
|
@ -54,7 +54,7 @@ end
|
|||
RSpec.shared_examples 'process nuget v2 $metadata service request' do |user_type, status, add_member = true|
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
|
@ -92,12 +92,13 @@ end
|
|||
RSpec.shared_examples 'process nuget metadata request at package name level' do |user_type, status, add_member = true|
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
||||
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/packages_metadata'
|
||||
it_behaves_like 'returning nuget metadata json response with json schema',
|
||||
'public_api/v4/packages/nuget/packages_metadata'
|
||||
|
||||
context 'with invalid format' do
|
||||
let(:url) { "/#{target_type}/#{target.id}/packages/nuget/metadata/#{package_name}/index.xls" }
|
||||
|
|
@ -110,20 +111,24 @@ RSpec.shared_examples 'process nuget metadata request at package name level' do
|
|||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
||||
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/packages_metadata'
|
||||
it_behaves_like 'returning nuget metadata json response with json schema',
|
||||
'public_api/v4/packages/nuget/packages_metadata'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'process nuget metadata request at package name and package version level' do |user_type, status, add_member = true|
|
||||
RSpec.shared_examples \
|
||||
'process nuget metadata request at package name and package version level' \
|
||||
do |user_type, status, add_member = true|
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
||||
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/package_metadata'
|
||||
it_behaves_like 'returning nuget metadata json response with json schema',
|
||||
'public_api/v4/packages/nuget/package_metadata'
|
||||
|
||||
context 'with invalid format' do
|
||||
let(:url) { "/#{target_type}/#{target.id}/packages/nuget/metadata/#{package_name}/#{package.version}.xls" }
|
||||
|
|
@ -136,7 +141,8 @@ RSpec.shared_examples 'process nuget metadata request at package name and packag
|
|||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
||||
it_behaves_like 'returning nuget metadata json response with json schema', 'public_api/v4/packages/nuget/package_metadata'
|
||||
it_behaves_like 'returning nuget metadata json response with json schema',
|
||||
'public_api/v4/packages/nuget/package_metadata'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -144,7 +150,7 @@ end
|
|||
RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, status, add_member = true|
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
|
@ -172,7 +178,7 @@ RSpec.shared_examples 'process nuget workhorse authorization' do |user_type, sta
|
|||
end
|
||||
|
||||
RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member = true, symbol_package = false|
|
||||
shared_context 'stub nuspec extraction service' do
|
||||
shared_context 'with nuspec extraction service stub' do
|
||||
before do
|
||||
Grape::Endpoint.before_each do |endpoint|
|
||||
allow(endpoint).to receive(:nuspec_file_service).and_return(service_result)
|
||||
|
|
@ -190,7 +196,9 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
|
|||
|
||||
before do
|
||||
allow_next_instance_of(::Packages::Nuget::ExtractRemoteMetadataFileService) do |service|
|
||||
allow(service).to receive(:execute).and_return(ServiceResponse.success(payload: fixture_file('packages/nuget/with_metadata.nuspec')))
|
||||
allow(service).to receive(:execute).and_return(
|
||||
ServiceResponse.success(payload: fixture_file('packages/nuget/with_metadata.nuspec'))
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -224,7 +232,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
|
|||
end
|
||||
|
||||
context 'when nuspec extraction fails' do
|
||||
include_context 'stub nuspec extraction service' do
|
||||
include_context 'with nuspec extraction service stub' do
|
||||
let(:service_result) { ServiceResponse.error(message: 'error', reason: :nuspec_extraction_failed) }
|
||||
end
|
||||
|
||||
|
|
@ -241,12 +249,12 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
|
|||
end
|
||||
|
||||
context 'when nuspec extraction fails with a different error', unless: symbol_package do
|
||||
include_context 'stub nuspec extraction service' do
|
||||
include_context 'with nuspec extraction service stub' do
|
||||
let(:service_result) { ServiceResponse.error(message: 'error', reason: :bad_request) }
|
||||
end
|
||||
|
||||
it 'returns a bad request' do
|
||||
expect { subject }.to change { target.packages.count }.by(0)
|
||||
expect { subject }.not_to change { target.packages.count }
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
|
|
@ -260,7 +268,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
|
|||
end
|
||||
|
||||
it 'returns a bad request' do
|
||||
expect { subject }.to change { target.packages.count }.by(0)
|
||||
expect { subject }.not_to change { target.packages.count }
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
|
|
@ -268,7 +276,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
|
|||
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
context 'with object storage disabled' do
|
||||
|
|
@ -296,7 +304,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
|
|||
|
||||
context 'with object storage enabled' do
|
||||
let(:tmp_object) do
|
||||
fog_connection.directories.new(key: 'packages').files.create( # rubocop:disable Rails/SaveBang
|
||||
fog_connection.directories.new(key: 'packages').files.create( # rubocop:disable Rails/SaveBang -- not the AR method
|
||||
key: "tmp/uploads/#{file_name}",
|
||||
body: 'content'
|
||||
)
|
||||
|
|
@ -314,8 +322,10 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
|
|||
|
||||
['123123', '../../123123'].each do |remote_id|
|
||||
context "with invalid remote_id: #{remote_id}" do
|
||||
include_context 'stub nuspec extraction service' do
|
||||
let(:service_result) { ServiceResponse.success(payload: fixture_file('packages/nuget/with_metadata.nuspec')) }
|
||||
include_context 'with nuspec extraction service stub' do
|
||||
let(:service_result) do
|
||||
ServiceResponse.success(payload: fixture_file('packages/nuget/with_metadata.nuspec'))
|
||||
end
|
||||
end
|
||||
|
||||
let(:params) do
|
||||
|
|
@ -336,7 +346,7 @@ RSpec.shared_examples 'process nuget upload' do |user_type, status, add_member =
|
|||
let(:file_key) { :file }
|
||||
|
||||
it 'does not create a package file' do
|
||||
expect { subject }.to change { ::Packages::PackageFile.count }.by(0)
|
||||
expect { subject }.not_to change { ::Packages::PackageFile.count }
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', :bad_request
|
||||
|
|
@ -368,7 +378,7 @@ RSpec.shared_examples 'process nuget download versions request' do |user_type, s
|
|||
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
|
@ -394,7 +404,7 @@ end
|
|||
RSpec.shared_examples 'process nuget download content request' do |user_type, status, add_member = true|
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
|
@ -410,7 +420,10 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
|
|||
end
|
||||
|
||||
context 'with invalid format' do
|
||||
let(:url) { "/#{target_type}/#{target.id}/packages/nuget/download/#{package.name}/#{package.version}/#{package.name}.#{package.version}.xls" }
|
||||
let(:url) do
|
||||
"/#{target_type}/#{target.id}/packages/nuget/download/" \
|
||||
"#{package.name}/#{package.version}/#{package.name}.#{package.version}.xls"
|
||||
end
|
||||
|
||||
it_behaves_like 'rejects nuget packages access', :anonymous, :not_found
|
||||
end
|
||||
|
|
@ -474,7 +487,7 @@ RSpec.shared_examples 'process nuget search request' do |user_type, status, add_
|
|||
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returns a valid json search response', status, 4, [1, 5, 5, 1]
|
||||
|
|
@ -515,7 +528,7 @@ end
|
|||
|
||||
RSpec.shared_examples 'process empty nuget search request' do |user_type, status, add_member = true|
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
target.send(:"add_#{user_type}", user) if add_member && user_type != :anonymous
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
|
@ -537,7 +550,7 @@ RSpec.shared_examples 'rejects nuget access with invalid target id' do |not_foun
|
|||
context 'with a target id with invalid integers' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
let(:target) { double(id: id) }
|
||||
let(:target) { instance_double(Group, id:) }
|
||||
|
||||
where(:id, :status) do
|
||||
'/../' | :bad_request
|
||||
|
|
@ -545,7 +558,7 @@ RSpec.shared_examples 'rejects nuget access with invalid target id' do |not_foun
|
|||
'%20' | :bad_request
|
||||
'%2e%2e%2f' | :bad_request
|
||||
'NaN' | :bad_request
|
||||
00002345 | not_found_response
|
||||
0o0002345 | not_found_response
|
||||
'anything25' | :bad_request
|
||||
end
|
||||
|
||||
|
|
@ -557,7 +570,7 @@ end
|
|||
|
||||
RSpec.shared_examples 'rejects nuget access with unknown target id' do |not_found_response: :unauthorized|
|
||||
context 'with an unknown target' do
|
||||
let(:target) { double(id: non_existing_record_id) }
|
||||
let(:target) { instance_double(Group, id: non_existing_record_id) }
|
||||
|
||||
context 'as anonymous' do
|
||||
it_behaves_like 'rejects nuget packages access', :anonymous, not_found_response
|
||||
|
|
@ -642,7 +655,8 @@ RSpec.shared_examples 'nuget authorize upload endpoint' do
|
|||
it { is_expected.to have_request_urgency(:low) }
|
||||
|
||||
context 'with valid project' do
|
||||
where(:visibility_level, :user_role, :member, :user_token, :sent_through, :shared_examples_name, :expected_status) do
|
||||
where(:visibility_level, :user_role, :member, :user_token, :sent_through, :shared_examples_name,
|
||||
:expected_status) do
|
||||
'PUBLIC' | :developer | true | true | :basic_auth | 'process nuget workhorse authorization' | :success
|
||||
'PUBLIC' | :guest | true | true | :basic_auth | 'rejects nuget packages access' | :forbidden
|
||||
'PUBLIC' | :developer | true | false | :basic_auth | 'rejects nuget packages access' | :unauthorized
|
||||
|
|
@ -740,7 +754,8 @@ RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
|
|||
it { is_expected.to have_request_urgency(:low) }
|
||||
|
||||
context 'with valid project' do
|
||||
where(:visibility_level, :user_role, :member, :user_token, :sent_through, :shared_examples_name, :expected_status) do
|
||||
where(:visibility_level, :user_role, :member, :user_token, :sent_through, :shared_examples_name,
|
||||
:expected_status) do
|
||||
'PUBLIC' | :developer | true | true | :basic_auth | 'process nuget upload' | :created
|
||||
'PUBLIC' | :guest | true | true | :basic_auth | 'rejects nuget packages access' | :forbidden
|
||||
'PUBLIC' | :developer | true | false | :basic_auth | 'rejects nuget packages access' | :unauthorized
|
||||
|
|
@ -805,7 +820,8 @@ RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
|
|||
update_visibility_to(Gitlab::VisibilityLevel.const_get(visibility_level, false))
|
||||
end
|
||||
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member], symbol_package
|
||||
it_behaves_like params[:shared_examples_name], params[:user_role], params[:expected_status], params[:member],
|
||||
symbol_package
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -819,7 +835,7 @@ RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
|
|||
|
||||
it_behaves_like 'rejects nuget access with invalid target id'
|
||||
|
||||
context 'file size above maximum limit' do
|
||||
context 'when file size above maximum limit' do
|
||||
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
|
||||
|
||||
before do
|
||||
|
|
@ -846,7 +862,10 @@ RSpec.shared_examples 'nuget upload endpoint' do |symbol_package: false|
|
|||
context 'when package duplicates are not allowed', unless: symbol_package do
|
||||
let(:params) { { package: fixture_file_upload('spec/fixtures/packages/nuget/package.nupkg') } }
|
||||
let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token).merge(workhorse_headers) }
|
||||
let!(:existing_package) { create(:nuget_package, project: project, name: 'DummyProject.DummyPackage', version: '1.0.0') }
|
||||
let!(:existing_package) do
|
||||
create(:nuget_package, project: project, name: 'DummyProject.DummyPackage', version: '1.0.0')
|
||||
end
|
||||
|
||||
let_it_be(:package_settings) do
|
||||
create(:namespace_package_setting, :group, namespace: project.namespace, nuget_duplicates_allowed: false)
|
||||
end
|
||||
|
|
@ -866,7 +885,7 @@ end
|
|||
RSpec.shared_examples 'process nuget delete request' do |user_type, status, auth|
|
||||
context "for user type #{user_type}" do
|
||||
before do
|
||||
target.send("add_#{user_type}", user) if user_type
|
||||
target.send(:"add_#{user_type}", user) if user_type
|
||||
end
|
||||
|
||||
it_behaves_like 'returning response status', status
|
||||
|
|
@ -928,7 +947,7 @@ RSpec.shared_examples 'nuget symbol file endpoint' do
|
|||
end
|
||||
|
||||
context 'when target does not exist' do
|
||||
let(:target) { double(id: non_existing_record_id) }
|
||||
let(:target) { instance_double(Group, id: non_existing_record_id) }
|
||||
|
||||
it_behaves_like 'returning response status', :not_found
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1436,10 +1436,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.121.0.tgz#57cacc895929aef4320632396373797a64b230ff"
|
||||
integrity sha512-ZekVjdMZrjrNEjdrOHsJYCu7A+ea3AkuNUxWIZ3FaNgJj4Oh21RlTP7bQKnRSXVhBbV1jg1PgzQ1ANEoCW8t4g==
|
||||
|
||||
"@gitlab/ui@106.2.0":
|
||||
version "106.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-106.2.0.tgz#d69f4d886c30703dde593a699a882d8e4b0df3eb"
|
||||
integrity sha512-Kpl5TWEIaQXFaoFZTyw8HaeQLgeohaWAinTPHOIr5zPB8J4TryAuGe92jg5LxoJ6/iNRBHhfZ5dYs25TSoiYlg==
|
||||
"@gitlab/ui@106.2.2":
|
||||
version "106.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-106.2.2.tgz#369f87b8d8bf621cdb4f149fd97c8d5d6d977233"
|
||||
integrity sha512-9GKTaNb0pDgxgPgIHoOvmZDRV56rQrnqt0UexBaiDlbY/GNTMJbLaybjy7/VVp+GjpE5XT3jmekjRTRjC7oIxg==
|
||||
dependencies:
|
||||
"@floating-ui/dom" "1.4.3"
|
||||
echarts "^5.3.2"
|
||||
|
|
|
|||
Loading…
Reference in New Issue