diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-20 13:43:29 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-20 13:43:29 +0300 |
commit | 3b1af5cc7ed2666ff18b718ce5d30fa5a2756674 (patch) | |
tree | 3bc4a40e0ee51ec27eabf917c537033c0c5b14d4 /app/assets/javascripts/clusters_list | |
parent | 9bba14be3f2c211bf79e15769cd9b77bc73a13bc (diff) |
Add latest changes from gitlab-org/gitlab@16-1-stable-eev16.1.0-rc42
Diffstat (limited to 'app/assets/javascripts/clusters_list')
9 files changed, 211 insertions, 219 deletions
diff --git a/app/assets/javascripts/clusters_list/components/agent_table.vue b/app/assets/javascripts/clusters_list/components/agent_table.vue index d7e98638a11..529be7169db 100644 --- a/app/assets/javascripts/clusters_list/components/agent_table.vue +++ b/app/assets/javascripts/clusters_list/components/agent_table.vue @@ -7,6 +7,8 @@ import { GlTooltip, GlTooltipDirective, GlPopover, + GlBadge, + GlPagination, } from '@gitlab/ui'; import semverLt from 'semver/functions/lt'; import semverInc from 'semver/functions/inc'; @@ -15,7 +17,7 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import timeagoMixin from '~/vue_shared/mixins/timeago'; import { helpPagePath } from '~/helpers/help_page_helper'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import { AGENT_STATUSES, I18N_AGENT_TABLE } from '../constants'; +import { MAX_LIST_COUNT, AGENT_STATUSES, I18N_AGENT_TABLE } from '../constants'; import { getAgentConfigPath } from '../clusters_util'; import DeleteAgentButton from './delete_agent_button.vue'; @@ -28,6 +30,8 @@ export default { GlSprintf, GlTooltip, GlPopover, + GlBadge, + GlPagination, TimeAgoTooltip, DeleteAgentButton, }, @@ -60,6 +64,12 @@ export default { type: Number, }, }, + data() { + return { + currentPage: 1, + limit: this.maxAgents ?? MAX_LIST_COUNT, + }; + }, computed: { fields() { const tdClass = 'gl-pt-3! gl-pb-4! gl-vertical-align-middle!'; @@ -114,6 +124,16 @@ export default { serverVersion() { return this.kasVersion || this.gitlabVersion; }, + showPagination() { + return !this.maxAgents && this.agents.length > this.limit; + }, + prevPage() { + return Math.max(this.currentPage - 1, 0); + }, + nextPage() { + const nextPage = this.currentPage + 1; + return nextPage > Math.ceil(this.agents.length / this.limit) ? null : nextPage; + }, }, methods: { getStatusCellId(item) { @@ -184,84 +204,105 @@ export default { </script> <template> - <gl-table - :items="agentsList" - :fields="fields" - stacked="md" - class="gl-mb-4!" - data-testid="cluster-agent-list-table" - > - <template #cell(name)="{ item }"> - <gl-link :href="item.webPath" data-testid="cluster-agent-name-link"> - {{ item.name }} - </gl-link> - </template> + <div> + <gl-table + :items="agentsList" + :fields="fields" + :per-page="limit" + :current-page="currentPage" + stacked="md" + class="gl-mb-4!" + data-testid="cluster-agent-list-table" + > + <template #cell(name)="{ item }"> + <gl-link :href="item.webPath" data-testid="cluster-agent-name-link">{{ item.name }}</gl-link + ><gl-badge v-if="item.isShared" class="gl-ml-3">{{ + $options.i18n.sharedBadgeText + }}</gl-badge> + </template> - <template #cell(status)="{ item }"> - <span - :id="getStatusCellId(item)" - class="gl-md-pr-5" - data-testid="cluster-agent-connection-status" - > - <span :class="$options.AGENT_STATUSES[item.status].class" class="gl-mr-3"> - <gl-icon :name="$options.AGENT_STATUSES[item.status].icon" :size="16" /></span - >{{ $options.AGENT_STATUSES[item.status].name }} - </span> - <gl-tooltip v-if="item.status === 'active'" :target="getStatusCellId(item)" placement="right"> - <gl-sprintf :message="$options.AGENT_STATUSES[item.status].tooltip.title" - ><template #timeAgo>{{ timeFormatted(item.lastContact) }}</template> - </gl-sprintf> - </gl-tooltip> - <gl-popover - v-else - :target="getStatusCellId(item)" - :title="$options.AGENT_STATUSES[item.status].tooltip.title" - placement="right" - container="viewport" - > - <p> - <gl-sprintf :message="$options.AGENT_STATUSES[item.status].tooltip.body" - ><template #timeAgo>{{ timeFormatted(item.lastContact) }}</template></gl-sprintf - > - </p> - <p class="gl-mb-0"> - <gl-link :href="$options.troubleshootingLink" target="_blank" class="gl-font-sm"> - {{ $options.i18n.troubleshootingText }}</gl-link - > - </p> - </gl-popover> - </template> + <template #cell(status)="{ item }"> + <span + :id="getStatusCellId(item)" + class="gl-md-pr-5" + data-testid="cluster-agent-connection-status" + > + <span :class="$options.AGENT_STATUSES[item.status].class" class="gl-mr-3"> + <gl-icon :name="$options.AGENT_STATUSES[item.status].icon" :size="16" /></span + >{{ $options.AGENT_STATUSES[item.status].name }} + </span> + <gl-tooltip + v-if="item.status === 'active'" + :target="getStatusCellId(item)" + placement="right" + > + <gl-sprintf :message="$options.AGENT_STATUSES[item.status].tooltip.title" + ><template #timeAgo>{{ timeFormatted(item.lastContact) }}</template> + </gl-sprintf> + </gl-tooltip> + <gl-popover + v-else + :target="getStatusCellId(item)" + :title="$options.AGENT_STATUSES[item.status].tooltip.title" + placement="right" + container="viewport" + > + <p> + <gl-sprintf :message="$options.AGENT_STATUSES[item.status].tooltip.body" + ><template #timeAgo>{{ timeFormatted(item.lastContact) }}</template></gl-sprintf + > + </p> + <p class="gl-mb-0"> + <gl-link :href="$options.troubleshootingLink" target="_blank" class="gl-font-sm"> + {{ $options.i18n.troubleshootingText }}</gl-link + > + </p> + </gl-popover> + </template> + + <template #cell(lastContact)="{ item }"> + <span data-testid="cluster-agent-last-contact"> + <time-ago-tooltip v-if="item.lastContact" :time="item.lastContact" /> + <span v-else>{{ $options.i18n.neverConnectedText }}</span> + </span> + </template> - <template #cell(lastContact)="{ item }"> - <span data-testid="cluster-agent-last-contact"> - <time-ago-tooltip v-if="item.lastContact" :time="item.lastContact" /> - <span v-else>{{ $options.i18n.neverConnectedText }}</span> - </span> - </template> + <template #cell(version)="{ item }"> + <span :id="getVersionCellId(item)" data-testid="cluster-agent-version"> + {{ getAgentVersionString(item) }} - <template #cell(version)="{ item }"> - <span :id="getVersionCellId(item)" data-testid="cluster-agent-version"> - {{ getAgentVersionString(item) }} + <gl-icon + v-if="isVersionMismatch(item) || isVersionOutdated(item)" + name="warning" + class="gl-text-orange-500 gl-ml-2" + /> + </span> - <gl-icon + <gl-popover v-if="isVersionMismatch(item) || isVersionOutdated(item)" - name="warning" - class="gl-text-orange-500 gl-ml-2" - /> - </span> + :target="getVersionCellId(item)" + :title="getVersionPopoverTitle(item)" + :data-testid="getPopoverTestId(item)" + placement="right" + container="viewport" + > + <div v-if="isVersionMismatch(item) && isVersionOutdated(item)"> + <p>{{ $options.i18n.versionMismatchText }}</p> - <gl-popover - v-if="isVersionMismatch(item) || isVersionOutdated(item)" - :target="getVersionCellId(item)" - :title="getVersionPopoverTitle(item)" - :data-testid="getPopoverTestId(item)" - placement="right" - container="viewport" - > - <div v-if="isVersionMismatch(item) && isVersionOutdated(item)"> - <p>{{ $options.i18n.versionMismatchText }}</p> + <p class="gl-mb-0"> + <gl-sprintf :message="$options.i18n.versionOutdatedText"> + <template #version>{{ serverVersion }}</template> + </gl-sprintf> + <gl-link :href="$options.versionUpdateLink" class="gl-font-sm"> + {{ $options.i18n.viewDocsText }}</gl-link + > + </p> + </div> + <p v-else-if="isVersionMismatch(item)" class="gl-mb-0"> + {{ $options.i18n.versionMismatchText }} + </p> - <p class="gl-mb-0"> + <p v-else-if="isVersionOutdated(item)" class="gl-mb-0"> <gl-sprintf :message="$options.i18n.versionOutdatedText"> <template #version>{{ serverVersion }}</template> </gl-sprintf> @@ -269,53 +310,54 @@ export default { {{ $options.i18n.viewDocsText }}</gl-link > </p> - </div> - <p v-else-if="isVersionMismatch(item)" class="gl-mb-0"> - {{ $options.i18n.versionMismatchText }} - </p> + </gl-popover> + </template> - <p v-else-if="isVersionOutdated(item)" class="gl-mb-0"> - <gl-sprintf :message="$options.i18n.versionOutdatedText"> - <template #version>{{ serverVersion }}</template> - </gl-sprintf> - <gl-link :href="$options.versionUpdateLink" class="gl-font-sm"> - {{ $options.i18n.viewDocsText }}</gl-link - > - </p> - </gl-popover> - </template> + <template #cell(agentID)="{ item }"> + <span data-testid="cluster-agent-id"> + {{ getAgentId(item) }} + </span> + </template> - <template #cell(agentID)="{ item }"> - <span data-testid="cluster-agent-id"> - {{ getAgentId(item) }} - </span> - </template> + <template #cell(configuration)="{ item }"> + <span data-testid="cluster-agent-configuration-link"> + <gl-link v-if="item.configFolder" :href="item.configFolder.webPath"> + {{ getAgentConfigPath(item.name) }} + </gl-link> - <template #cell(configuration)="{ item }"> - <span data-testid="cluster-agent-configuration-link"> - <gl-link v-if="item.configFolder" :href="item.configFolder.webPath"> - {{ getAgentConfigPath(item.name) }} - </gl-link> + <span v-else-if="item.isShared"> + {{ $options.i18n.externalConfigText }} + </span> - <span v-else - >{{ $options.i18n.defaultConfigText }} - <gl-link - v-gl-tooltip - :href="$options.configHelpLink" - :title="$options.i18n.defaultConfigTooltip" - :aria-label="$options.i18n.defaultConfigTooltip" - class="gl-vertical-align-middle" - ><gl-icon name="question-o" :size="14" /></gl-link - ></span> - </span> - </template> + <span v-else + >{{ $options.i18n.defaultConfigText }} + <gl-link + v-gl-tooltip + :href="$options.configHelpLink" + :title="$options.i18n.defaultConfigTooltip" + :aria-label="$options.i18n.defaultConfigTooltip" + class="gl-vertical-align-middle" + ><gl-icon name="question-o" :size="14" /></gl-link + ></span> + </span> + </template> + + <template #cell(options)="{ item }"> + <delete-agent-button + v-if="!item.isShared" + :agent="item" + :default-branch-name="defaultBranchName" + /> + </template> + </gl-table> - <template #cell(options)="{ item }"> - <delete-agent-button - :agent="item" - :default-branch-name="defaultBranchName" - :max-agents="maxAgents" - /> - </template> - </gl-table> + <gl-pagination + v-if="showPagination" + v-model="currentPage" + :prev-page="prevPage" + :next-page="nextPage" + align="center" + class="gl-mt-5" + /> + </div> </template> diff --git a/app/assets/javascripts/clusters_list/components/agents.vue b/app/assets/javascripts/clusters_list/components/agents.vue index 36f0f8e61ba..b1765d336c8 100644 --- a/app/assets/javascripts/clusters_list/components/agents.vue +++ b/app/assets/javascripts/clusters_list/components/agents.vue @@ -1,9 +1,9 @@ <script> -import { GlAlert, GlKeysetPagination, GlLoadingIcon, GlBanner } from '@gitlab/ui'; +import { GlAlert, GlLoadingIcon, GlBanner } from '@gitlab/ui'; import { s__ } from '~/locale'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; -import { MAX_LIST_COUNT, AGENT_FEEDBACK_ISSUE, AGENT_FEEDBACK_KEY } from '../constants'; +import { AGENT_FEEDBACK_ISSUE, AGENT_FEEDBACK_KEY } from '../constants'; import getAgentsQuery from '../graphql/queries/get_agents.query.graphql'; import { getAgentLastContact, getAgentStatus } from '../clusters_util'; import AgentEmptyState from './agent_empty_state.vue'; @@ -27,7 +27,6 @@ export default { return { defaultBranchName: this.defaultBranchName, projectPath: this.projectPath, - ...this.cursor, }; }, update(data) { @@ -37,13 +36,15 @@ export default { result() { this.emitAgentsLoaded(); }, + error() { + this.queryErrored = true; + }, }, }, components: { AgentEmptyState, AgentTable, GlAlert, - GlKeysetPagination, GlLoadingIcon, GlBanner, LocalStorageSync, @@ -69,41 +70,41 @@ export default { }, data() { return { - cursor: { - first: this.limit ? this.limit : MAX_LIST_COUNT, - last: null, - }, folderList: {}, feedbackBannerDismissed: false, + queryErrored: false, }; }, computed: { agentList() { - let list = this.agents?.project?.clusterAgents?.nodes; + const localAgents = this.agents?.project?.clusterAgents?.nodes || []; + const sharedAgents = [ + ...(this.agents?.project?.ciAccessAuthorizedAgents?.nodes || []), + ...(this.agents?.project?.userAccessAuthorizedAgents?.nodes || []), + ].map((node) => { + return { + ...node.agent, + isShared: true, + }; + }); - if (list) { - list = list.map((agent) => { + const filteredList = [...localAgents, ...sharedAgents] + .filter((node, index, list) => { + return node && index === list.findIndex((agent) => agent.id === node.id); + }) + .map((agent) => { const configFolder = this.folderList[agent.name]; const lastContact = getAgentLastContact(agent?.tokens?.nodes); const status = getAgentStatus(lastContact); return { ...agent, configFolder, lastContact, status }; - }); - } + }) + .sort((a, b) => b.lastUsedAt - a.lastUsedAt); - return list; - }, - agentPageInfo() { - return this.agents?.project?.clusterAgents?.pageInfo || {}; + return filteredList; }, isLoading() { return this.$apollo.queries.agents.loading; }, - showPagination() { - return !this.limit && (this.agentPageInfo.hasPreviousPage || this.agentPageInfo.hasNextPage); - }, - treePageInfo() { - return this.agents?.project?.repository?.tree?.trees?.pageInfo || {}; - }, feedbackBannerEnabled() { return this.glFeatures.showGitlabAgentFeedback; }, @@ -112,22 +113,6 @@ export default { }, }, methods: { - nextPage() { - this.cursor = { - first: MAX_LIST_COUNT, - last: null, - afterAgent: this.agentPageInfo.endCursor, - afterTree: this.treePageInfo.endCursor, - }; - }, - prevPage() { - this.cursor = { - first: null, - last: MAX_LIST_COUNT, - beforeAgent: this.agentPageInfo.startCursor, - beforeTree: this.treePageInfo.endCursor, - }; - }, updateTreeList(data) { const configFolders = data?.project?.repository?.tree?.trees?.nodes; @@ -138,8 +123,7 @@ export default { } }, emitAgentsLoaded() { - const count = this.agents?.project?.clusterAgents?.count; - this.$emit('onAgentsLoad', count); + this.$emit('onAgentsLoad', this.agentList?.length); }, handleBannerClose() { this.feedbackBannerDismissed = true; @@ -151,7 +135,7 @@ export default { <template> <gl-loading-icon v-if="isLoading" size="lg" /> - <section v-else-if="agentList"> + <section v-else-if="!queryErrored"> <div v-if="agentList.length"> <local-storage-sync v-if="feedbackBannerEnabled" @@ -174,12 +158,8 @@ export default { <agent-table :agents="agentList" :default-branch-name="defaultBranchName" - :max-agents="cursor.first" + :max-agents="limit" /> - - <div v-if="showPagination" class="gl-display-flex gl-justify-content-center gl-mt-5"> - <gl-keyset-pagination v-bind="agentPageInfo" @prev="prevPage" @next="nextPage" /> - </div> </div> <agent-empty-state v-else /> diff --git a/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue b/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue index 365e0384d87..75850cbb108 100644 --- a/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue +++ b/app/assets/javascripts/clusters_list/components/available_agents_dropdown.vue @@ -58,8 +58,6 @@ export default { selectAgent(agent) { this.$emit('agentSelected', agent); this.selectedAgent = agent; - - this.$refs.dropdown.closeAndFocus(); }, onKeyEnter() { if (!this.searchTerm?.length) { @@ -76,7 +74,6 @@ export default { <template> <div @keydown.enter.stop.prevent="onKeyEnter"> <gl-collapsible-listbox - ref="dropdown" v-model="selectedAgent" class="gl-w-full" toggle-class="select-agent-dropdown" diff --git a/app/assets/javascripts/clusters_list/components/delete_agent_button.vue b/app/assets/javascripts/clusters_list/components/delete_agent_button.vue index 913db87f019..4088d5c79f7 100644 --- a/app/assets/javascripts/clusters_list/components/delete_agent_button.vue +++ b/app/assets/javascripts/clusters_list/components/delete_agent_button.vue @@ -39,11 +39,6 @@ export default { required: false, type: String, }, - maxAgents: { - default: null, - required: false, - type: Number, - }, }, data() { return { @@ -64,8 +59,6 @@ export default { getAgentsQueryVariables() { return { defaultBranchName: this.defaultBranchName, - first: this.maxAgents, - last: null, projectPath: this.projectPath, }; }, diff --git a/app/assets/javascripts/clusters_list/components/install_agent_modal.vue b/app/assets/javascripts/clusters_list/components/install_agent_modal.vue index 444b9ac2a14..55e62d1c698 100644 --- a/app/assets/javascripts/clusters_list/components/install_agent_modal.vue +++ b/app/assets/javascripts/clusters_list/components/install_agent_modal.vue @@ -13,10 +13,9 @@ import { MODAL_TYPE_EMPTY, MODAL_TYPE_REGISTER, } from '../constants'; -import { addAgentToStore, addAgentConfigToStore } from '../graphql/cache_update'; +import { addAgentConfigToStore } from '../graphql/cache_update'; import createAgent from '../graphql/mutations/create_agent.mutation.graphql'; import createAgentToken from '../graphql/mutations/create_agent_token.mutation.graphql'; -import getAgentsQuery from '../graphql/queries/get_agents.query.graphql'; import agentConfigurations from '../graphql/queries/agent_configurations.query.graphql'; import AvailableAgentsDropdown from './available_agents_dropdown.vue'; import AgentToken from './agent_token.vue'; @@ -148,14 +147,6 @@ export default { projectPath: this.projectPath, }, }, - update: (store, { data: { createClusterAgent } }) => { - addAgentToStore( - store, - createClusterAgent, - getAgentsQuery, - this.getAgentsQueryVariables, - ); - }, }) .then(({ data: { createClusterAgent } }) => { return createClusterAgent; diff --git a/app/assets/javascripts/clusters_list/constants.js b/app/assets/javascripts/clusters_list/constants.js index fe3fa22fea3..3ce10f7c3a2 100644 --- a/app/assets/javascripts/clusters_list/constants.js +++ b/app/assets/javascripts/clusters_list/constants.js @@ -1,7 +1,7 @@ import { __, s__, sprintf } from '~/locale'; import { helpPagePath } from '~/helpers/help_page_helper'; -export const MAX_LIST_COUNT = 25; +export const MAX_LIST_COUNT = 20; export const INSTALL_AGENT_MODAL_ID = 'install-agent'; export const ACTIVE_CONNECTION_TIME = 480000; export const NAME_MAX_LENGTH = 50; @@ -86,6 +86,8 @@ export const I18N_AGENT_TABLE = { viewDocsText: s__('ClusterAgents|How to update an agent?'), defaultConfigText: s__('ClusterAgents|Default configuration'), defaultConfigTooltip: s__('ClusterAgents|What is default configuration?'), + sharedBadgeText: s__('ClusterAgents|shared'), + externalConfigText: s__('ClusterAgents|External project'), }; export const I18N_AGENT_TOKEN = { diff --git a/app/assets/javascripts/clusters_list/graphql/cache_update.js b/app/assets/javascripts/clusters_list/graphql/cache_update.js index e68f6a378c0..1c58652744d 100644 --- a/app/assets/javascripts/clusters_list/graphql/cache_update.js +++ b/app/assets/javascripts/clusters_list/graphql/cache_update.js @@ -2,27 +2,6 @@ import produce from 'immer'; export const hasErrors = ({ errors = [] }) => errors?.length; -export function addAgentToStore(store, createClusterAgent, query, variables) { - if (!hasErrors(createClusterAgent)) { - const { clusterAgent } = createClusterAgent; - const sourceData = store.readQuery({ - query, - variables, - }); - - const data = produce(sourceData, (draftData) => { - draftData.project.clusterAgents.nodes.push(clusterAgent); - draftData.project.clusterAgents.count += 1; - }); - - store.writeQuery({ - query, - variables, - data, - }); - } -} - export function addAgentConfigToStore( store, clusterAgentTokenCreate, @@ -65,7 +44,12 @@ export function removeAgentFromStore(store, deleteClusterAgent, query, variables draftData.project.clusterAgents.nodes = draftData.project.clusterAgents.nodes.filter( ({ id }) => id !== deleteClusterAgent.id, ); - draftData.project.clusterAgents.count -= 1; + draftData.project.ciAccessAuthorizedAgents.nodes = draftData.project.ciAccessAuthorizedAgents.nodes.filter( + ({ agent }) => agent.id !== deleteClusterAgent.id, + ); + draftData.project.userAccessAuthorizedAgents.nodes = draftData.project.userAccessAuthorizedAgents.nodes.filter( + ({ agent }) => agent.id !== deleteClusterAgent.id, + ); }); store.writeQuery({ diff --git a/app/assets/javascripts/clusters_list/graphql/fragments/cluster_agent.fragment.graphql b/app/assets/javascripts/clusters_list/graphql/fragments/cluster_agent.fragment.graphql index 05d2525ab98..31897b50407 100644 --- a/app/assets/javascripts/clusters_list/graphql/fragments/cluster_agent.fragment.graphql +++ b/app/assets/javascripts/clusters_list/graphql/fragments/cluster_agent.fragment.graphql @@ -2,6 +2,7 @@ fragment ClusterAgentFragment on ClusterAgent { id name webPath + createdAt connections { nodes { metadata { diff --git a/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql b/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql index 76920a0aef4..2a4f7b42eff 100644 --- a/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql +++ b/app/assets/javascripts/clusters_list/graphql/queries/get_agents.query.graphql @@ -1,26 +1,28 @@ -#import "~/graphql_shared/fragments/page_info.fragment.graphql" #import "../fragments/cluster_agent.fragment.graphql" -query getAgents( - $defaultBranchName: String! - $projectPath: ID! - $first: Int - $last: Int - $afterAgent: String - $beforeAgent: String -) { +query getAgents($defaultBranchName: String!, $projectPath: ID!) { project(fullPath: $projectPath) { id - clusterAgents(first: $first, last: $last, before: $beforeAgent, after: $afterAgent) { + clusterAgents { nodes { ...ClusterAgentFragment } + } - pageInfo { - ...PageInfo + ciAccessAuthorizedAgents { + nodes { + agent { + ...ClusterAgentFragment + } } + } - count + userAccessAuthorizedAgents { + nodes { + agent { + ...ClusterAgentFragment + } + } } repository { |