diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-15 18:10:57 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-15 18:10:57 +0300 |
commit | e5c7d631a84940c66e46f1824ba7ce0c7f1d0ea4 (patch) | |
tree | ac1cce129d8c8cdcee6a668e27d8034d2da172e4 /app | |
parent | 0687020509cafe1e24eb0bfa0e0f5c9f6c4799d2 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
55 files changed, 599 insertions, 280 deletions
diff --git a/app/assets/javascripts/clusters_list/clusters_util.js b/app/assets/javascripts/clusters_list/clusters_util.js index 9b870134512..c78c93fe1ba 100644 --- a/app/assets/javascripts/clusters_list/clusters_util.js +++ b/app/assets/javascripts/clusters_list/clusters_util.js @@ -6,3 +6,7 @@ export function generateAgentRegistrationCommand(agentToken, kasAddress) { --agent-version stable \\ --namespace gitlab-kubernetes-agent | kubectl apply -f -`; } + +export function getAgentConfigPath(clusterAgentName) { + return `.gitlab/agents/${clusterAgentName}`; +} diff --git a/app/assets/javascripts/clusters_list/components/agent_empty_state.vue b/app/assets/javascripts/clusters_list/components/agent_empty_state.vue index 06092039eb7..5c6f9de0624 100644 --- a/app/assets/javascripts/clusters_list/components/agent_empty_state.vue +++ b/app/assets/javascripts/clusters_list/components/agent_empty_state.vue @@ -1,10 +1,19 @@ <script> import { GlButton, GlEmptyState, GlLink, GlSprintf, GlAlert, GlModalDirective } from '@gitlab/ui'; +import { helpPagePath } from '~/helpers/help_page_helper'; import { INSTALL_AGENT_MODAL_ID, I18N_AGENTS_EMPTY_STATE } from '../constants'; export default { i18n: I18N_AGENTS_EMPTY_STATE, modalId: INSTALL_AGENT_MODAL_ID, + multipleClustersDocsUrl: helpPagePath('user/project/clusters/multiple_kubernetes_clusters'), + installDocsUrl: helpPagePath('administration/clusters/kas'), + getStartedDocsUrl: helpPagePath('user/clusters/agent/index', { + anchor: 'define-a-configuration-repository', + }), + integrationsDocsUrl: helpPagePath('user/clusters/agent/index', { + anchor: 'get-started-with-gitops-and-the-gitlab-agent', + }), components: { GlButton, GlEmptyState, @@ -15,14 +24,7 @@ export default { directives: { GlModalDirective, }, - inject: [ - 'emptyStateImage', - 'projectPath', - 'multipleClustersDocsUrl', - 'installDocsUrl', - 'getStartedDocsUrl', - 'integrationDocsUrl', - ], + inject: ['emptyStateImage', 'projectPath'], props: { hasConfigurations: { type: Boolean, @@ -47,7 +49,7 @@ export default { <gl-sprintf :message="$options.i18n.multipleClustersText"> <template #link="{ content }"> <gl-link - :href="multipleClustersDocsUrl" + :href="$options.multipleClustersDocsUrl" target="_blank" data-testid="multiple-clusters-docs-link" > @@ -58,7 +60,7 @@ export default { </p> <p class="mw-460 gl-mx-auto"> - <gl-link :href="installDocsUrl" target="_blank" data-testid="install-docs-link"> + <gl-link :href="$options.installDocsUrl" target="_blank" data-testid="install-docs-link"> {{ $options.i18n.learnMoreText }} </gl-link> </p> @@ -75,7 +77,7 @@ export default { <gl-button category="primary" variant="info" - :href="getStartedDocsUrl" + :href="$options.getStartedDocsUrl" target="_blank" class="gl-ml-0!" > diff --git a/app/assets/javascripts/clusters_list/components/agent_table.vue b/app/assets/javascripts/clusters_list/components/agent_table.vue index 487e512c06d..000730ac1ba 100644 --- a/app/assets/javascripts/clusters_list/components/agent_table.vue +++ b/app/assets/javascripts/clusters_list/components/agent_table.vue @@ -1,6 +1,5 @@ <script> import { - GlButton, GlLink, GlModalDirective, GlTable, @@ -12,11 +11,12 @@ import { import { s__ } from '~/locale'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import timeagoMixin from '~/vue_shared/mixins/timeago'; -import { INSTALL_AGENT_MODAL_ID, AGENT_STATUSES, TROUBLESHOOTING_LINK } from '../constants'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import { INSTALL_AGENT_MODAL_ID, AGENT_STATUSES } from '../constants'; +import { getAgentConfigPath } from '../clusters_util'; export default { components: { - GlButton, GlLink, GlTable, GlIcon, @@ -29,10 +29,12 @@ export default { GlModalDirective, }, mixins: [timeagoMixin], - inject: ['integrationDocsUrl'], INSTALL_AGENT_MODAL_ID, AGENT_STATUSES, - TROUBLESHOOTING_LINK, + + troubleshooting_link: helpPagePath('user/clusters/agent/index', { + anchor: 'troubleshooting', + }), props: { agents: { required: true, @@ -41,112 +43,102 @@ export default { }, computed: { fields() { + const tdClass = 'gl-py-5!'; return [ { key: 'name', label: s__('ClusterAgents|Name'), + tdClass, }, { key: 'status', label: s__('ClusterAgents|Connection status'), + tdClass, }, { key: 'lastContact', label: s__('ClusterAgents|Last contact'), + tdClass, }, { key: 'configuration', label: s__('ClusterAgents|Configuration'), + tdClass, }, ]; }, }, + methods: { + getCellId(item) { + return `connection-status-${item.name}`; + }, + getAgentConfigPath, + }, }; </script> <template> - <div> - <div class="gl-display-block gl-text-right gl-my-3"> - <gl-button - v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" - variant="confirm" - category="primary" - >{{ s__('ClusterAgents|Install a new GitLab Agent') }} - </gl-button> - </div> + <gl-table + :items="agents" + :fields="fields" + stacked="md" + head-variant="white" + thead-class="gl-border-b-solid gl-border-b-2 gl-border-b-gray-100" + 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> - <gl-table - :items="agents" - :fields="fields" - stacked="md" - head-variant="white" - thead-class="gl-border-b-solid gl-border-b-1 gl-border-b-gray-100" - 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> - - <template #cell(status)="{ item }"> - <span - :id="`connection-status-${item.name}`" - class="gl-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="12" /></span - >{{ $options.AGENT_STATUSES[item.status].name }} - </span> - <gl-tooltip - v-if="item.status === 'active'" - :target="`connection-status-${item.name}`" - 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="`connection-status-${item.name}`" - :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"> - {{ s__('ClusterAgents|For more troubleshooting information go to') }} - <gl-link :href="$options.TROUBLESHOOTING_LINK" target="_blank" class="gl-font-sm"> - {{ $options.TROUBLESHOOTING_LINK }}</gl-link - > - </p> - </gl-popover> - </template> + <template #cell(status)="{ item }"> + <span :id="getCellId(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="12" /></span + >{{ $options.AGENT_STATUSES[item.status].name }} + </span> + <gl-tooltip v-if="item.status === 'active'" :target="getCellId(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="getCellId(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.troubleshooting_link" target="_blank" class="gl-font-sm"> + {{ s__('ClusterAgents|Learn how to troubleshoot') }}</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>{{ s__('ClusterAgents|Never') }}</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>{{ s__('ClusterAgents|Never') }}</span> + </span> + </template> - <template #cell(configuration)="{ item }"> - <span data-testid="cluster-agent-configuration-link"> - <!-- eslint-disable @gitlab/vue-require-i18n-strings --> - <gl-link v-if="item.configFolder" :href="item.configFolder.webPath"> - .gitlab/agents/{{ 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>.gitlab/agents/{{ item.name }}</span> - <!-- eslint-enable @gitlab/vue-require-i18n-strings --> - </span> - </template> - </gl-table> - </div> + <span v-else>{{ getAgentConfigPath(item.name) }}</span> + </span> + </template> + </gl-table> </template> diff --git a/app/assets/javascripts/clusters_list/components/agents.vue b/app/assets/javascripts/clusters_list/components/agents.vue index ed44c1f5fa7..4fc79b32b9c 100644 --- a/app/assets/javascripts/clusters_list/components/agents.vue +++ b/app/assets/javascripts/clusters_list/components/agents.vue @@ -4,7 +4,6 @@ import { MAX_LIST_COUNT, ACTIVE_CONNECTION_TIME } from '../constants'; import getAgentsQuery from '../graphql/queries/get_agents.query.graphql'; import AgentEmptyState from './agent_empty_state.vue'; import AgentTable from './agent_table.vue'; -import InstallAgentModal from './install_agent_modal.vue'; export default { apollo: { @@ -26,7 +25,6 @@ export default { components: { AgentEmptyState, AgentTable, - InstallAgentModal, GlAlert, GlKeysetPagination, GlLoadingIcon, @@ -135,19 +133,18 @@ export default { </script> <template> - <gl-loading-icon v-if="isLoading" size="md" class="gl-mt-3" /> + <gl-loading-icon v-if="isLoading" size="md" /> - <section v-else-if="agentList" class="gl-mt-3"> + <section v-else-if="agentList"> <div v-if="agentList.length"> - <AgentTable :agents="agentList" /> + <agent-table :agents="agentList" /> <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> - <AgentEmptyState v-else :has-configurations="hasConfigurations" /> - <InstallAgentModal @agentRegistered="reloadAgents" /> + <agent-empty-state v-else :has-configurations="hasConfigurations" /> </section> <gl-alert v-else variant="danger" :dismissible="false"> diff --git a/app/assets/javascripts/clusters_list/components/clusters.vue b/app/assets/javascripts/clusters_list/components/clusters.vue index cf0587341b5..bc27563b6d5 100644 --- a/app/assets/javascripts/clusters_list/components/clusters.vue +++ b/app/assets/javascripts/clusters_list/components/clusters.vue @@ -57,30 +57,37 @@ export default { }, }, fields() { + const tdClass = 'gl-py-5!'; return [ { key: 'name', label: __('Kubernetes cluster'), + tdClass, }, { key: 'environment_scope', label: __('Environment scope'), + tdClass, }, { key: 'node_size', label: __('Nodes'), + tdClass, }, { key: 'total_cpu', label: __('Total cores (CPUs)'), + tdClass, }, { key: 'total_memory', label: __('Total memory (GB)'), + tdClass, }, { key: 'cluster_type', label: __('Cluster level'), + tdClass, formatter: (value) => CLUSTER_TYPES[value], }, ]; @@ -201,7 +208,7 @@ export default { </script> <template> - <gl-loading-icon v-if="loadingClusters" size="md" class="gl-mt-3" /> + <gl-loading-icon v-if="loadingClusters" size="md" /> <section v-else> <ancestor-notice /> @@ -210,10 +217,11 @@ export default { v-if="hasClusters" :items="clusters" :fields="fields" + fixed stacked="md" head-variant="white" - thead-class="gl-border-b-solid gl-border-b-1 gl-border-b-gray-100" - class="qa-clusters-table" + thead-class="gl-border-b-solid gl-border-b-2 gl-border-b-gray-100" + class="qa-clusters-table gl-mb-4!" data-testid="cluster_list_table" > <template #cell(name)="{ item }"> diff --git a/app/assets/javascripts/clusters_list/components/clusters_actions.vue b/app/assets/javascripts/clusters_list/components/clusters_actions.vue new file mode 100644 index 00000000000..25f67462223 --- /dev/null +++ b/app/assets/javascripts/clusters_list/components/clusters_actions.vue @@ -0,0 +1,44 @@ +<script> +import { GlDropdown, GlDropdownItem, GlModalDirective } from '@gitlab/ui'; +import { INSTALL_AGENT_MODAL_ID, CLUSTERS_ACTIONS } from '../constants'; + +export default { + i18n: CLUSTERS_ACTIONS, + INSTALL_AGENT_MODAL_ID, + components: { + GlDropdown, + GlDropdownItem, + }, + directives: { + GlModalDirective, + }, + inject: ['newClusterPath', 'addClusterPath'], +}; +</script> + +<template> + <div class="nav-controls gl-ml-auto"> + <gl-dropdown + ref="dropdown" + v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" + category="primary" + variant="confirm" + :text="$options.i18n.actionsButton" + split + right + > + <gl-dropdown-item :href="newClusterPath" data-testid="new-cluster-link" @click.stop> + {{ $options.i18n.createNewCluster }} + </gl-dropdown-item> + <gl-dropdown-item + v-gl-modal-directive="$options.INSTALL_AGENT_MODAL_ID" + data-testid="connect-new-agent-link" + > + {{ $options.i18n.connectWithAgent }} + </gl-dropdown-item> + <gl-dropdown-item :href="addClusterPath" data-testid="connect-cluster-link" @click.stop> + {{ $options.i18n.connectExistingCluster }} + </gl-dropdown-item> + </gl-dropdown> + </div> +</template> diff --git a/app/assets/javascripts/clusters_list/components/clusters_empty_state.vue b/app/assets/javascripts/clusters_list/components/clusters_empty_state.vue index 1f153a4f38e..993507b18bf 100644 --- a/app/assets/javascripts/clusters_list/components/clusters_empty_state.vue +++ b/app/assets/javascripts/clusters_list/components/clusters_empty_state.vue @@ -14,6 +14,7 @@ export default { }, inject: ['emptyStateHelpText', 'clustersEmptyStateImage', 'newClusterPath'], learnMoreHelpUrl: helpPagePath('user/project/clusters/index'), + multipleClustersHelpUrl: helpPagePath('user/project/clusters/multiple_kubernetes_clusters'), computed: { ...mapState(['canAddCluster']), }, diff --git a/app/assets/javascripts/clusters_list/components/clusters_main_view.vue b/app/assets/javascripts/clusters_list/components/clusters_main_view.vue new file mode 100644 index 00000000000..c87cf593305 --- /dev/null +++ b/app/assets/javascripts/clusters_list/components/clusters_main_view.vue @@ -0,0 +1,68 @@ +<script> +import { GlTabs, GlTab } from '@gitlab/ui'; +import { CLUSTERS_TABS } from '../constants'; +import Agents from './agents.vue'; +import InstallAgentModal from './install_agent_modal.vue'; +import ClustersActions from './clusters_actions.vue'; +import Clusters from './clusters.vue'; + +export default { + components: { + GlTabs, + GlTab, + ClustersActions, + Clusters, + Agents, + InstallAgentModal, + }, + CLUSTERS_TABS, + props: { + defaultBranchName: { + default: '.noBranch', + required: false, + type: String, + }, + }, + data() { + return { + selectedTabIndex: 0, + }; + }, + methods: { + onTabChange(tabName) { + this.selectedTabIndex = CLUSTERS_TABS.findIndex((tab) => tab.queryParamValue === tabName); + }, + }, +}; +</script> +<template> + <div> + <gl-tabs + v-model="selectedTabIndex" + sync-active-tab-with-query-params + nav-class="gl-flex-grow-1 gl-align-items-center" + lazy + > + <gl-tab + v-for="(tab, idx) in $options.CLUSTERS_TABS" + :key="idx" + :title="tab.title" + :query-param-value="tab.queryParamValue" + class="gl-line-height-20 gl-mt-5" + > + <component + :is="tab.component" + :default-branch-name="defaultBranchName" + data-testid="clusters-tab-component" + @changeTab="onTabChange" + /> + </gl-tab> + + <template #tabs-end> + <clusters-actions /> + </template> + </gl-tabs> + + <install-agent-modal :default-branch-name="defaultBranchName" /> + </div> +</template> 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 28da1e146c6..0efd3844cc9 100644 --- a/app/assets/javascripts/clusters_list/components/install_agent_modal.vue +++ b/app/assets/javascripts/clusters_list/components/install_agent_modal.vue @@ -12,9 +12,11 @@ import { helpPagePath } from '~/helpers/help_page_helper'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import CodeBlock from '~/vue_shared/components/code_block.vue'; import { generateAgentRegistrationCommand } from '../clusters_util'; -import { INSTALL_AGENT_MODAL_ID, I18N_INSTALL_AGENT_MODAL } from '../constants'; +import { INSTALL_AGENT_MODAL_ID, I18N_INSTALL_AGENT_MODAL, MAX_LIST_COUNT } from '../constants'; +import { addAgentToStore } 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 AvailableAgentsDropdown from './available_agents_dropdown.vue'; export default { @@ -33,12 +35,20 @@ export default { GlSprintf, }, inject: ['projectPath', 'kasAddress'], + props: { + defaultBranchName: { + default: '.noBranch', + required: false, + type: String, + }, + }, data() { return { registering: false, agentName: null, agentToken: null, error: null, + clusterAgent: null, }; }, computed: { @@ -62,20 +72,24 @@ export default { advancedInstallPath() { return helpPagePath('user/clusters/agent/install/index', { anchor: 'advanced-installation' }); }, + getAgentsQueryVariables() { + return { + defaultBranchName: this.defaultBranchName, + first: MAX_LIST_COUNT, + last: null, + projectPath: this.projectPath, + }; + }, }, methods: { setAgentName(name) { this.agentName = name; }, - cancelClicked() { - this.$refs.modal.hide(); - }, - doneClicked() { - this.$emit('agentRegistered'); + closeModal() { this.$refs.modal.hide(); }, resetModal() { - this.registering = null; + this.registering = false; this.agentName = null; this.agentToken = null; this.error = null; @@ -90,6 +104,14 @@ export default { projectPath: this.projectPath, }, }, + update: (store, { data: { createClusterAgent } }) => { + addAgentToStore( + store, + createClusterAgent, + getAgentsQuery, + this.getAgentsQueryVariables, + ); + }, }) .then(({ data: { createClusterAgent } }) => createClusterAgent); }, @@ -117,6 +139,8 @@ export default { throw new Error(agentErrors[0]); } + this.clusterAgent = clusterAgent; + const { errors: tokenErrors, secret } = await this.createAgentTokenMutation( clusterAgent.id, ); @@ -240,10 +264,10 @@ export default { </template> <template #modal-footer> - <gl-button v-if="canCancel" @click="cancelClicked">{{ $options.i18n.cancel }} </gl-button> + <gl-button v-if="canCancel" @click="closeModal">{{ $options.i18n.cancel }} </gl-button> - <gl-button v-if="registered" variant="confirm" category="primary" @click="doneClicked" - >{{ $options.i18n.done }} + <gl-button v-if="registered" variant="confirm" category="primary" @click="closeModal" + >{{ $options.i18n.close }} </gl-button> <gl-button @@ -252,7 +276,7 @@ export default { variant="confirm" category="primary" @click="registerAgent" - >{{ $options.i18n.next }} + >{{ $options.i18n.registerAgentButton }} </gl-button> </template> </gl-modal> diff --git a/app/assets/javascripts/clusters_list/constants.js b/app/assets/javascripts/clusters_list/constants.js index d77e183f95b..36e6a065cbf 100644 --- a/app/assets/javascripts/clusters_list/constants.js +++ b/app/assets/javascripts/clusters_list/constants.js @@ -3,8 +3,6 @@ import { __, s__, sprintf } from '~/locale'; export const MAX_LIST_COUNT = 25; export const INSTALL_AGENT_MODAL_ID = 'install-agent'; export const ACTIVE_CONNECTION_TIME = 480000; -export const TROUBLESHOOTING_LINK = - 'https://docs.gitlab.com/ee/user/clusters/agent/#troubleshooting'; export const CLUSTER_ERRORS = { default: { @@ -66,8 +64,8 @@ export const STATUSES = { }; export const I18N_INSTALL_AGENT_MODAL = { - next: __('Next'), - done: __('Done'), + registerAgentButton: s__('ClusterAgents|Register Agent'), + close: __('Close'), cancel: __('Cancel'), modalTitle: s__('ClusterAgents|Install new Agent'), @@ -168,3 +166,23 @@ export const I18N_CLUSTERS_EMPTY_STATE = { learnMoreLinkText: s__('ClusterIntegration|Learn more about the GitLab managed clusters'), buttonText: s__('ClusterIntegration|Connect with a certificate'), }; + +export const CLUSTERS_TABS = [ + { + title: s__('ClusterAgents|Agent'), + component: 'agents', + queryParamValue: 'agent', + }, + { + title: s__('ClusterAgents|Certificate based'), + component: 'clusters', + queryParamValue: 'certificate_based', + }, +]; + +export const CLUSTERS_ACTIONS = { + actionsButton: s__('ClusterAgents|Actions'), + createNewCluster: s__('ClusterAgents|Create new cluster'), + connectWithAgent: s__('ClusterAgents|Connect with Agent'), + connectExistingCluster: s__('ClusterAgents|Connect with certificate'), +}; diff --git a/app/assets/javascripts/clusters_list/graphql/cache_update.js b/app/assets/javascripts/clusters_list/graphql/cache_update.js new file mode 100644 index 00000000000..ce6daecc908 --- /dev/null +++ b/app/assets/javascripts/clusters_list/graphql/cache_update.js @@ -0,0 +1,28 @@ +import produce from 'immer'; +import { getAgentConfigPath } from '../clusters_util'; + +export function addAgentToStore(store, createClusterAgent, query, variables) { + const { clusterAgent } = createClusterAgent; + const sourceData = store.readQuery({ + query, + variables, + }); + + const data = produce(sourceData, (draftData) => { + const configuration = { + name: clusterAgent.name, + path: getAgentConfigPath(clusterAgent.name), + webPath: clusterAgent.webPath, + __typename: 'TreeEntry', + }; + + draftData.project.clusterAgents.nodes.push(clusterAgent); + draftData.project.repository.tree.trees.nodes.push(configuration); + }); + + store.writeQuery({ + query, + variables, + data, + }); +} 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 new file mode 100644 index 00000000000..9b40260471c --- /dev/null +++ b/app/assets/javascripts/clusters_list/graphql/fragments/cluster_agent.fragment.graphql @@ -0,0 +1,10 @@ +fragment ClusterAgentFragment on ClusterAgent { + id + name + webPath + tokens { + nodes { + lastUsedAt + } + } +} diff --git a/app/assets/javascripts/clusters_list/graphql/mutations/create_agent.mutation.graphql b/app/assets/javascripts/clusters_list/graphql/mutations/create_agent.mutation.graphql index c29756159f5..996b388089b 100644 --- a/app/assets/javascripts/clusters_list/graphql/mutations/create_agent.mutation.graphql +++ b/app/assets/javascripts/clusters_list/graphql/mutations/create_agent.mutation.graphql @@ -1,7 +1,9 @@ +#import "../fragments/cluster_agent.fragment.graphql" + mutation createClusterAgent($input: CreateClusterAgentInput!) { createClusterAgent(input: $input) { clusterAgent { - id + ...ClusterAgentFragment } errors } 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 61989e00d9e..397895b7d01 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,4 +1,5 @@ #import "~/graphql_shared/fragments/pageInfo.fragment.graphql" +#import "../fragments/cluster_agent.fragment.graphql" query getAgents( $defaultBranchName: String! @@ -13,14 +14,7 @@ query getAgents( project(fullPath: $projectPath) { clusterAgents(first: $first, last: $last, before: $beforeAgent, after: $afterAgent) { nodes { - id - name - webPath - tokens { - nodes { - lastUsedAt - } - } + ...ClusterAgentFragment } pageInfo { diff --git a/app/assets/javascripts/clusters_list/index.js b/app/assets/javascripts/clusters_list/index.js index de18965abbd..7f1ef37814b 100644 --- a/app/assets/javascripts/clusters_list/index.js +++ b/app/assets/javascripts/clusters_list/index.js @@ -1,11 +1,11 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import loadClusters from './load_clusters'; -import loadAgents from './load_agents'; +import loadMainView from './load_main_view'; Vue.use(VueApollo); export default () => { loadClusters(Vue); - loadAgents(Vue, VueApollo); + loadMainView(Vue, VueApollo); }; diff --git a/app/assets/javascripts/clusters_list/load_agents.js b/app/assets/javascripts/clusters_list/load_agents.js deleted file mode 100644 index e65de67515a..00000000000 --- a/app/assets/javascripts/clusters_list/load_agents.js +++ /dev/null @@ -1,44 +0,0 @@ -import createDefaultClient from '~/lib/graphql'; -import Agents from './components/agents.vue'; - -export default (Vue, VueApollo) => { - const el = document.querySelector('#js-cluster-agents-list'); - - if (!el) { - return null; - } - - const defaultClient = createDefaultClient(); - - const { - emptyStateImage, - defaultBranchName, - projectPath, - multipleClustersDocsUrl, - installDocsUrl, - getStartedDocsUrl, - integrationDocsUrl, - kasAddress, - } = el.dataset; - - return new Vue({ - el, - apolloProvider: new VueApollo({ defaultClient }), - provide: { - emptyStateImage, - projectPath, - multipleClustersDocsUrl, - installDocsUrl, - getStartedDocsUrl, - integrationDocsUrl, - kasAddress, - }, - render(createElement) { - return createElement(Agents, { - props: { - defaultBranchName, - }, - }); - }, - }); -}; diff --git a/app/assets/javascripts/clusters_list/load_main_view.js b/app/assets/javascripts/clusters_list/load_main_view.js new file mode 100644 index 00000000000..08c99b46e16 --- /dev/null +++ b/app/assets/javascripts/clusters_list/load_main_view.js @@ -0,0 +1,50 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; +import ClustersMainView from './components/clusters_main_view.vue'; +import { createStore } from './store'; + +Vue.use(VueApollo); + +export default () => { + const el = document.querySelector('.js-clusters-main-view'); + + if (!el) { + return null; + } + + const defaultClient = createDefaultClient(); + + const { + emptyStateImage, + defaultBranchName, + projectPath, + kasAddress, + newClusterPath, + addClusterPath, + emptyStateHelpText, + clustersEmptyStateImage, + } = el.dataset; + + return new Vue({ + el, + apolloProvider: new VueApollo({ defaultClient }), + provide: { + emptyStateImage, + projectPath, + kasAddress, + newClusterPath, + addClusterPath, + emptyStateHelpText, + clustersEmptyStateImage, + }, + store: createStore(el.dataset), + render(createElement) { + return createElement(ClustersMainView, { + props: { + defaultBranchName, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/mr_notes/index.js b/app/assets/javascripts/mr_notes/index.js index ea3e4e5604c..a1377415efe 100644 --- a/app/assets/javascripts/mr_notes/index.js +++ b/app/assets/javascripts/mr_notes/index.js @@ -23,8 +23,8 @@ export default function initMrNotes() { initNotesApp(); document.addEventListener('merged:UpdateActions', () => { - initRevertCommitModal(); - initCherryPickCommitModal(); + initRevertCommitModal('i_code_review_post_merge_submit_revert_modal'); + initCherryPickCommitModal('i_code_review_post_merge_submit_cherry_pick_modal'); }); requestIdleCallback(() => { diff --git a/app/assets/javascripts/projects/commit/components/form_modal.vue b/app/assets/javascripts/projects/commit/components/form_modal.vue index ec7d37644a8..f9dd72119d1 100644 --- a/app/assets/javascripts/projects/commit/components/form_modal.vue +++ b/app/assets/javascripts/projects/commit/components/form_modal.vue @@ -1,6 +1,7 @@ <script> import { GlModal, GlForm, GlFormCheckbox, GlSprintf, GlFormGroup } from '@gitlab/ui'; import { mapActions, mapState } from 'vuex'; +import api from '~/api'; import { BV_SHOW_MODAL } from '~/lib/utils/constants'; import csrf from '~/lib/utils/csrf'; import eventHub from '../event_hub'; @@ -40,6 +41,11 @@ export default { required: false, default: false, }, + primaryActionEventName: { + type: String, + required: false, + default: null, + }, }, data() { return { @@ -83,6 +89,10 @@ export default { this.$root.$emit(BV_SHOW_MODAL, this.modalId); }, handlePrimary() { + if (this.primaryActionEventName) { + api.trackRedisHllUserEvent(this.primaryActionEventName); + } + this.$refs.form.$el.submit(); }, resetModalHandler() { diff --git a/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js index 47ee8237fea..b21fd1a74de 100644 --- a/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js +++ b/app/assets/javascripts/projects/commit/init_cherry_pick_commit_modal.js @@ -9,7 +9,7 @@ import { } from './constants'; import createStore from './store'; -export default function initInviteMembersModal() { +export default function initInviteMembersModal(primaryActionEventName) { const el = document.querySelector('.js-cherry-pick-commit-modal'); if (!el) { return false; @@ -52,6 +52,7 @@ export default function initInviteMembersModal() { openModal: OPEN_CHERRY_PICK_MODAL, modalId: CHERRY_PICK_MODAL_ID, isCherryPick: true, + primaryActionEventName, }, }), }); diff --git a/app/assets/javascripts/projects/commit/init_revert_commit_modal.js b/app/assets/javascripts/projects/commit/init_revert_commit_modal.js index df26aa3c830..849b2f4858c 100644 --- a/app/assets/javascripts/projects/commit/init_revert_commit_modal.js +++ b/app/assets/javascripts/projects/commit/init_revert_commit_modal.js @@ -10,7 +10,7 @@ import { } from './constants'; import createStore from './store'; -export default function initInviteMembersModal() { +export default function initInviteMembersModal(primaryActionEventName) { const el = document.querySelector('.js-revert-commit-modal'); if (!el) { return false; @@ -49,6 +49,7 @@ export default function initInviteMembersModal() { i18n: { ...I18N_REVERT_MODAL, ...I18N_MODAL }, openModal: OPEN_REVERT_MODAL, modalId: REVERT_MODAL_ID, + primaryActionEventName, }, }), }); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue index a1759b1a815..84dac95ce74 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue @@ -1,6 +1,7 @@ <script> /* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlLoadingIcon, GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui'; +import api from '~/api'; import createFlash from '~/flash'; import { s__, __ } from '~/locale'; import { OPEN_REVERT_MODAL, OPEN_CHERRY_PICK_MODAL } from '~/projects/commit/constants'; @@ -83,6 +84,8 @@ export default { removeSourceBranch() { this.isMakingRequest = true; + api.trackRedisHllUserEvent('i_code_review_post_merge_delete_branch'); + this.service .removeSourceBranch() .then((res) => res.data) @@ -103,9 +106,13 @@ export default { }); }, openRevertModal() { + api.trackRedisHllUserEvent('i_code_review_post_merge_click_revert'); + modalEventHub.$emit(OPEN_REVERT_MODAL); }, openCherryPickModal() { + api.trackRedisHllUserEvent('i_code_review_post_merge_click_cherry_pick'); + modalEventHub.$emit(OPEN_CHERRY_PICK_MODAL); }, }, diff --git a/app/assets/javascripts/vue_shared/components/registry/title_area.vue b/app/assets/javascripts/vue_shared/components/registry/title_area.vue index 4b21ec0330a..d108d8d689d 100644 --- a/app/assets/javascripts/vue_shared/components/registry/title_area.vue +++ b/app/assets/javascripts/vue_shared/components/registry/title_area.vue @@ -69,20 +69,23 @@ export default { /> <div class="gl-display-flex gl-flex-direction-column"> - <h1 class="gl-font-size-h1 gl-mt-3 gl-mb-2" data-testid="title"> + <h2 class="gl-font-size-h1 gl-mt-3 gl-mb-0" data-testid="title"> <slot name="title">{{ title }}</slot> - </h1> + </h2> <div v-if="$slots['sub-header']" - class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-1" + class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-mt-3" > <slot name="sub-header"></slot> </div> </div> </div> - <div class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3"> + <div + v-if="metadataSlots.length > 0" + class="gl-display-flex gl-flex-wrap gl-align-items-center gl-mt-3" + > <template v-if="!metadataLoading"> <div v-for="(row, metadataIndex) in metadataSlots" diff --git a/app/assets/stylesheets/pages/clusters.scss b/app/assets/stylesheets/pages/clusters.scss index f54764f8d05..b450bca4f41 100644 --- a/app/assets/stylesheets/pages/clusters.scss +++ b/app/assets/stylesheets/pages/clusters.scss @@ -20,4 +20,23 @@ @include gl-flex-wrap; } } + + .gl-card-body { + @include media-breakpoint-up(sm) { + @include gl-pt-2; + min-height: 372px; + } + } + + @include media-breakpoint-down(xs) { + .nav-controls { + @include gl-w-full; + order: -1; + + .gl-new-dropdown, + .split-content-button { + @include gl-w-full; + } + } + } } diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb index bc464b8809b..93b6b4e8fe2 100644 --- a/app/helpers/clusters_helper.rb +++ b/app/helpers/clusters_helper.rb @@ -16,19 +16,6 @@ module ClustersHelper clusterable.is_a?(Project) end - def js_cluster_agents_list_data(clusterable_project) - { - default_branch_name: clusterable_project.default_branch, - empty_state_image: image_path('illustrations/empty-state/empty-state-agents.svg'), - project_path: clusterable_project.full_path, - multiple_clusters_docs_url: help_page_path('user/project/clusters/multiple_kubernetes_clusters'), - install_docs_url: help_page_path('administration/clusters/kas'), - get_started_docs_url: help_page_path('user/clusters/agent/index', anchor: 'define-a-configuration-repository'), - integration_docs_url: help_page_path('user/clusters/agent/index', anchor: 'get-started-with-gitops-and-the-gitlab-agent'), - kas_address: Gitlab::Kas.external_url - } - end - def js_clusters_list_data(clusterable) { ancestor_help_path: help_page_path('user/group/clusters/index', anchor: 'cluster-precedence'), @@ -45,6 +32,16 @@ module ClustersHelper } end + def js_clusters_data(clusterable) + { + default_branch_name: clusterable.default_branch, + empty_state_image: image_path('illustrations/empty-state/empty-state-agents.svg'), + project_path: clusterable.full_path, + add_cluster_path: clusterable.new_path(tab: 'add'), + kas_address: Gitlab::Kas.external_url + }.merge(js_clusters_list_data(clusterable)) + end + def js_cluster_form_data(cluster, can_edit) { enabled: cluster.enabled?.to_s, diff --git a/app/helpers/routing/pseudonymization_helper.rb b/app/helpers/routing/pseudonymization_helper.rb index 58cbf8dbdea..ac30669dc83 100644 --- a/app/helpers/routing/pseudonymization_helper.rb +++ b/app/helpers/routing/pseudonymization_helper.rb @@ -3,10 +3,7 @@ module Routing module PseudonymizationHelper class MaskHelper - QUERY_PARAMS_TO_MASK = %w[ - assignee_username - author_username - ].freeze + QUERY_PARAMS_TO_NOT_MASK = %w[].freeze def initialize(request_object, group, project) @request = request_object @@ -58,10 +55,10 @@ module Routing query_string_hash = Rack::Utils.parse_nested_query(@request.query_string) - QUERY_PARAMS_TO_MASK.each do |maskable_attribute| - next unless query_string_hash.has_key?(maskable_attribute) + query_string_hash.keys.each do |key| + next if QUERY_PARAMS_TO_NOT_MASK.include?(key) - query_string_hash[maskable_attribute] = "masked_#{maskable_attribute}" + query_string_hash[key] = "masked_#{key}" end query_string_hash diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 7737614ae25..0f7b6388441 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,6 +1,10 @@ # frozen_string_literal: true class ApplicationRecord < ActiveRecord::Base + include DatabaseReflection + include Transactions + include LegacyBulkInsert + self.abstract_class = true alias_method :reset, :reload diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 028279b6150..54ec8b2c3e4 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -244,11 +244,11 @@ module ApplicationSettingImplementation end def home_page_url_column_exists? - ::Gitlab::Database.main.cached_column_exists?(:application_settings, :home_page_url) + ApplicationSetting.database.cached_column_exists?(:home_page_url) end def help_page_support_url_column_exists? - ::Gitlab::Database.main.cached_column_exists?(:application_settings, :help_page_support_url) + ApplicationSetting.database.cached_column_exists?(:help_page_support_url) end def disabled_oauth_sign_in_sources=(sources) diff --git a/app/models/concerns/cascading_namespace_setting_attribute.rb b/app/models/concerns/cascading_namespace_setting_attribute.rb index c70c6dca105..731729a1ed5 100644 --- a/app/models/concerns/cascading_namespace_setting_attribute.rb +++ b/app/models/concerns/cascading_namespace_setting_attribute.rb @@ -127,7 +127,7 @@ module CascadingNamespaceSettingAttribute end def alias_boolean(attribute) - return unless Gitlab::Database.main.exists? && type_for_attribute(attribute).type == :boolean + return unless database.exists? && type_for_attribute(attribute).type == :boolean alias_method :"#{attribute}?", attribute end diff --git a/app/models/concerns/database_reflection.rb b/app/models/concerns/database_reflection.rb new file mode 100644 index 00000000000..1842f5bf4ec --- /dev/null +++ b/app/models/concerns/database_reflection.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# A module that makes it easier/less verbose to reflect upon a database +# connection. +# +# Using this module you can write this: +# +# User.database.database_name +# +# Instead of this: +# +# Gitlab::Database::Reflection.new(User).database_name +module DatabaseReflection + extend ActiveSupport::Concern + + class_methods do + def database + @database_reflection ||= ::Gitlab::Database::Reflection.new(self) + end + end +end diff --git a/app/models/concerns/legacy_bulk_insert.rb b/app/models/concerns/legacy_bulk_insert.rb new file mode 100644 index 00000000000..1249dfb70cd --- /dev/null +++ b/app/models/concerns/legacy_bulk_insert.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module LegacyBulkInsert + extend ActiveSupport::Concern + + class_methods do + # Bulk inserts a number of rows into a table, optionally returning their + # IDs. + # + # This method is deprecated, and you should use the BulkInsertSafe module + # instead. + # + # table - The name of the table to insert the rows into. + # rows - An Array of Hash instances, each mapping the columns to their + # values. + # return_ids - When set to true the return value will be an Array of IDs of + # the inserted rows + # disable_quote - A key or an Array of keys to exclude from quoting (You + # become responsible for protection from SQL injection for + # these keys!) + # on_conflict - Defines an upsert. Values can be: :disabled (default) or + # :do_nothing + def legacy_bulk_insert(table, rows, return_ids: false, disable_quote: [], on_conflict: nil) + return if rows.empty? + + keys = rows.first.keys + columns = keys.map { |key| connection.quote_column_name(key) } + + disable_quote = Array(disable_quote).to_set + tuples = rows.map do |row| + keys.map do |k| + disable_quote.include?(k) ? row[k] : connection.quote(row[k]) + end + end + + sql = <<-EOF + INSERT INTO #{table} (#{columns.join(', ')}) + VALUES #{tuples.map { |tuple| "(#{tuple.join(', ')})" }.join(', ')} + EOF + + sql = "#{sql} ON CONFLICT DO NOTHING" if on_conflict == :do_nothing + + sql = "#{sql} RETURNING id" if return_ids + + result = connection.execute(sql) + + if return_ids + result.values.map { |tuple| tuple[0].to_i } + else + [] + end + end + end +end diff --git a/app/models/concerns/sha256_attribute.rb b/app/models/concerns/sha256_attribute.rb index 17fda6c806c..3c906642b1a 100644 --- a/app/models/concerns/sha256_attribute.rb +++ b/app/models/concerns/sha256_attribute.rb @@ -39,7 +39,7 @@ module Sha256Attribute end def database_exists? - Gitlab::Database.main.exists? + database.exists? end end end diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb index 27277bc5296..ba7c6c0cd8b 100644 --- a/app/models/concerns/sha_attribute.rb +++ b/app/models/concerns/sha_attribute.rb @@ -32,7 +32,7 @@ module ShaAttribute end def database_exists? - Gitlab::Database.main.exists? + database.exists? end end end diff --git a/app/models/concerns/transactions.rb b/app/models/concerns/transactions.rb new file mode 100644 index 00000000000..a186ebc8475 --- /dev/null +++ b/app/models/concerns/transactions.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Transactions + extend ActiveSupport::Concern + + class_methods do + # inside_transaction? will return true if the caller is running within a + # transaction. Handles special cases when running inside a test environment, + # where tests may be wrapped in transactions + def inside_transaction? + base = Rails.env.test? ? @open_transactions_baseline.to_i : 0 + + connection.open_transactions > base + end + + # These methods that access @open_transactions_baseline are not thread-safe. + # These are fine though because we only call these in RSpec's main thread. + # If we decide to run specs multi-threaded, we would need to use something + # like ThreadGroup to keep track of this value + def set_open_transactions_baseline + @open_transactions_baseline = connection.open_transactions + end + + def reset_open_transactions_baseline + @open_transactions_baseline = 0 + end + end +end diff --git a/app/models/concerns/x509_serial_number_attribute.rb b/app/models/concerns/x509_serial_number_attribute.rb index dfb1e151b41..e51ed95bf70 100644 --- a/app/models/concerns/x509_serial_number_attribute.rb +++ b/app/models/concerns/x509_serial_number_attribute.rb @@ -39,7 +39,7 @@ module X509SerialNumberAttribute end def database_exists? - Gitlab::Database.main.exists? + database.exists? end end end diff --git a/app/models/deployment.rb b/app/models/deployment.rb index f7a7f7a4a5f..5fddc661602 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -300,7 +300,7 @@ class Deployment < ApplicationRecord "#{id} as deployment_id", "#{environment_id} as environment_id").to_sql - # We don't use `Gitlab::Database.main.bulk_insert` here so that we don't need to + # We don't use `ApplicationRecord.legacy_bulk_insert` here so that we don't need to # first pluck lots of IDs into memory. # # We also ignore any duplicates so this method can be called multiple times diff --git a/app/models/design_management/version.rb b/app/models/design_management/version.rb index 6cda03557d1..5819404efb9 100644 --- a/app/models/design_management/version.rb +++ b/app/models/design_management/version.rb @@ -88,7 +88,7 @@ module DesignManagement rows = design_actions.map { |action| action.row_attrs(version) } - Gitlab::Database.main.bulk_insert(::DesignManagement::Action.table_name, rows) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert(::DesignManagement::Action.table_name, rows) # rubocop:disable Gitlab/BulkInsert version.designs.reset version.validate! design_actions.each(&:performed) diff --git a/app/models/merge_request_context_commit.rb b/app/models/merge_request_context_commit.rb index 09824ed4468..ebbdecf8aa7 100644 --- a/app/models/merge_request_context_commit.rb +++ b/app/models/merge_request_context_commit.rb @@ -26,7 +26,7 @@ class MergeRequestContextCommit < ApplicationRecord # create MergeRequestContextCommit by given commit sha and it's diff file record def self.bulk_insert(rows, **args) - Gitlab::Database.main.bulk_insert('merge_request_context_commits', rows, **args) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert('merge_request_context_commits', rows, **args) # rubocop:disable Gitlab/BulkInsert end def to_commit diff --git a/app/models/merge_request_context_commit_diff_file.rb b/app/models/merge_request_context_commit_diff_file.rb index b9efebe3af2..fdf57068928 100644 --- a/app/models/merge_request_context_commit_diff_file.rb +++ b/app/models/merge_request_context_commit_diff_file.rb @@ -14,7 +14,7 @@ class MergeRequestContextCommitDiffFile < ApplicationRecord # create MergeRequestContextCommitDiffFile by given diff file record(s) def self.bulk_insert(*args) - Gitlab::Database.main.bulk_insert('merge_request_context_commit_diff_files', *args) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert('merge_request_context_commit_diff_files', *args) # rubocop:disable Gitlab/BulkInsert end def path diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 13ef47bec45..2516ff05bda 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -515,7 +515,7 @@ class MergeRequestDiff < ApplicationRecord transaction do MergeRequestDiffFile.where(merge_request_diff_id: id).delete_all - Gitlab::Database.main.bulk_insert('merge_request_diff_files', rows) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert('merge_request_diff_files', rows) # rubocop:disable Gitlab/BulkInsert save! end @@ -535,7 +535,7 @@ class MergeRequestDiff < ApplicationRecord transaction do MergeRequestDiffFile.where(merge_request_diff_id: id).delete_all - Gitlab::Database.main.bulk_insert('merge_request_diff_files', rows) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert('merge_request_diff_files', rows) # rubocop:disable Gitlab/BulkInsert update!(stored_externally: false) end @@ -595,7 +595,7 @@ class MergeRequestDiff < ApplicationRecord rows = build_external_merge_request_diff_files(rows) if use_external_diff? # Faster inserts - Gitlab::Database.main.bulk_insert('merge_request_diff_files', rows) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert('merge_request_diff_files', rows) # rubocop:disable Gitlab/BulkInsert end def build_external_diff_tempfile(rows) diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb index b1cae0d1e49..66f1e45fd49 100644 --- a/app/models/merge_request_diff_commit.rb +++ b/app/models/merge_request_diff_commit.rb @@ -74,7 +74,7 @@ class MergeRequestDiffCommit < ApplicationRecord ) end - Gitlab::Database.main.bulk_insert(self.table_name, rows) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert(self.table_name, rows) # rubocop:disable Gitlab/BulkInsert end def self.prepare_commits_for_bulk_insert(commits) diff --git a/app/services/design_management/copy_design_collection/copy_service.rb b/app/services/design_management/copy_design_collection/copy_service.rb index c43696442d2..5e557e9ea53 100644 --- a/app/services/design_management/copy_design_collection/copy_service.rb +++ b/app/services/design_management/copy_design_collection/copy_service.rb @@ -181,12 +181,12 @@ module DesignManagement ) end - # TODO Replace `Gitlab::Database.main.bulk_insert` with `BulkInsertSafe` + # TODO Replace `ApplicationRecord.legacy_bulk_insert` with `BulkInsertSafe` # once https://gitlab.com/gitlab-org/gitlab/-/issues/247718 is fixed. # When this is fixed, we can remove the call to # `with_project_iid_supply` above, since the objects will be instantiated # and callbacks (including `ensure_project_iid!`) will fire. - ::Gitlab::Database.main.bulk_insert( # rubocop:disable Gitlab/BulkInsert + ::ApplicationRecord.legacy_bulk_insert( # rubocop:disable Gitlab/BulkInsert DesignManagement::Design.table_name, new_rows, return_ids: true @@ -207,9 +207,9 @@ module DesignManagement ) end - # TODO Replace `Gitlab::Database.main.bulk_insert` with `BulkInsertSafe` + # TODO Replace `ApplicationRecord.legacy_bulk_insert` with `BulkInsertSafe` # once https://gitlab.com/gitlab-org/gitlab/-/issues/247718 is fixed. - ::Gitlab::Database.main.bulk_insert( # rubocop:disable Gitlab/BulkInsert + ::ApplicationRecord.legacy_bulk_insert( # rubocop:disable Gitlab/BulkInsert DesignManagement::Version.table_name, new_rows, return_ids: true @@ -239,7 +239,7 @@ module DesignManagement end # We cannot use `BulkInsertSafe` because of the uploader mounted in `Action`. - ::Gitlab::Database.main.bulk_insert( # rubocop:disable Gitlab/BulkInsert + ::ApplicationRecord.legacy_bulk_insert( # rubocop:disable Gitlab/BulkInsert DesignManagement::Action.table_name, new_rows ) @@ -278,7 +278,7 @@ module DesignManagement # We cannot use `BulkInsertSafe` due to the LfsObjectsProject#update_project_statistics # callback that fires after_commit. - ::Gitlab::Database.main.bulk_insert( # rubocop:disable Gitlab/BulkInsert + ::ApplicationRecord.legacy_bulk_insert( # rubocop:disable Gitlab/BulkInsert LfsObjectsProject.table_name, new_rows, on_conflict: :do_nothing # Upsert diff --git a/app/services/issuable/clone/attributes_rewriter.rb b/app/services/issuable/clone/attributes_rewriter.rb index d8b639bb422..279d3051848 100644 --- a/app/services/issuable/clone/attributes_rewriter.rb +++ b/app/services/issuable/clone/attributes_rewriter.rb @@ -99,7 +99,7 @@ module Issuable yield(event) end.compact - Gitlab::Database.main.bulk_insert(table_name, events) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert(table_name, events) # rubocop:disable Gitlab/BulkInsert end end diff --git a/app/services/packages/create_dependency_service.rb b/app/services/packages/create_dependency_service.rb index 2c80ec66dbc..10a86e44cb0 100644 --- a/app/services/packages/create_dependency_service.rb +++ b/app/services/packages/create_dependency_service.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true module Packages + # rubocop: disable Gitlab/BulkInsert class CreateDependencyService < BaseService attr_reader :package, :dependencies @@ -51,7 +52,7 @@ module Packages } end - ids = database.bulk_insert(Packages::Dependency.table_name, rows, return_ids: true, on_conflict: :do_nothing) + ids = ApplicationRecord.legacy_bulk_insert(Packages::Dependency.table_name, rows, return_ids: true, on_conflict: :do_nothing) return ids if ids.size == names_and_version_patterns.size Packages::Dependency.uncached do @@ -72,11 +73,8 @@ module Packages } end - database.bulk_insert(Packages::DependencyLink.table_name, rows) - end - - def database - ::Gitlab::Database.main + ApplicationRecord.legacy_bulk_insert(Packages::DependencyLink.table_name, rows) end end + # rubocop: enable Gitlab/BulkInsert end diff --git a/app/services/packages/nuget/create_dependency_service.rb b/app/services/packages/nuget/create_dependency_service.rb index 3fc42056d43..85f295ac7b7 100644 --- a/app/services/packages/nuget/create_dependency_service.rb +++ b/app/services/packages/nuget/create_dependency_service.rb @@ -41,7 +41,7 @@ module Packages } end - ::Gitlab::Database.main.bulk_insert(::Packages::Nuget::DependencyLinkMetadatum.table_name, rows.compact) # rubocop:disable Gitlab/BulkInsert + ::ApplicationRecord.legacy_bulk_insert(::Packages::Nuget::DependencyLinkMetadatum.table_name, rows.compact) # rubocop:disable Gitlab/BulkInsert end def raw_dependency_for(dependency) diff --git a/app/services/packages/update_tags_service.rb b/app/services/packages/update_tags_service.rb index 2bdf75a6617..f29c54dacb9 100644 --- a/app/services/packages/update_tags_service.rb +++ b/app/services/packages/update_tags_service.rb @@ -15,7 +15,7 @@ module Packages tags_to_create = @tags - existing_tags @package.tags.with_name(tags_to_destroy).delete_all if tags_to_destroy.any? - ::Gitlab::Database.main.bulk_insert(Packages::Tag.table_name, rows(tags_to_create)) if tags_to_create.any? # rubocop:disable Gitlab/BulkInsert + ::ApplicationRecord.legacy_bulk_insert(Packages::Tag.table_name, rows(tags_to_create)) if tags_to_create.any? # rubocop:disable Gitlab/BulkInsert end private diff --git a/app/services/projects/detect_repository_languages_service.rb b/app/services/projects/detect_repository_languages_service.rb index 0356a6b0ccd..9db0b71d106 100644 --- a/app/services/projects/detect_repository_languages_service.rb +++ b/app/services/projects/detect_repository_languages_service.rb @@ -21,7 +21,7 @@ module Projects .update_all(share: update[:share]) end - Gitlab::Database.main.bulk_insert( # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert( # rubocop:disable Gitlab/BulkInsert RepositoryLanguage.table_name, detection.insertions(matching_programming_languages) ) diff --git a/app/services/projects/lfs_pointers/lfs_link_service.rb b/app/services/projects/lfs_pointers/lfs_link_service.rb index 7c00b9e6105..cf3cc5cd8e0 100644 --- a/app/services/projects/lfs_pointers/lfs_link_service.rb +++ b/app/services/projects/lfs_pointers/lfs_link_service.rb @@ -38,7 +38,7 @@ module Projects rows = existent_lfs_objects .not_linked_to_project(project) .map { |existing_lfs_object| { project_id: project.id, lfs_object_id: existing_lfs_object.id } } - Gitlab::Database.main.bulk_insert(:lfs_objects_projects, rows) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert(:lfs_objects_projects, rows) # rubocop:disable Gitlab/BulkInsert iterations += 1 linked_existing_objects += existent_lfs_objects.map(&:oid) diff --git a/app/services/resource_events/change_labels_service.rb b/app/services/resource_events/change_labels_service.rb index bc2d3a946cc..03ac839c509 100644 --- a/app/services/resource_events/change_labels_service.rb +++ b/app/services/resource_events/change_labels_service.rb @@ -23,7 +23,7 @@ module ResourceEvents label_hash.merge(label_id: label.id, action: ResourceLabelEvent.actions['remove']) end - Gitlab::Database.main.bulk_insert(ResourceLabelEvent.table_name, labels) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert(ResourceLabelEvent.table_name, labels) # rubocop:disable Gitlab/BulkInsert resource.expire_note_etag_cache Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_label_changed_action(author: user) if resource.is_a?(Issue) diff --git a/app/services/suggestions/create_service.rb b/app/services/suggestions/create_service.rb index eb98ed57d55..239cd86e0ec 100644 --- a/app/services/suggestions/create_service.rb +++ b/app/services/suggestions/create_service.rb @@ -25,7 +25,7 @@ module Suggestions end rows.in_groups_of(100, false) do |rows| - Gitlab::Database.main.bulk_insert('suggestions', rows) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert('suggestions', rows) # rubocop:disable Gitlab/BulkInsert end Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_add_suggestion_action(note: @note) diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 681e7ccb613..4197d5b961f 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -153,9 +153,9 @@ %span.float-right #{Rails::VERSION::STRING} %p - = Gitlab::Database.main.human_adapter_name + = ApplicationRecord.database.human_adapter_name %span.float-right - = Gitlab::Database.main.version + = ApplicationRecord.database.version %p = _('Redis') %span.float-right diff --git a/app/views/admin/users/_tabs.html.haml b/app/views/admin/users/_tabs.html.haml index 90f06eeaf3f..6c14e1189fe 100644 --- a/app/views/admin/users/_tabs.html.haml +++ b/app/views/admin/users/_tabs.html.haml @@ -1,7 +1,3 @@ -%ul.nav-links.nav-tabs.nav.js-users-tabs{ role: 'tablist' } - %li.nav-item{ role: 'presentation' } - %a.nav-link{ href: admin_users_path, class: active_when(current_page?(admin_users_path)), role: 'tab' } - = s_('AdminUsers|Users') - %li.nav-item{ role: 'presentation' } - %a.nav-link{ href: admin_cohorts_path, class: active_when(current_page?(admin_cohorts_path)), role: 'tab' } - = s_('AdminUsers|Cohorts') += gl_tabs_nav({ class: 'js-users-tabs' }) do + = gl_tab_link_to s_('AdminUsers|Users'), admin_users_path + = gl_tab_link_to s_('AdminUsers|Cohorts'), admin_cohorts_path diff --git a/app/views/clusters/clusters/index.html.haml b/app/views/clusters/clusters/index.html.haml index 45287a01cc9..457e34b306a 100644 --- a/app/views/clusters/clusters/index.html.haml +++ b/app/views/clusters/clusters/index.html.haml @@ -3,24 +3,10 @@ = render_gcp_signup_offer -.clusters-container.gl-my-2 +.clusters-container - if display_cluster_agents?(clusterable) - .js-toggle-container - %ul.nav-links.nav-tabs.nav{ role: 'tablist' } - %li.nav-item{ role: 'presentation' } - %a.nav-link.active{ href: "#certificate-clusters-pane", id: "certificate-clusters-tab", data: { toggle: 'tab' }, role: 'tab' } - %span= s_('ClusterIntegration|Clusters connected with a certificate') - - %li.nav-item{ role: 'presentation' } - %a.nav-link{ href: "#agent-clusters-pane", id: "agent-clusters-tab", data: { toggle: 'tab' }, role: 'tab' } - %span= s_('ClusterIntegration|GitLab Agent managed clusters') - - .tab-content - .tab-pane.active{ id: 'certificate-clusters-pane', role: 'tabpanel' } - = render 'cluster_list', clusters: @clusters - - .tab-pane{ id: 'agent-clusters-pane', role: 'tabpanel' } - #js-cluster-agents-list{ data: js_cluster_agents_list_data(clusterable) } + .gl-my-6 + .js-clusters-main-view{ data: js_clusters_data(clusterable) } - else = render 'cluster_list', clusters: @clusters diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml index eb9caa796b5..0d904de9372 100644 --- a/app/views/users/_overview.html.haml +++ b/app/views/users/_overview.html.haml @@ -10,21 +10,20 @@ = s_('UserProfile|Retry') .user-calendar-activities - if @user.user_readme - .row - .col-12.gl-my-6 - .gl-border-gray-100.gl-border-1.gl-border-solid.gl-rounded-small.gl-py-4.gl-px-6 - .gl-display-flex - %ol.breadcrumb.gl-breadcrumb-list.gl-mb-4 - %li.breadcrumb-item.gl-breadcrumb-item - = link_to @user.username, project_path(@user.user_project) - %span.gl-breadcrumb-separator - = sprite_icon("chevron-right", size: 16) - %li.breadcrumb-item.gl-breadcrumb-item - = link_to @user.user_readme.path, @user.user_project.readme_url - - if current_user == @user - .gl-ml-auto - = link_to _('Edit'), edit_blob_path(@user.user_project, @user.user_project.default_branch, @user.user_readme.path) - = render 'projects/blob/viewer', viewer: @user.user_readme.rich_viewer, load_async: false + .row.justify-content-center + .col-12.col-md-10.col-lg-8.gl-my-6 + .gl-display-flex + %ol.breadcrumb.gl-breadcrumb-list.gl-mb-4 + %li.breadcrumb-item.gl-breadcrumb-item + = link_to @user.username, project_path(@user.user_project) + %span.gl-breadcrumb-separator + = sprite_icon("chevron-right", size: 16) + %li.breadcrumb-item.gl-breadcrumb-item + = link_to @user.user_readme.path, @user.user_project.readme_url + - if current_user == @user + .gl-ml-auto + = link_to _('Edit'), edit_blob_path(@user.user_project, @user.user_project.default_branch, @user.user_readme.path) + = render 'projects/blob/viewer', viewer: @user.user_readme.rich_viewer, load_async: false .row %div{ class: activity_pane_class } - if can?(current_user, :read_cross_project) diff --git a/app/workers/gitlab/jira_import/import_issue_worker.rb b/app/workers/gitlab/jira_import/import_issue_worker.rb index eabe7328b92..3824cc1f3ef 100644 --- a/app/workers/gitlab/jira_import/import_issue_worker.rb +++ b/app/workers/gitlab/jira_import/import_issue_worker.rb @@ -54,7 +54,7 @@ module Gitlab label_link_attrs << build_label_attrs(issue_id, import_label_id.to_i) - Gitlab::Database.main.bulk_insert(LabelLink.table_name, label_link_attrs) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert(LabelLink.table_name, label_link_attrs) # rubocop:disable Gitlab/BulkInsert end def assign_issue(project_id, issue_id, assignee_ids) @@ -62,7 +62,7 @@ module Gitlab assignee_attrs = assignee_ids.map { |user_id| { issue_id: issue_id, user_id: user_id } } - Gitlab::Database.main.bulk_insert(IssueAssignee.table_name, assignee_attrs) # rubocop:disable Gitlab/BulkInsert + ApplicationRecord.legacy_bulk_insert(IssueAssignee.table_name, assignee_attrs) # rubocop:disable Gitlab/BulkInsert end def build_label_attrs(issue_id, label_id) |