diff --git a/app/assets/javascripts/token_access/components/autopopulate_allowlist_modal.vue b/app/assets/javascripts/token_access/components/autopopulate_allowlist_modal.vue
index 3b6df8c2e5e4ab102a29ef1142da6b4954045dbb..87077cbf949ce1775490c726f7147bb9fe47690a 100644
--- a/app/assets/javascripts/token_access/components/autopopulate_allowlist_modal.vue
+++ b/app/assets/javascripts/token_access/components/autopopulate_allowlist_modal.vue
@@ -2,7 +2,6 @@
import { GlAlert, GlLink, GlModal, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { __, s__, sprintf } from '~/locale';
-import autopopulateAllowlistMutation from '../graphql/mutations/autopopulate_allowlist.mutation.graphql';
export default {
name: 'AutopopulateAllowlistModal',
@@ -33,12 +32,6 @@ export default {
},
},
apollo: {},
- data() {
- return {
- errorMessage: false,
- isAutopopulating: false,
- };
- },
computed: {
authLogExceedsLimitMessage() {
return sprintf(
@@ -56,14 +49,12 @@ export default {
text: __('Add entries'),
attributes: {
variant: 'confirm',
- loading: this.isAutopopulating,
},
},
actionSecondary: {
text: __('Cancel'),
attributes: {
variant: 'default',
- disabled: this.isAutopopulating,
},
},
};
@@ -77,48 +68,15 @@ export default {
},
},
methods: {
- async autopopulateAllowlist() {
- this.isAutopopulating = true;
- this.errorMessage = null;
-
- try {
- const {
- data: {
- ciJobTokenScopeAutopopulateAllowlist: { errors },
- },
- } = await this.$apollo.mutate({
- mutation: autopopulateAllowlistMutation,
- variables: {
- projectPath: this.fullPath,
- },
- });
-
- if (errors.length) {
- throw new Error(errors[0]);
- }
-
- this.$emit('refetch-allowlist');
- this.hideModal();
- this.$toast.show(
- s__('CICD|Authentication log entries were successfully added to the allowlist.'),
- );
- } catch (error) {
- this.errorMessage =
- error?.message ||
- s__(
- 'CICD|An error occurred while adding the authentication log entries. Please try again.',
- );
- } finally {
- this.isAutopopulating = false;
- }
+ autopopulateAllowlist() {
+ this.$emit('autopopulate-allowlist');
},
hideModal() {
- this.errorMessage = null;
this.$emit('hide');
},
},
compactionAlgorithmHelpPage: helpPagePath('ci/jobs/ci_job_token', {
- anchor: 'auto-populate-a-projects-allowlist',
+ anchor: 'allowlist-compaction',
}),
};
@@ -135,9 +93,6 @@ export default {
@canceled="hideModal"
@hidden="hideModal"
>
-
- {{ errorMessage }}
-
{{ authLogExceedsLimitMessage }}
@@ -163,13 +118,16 @@ export default {
{{ projectName }}
+
+ {{ content }}
+
@@ -182,7 +140,7 @@ export default {
{{
s__(
- 'CICD|The process to add entries could take a moment to complete with large logs or allowlists.',
+ 'CICD|The process might take a moment to complete for large authentication logs or allowlists.',
)
}}
diff --git a/app/assets/javascripts/token_access/components/inbound_token_access.vue b/app/assets/javascripts/token_access/components/inbound_token_access.vue
index 3f81fbc7a7bfb1dabcdbe2509281c8f24ce8c094..dbc9d0be3ced029aed28c45c08f9caae5fa71b69 100644
--- a/app/assets/javascripts/token_access/components/inbound_token_access.vue
+++ b/app/assets/javascripts/token_access/components/inbound_token_access.vue
@@ -23,6 +23,7 @@ import inboundRemoveGroupCIJobTokenScopeMutation from '../graphql/mutations/inbo
import inboundUpdateCIJobTokenScopeMutation from '../graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql';
import inboundGetCIJobTokenScopeQuery from '../graphql/queries/inbound_get_ci_job_token_scope.query.graphql';
import inboundGetGroupsAndProjectsWithCIJobTokenScopeQuery from '../graphql/queries/inbound_get_groups_and_projects_with_ci_job_token_scope.query.graphql';
+import autopopulateAllowlistMutation from '../graphql/mutations/autopopulate_allowlist.mutation.graphql';
import getCiJobTokenScopeAllowlistQuery from '../graphql/queries/get_ci_job_token_scope_allowlist.query.graphql';
import getAuthLogCountQuery from '../graphql/queries/get_auth_log_count.query.graphql';
import removeAutopopulatedEntriesMutation from '../graphql/mutations/remove_autopopulated_entries.mutation.graphql';
@@ -71,16 +72,6 @@ export default {
text: s__('CICD|Only this project and any groups and projects in the allowlist'),
},
],
- crudFormActions: [
- {
- text: __('Group or project'),
- value: JOB_TOKEN_FORM_ADD_GROUP_OR_PROJECT,
- },
- {
- text: __('All projects in authentication log'),
- value: JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG,
- },
- ],
components: {
AutopopulateAllowlistModal,
GlAlert,
@@ -206,6 +197,23 @@ export default {
anchor: 'control-job-token-access-to-your-project',
});
},
+ crudFormActions() {
+ const actions = [
+ {
+ text: __('Group or project'),
+ value: JOB_TOKEN_FORM_ADD_GROUP_OR_PROJECT,
+ },
+ ];
+
+ if (this.authLogCount > 0) {
+ actions.push({
+ text: __('All projects in authentication log'),
+ value: JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG,
+ });
+ }
+
+ return actions;
+ },
allowlist() {
const { groups, projects } = this.groupsAndProjectsWithAccess;
return [...groups, ...projects];
@@ -224,6 +232,9 @@ export default {
},
];
},
+ hasAutoPopulatedEntries() {
+ return this.allowlist.filter((entry) => entry.autopopulated).length > 0;
+ },
groupCount() {
return this.groupsAndProjectsWithAccess.groups.length;
},
@@ -317,6 +328,43 @@ export default {
this.refetchGroupsAndProjects();
return Promise.resolve();
},
+ async autopopulateAllowlist() {
+ this.hideSelectedAction();
+ this.autopopulationErrorMessage = null;
+ this.allowlistLoadingMessage = s__(
+ 'CICD|Auto-populating allowlist entries. Please wait while the action completes.',
+ );
+
+ try {
+ const {
+ data: {
+ ciJobTokenScopeAutopopulateAllowlist: { errors },
+ },
+ } = await this.$apollo.mutate({
+ mutation: autopopulateAllowlistMutation,
+ variables: {
+ projectPath: this.fullPath,
+ },
+ });
+
+ if (errors.length) {
+ this.autopopulationErrorMessage = errors[0].message;
+ return;
+ }
+
+ this.$apollo.queries.inboundJobTokenScopeEnabled.refetch();
+ this.refetchAllowlist();
+ this.$toast.show(
+ s__('CICD|Authentication log entries were successfully added to the allowlist.'),
+ );
+ } catch {
+ this.autopopulationErrorMessage = s__(
+ 'CICD|An error occurred while adding the authentication log entries. Please try again.',
+ );
+ } finally {
+ this.allowlistLoadingMessage = '';
+ }
+ },
async removeAutopopulatedEntries() {
this.hideSelectedAction();
this.autopopulationErrorMessage = null;
@@ -388,7 +436,7 @@ export default {
:project-name="projectName"
:show-modal="showAutopopulateModal"
@hide="hideSelectedAction"
- @refetch-allowlist="refetchAllowlist"
+ @autopopulate-allowlist="autopopulateAllowlist"
/>
}}
+
+- Introduced in [GitLab 17.10](https://gitlab.com/gitlab-org/gitlab/-/issues/498125). [Deployed behind the `:authentication_logs_migration_for_allowlist` feature flag](../../user/feature_flags.md), disabled by default.
+
+{{< /history >}}
+
To auto-populate the allowlist through the UI:
1. On the left sidebar, select **Search or go** to and find your project.
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 8df79e3e57e799a118927ebcdecb09271c3a735a..fd9ca4e65b9829d09f1214d61fc7644100bea5f0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -11251,6 +11251,9 @@ msgstr ""
msgid "CICD|Auto DevOps"
msgstr ""
+msgid "CICD|Auto-populating allowlist entries. Please wait while the action completes."
+msgstr ""
+
msgid "CICD|Automatic deployment to staging, manual deployment to production"
msgstr ""
@@ -11371,7 +11374,7 @@ msgstr ""
msgid "CICD|The allowlist can contain a maximum of %{projectAllowlistLimit} groups and projects."
msgstr ""
-msgid "CICD|The process to add entries could take a moment to complete with large logs or allowlists."
+msgid "CICD|The process might take a moment to complete for large authentication logs or allowlists."
msgstr ""
msgid "CICD|There are several CI/CD limits in place."
@@ -11398,7 +11401,7 @@ msgstr ""
msgid "CICD|When enabled, all projects must use their allowlist to control CI/CD job token access between projects. The option to allow access from all groups and projects is hidden. %{link_start}Learn More.%{link_end}"
msgstr ""
-msgid "CICD|You're about to add all entries from the authentication log to the allowlist for %{projectName}. Duplicate entries will be ignored."
+msgid "CICD|You're about to add all entries from the authentication log to the allowlist for %{projectName}. This will also update the Job Token setting to %{codeStart}This project and any groups and projects in the allowlist%{codeEnd}, if not already set. Duplicate entries will be ignored."
msgstr ""
msgid "CICD|group enabled"
diff --git a/spec/frontend/token_access/autopopulate_allowlist_modal_spec.js b/spec/frontend/token_access/autopopulate_allowlist_modal_spec.js
index e0eac4f7236fd983b5553332b1d132a8b44a28a4..6e460c96ccbb0d6b4e592e1483d60c0aa0c5f826 100644
--- a/spec/frontend/token_access/autopopulate_allowlist_modal_spec.js
+++ b/spec/frontend/token_access/autopopulate_allowlist_modal_spec.js
@@ -1,44 +1,22 @@
import { GlAlert, GlLink, GlModal, GlSprintf } from '@gitlab/ui';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import { createMockDirective } from 'helpers/vue_mock_directive';
-import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import AutopopulateAllowlistMutation from '~/token_access/graphql/mutations/autopopulate_allowlist.mutation.graphql';
import AutopopulateAllowlistModal from '~/token_access/components/autopopulate_allowlist_modal.vue';
-import { mockAutopopulateAllowlistResponse, mockAutopopulateAllowlistError } from './mock_data';
const projectName = 'My project';
const fullPath = 'root/my-repo';
-Vue.use(VueApollo);
-const mockToastShow = jest.fn();
-
describe('AutopopulateAllowlistModal component', () => {
let wrapper;
- let mockApollo;
- let mockAutopopulateMutation;
const findAlert = () => wrapper.findComponent(GlAlert);
const findModal = () => wrapper.findComponent(GlModal);
const findLink = () => wrapper.findComponent(GlLink);
const createComponent = ({ props } = {}) => {
- const handlers = [[AutopopulateAllowlistMutation, mockAutopopulateMutation]];
- mockApollo = createMockApollo(handlers);
-
wrapper = shallowMountExtended(AutopopulateAllowlistModal, {
- apolloProvider: mockApollo,
provide: {
fullPath,
},
- mocks: {
- $toast: { show: mockToastShow },
- },
- directives: {
- GlTooltip: createMockDirective('gl-tooltip'),
- },
propsData: {
authLogExceedsLimit: false,
projectAllowlistLimit: 4,
@@ -52,10 +30,6 @@ describe('AutopopulateAllowlistModal component', () => {
});
};
- beforeEach(() => {
- mockAutopopulateMutation = jest.fn();
- });
-
describe('template', () => {
beforeEach(() => {
createComponent();
@@ -90,7 +64,7 @@ describe('AutopopulateAllowlistModal component', () => {
it('renders help link', () => {
expect(findLink().text()).toBe('What is the compaction algorithm?');
expect(findLink().attributes('href')).toBe(
- '/help/ci/jobs/ci_job_token#auto-populate-a-projects-allowlist',
+ '/help/ci/jobs/ci_job_token#allowlist-compaction',
);
});
});
@@ -112,66 +86,15 @@ describe('AutopopulateAllowlistModal component', () => {
);
});
- describe('when mutation is running', () => {
- beforeEach(() => {
- mockAutopopulateMutation.mockResolvedValue(mockAutopopulateAllowlistResponse);
+ describe('when clicking on the primary button', () => {
+ it('emits the remove-entries event', () => {
createComponent();
- });
- it('shows loading state for confirm button and disables cancel button', async () => {
- expect(findModal().props('actionPrimary').attributes).toMatchObject({ loading: false });
- expect(findModal().props('actionSecondary').attributes).toMatchObject({ disabled: false });
+ expect(wrapper.emitted('autopopulate-allowlist')).toBeUndefined();
findModal().vm.$emit('primary', { preventDefault: jest.fn() });
- await nextTick();
-
- expect(findModal().props('actionPrimary').attributes).toMatchObject({ loading: true });
- expect(findModal().props('actionSecondary').attributes).toMatchObject({ disabled: true });
- });
- });
-
- describe('when mutation is successful', () => {
- beforeEach(async () => {
- mockAutopopulateMutation.mockResolvedValue(mockAutopopulateAllowlistResponse);
-
- createComponent();
- findModal().vm.$emit('primary', { preventDefault: jest.fn() });
- await waitForPromises();
- });
-
- it('calls the mutation', () => {
- expect(mockAutopopulateMutation).toHaveBeenCalledWith({ projectPath: fullPath });
- });
-
- it('shows toast message', () => {
- expect(mockToastShow).toHaveBeenCalledWith(
- 'Authentication log entries were successfully added to the allowlist.',
- );
- });
-
- it('emits events for refetching data and hiding modal', () => {
- expect(wrapper.emitted('refetch-allowlist')).toHaveLength(1);
- expect(wrapper.emitted('hide')).toHaveLength(1);
- });
- });
-
- describe('when mutation fails', () => {
- beforeEach(async () => {
- createComponent();
- findModal().vm.$emit('primary', { preventDefault: jest.fn() });
- await waitForPromises();
-
- mockAutopopulateMutation.mockResolvedValue(mockAutopopulateAllowlistError);
- });
-
- it('renders alert', () => {
- expect(findAlert().exists()).toBe(true);
- });
- it('does not render toast message or emit events', () => {
- expect(mockToastShow).not.toHaveBeenCalledWith();
- expect(wrapper.emitted('refetch-allowlist')).toBeUndefined();
- expect(wrapper.emitted('hide')).toBeUndefined();
+ expect(wrapper.emitted('autopopulate-allowlist')).toHaveLength(1);
});
});
});
diff --git a/spec/frontend/token_access/inbound_token_access_spec.js b/spec/frontend/token_access/inbound_token_access_spec.js
index b3c9aabcc8c052d1da23c18b7df5d45cc3ea9483..bf9207b3611c2e6f6f7b70d59e3681e4c00ed7cc 100644
--- a/spec/frontend/token_access/inbound_token_access_spec.js
+++ b/spec/frontend/token_access/inbound_token_access_spec.js
@@ -18,6 +18,7 @@ import {
import AutopopulateAllowlistModal from '~/token_access/components/autopopulate_allowlist_modal.vue';
import NamespaceForm from '~/token_access/components/namespace_form.vue';
import RemoveAutopopulatedEntriesModal from '~/token_access/components/remove_autopopulated_entries_modal.vue';
+import autopopulateAllowlistMutation from '~/token_access/graphql/mutations/autopopulate_allowlist.mutation.graphql';
import inboundRemoveGroupCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_remove_group_ci_job_token_scope.mutation.graphql';
import inboundRemoveProjectCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_remove_project_ci_job_token_scope.mutation.graphql';
import inboundUpdateCIJobTokenScopeMutation from '~/token_access/graphql/mutations/inbound_update_ci_job_token_scope.mutation.graphql';
@@ -38,6 +39,7 @@ import {
inboundRemoveNamespaceSuccess,
inboundUpdateScopeSuccessResponse,
mockAuthLogsCountResponse,
+ mockAutopopulateAllowlistResponse,
mockRemoveAutopopulatedEntriesResponse,
} from './mock_data';
@@ -53,6 +55,13 @@ describe('TokenAccess component', () => {
let wrapper;
const authLogCountResponseHandler = jest.fn().mockResolvedValue(mockAuthLogsCountResponse(4));
+ const authLogZeroCountResponseHandler = jest.fn().mockResolvedValue(mockAuthLogsCountResponse(0));
+ const autopopulateAllowlistResponseHandler = jest
+ .fn()
+ .mockResolvedValue(mockAutopopulateAllowlistResponse());
+ const autopopulateAllowlistResponseErrorHandler = jest
+ .fn()
+ .mockResolvedValue(mockAutopopulateAllowlistResponse({ errorMessage: message }));
const inboundJobTokenScopeEnabledResponseHandler = jest
.fn()
.mockResolvedValue(inboundJobTokenScopeEnabledResponse);
@@ -61,7 +70,10 @@ describe('TokenAccess component', () => {
.mockResolvedValue(inboundJobTokenScopeDisabledResponse);
const inboundGroupsAndProjectsWithScopeResponseHandler = jest
.fn()
- .mockResolvedValue(inboundGroupsAndProjectsWithScopeResponse);
+ .mockResolvedValue(inboundGroupsAndProjectsWithScopeResponse(true));
+ const inboundGroupsAndProjectsWithoutAutopopulatedEntriesResponseHandler = jest
+ .fn()
+ .mockResolvedValue(inboundGroupsAndProjectsWithScopeResponse(false));
const inboundRemoveGroupSuccessHandler = jest
.fn()
.mockResolvedValue(inboundRemoveNamespaceSuccess);
@@ -474,7 +486,9 @@ describe('TokenAccess component', () => {
inboundGetGroupsAndProjectsWithCIJobTokenScopeQuery,
inboundGroupsAndProjectsWithScopeResponseHandler,
],
+ [autopopulateAllowlistMutation, autopopulateAllowlistResponseHandler],
[removeAutopopulatedEntriesMutation, removeAutopopulatedEntriesMutationHandler],
+ [getAuthLogCountQuery, authLogCountResponseHandler],
],
{
authenticationLogsMigrationForAllowlist: true,
@@ -515,15 +529,112 @@ describe('TokenAccess component', () => {
expect(findFormSelector().props('selected')).toBe(null);
});
- it('refetches allowlist when autopopulate mutation is successful', async () => {
+ it('shows loading state while autopopulating entries', async () => {
+ expect(findCountLoadingIcon().exists()).toBe(false);
+ expect(findTokenAccessTable().props('loading')).toBe(false);
+
+ findFormSelector().vm.$emit('select', JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG);
+ findAutopopulateAllowlistModal().vm.$emit('autopopulate-allowlist');
+
+ await nextTick();
+
+ expect(findCountLoadingIcon().exists()).toBe(true);
+ expect(findTokenAccessTable().props('loading')).toBe(true);
+ expect(findTokenAccessTable().props('loadingMessage')).toBe(
+ 'Auto-populating allowlist entries. Please wait while the action completes.',
+ );
+ });
+
+ it('resets loading state after autopopulating entries', async () => {
+ findFormSelector().vm.$emit('select', JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG);
+ findAutopopulateAllowlistModal().vm.$emit('autopopulate-allowlist');
+
+ await nextTick();
+
+ expect(findTokenAccessTable().props('loadingMessage')).toBe(
+ 'Auto-populating allowlist entries. Please wait while the action completes.',
+ );
+
+ await waitForPromises();
+
+ expect(findCountLoadingIcon().exists()).toBe(false);
+ expect(findTokenAccessTable().props('loading')).toBe(false);
+ expect(findTokenAccessTable().props('loadingMessage')).toBe('');
+ });
+
+ it('calls the autopopulate allowlist mutation and refetches allowlist and job token setting', async () => {
+ expect(autopopulateAllowlistResponseHandler).toHaveBeenCalledTimes(0);
expect(inboundGroupsAndProjectsWithScopeResponseHandler).toHaveBeenCalledTimes(1);
+ expect(inboundJobTokenScopeEnabledResponseHandler).toHaveBeenCalledTimes(1);
findFormSelector().vm.$emit('select', JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG);
- findAutopopulateAllowlistModal().vm.$emit('refetch-allowlist');
+ findAutopopulateAllowlistModal().vm.$emit('autopopulate-allowlist');
+ await waitForPromises();
await nextTick();
+ expect(autopopulateAllowlistResponseHandler).toHaveBeenCalledTimes(1);
expect(inboundGroupsAndProjectsWithScopeResponseHandler).toHaveBeenCalledTimes(2);
- expect(findFormSelector().props('selected')).toBe(null);
+ expect(inboundJobTokenScopeEnabledResponseHandler).toHaveBeenCalledTimes(2);
+ });
+
+ it('shows error alert when mutation returns an error', async () => {
+ createComponent(
+ [
+ [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler],
+ [
+ inboundGetGroupsAndProjectsWithCIJobTokenScopeQuery,
+ inboundGroupsAndProjectsWithScopeResponseHandler,
+ ],
+ [autopopulateAllowlistMutation, autopopulateAllowlistResponseErrorHandler],
+ [getAuthLogCountQuery, authLogCountResponseHandler],
+ ],
+ {
+ authenticationLogsMigrationForAllowlist: true,
+ stubs: { CrudComponent, GlDisclosureDropdown, GlDisclosureDropdownItem },
+ },
+ );
+
+ await waitForPromises();
+
+ expect(findAutopopulationAlert().exists()).toBe(false);
+
+ findFormSelector().vm.$emit('select', JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG);
+ findAutopopulateAllowlistModal().vm.$emit('autopopulate-allowlist');
+ await waitForPromises();
+ await nextTick();
+
+ expect(findAutopopulationAlert().text()).toBe('An error occurred');
+ });
+
+ it('shows error alert when mutation fails', async () => {
+ createComponent(
+ [
+ [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler],
+ [
+ inboundGetGroupsAndProjectsWithCIJobTokenScopeQuery,
+ inboundGroupsAndProjectsWithScopeResponseHandler,
+ ],
+ [autopopulateAllowlistMutation, failureHandler],
+ [getAuthLogCountQuery, authLogCountResponseHandler],
+ ],
+ {
+ authenticationLogsMigrationForAllowlist: true,
+ stubs: { CrudComponent, GlDisclosureDropdown, GlDisclosureDropdownItem },
+ },
+ );
+
+ await waitForPromises();
+
+ expect(findAutopopulationAlert().exists()).toBe(false);
+
+ findFormSelector().vm.$emit('select', JOB_TOKEN_FORM_AUTOPOPULATE_AUTH_LOG);
+ findAutopopulateAllowlistModal().vm.$emit('autopopulate-allowlist');
+ await waitForPromises();
+ await nextTick();
+
+ expect(findAutopopulationAlert().text()).toBe(
+ 'An error occurred while adding the authentication log entries. Please try again.',
+ );
});
});
@@ -561,6 +672,21 @@ describe('TokenAccess component', () => {
);
});
+ it('resets loading state after removing autopopulated entries', async () => {
+ triggerRemoveEntries();
+ await nextTick();
+
+ expect(findTokenAccessTable().props('loadingMessage')).toBe(
+ 'Removing auto-added allowlist entries. Please wait while the action completes.',
+ );
+
+ await waitForPromises();
+
+ expect(findCountLoadingIcon().exists()).toBe(false);
+ expect(findTokenAccessTable().props('loading')).toBe(false);
+ expect(findTokenAccessTable().props('loadingMessage')).toBe('');
+ });
+
it('calls the remove autopopulated entries mutation and refetches allowlist', async () => {
expect(removeAutopopulatedEntriesMutationHandler).toHaveBeenCalledTimes(0);
expect(inboundGroupsAndProjectsWithScopeResponseHandler).toHaveBeenCalledTimes(1);
@@ -592,6 +718,7 @@ describe('TokenAccess component', () => {
inboundGroupsAndProjectsWithScopeResponseHandler,
],
[removeAutopopulatedEntriesMutation, removeAutopopulatedEntriesMutationErrorHandler],
+ [getAuthLogCountQuery, authLogCountResponseHandler],
],
{
authenticationLogsMigrationForAllowlist: true,
@@ -599,6 +726,8 @@ describe('TokenAccess component', () => {
},
);
+ await waitForPromises();
+
expect(findAutopopulationAlert().exists()).toBe(false);
triggerRemoveEntries();
@@ -617,6 +746,7 @@ describe('TokenAccess component', () => {
inboundGroupsAndProjectsWithScopeResponseHandler,
],
[removeAutopopulatedEntriesMutation, failureHandler],
+ [getAuthLogCountQuery, authLogCountResponseHandler],
],
{
authenticationLogsMigrationForAllowlist: true,
@@ -624,6 +754,8 @@ describe('TokenAccess component', () => {
},
);
+ await waitForPromises();
+
expect(findAutopopulationAlert().exists()).toBe(false);
triggerRemoveEntries();
@@ -652,6 +784,39 @@ describe('TokenAccess component', () => {
expect(findRemoveAutopopulatedEntriesModal().props('showModal')).toBe(true);
});
});
+
+ describe('allowlist actions', () => {
+ beforeEach(async () => {
+ await createComponent(
+ [
+ [inboundGetCIJobTokenScopeQuery, inboundJobTokenScopeEnabledResponseHandler],
+ [
+ inboundGetGroupsAndProjectsWithCIJobTokenScopeQuery,
+ inboundGroupsAndProjectsWithoutAutopopulatedEntriesResponseHandler,
+ ],
+ [getAuthLogCountQuery, authLogZeroCountResponseHandler],
+ ],
+ {
+ authenticationLogsMigrationForAllowlist: true,
+ stubs: { CrudComponent, GlDisclosureDropdown, GlDisclosureDropdownItem },
+ },
+ );
+ await nextTick();
+ });
+
+ it('hides add auth log entries option if auth log count is zero', () => {
+ expect(findFormSelector().props('items')).toMatchObject([
+ {
+ text: 'Group or project',
+ value: 'JOB_TOKEN_FORM_ADD_GROUP_OR_PROJECT',
+ },
+ ]);
+ });
+
+ it('hides remove auth log entries option if there are no autopopulated entries', () => {
+ expect(findAllowlistOptions().exists()).toBe(false);
+ });
+ });
});
describe.each`
diff --git a/spec/frontend/token_access/mock_data.js b/spec/frontend/token_access/mock_data.js
index b7fd38afb6a7c5be8ea823618d8d7e21e2dfcb85..d1e107b2265ff3ba88181d193a6c6dc57c652e3e 100644
--- a/spec/frontend/token_access/mock_data.js
+++ b/spec/frontend/token_access/mock_data.js
@@ -164,7 +164,7 @@ export const inboundJobTokenScopeDisabledResponse = {
},
};
-export const inboundGroupsAndProjectsWithScopeResponse = {
+export const inboundGroupsAndProjectsWithScopeResponse = (hasAutopopulatedEntries = true) => ({
data: {
project: {
__typename: 'Project',
@@ -197,12 +197,14 @@ export const inboundGroupsAndProjectsWithScopeResponse = {
},
],
},
- groupAllowlistAutopopulatedIds: ['gid://gitlab/Group/45'],
- inboundAllowlistAutopopulatedIds: ['gid://gitlab/Project/23'],
+ groupAllowlistAutopopulatedIds: hasAutopopulatedEntries ? ['gid://gitlab/Group/45'] : [],
+ inboundAllowlistAutopopulatedIds: hasAutopopulatedEntries
+ ? ['gid://gitlab/Project/23']
+ : [],
},
},
},
-};
+});
export const getSaveNamespaceHandler = (error) =>
jest.fn().mockResolvedValue({
@@ -301,28 +303,15 @@ export const mockAuthLogsResponse = (hasNextPage = false) => ({
},
});
-export const mockAutopopulateAllowlistResponse = {
+export const mockAutopopulateAllowlistResponse = ({ errorMessage } = {}) => ({
data: {
ciJobTokenScopeAutopopulateAllowlist: {
status: 'complete',
- errors: [],
- __typename: 'CiJobTokenScopeAutopopulateAllowlistPayload',
- },
- },
-};
-
-export const mockAutopopulateAllowlistError = {
- data: {
- ciJobTokenScopeAutopopulateAllowlist: {
- errors: [
- {
- message: 'An error occurred',
- },
- ],
+ errors: errorMessage ? [{ message: errorMessage }] : [],
__typename: 'CiJobTokenScopeAutopopulateAllowlistPayload',
},
},
-};
+});
export const mockRemoveAutopopulatedEntriesResponse = ({ errorMessage } = {}) => ({
data: {