diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-05 10:20:03 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-07-05 10:20:03 +0000 |
commit | d2612b42b9da6638d70b9d7144f6d427070d042d (patch) | |
tree | ed7de87d4b112cae8a45ba186d717ca9768c7d4e /app | |
parent | d80373b353005e70f44eca8a3bc4a4c5cfbf0e9e (diff) | |
download | gitlab-ce-d2612b42b9da6638d70b9d7144f6d427070d042d.tar.gz |
Add latest changes from gitlab-org/gitlab@15-1-stable-ee
Diffstat (limited to 'app')
8 files changed, 306 insertions, 275 deletions
diff --git a/app/assets/javascripts/clusters/agents/components/create_token_button.vue b/app/assets/javascripts/clusters/agents/components/create_token_button.vue index 74155d7819a..67a178b5f98 100644 --- a/app/assets/javascripts/clusters/agents/components/create_token_button.vue +++ b/app/assets/javascripts/clusters/agents/components/create_token_button.vue @@ -1,154 +1,23 @@ <script> -import { - GlButton, - GlModalDirective, - GlTooltip, - GlModal, - GlFormGroup, - GlFormInput, - GlFormTextarea, - GlAlert, -} from '@gitlab/ui'; -import { s__, __ } from '~/locale'; -import Tracking from '~/tracking'; -import AgentToken from '~/clusters_list/components/agent_token.vue'; -import { - CREATE_TOKEN_MODAL, - EVENT_LABEL_MODAL, - EVENT_ACTIONS_OPEN, - EVENT_ACTIONS_CLICK, - TOKEN_NAME_LIMIT, - TOKEN_STATUS_ACTIVE, -} from '../constants'; -import createNewAgentToken from '../graphql/mutations/create_new_agent_token.mutation.graphql'; -import getClusterAgentQuery from '../graphql/queries/get_cluster_agent.query.graphql'; -import { addAgentTokenToStore } from '../graphql/cache_update'; - -const trackingMixin = Tracking.mixin({ label: EVENT_LABEL_MODAL }); +import { GlButton, GlModalDirective, GlTooltip } from '@gitlab/ui'; +import { s__ } from '~/locale'; +import { CREATE_TOKEN_MODAL } from '../constants'; export default { components: { - AgentToken, GlButton, GlTooltip, - GlModal, - GlFormGroup, - GlFormInput, - GlFormTextarea, - GlAlert, }, directives: { GlModalDirective, }, - mixins: [trackingMixin], - inject: ['agentName', 'projectPath', 'canAdminCluster'], - props: { - clusterAgentId: { - required: true, - type: String, - }, - cursor: { - required: true, - type: Object, - }, - }, + inject: ['canAdminCluster'], modalId: CREATE_TOKEN_MODAL, - EVENT_ACTIONS_OPEN, - EVENT_ACTIONS_CLICK, - EVENT_LABEL_MODAL, - TOKEN_NAME_LIMIT, i18n: { createTokenButton: s__('ClusterAgents|Create token'), - modalTitle: s__('ClusterAgents|Create agent access token'), - unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'), - errorTitle: s__('ClusterAgents|Failed to create a token'), dropdownDisabledHint: s__( 'ClusterAgents|Requires a Maintainer or greater role to perform these actions', ), - modalCancel: __('Cancel'), - modalClose: __('Close'), - tokenNameLabel: __('Name'), - tokenDescriptionLabel: __('Description (optional)'), - }, - data() { - return { - token: { - name: null, - description: null, - }, - agentToken: null, - error: null, - loading: false, - variables: { - agentName: this.agentName, - projectPath: this.projectPath, - tokenStatus: TOKEN_STATUS_ACTIVE, - ...this.cursor, - }, - }; - }, - computed: { - modalBtnDisabled() { - return this.loading || !this.hasTokenName; - }, - hasTokenName() { - return Boolean(this.token.name?.length); - }, - }, - methods: { - async createToken() { - this.loading = true; - this.error = null; - - try { - const { errors: tokenErrors, secret } = await this.createAgentTokenMutation(); - - if (tokenErrors?.length > 0) { - throw new Error(tokenErrors[0]); - } - - this.agentToken = secret; - } catch (error) { - if (error) { - this.error = error.message; - } else { - this.error = this.$options.i18n.unknownError; - } - } finally { - this.loading = false; - } - }, - resetModal() { - this.agentToken = null; - this.token.name = null; - this.token.description = null; - this.error = null; - }, - closeModal() { - this.$refs.modal.hide(); - }, - createAgentTokenMutation() { - return this.$apollo - .mutate({ - mutation: createNewAgentToken, - variables: { - input: { - clusterAgentId: this.clusterAgentId, - name: this.token.name, - description: this.token.description, - }, - }, - update: (store, { data: { clusterAgentTokenCreate } }) => { - addAgentTokenToStore( - store, - clusterAgentTokenCreate, - getClusterAgentQuery, - this.variables, - ); - }, - }) - .then(({ data: { clusterAgentTokenCreate } }) => clusterAgentTokenCreate); - }, }, }; </script> @@ -170,82 +39,5 @@ export default { :title="$options.i18n.dropdownDisabledHint" /> </div> - - <gl-modal - ref="modal" - :modal-id="$options.modalId" - :title="$options.i18n.modalTitle" - static - lazy - @hidden="resetModal" - @show="track($options.EVENT_ACTIONS_OPEN)" - > - <gl-alert - v-if="error" - :title="$options.i18n.errorTitle" - :dismissible="false" - variant="danger" - class="gl-mb-5" - > - {{ error }} - </gl-alert> - - <template v-if="!agentToken"> - <gl-form-group :label="$options.i18n.tokenNameLabel"> - <gl-form-input - v-model="token.name" - :max-length="$options.TOKEN_NAME_LIMIT" - :disabled="loading" - required - /> - </gl-form-group> - - <gl-form-group :label="$options.i18n.tokenDescriptionLabel"> - <gl-form-textarea v-model="token.description" :disabled="loading" name="description" /> - </gl-form-group> - </template> - - <agent-token - v-else - :agent-name="agentName" - :agent-token="agentToken" - :modal-id="$options.modalId" - /> - - <template #modal-footer> - <gl-button - v-if="!agentToken && !loading" - :data-track-action="$options.EVENT_ACTIONS_CLICK" - :data-track-label="$options.EVENT_LABEL_MODAL" - data-track-property="close" - data-testid="agent-token-close-button" - @click="closeModal" - >{{ $options.i18n.modalCancel }} - </gl-button> - - <gl-button - v-if="!agentToken" - :disabled="modalBtnDisabled" - :loading="loading" - :data-track-action="$options.EVENT_ACTIONS_CLICK" - :data-track-label="$options.EVENT_LABEL_MODAL" - data-track-property="create-token" - variant="confirm" - type="submit" - @click="createToken" - >{{ $options.i18n.createTokenButton }} - </gl-button> - - <gl-button - v-else - :data-track-action="$options.EVENT_ACTIONS_CLICK" - :data-track-label="$options.EVENT_LABEL_MODAL" - data-track-property="close" - variant="confirm" - @click="closeModal" - >{{ $options.i18n.modalClose }} - </gl-button> - </template> - </gl-modal> </div> </template> diff --git a/app/assets/javascripts/clusters/agents/components/create_token_modal.vue b/app/assets/javascripts/clusters/agents/components/create_token_modal.vue new file mode 100644 index 00000000000..451e1ee1d67 --- /dev/null +++ b/app/assets/javascripts/clusters/agents/components/create_token_modal.vue @@ -0,0 +1,218 @@ +<script> +import { GlButton, GlModal, GlFormGroup, GlFormInput, GlFormTextarea, GlAlert } from '@gitlab/ui'; +import { s__, __ } from '~/locale'; +import Tracking from '~/tracking'; +import AgentToken from '~/clusters_list/components/agent_token.vue'; +import { + CREATE_TOKEN_MODAL, + EVENT_LABEL_MODAL, + EVENT_ACTIONS_OPEN, + EVENT_ACTIONS_CLICK, + TOKEN_NAME_LIMIT, + TOKEN_STATUS_ACTIVE, +} from '../constants'; +import createNewAgentToken from '../graphql/mutations/create_new_agent_token.mutation.graphql'; +import getClusterAgentQuery from '../graphql/queries/get_cluster_agent.query.graphql'; +import { addAgentTokenToStore } from '../graphql/cache_update'; + +const trackingMixin = Tracking.mixin({ label: EVENT_LABEL_MODAL }); + +export default { + components: { + AgentToken, + GlButton, + GlModal, + GlFormGroup, + GlFormInput, + GlFormTextarea, + GlAlert, + }, + mixins: [trackingMixin], + inject: ['agentName', 'projectPath'], + props: { + clusterAgentId: { + required: true, + type: String, + }, + cursor: { + required: true, + type: Object, + }, + }, + modalId: CREATE_TOKEN_MODAL, + EVENT_ACTIONS_OPEN, + EVENT_ACTIONS_CLICK, + EVENT_LABEL_MODAL, + TOKEN_NAME_LIMIT, + i18n: { + createTokenButton: s__('ClusterAgents|Create token'), + modalTitle: s__('ClusterAgents|Create agent access token'), + unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'), + errorTitle: s__('ClusterAgents|Failed to create a token'), + modalCancel: __('Cancel'), + modalClose: __('Close'), + tokenNameLabel: __('Name'), + tokenDescriptionLabel: __('Description (optional)'), + }, + data() { + return { + token: { + name: null, + description: null, + }, + agentToken: null, + error: null, + loading: false, + variables: { + agentName: this.agentName, + projectPath: this.projectPath, + tokenStatus: TOKEN_STATUS_ACTIVE, + ...this.cursor, + }, + }; + }, + computed: { + modalBtnDisabled() { + return this.loading || !this.hasTokenName; + }, + hasTokenName() { + return Boolean(this.token.name?.length); + }, + }, + methods: { + async createToken() { + this.loading = true; + this.error = null; + + try { + const { errors: tokenErrors, secret } = await this.createAgentTokenMutation(); + + if (tokenErrors?.length > 0) { + throw new Error(tokenErrors[0]); + } + this.agentToken = secret; + } catch (error) { + this.error = error ? error.message : this.$options.i18n.unknownError; + } finally { + this.loading = false; + } + }, + resetModal() { + this.agentToken = null; + this.token.name = null; + this.token.description = null; + this.error = null; + }, + closeModal() { + this.$refs.modal.hide(); + }, + createAgentTokenMutation() { + return this.$apollo + .mutate({ + mutation: createNewAgentToken, + variables: { + input: { + clusterAgentId: this.clusterAgentId, + name: this.token.name, + description: this.token.description, + }, + }, + update: (store, { data: { clusterAgentTokenCreate } }) => { + addAgentTokenToStore( + store, + clusterAgentTokenCreate, + getClusterAgentQuery, + this.variables, + ); + }, + }) + .then(({ data: { clusterAgentTokenCreate } }) => clusterAgentTokenCreate); + }, + }, +}; +</script> + +<template> + <gl-modal + ref="modal" + :modal-id="$options.modalId" + :title="$options.i18n.modalTitle" + static + lazy + @hidden="resetModal" + @show="track($options.EVENT_ACTIONS_OPEN)" + > + <gl-alert + v-if="error" + :title="$options.i18n.errorTitle" + :dismissible="false" + variant="danger" + class="gl-mb-5" + > + {{ error }} + </gl-alert> + + <template v-if="!agentToken"> + <gl-form-group :label="$options.i18n.tokenNameLabel" label-for="token-name"> + <gl-form-input + id="token-name" + v-model="token.name" + :max-length="$options.TOKEN_NAME_LIMIT" + :disabled="loading" + required + /> + </gl-form-group> + + <gl-form-group :label="$options.i18n.tokenDescriptionLabel" label-for="token-description"> + <gl-form-textarea + id="token-description" + v-model="token.description" + :disabled="loading" + name="description" + /> + </gl-form-group> + </template> + + <agent-token + v-else + :agent-name="agentName" + :agent-token="agentToken" + :modal-id="$options.modalId" + /> + + <template #modal-footer> + <gl-button + v-if="!agentToken && !loading" + :data-track-action="$options.EVENT_ACTIONS_CLICK" + :data-track-label="$options.EVENT_LABEL_MODAL" + data-track-property="close" + data-testid="agent-token-close-button" + @click="closeModal" + >{{ $options.i18n.modalCancel }} + </gl-button> + + <gl-button + v-if="!agentToken" + :disabled="modalBtnDisabled" + :loading="loading" + :data-track-action="$options.EVENT_ACTIONS_CLICK" + :data-track-label="$options.EVENT_LABEL_MODAL" + data-track-property="create-token" + variant="confirm" + type="submit" + @click="createToken" + >{{ $options.i18n.createTokenButton }} + </gl-button> + + <gl-button + v-else + :data-track-action="$options.EVENT_ACTIONS_CLICK" + :data-track-label="$options.EVENT_LABEL_MODAL" + data-track-property="close" + variant="confirm" + @click="closeModal" + >{{ $options.i18n.modalClose }} + </gl-button> + </template> + </gl-modal> +</template> diff --git a/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue b/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue index 7d36cbb170d..f0af0da4bb4 100644 --- a/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue +++ b/app/assets/javascripts/clusters/agents/components/revoke_token_button.vue @@ -148,7 +148,7 @@ export default { }, hideModal() { this.resetModal(); - this.$refs.modal.hide(); + this.$refs.modal?.hide(); }, }, }; diff --git a/app/assets/javascripts/clusters/agents/components/token_table.vue b/app/assets/javascripts/clusters/agents/components/token_table.vue index 9e64c9da712..f74d66f6b8f 100644 --- a/app/assets/javascripts/clusters/agents/components/token_table.vue +++ b/app/assets/javascripts/clusters/agents/components/token_table.vue @@ -3,6 +3,7 @@ import { GlEmptyState, GlTable, GlTooltip, GlTruncate } from '@gitlab/ui'; import { s__ } from '~/locale'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import CreateTokenButton from './create_token_button.vue'; +import CreateTokenModal from './create_token_modal.vue'; import RevokeTokenButton from './revoke_token_button.vue'; export default { @@ -13,6 +14,7 @@ export default { GlTruncate, TimeAgoTooltip, CreateTokenButton, + CreateTokenModal, RevokeTokenButton, }, i18n: { @@ -85,57 +87,57 @@ export default { </script> <template> - <div v-if="tokens.length"> - <create-token-button - class="gl-text-right gl-my-5" - :cluster-agent-id="clusterAgentId" - :cursor="cursor" - /> + <div> + <div v-if="tokens.length"> + <create-token-button class="gl-text-right gl-my-5" /> - <gl-table - :items="tokens" - :fields="fields" - fixed - stacked="md" - head-variant="white" - thead-class="gl-border-b-solid gl-border-b-2 gl-border-b-gray-100" - > - <template #cell(lastUsed)="{ item }"> - <time-ago-tooltip v-if="item.lastUsedAt" :time="item.lastUsedAt" /> - <span v-else>{{ $options.i18n.neverUsed }}</span> - </template> + <gl-table + :items="tokens" + :fields="fields" + fixed + stacked="md" + head-variant="white" + thead-class="gl-border-b-solid gl-border-b-2 gl-border-b-gray-100" + > + <template #cell(lastUsed)="{ item }"> + <time-ago-tooltip v-if="item.lastUsedAt" :time="item.lastUsedAt" /> + <span v-else>{{ $options.i18n.neverUsed }}</span> + </template> - <template #cell(createdAt)="{ item }"> - <time-ago-tooltip :time="item.createdAt" /> - </template> + <template #cell(createdAt)="{ item }"> + <time-ago-tooltip :time="item.createdAt" /> + </template> - <template #cell(createdBy)="{ item }"> - <span>{{ createdByName(item) }}</span> - </template> + <template #cell(createdBy)="{ item }"> + <span>{{ createdByName(item) }}</span> + </template> - <template #cell(description)="{ item }"> - <div v-if="item.description" :id="`tooltip-description-container-${item.id}`"> - <gl-truncate :id="`tooltip-description-${item.id}`" :text="item.description" /> + <template #cell(description)="{ item }"> + <div v-if="item.description" :id="`tooltip-description-container-${item.id}`"> + <gl-truncate :id="`tooltip-description-${item.id}`" :text="item.description" /> - <gl-tooltip - :container="`tooltip-description-container-${item.id}`" - :target="`tooltip-description-${item.id}`" - placement="top" - > - {{ item.description }} - </gl-tooltip> - </div> - </template> + <gl-tooltip + :container="`tooltip-description-container-${item.id}`" + :target="`tooltip-description-${item.id}`" + placement="top" + > + {{ item.description }} + </gl-tooltip> + </div> + </template> - <template #cell(actions)="{ item }"> - <revoke-token-button :token="item" :cluster-agent-id="clusterAgentId" :cursor="cursor" /> + <template #cell(actions)="{ item }"> + <revoke-token-button :token="item" :cluster-agent-id="clusterAgentId" :cursor="cursor" /> + </template> + </gl-table> + </div> + + <gl-empty-state v-else :title="$options.i18n.noTokens"> + <template #actions> + <create-token-button /> </template> - </gl-table> - </div> + </gl-empty-state> - <gl-empty-state v-else :title="$options.i18n.noTokens"> - <template #actions> - <create-token-button :cluster-agent-id="clusterAgentId" :cursor="cursor" /> - </template> - </gl-empty-state> + <create-token-modal :cluster-agent-id="clusterAgentId" :cursor="cursor" /> + </div> </template> diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js index 2c2f957a75d..186946a83ad 100644 --- a/app/assets/javascripts/projects/project_new.js +++ b/app/assets/javascripts/projects/project_new.js @@ -276,28 +276,33 @@ const bindEvents = () => { ); let isProjectImportUrlDirty = false; - $projectImportUrl.addEventListener('blur', () => { - isProjectImportUrlDirty = true; - debouncedUpdateUrlPathWarningVisibility(); - }); - $projectImportUrl.addEventListener('keyup', () => { - deriveProjectPathFromUrl($projectImportUrl); - }); + + if ($projectImportUrl) { + $projectImportUrl.addEventListener('blur', () => { + isProjectImportUrlDirty = true; + debouncedUpdateUrlPathWarningVisibility(); + }); + $projectImportUrl.addEventListener('keyup', () => { + deriveProjectPathFromUrl($projectImportUrl); + }); + } [$projectImportUrl, $projectImportUrlUser, $projectImportUrlPassword].forEach(($f) => { - if ($f?.on) { - $f.on('input', () => { - if (isProjectImportUrlDirty) { - debouncedUpdateUrlPathWarningVisibility(); - } - }); - } else { - $f.addEventListener('input', () => { + if (!$f) return false; + + if ($f.on) { + return $f.on('input', () => { if (isProjectImportUrlDirty) { debouncedUpdateUrlPathWarningVisibility(); } }); } + + return $f.addEventListener('input', () => { + if (isProjectImportUrlDirty) { + debouncedUpdateUrlPathWarningVisibility(); + } + }); }); $projectImportForm.on('submit', async (e) => { diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb index 1d56cddca63..891df5180d8 100644 --- a/app/uploaders/object_storage.rb +++ b/app/uploaders/object_storage.rb @@ -353,6 +353,20 @@ module ObjectStorage } end + def store_path(*args) + if self.object_store == Store::REMOTE + # We allow administrators to create "sub buckets" by setting a prefix. + # This makes it possible to deploy GitLab with only one object storage + # bucket. Because the prefix is configuration data we do not want to + # store it in the uploads table via RecordsUploads. That means that the + # prefix cannot be part of store_dir. This is why we chose to implement + # the prefix support here in store_path. + File.join([self.class.object_store_options.bucket_prefix, super].compact) + else + super + end + end + # Returns all the possible paths for an upload. # the `upload.path` is a lookup parameter, and it may change # depending on the `store` param. diff --git a/app/workers/namespaces/process_sync_events_worker.rb b/app/workers/namespaces/process_sync_events_worker.rb index 36c4ab2058d..2bf2a4a6ef8 100644 --- a/app/workers/namespaces/process_sync_events_worker.rb +++ b/app/workers/namespaces/process_sync_events_worker.rb @@ -13,7 +13,7 @@ module Namespaces urgency :high idempotent! - deduplicate :until_executed + deduplicate :until_executing def perform results = ::Ci::ProcessSyncEventsService.new( diff --git a/app/workers/projects/process_sync_events_worker.rb b/app/workers/projects/process_sync_events_worker.rb index 92322a9ea99..57f3e3dee5e 100644 --- a/app/workers/projects/process_sync_events_worker.rb +++ b/app/workers/projects/process_sync_events_worker.rb @@ -13,7 +13,7 @@ module Projects urgency :high idempotent! - deduplicate :until_executed + deduplicate :until_executing def perform results = ::Ci::ProcessSyncEventsService.new( |