Skip to content
Snippets Groups Projects

Protected containers: Create protection rules in project settings

12 files
+ 668
1
Compare changes
  • Side-by-side
  • Inline
Files
12
<script>
import {
GlAlert,
GlCard,
GlTable,
GlLoadingIcon,
GlKeysetPagination,
GlModalDirective,
} from '@gitlab/ui';
import protectionRulesQuery from '~/packages_and_registries/settings/project/graphql/queries/get_container_protection_rules.query.graphql';
import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
import { s__, __ } from '~/locale';
const PAGINATION_DEFAULT_PER_PAGE = 10;
const I18N_PUSH_PROTECTED_UP_TO_ACCESS_LEVEL = s__(
'ContainerRegistry|Push protected up to access level',
);
const I18N_DELETE_PROTECTED_UP_TO_ACCESS_LEVEL = s__(
'ContainerRegistry|Delete protected up to access level',
);
const ACCESS_LEVEL_GRAPHQL_VALUE_TO_LABEL = {
DEVELOPER: __('Developer'),
MAINTAINER: __('Maintainer'),
OWNER: __('Owner'),
};
export default {
components: {
GlAlert,
GlCard,
GlKeysetPagination,
GlLoadingIcon,
GlTable,
SettingsBlock,
},
directives: {
GlModal: GlModalDirective,
},
inject: ['projectPath'],
i18n: {
settingBlockTitle: s__('ContainerRegistry|Container protection rules'),
settingBlockDescription: s__(
'ContainerRegistry|When a container is protected then only certain user roles are able to update and delete the protected container. This helps to avoid tampering with the container.',
),
},
data() {
return {
protectionRules: [],
protectionRuleFormVisibility: false,
protectionRulesQueryPayload: { nodes: [], pageInfo: {} },
protectionRulesQueryPaginationParams: { first: PAGINATION_DEFAULT_PER_PAGE },
protectionRuleMutationInProgress: false,
protectionRuleMutationItem: null,
alertErrorMessage: '',
};
},
computed: {
tableItems() {
return this.protectionRulesQueryResult.map((protectionRule) => {
return {
deleteProtectedUpToAccessLevel:
ACCESS_LEVEL_GRAPHQL_VALUE_TO_LABEL[protectionRule.deleteProtectedUpToAccessLevel],
pushProtectedUpToAccessLevel:
ACCESS_LEVEL_GRAPHQL_VALUE_TO_LABEL[protectionRule.pushProtectedUpToAccessLevel],
repositoryPathPattern: protectionRule.repositoryPathPattern,
};
});
},
protectionRulesQueryPageInfo() {
return this.protectionRulesQueryPayload.pageInfo;
},
protectionRulesQueryResult() {
return this.protectionRulesQueryPayload.nodes;
},
isLoadingprotectionRules() {
return this.$apollo.queries.protectionRulesQueryPayload.loading;
},
},
apollo: {
protectionRulesQueryPayload: {
query: protectionRulesQuery,
variables() {
return {
projectPath: this.projectPath,
...this.protectionRulesQueryPaginationParams,
};
},
update(data) {
return data.project?.containerRegistryProtectionRules ?? this.protectionRulesQueryPayload;
},
error(e) {
this.alertErrorMessage = e.message;
},
},
},
methods: {
showProtectionRuleForm() {
this.protectionRuleFormVisibility = true;
},
hideProtectionRuleForm() {
this.protectionRuleFormVisibility = false;
},
refetchProtectionRules() {
this.$apollo.queries.protectionRulesQueryPayload.refetch();
this.hideProtectionRuleForm();
},
onNextPage() {
this.protectionRulesQueryPaginationParams = {
after: this.protectionRulesQueryPageInfo.endCursor,
first: PAGINATION_DEFAULT_PER_PAGE,
};
},
onPrevPage() {
this.protectionRulesQueryPaginationParams = {
before: this.protectionRulesQueryPageInfo.startCursor,
last: PAGINATION_DEFAULT_PER_PAGE,
};
},
showProtectionRuleDeletionConfirmModal(protectionRule) {
this.protectionRuleMutationItem = protectionRule;
},
clearAlertMessage() {
this.alertErrorMessage = '';
},
resetProtectionRuleMutation() {
this.protectionRuleMutationItem = null;
this.protectionRuleMutationInProgress = false;
},
isProtectionRulePushProtectedUpToAccessLevelFormSelectDisabled(item) {
return this.isProtectionRuleMutationInProgress(item);
},
isProtectionRuleDeleteButtonDisabled(item) {
return this.isProtectionRuleMutationInProgress(item);
},
isProtectionRuleMutationInProgress(item) {
return this.protectionRuleMutationItem === item && this.protectionRuleMutationInProgress;
},
},
fields: [
{
key: 'repositoryPathPattern',
label: s__('ContainerRegistry|Repository path pattern'),
tdClass: 'gl-w-30',
},
{
key: 'pushProtectedUpToAccessLevel',
label: I18N_PUSH_PROTECTED_UP_TO_ACCESS_LEVEL,
tdClass: 'gl-w-15',
},
{
key: 'deleteProtectedUpToAccessLevel',
label: I18N_DELETE_PROTECTED_UP_TO_ACCESS_LEVEL,
tdClass: 'gl-w-15',
},
],
};
</script>
<template>
<settings-block>
<template #title>{{ $options.i18n.settingBlockTitle }}</template>
<template #description>
{{ $options.i18n.settingBlockDescription }}
</template>
<template #default>
<gl-card
class="gl-new-card"
header-class="gl-new-card-header"
body-class="gl-new-card-body gl-px-0"
>
<template #header>
<div class="gl-new-card-title-wrapper gl-justify-content-space-between">
<h3 class="gl-new-card-title">{{ $options.i18n.settingBlockTitle }}</h3>
</div>
</template>
<template #default>
<gl-alert
v-if="alertErrorMessage"
class="gl-mb-5"
variant="danger"
@dismiss="clearAlertMessage"
>
{{ alertErrorMessage }}
</gl-alert>
<gl-table
:items="tableItems"
:fields="$options.fields"
show-empty
stacked="md"
:aria-label="$options.i18n.settingBlockTitle"
:busy="isLoadingprotectionRules"
>
<template #table-busy>
<gl-loading-icon size="sm" class="gl-my-5" />
</template>
</gl-table>
<div class="gl-display-flex gl-justify-content-center gl-mb-3">
<gl-keyset-pagination
v-bind="protectionRulesQueryPageInfo"
:prev-text="__('Previous')"
:next-text="__('Next')"
@prev="onPrevPage"
@next="onNextPage"
/>
</div>
</template>
</gl-card>
</template>
</settings-block>
</template>
Loading