From 1deb169a0bc6369b21af9729eae087eca2ffce83 Mon Sep 17 00:00:00 2001 From: Rahul Chanila <rchanila@gitlab.com> Date: Mon, 19 Feb 2024 01:42:53 +0000 Subject: [PATCH] Adds custom form for GAR integration settings Adds new component under ee/integrations/edit/sections Refactor the integration module to add sections EE: true --- .../javascripts/integrations/constants.js | 5 +++ .../edit/components/integration_form.vue | 6 ++- .../components/integration_forms/section.vue | 4 ++ .../javascripts/integrations/edit/index.js | 6 +++ .../google_cloud_artifact_registry.vue | 45 +++++++++++++++++++ ee/app/helpers/ee/integrations_helper.rb | 7 +++ .../artifact_registry.rb | 16 +++++++ .../_help.html.haml | 9 ++++ .../user_activates_artifact_registry_spec.rb | 4 ++ .../google_cloud_artifact_registry_spec.js | 36 +++++++++++++++ .../frontend/integrations/edit/mock_data.js | 9 ++++ .../helpers/ee/integrations_helper_spec.rb | 11 +++++ .../artifact_registry_spec.rb | 6 +++ locale/gitlab.pot | 12 +++++ .../edit/components/integration_form_spec.js | 37 +++++++++++++++ 15 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 ee/app/assets/javascripts/integrations/edit/components/sections/google_cloud_artifact_registry.vue create mode 100644 ee/app/views/shared/integrations/google_cloud_platform_artifact_registry/_help.html.haml create mode 100644 ee/spec/frontend/integrations/edit/components/sections/google_cloud_artifact_registry_spec.js create mode 100644 ee/spec/frontend/integrations/edit/mock_data.js diff --git a/app/assets/javascripts/integrations/constants.js b/app/assets/javascripts/integrations/constants.js index 12f5fa211375..01b72cbee3dc 100644 --- a/app/assets/javascripts/integrations/constants.js +++ b/app/assets/javascripts/integrations/constants.js @@ -34,6 +34,7 @@ export const integrationFormSections = { TRIGGER: 'trigger', APPLE_APP_STORE: 'apple_app_store', GOOGLE_PLAY: 'google_play', + GOOGLE_CLOUD_ARTIFACT_REGISTRY: 'google_cloud_artifact_registry', }; export const integrationFormSectionComponents = { @@ -44,6 +45,8 @@ export const integrationFormSectionComponents = { [integrationFormSections.TRIGGER]: 'IntegrationSectionTrigger', [integrationFormSections.APPLE_APP_STORE]: 'IntegrationSectionAppleAppStore', [integrationFormSections.GOOGLE_PLAY]: 'IntegrationSectionGooglePlay', + [integrationFormSections.GOOGLE_CLOUD_ARTIFACT_REGISTRY]: + 'IntegrationSectionGoogleCloudArtifactRegistry', }; export const integrationTriggerEvents = { @@ -118,6 +121,8 @@ export const placeholderForType = { [INTEGRATION_TYPE_MATTERMOST]: __('my-channel'), }; +export const INTEGRATION_FORM_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY = + 'google_cloud_platform_artifact_registry'; export const INTEGRATION_FORM_TYPE_JIRA = 'jira'; export const INTEGRATION_FORM_TYPE_SLACK = 'gitlab_slack_application'; diff --git a/app/assets/javascripts/integrations/edit/components/integration_form.vue b/app/assets/javascripts/integrations/edit/components/integration_form.vue index eb3a17da974e..d592f75ec5db 100644 --- a/app/assets/javascripts/integrations/edit/components/integration_form.vue +++ b/app/assets/javascripts/integrations/edit/components/integration_form.vue @@ -10,6 +10,7 @@ import { I18N_FETCH_TEST_SETTINGS_DEFAULT_ERROR_MESSAGE, I18N_DEFAULT_ERROR_MESSAGE, I18N_SUCCESSFUL_CONNECTION_MESSAGE, + INTEGRATION_FORM_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY, INTEGRATION_FORM_TYPE_SLACK, } from '~/integrations/constants'; import { refreshCurrentPage } from '~/lib/utils/url_utility'; @@ -73,8 +74,11 @@ export default { isSlackIntegration() { return this.propsSource.type === INTEGRATION_FORM_TYPE_SLACK; }, + isGoogleCloudArtifactRegistryIntegration() { + return this.propsSource.type === INTEGRATION_FORM_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY; + }, showHelpHtml() { - if (this.isSlackIntegration) { + if (this.isSlackIntegration || this.isGoogleCloudArtifactRegistryIntegration) { return this.helpHtml; } return !this.hasSections && this.helpHtml; diff --git a/app/assets/javascripts/integrations/edit/components/integration_forms/section.vue b/app/assets/javascripts/integrations/edit/components/integration_forms/section.vue index d322e3b4cd29..80454e49d08d 100644 --- a/app/assets/javascripts/integrations/edit/components/integration_forms/section.vue +++ b/app/assets/javascripts/integrations/edit/components/integration_forms/section.vue @@ -37,6 +37,10 @@ export default { import( /* webpackChunkName: 'IntegrationSectionGooglePlay' */ '~/integrations/edit/components/sections/google_play.vue' ), + IntegrationSectionGoogleCloudArtifactRegistry: () => + import( + /* webpackChunkName: 'IntegrationSectionGoogleCloudArtifactRegistry' */ 'ee_component/integrations/edit/components/sections/google_cloud_artifact_registry.vue' + ), }, directives: { SafeHtml, diff --git a/app/assets/javascripts/integrations/edit/index.js b/app/assets/javascripts/integrations/edit/index.js index b53bcd50f169..44d489ef5df2 100644 --- a/app/assets/javascripts/integrations/edit/index.js +++ b/app/assets/javascripts/integrations/edit/index.js @@ -35,6 +35,7 @@ function parseDatasetToProps(data) { vulnerabilitiesIssuetype, jiraIssueTransitionAutomatic, jiraIssueTransitionId, + artifactRegistryPath, redirectTo, upgradeSlackUrl, ...booleanAttributes @@ -42,6 +43,7 @@ function parseDatasetToProps(data) { const { showActive, activated, + operating, activateDisabled, editable, canTest, @@ -57,6 +59,7 @@ function parseDatasetToProps(data) { return { initialActivated: activated, + operating, showActive, activateDisabled, type, @@ -82,6 +85,9 @@ function parseDatasetToProps(data) { initialVulnerabilitiesIssuetype: vulnerabilitiesIssuetype, initialProjectKey: projectKey, }, + googleCloudArtifactRegistryProps: { + artifactRegistryPath, + }, learnMorePath, aboutPricingUrl, triggerEvents: JSON.parse(triggerEvents), diff --git a/ee/app/assets/javascripts/integrations/edit/components/sections/google_cloud_artifact_registry.vue b/ee/app/assets/javascripts/integrations/edit/components/sections/google_cloud_artifact_registry.vue new file mode 100644 index 000000000000..035ca1e6ab0b --- /dev/null +++ b/ee/app/assets/javascripts/integrations/edit/components/sections/google_cloud_artifact_registry.vue @@ -0,0 +1,45 @@ +<script> +// eslint-disable-next-line no-restricted-imports +import { mapGetters } from 'vuex'; +import { GlButton } from '@gitlab/ui'; +import Connection from '~/integrations/edit/components/sections/connection.vue'; + +export default { + name: 'IntegrationSectionGoogleCloudArtifactRegistry', + components: { + Connection, + GlButton, + }, + computed: { + ...mapGetters(['propsSource']), + dynamicFields() { + return this.propsSource.fields; + }, + artifactRegistryPath() { + return this.propsSource.googleCloudArtifactRegistryProps?.artifactRegistryPath; + }, + operating() { + return this.propsSource.operating; + }, + }, +}; +</script> + +<template> + <div> + <template v-if="operating"> + <div class="gl-display-flex gl-gap-3"> + <gl-button + :href="artifactRegistryPath" + icon="deployments" + category="primary" + variant="default" + > + {{ s__('GoogleArtifactRegistry|View artifacts') }} + </gl-button> + </div> + <hr /> + </template> + <connection :fields="dynamicFields" /> + </div> +</template> diff --git a/ee/app/helpers/ee/integrations_helper.rb b/ee/app/helpers/ee/integrations_helper.rb index 10e916d15295..c6c6fb44bb5c 100644 --- a/ee/app/helpers/ee/integrations_helper.rb +++ b/ee/app/helpers/ee/integrations_helper.rb @@ -24,6 +24,13 @@ def integration_form_data(integration, project: nil, group: nil) ) end + if integration.is_a?(::Integrations::GoogleCloudPlatform::ArtifactRegistry) + form_data.merge!( + artifact_registry_path: project_google_cloud_platform_artifact_registry_index_path(project), + operating: integration.operating?.to_s + ) + end + form_data end diff --git a/ee/app/models/integrations/google_cloud_platform/artifact_registry.rb b/ee/app/models/integrations/google_cloud_platform/artifact_registry.rb index 4570256c9709..8da8690bff3b 100644 --- a/ee/app/models/integrations/google_cloud_platform/artifact_registry.rb +++ b/ee/app/models/integrations/google_cloud_platform/artifact_registry.rb @@ -3,6 +3,8 @@ module Integrations module GoogleCloudPlatform class ArtifactRegistry < Integration + SECTION_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY = 'google_cloud_artifact_registry' + attribute :alert_events, default: false attribute :commit_events, default: false attribute :confidential_issues_events, default: false @@ -28,31 +30,37 @@ class ArtifactRegistry < Integration field :artifact_registry_project_id, required: true, + section: SECTION_TYPE_CONNECTION, title: -> { s_('GoogleCloudPlatformService|Google Cloud project ID') }, description: -> { s_('GoogleCloudPlatformService|ID of the Google Cloud project.') } field :workload_identity_pool_project_number, required: true, + section: SECTION_TYPE_CONNECTION, title: -> { s_('GoogleCloudPlatformService|Workload Identity Pool project number') }, description: -> { s_('GoogleCloudPlatformService|Project number of the Workload Identity Pool.') } field :workload_identity_pool_id, required: true, + section: SECTION_TYPE_CONNECTION, title: -> { s_('GoogleCloudPlatformService|Workload Identity Pool ID') }, description: -> { s_('GoogleCloudPlatformService|ID of the Workload Identity Pool.') } field :workload_identity_pool_provider_id, required: true, + section: SECTION_TYPE_CONNECTION, title: -> { s_('GoogleCloudPlatformService|Workload Identity Pool provider ID') }, description: -> { s_('GoogleCloudPlatformService|ID of the Workload Identity Pool provider.') } field :artifact_registry_location, required: true, + section: SECTION_TYPE_CONNECTION, title: -> { s_('GoogleCloudPlatformService|Location of Artifact Registry repository') }, description: -> { s_('GoogleCloudPlatformService|Location of Artifact Registry repository.') } field :artifact_registry_repositories, required: true, + section: SECTION_TYPE_CONNECTION, title: -> { s_('GoogleCloudPlatformService|Repository of Artifact Registry') }, help: -> { s_('GoogleCloudPlatformService|Repository of Artifact Registry.') }, description: -> { s_('GoogleCloudPlatformService|Repository of Artifact Registry.') } @@ -71,6 +79,14 @@ def self.to_param 'google_cloud_platform_artifact_registry' end + def sections + [ + { + type: SECTION_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY + } + ] + end + def self.supported_events [] end diff --git a/ee/app/views/shared/integrations/google_cloud_platform_artifact_registry/_help.html.haml b/ee/app/views/shared/integrations/google_cloud_platform_artifact_registry/_help.html.haml new file mode 100644 index 000000000000..47ae6c77d142 --- /dev/null +++ b/ee/app/views/shared/integrations/google_cloud_platform_artifact_registry/_help.html.haml @@ -0,0 +1,9 @@ +.info-well + .well-segment + %p + = s_("GoogleArtifactRegistry|Manage your artifacts in Google Artifact Registry:") + %ul + %li + = safe_format(s_("GoogleArtifactRegistry|View them in %{strong_start}Deploy > Google Artifact Registry%{strong_end}."), tag_pair(tag.strong, :strong_start, :strong_end)) + %li + = s_("GoogleArtifactRegistry|Push new ones from your CI/CD pipeline.") diff --git a/ee/spec/features/projects/integrations/google_cloud_platform/user_activates_artifact_registry_spec.rb b/ee/spec/features/projects/integrations/google_cloud_platform/user_activates_artifact_registry_spec.rb index 0865a76d4846..3d31f48157f7 100644 --- a/ee/spec/features/projects/integrations/google_cloud_platform/user_activates_artifact_registry_spec.rb +++ b/ee/spec/features/projects/integrations/google_cloud_platform/user_activates_artifact_registry_spec.rb @@ -14,6 +14,8 @@ it 'activates integration', :js do visit_project_integration('Google Cloud Artifact Registry') + expect(page).not_to have_link('View artifacts') + fill_in s_('GoogleCloudPlatformService|Workload Identity Pool project number'), with: integration.workload_identity_pool_project_number fill_in s_('GoogleCloudPlatformService|Workload Identity Pool ID'), @@ -30,5 +32,7 @@ click_save_integration expect(page).to have_content('Google Cloud Artifact Registry settings saved and active.') + + expect(page).to have_link('View artifacts') end end diff --git a/ee/spec/frontend/integrations/edit/components/sections/google_cloud_artifact_registry_spec.js b/ee/spec/frontend/integrations/edit/components/sections/google_cloud_artifact_registry_spec.js new file mode 100644 index 000000000000..5976146ae384 --- /dev/null +++ b/ee/spec/frontend/integrations/edit/components/sections/google_cloud_artifact_registry_spec.js @@ -0,0 +1,36 @@ +import { GlButton } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import IntegrationSectionGoogleCloudArtifactRegistry from 'ee_component/integrations/edit/components/sections/google_cloud_artifact_registry.vue'; +import { createStore } from '~/integrations/edit/store'; + +import { mockIntegrationProps } from '../../mock_data'; + +describe('IntegrationSectionGoogleCloudArtifactRegistry', () => { + let wrapper; + + const findViewArtifactsButton = () => wrapper.findComponent(GlButton); + + const createComponent = ({ operating = true } = {}) => { + const store = createStore({ + customState: { ...mockIntegrationProps, operating }, + }); + + wrapper = shallowMount(IntegrationSectionGoogleCloudArtifactRegistry, { + store, + }); + }; + + it('renders a button to view artifacts', () => { + createComponent(); + + expect(findViewArtifactsButton().text()).toBe('View artifacts'); + expect(findViewArtifactsButton().props('icon')).toBe('deployments'); + expect(findViewArtifactsButton().attributes('href')).toBe('/path/to/artifact/registry'); + }); + + it('hides button to view artifacts when `operating=false`', () => { + createComponent({ operating: false }); + + expect(findViewArtifactsButton().exists()).toBe(false); + }); +}); diff --git a/ee/spec/frontend/integrations/edit/mock_data.js b/ee/spec/frontend/integrations/edit/mock_data.js new file mode 100644 index 000000000000..1bce58d839ab --- /dev/null +++ b/ee/spec/frontend/integrations/edit/mock_data.js @@ -0,0 +1,9 @@ +export const mockIntegrationProps = { + id: 25, + operating: true, + googleCloudArtifactRegistryProps: { + artifactRegistryPath: '/path/to/artifact/registry', + }, + sections: [], + fields: [], +}; diff --git a/ee/spec/helpers/ee/integrations_helper_spec.rb b/ee/spec/helpers/ee/integrations_helper_spec.rb index 8ebda34d6454..3a251d600f1d 100644 --- a/ee/spec/helpers/ee/integrations_helper_spec.rb +++ b/ee/spec/helpers/ee/integrations_helper_spec.rb @@ -54,6 +54,17 @@ end end end + + context 'with Google Cloud Artifact Registry integration' do + let_it_be_with_refind(:integration) { create(:google_cloud_platform_artifact_registry_integration, project: project) } + + it 'includes Google Cloud Artifact Registry fields' do + is_expected.to include( + artifact_registry_path: project_google_cloud_platform_artifact_registry_index_path(project), + operating: 'true' + ) + end + end end describe '#jira_issues_show_data' do diff --git a/ee/spec/models/integrations/google_cloud_platform/artifact_registry_spec.rb b/ee/spec/models/integrations/google_cloud_platform/artifact_registry_spec.rb index 50131786a6c5..3872425c1674 100644 --- a/ee/spec/models/integrations/google_cloud_platform/artifact_registry_spec.rb +++ b/ee/spec/models/integrations/google_cloud_platform/artifact_registry_spec.rb @@ -123,4 +123,10 @@ end end end + + describe '#sections' do + subject { integration.sections } + + it { is_expected.to eq([{ type: 'google_cloud_artifact_registry' }]) } + end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a658c22500e5..f6c1ee79b9b9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -23279,6 +23279,9 @@ msgstr "" msgid "GoogleArtifactRegistry|Location" msgstr "" +msgid "GoogleArtifactRegistry|Manage your artifacts in Google Artifact Registry:" +msgstr "" + msgid "GoogleArtifactRegistry|Media type" msgstr "" @@ -23294,6 +23297,9 @@ msgstr "" msgid "GoogleArtifactRegistry|Project ID" msgstr "" +msgid "GoogleArtifactRegistry|Push new ones from your CI/CD pipeline." +msgstr "" + msgid "GoogleArtifactRegistry|Repository" msgstr "" @@ -23306,6 +23312,12 @@ msgstr "" msgid "GoogleArtifactRegistry|Updated" msgstr "" +msgid "GoogleArtifactRegistry|View artifacts" +msgstr "" + +msgid "GoogleArtifactRegistry|View them in %{strong_start}Deploy > Google Artifact Registry%{strong_end}." +msgstr "" + msgid "GoogleArtifactRegistry|Virtual size" msgstr "" diff --git a/spec/frontend/integrations/edit/components/integration_form_spec.js b/spec/frontend/integrations/edit/components/integration_form_spec.js index cef8fb0b7201..f85449e6bc8f 100644 --- a/spec/frontend/integrations/edit/components/integration_form_spec.js +++ b/spec/frontend/integrations/edit/components/integration_form_spec.js @@ -18,6 +18,7 @@ import { I18N_SUCCESSFUL_CONNECTION_MESSAGE, I18N_DEFAULT_ERROR_MESSAGE, INTEGRATION_FORM_TYPE_SLACK, + INTEGRATION_FORM_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY, } from '~/integrations/constants'; import { createStore } from '~/integrations/edit/store'; import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status'; @@ -564,4 +565,40 @@ describe('IntegrationForm', () => { ); }); }); + + describe('Google Cloud Artifact Registry integration', () => { + describe('Help and sections rendering', () => { + const dummyHelp = 'Foo Help'; + + it.each` + integration | helpHtml | sections | shouldShowSections | shouldShowHelp + ${INTEGRATION_FORM_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY} | ${''} | ${[]} | ${false} | ${false} + ${INTEGRATION_FORM_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY} | ${dummyHelp} | ${[]} | ${false} | ${true} + ${INTEGRATION_FORM_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false} + ${INTEGRATION_FORM_TYPE_GOOGLE_CLOUD_ARTIFACT_REGISTRY} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${true} + ${'foo'} | ${''} | ${[]} | ${false} | ${false} + ${'foo'} | ${dummyHelp} | ${[]} | ${false} | ${true} + ${'foo'} | ${undefined} | ${[mockSectionConnection]} | ${true} | ${false} + ${'foo'} | ${dummyHelp} | ${[mockSectionConnection]} | ${true} | ${false} + `( + '$sections sections, and "$helpHtml" helpHtml for "$integration" integration', + ({ integration, helpHtml, sections, shouldShowSections, shouldShowHelp }) => { + createComponent({ + provide: { + helpHtml, + }, + customStateProps: { + sections, + type: integration, + }, + }); + expect(findAllSections().length > 0).toEqual(shouldShowSections); + expect(findHelpHtml().exists()).toBe(shouldShowHelp); + if (shouldShowHelp) { + expect(findHelpHtml().html()).toContain(helpHtml); + } + }, + ); + }); + }); }); -- GitLab