From ab15b68754fae11e160321fea4f018891a08b3b3 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 7 Sep 2022 12:12:40 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../behaviors/shortcuts/shortcuts_issuable.js | 30 ++++++++----- .../board_card_move_to_position.vue | 31 ++++---------- .../boards/components/board_list.vue | 13 ++++-- .../javascripts/boards/stores/actions.js | 25 ++++++++++- .../boards/stores/mutation_types.js | 1 + .../javascripts/boards/stores/mutations.js | 41 ++++++++++++++++-- app/assets/javascripts/boards/stores/state.js | 1 + app/models/ci/bridge.rb | 2 +- app/models/ci/build.rb | 3 +- app/models/ci/partition.rb | 6 +++ .../notifications/_email_settings.html.haml | 2 +- .../profiles/notifications/show.html.haml | 2 +- .../20210216182006_source_code_pushes.yml | 2 +- db/docs/ci_partitions.yml | 9 ++++ .../20220902065314_create_ci_partitions.rb | 9 ++++ ...2065316_create_default_partition_record.rb | 21 ++++++++++ ...902065317_add_partition_id_to_ci_builds.rb | 11 +++++ ..._add_partition_id_to_ci_builds_metadata.rb | 9 ++++ ...11_add_partition_id_to_ci_job_artifacts.rb | 9 ++++ ...065623_add_partition_id_to_ci_pipelines.rb | 9 ++++ ...902065635_add_partition_id_to_ci_stages.rb | 9 ++++ ...d_partition_id_to_ci_pipeline_variables.rb | 9 ++++ db/schema_migrations/20220902065314 | 1 + db/schema_migrations/20220902065316 | 1 + db/schema_migrations/20220902065317 | 1 + db/schema_migrations/20220902065558 | 1 + db/schema_migrations/20220902065611 | 1 + db/schema_migrations/20220902065623 | 1 + db/schema_migrations/20220902065635 | 1 + db/schema_migrations/20220902065647 | 1 + db/structure.sql | 30 ++++++++++++- doc/api/members.md | 24 ++++------- doc/ci/mobile_devops.md | 42 +++++++++++++++++++ doc/development/service_ping/implement.md | 2 +- .../service_ping/metrics_instrumentation.md | 10 +++-- lib/gitlab/database/gitlab_schemas.yml | 1 + .../metrics/instrumentations/redis_metric.rb | 22 ++++++---- locale/gitlab.pot | 12 ++++++ package.json | 4 +- spec/db/schema_spec.rb | 7 +++- .../board_card_move_to_position_spec.js | 38 ++++++----------- spec/frontend/boards/stores/actions_spec.js | 38 ++++++++++++++++- spec/frontend/boards/stores/mutations_spec.js | 25 +++++++++++ .../ide/stores/modules/commit/actions_spec.js | 2 +- .../instrumentations/redis_metric_spec.rb | 8 ++-- yarn.lock | 16 +++---- 46 files changed, 420 insertions(+), 123 deletions(-) create mode 100644 app/models/ci/partition.rb create mode 100644 db/docs/ci_partitions.yml create mode 100644 db/migrate/20220902065314_create_ci_partitions.rb create mode 100644 db/migrate/20220902065316_create_default_partition_record.rb create mode 100644 db/migrate/20220902065317_add_partition_id_to_ci_builds.rb create mode 100644 db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb create mode 100644 db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb create mode 100644 db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb create mode 100644 db/migrate/20220902065635_add_partition_id_to_ci_stages.rb create mode 100644 db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb create mode 100644 db/schema_migrations/20220902065314 create mode 100644 db/schema_migrations/20220902065316 create mode 100644 db/schema_migrations/20220902065317 create mode 100644 db/schema_migrations/20220902065558 create mode 100644 db/schema_migrations/20220902065611 create mode 100644 db/schema_migrations/20220902065623 create mode 100644 db/schema_migrations/20220902065635 create mode 100644 db/schema_migrations/20220902065647 create mode 100644 doc/ci/mobile_devops.md diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js index 82229b5aa8f..97ba9e15c0f 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js @@ -1,9 +1,11 @@ import $ from 'jquery'; +import ClipboardJS from 'clipboard'; import Mousetrap from 'mousetrap'; -import { clickCopyToClipboardButton } from '~/behaviors/copy_to_clipboard'; import { getSelectedFragment } from '~/lib/utils/common_utils'; import { isElementVisible } from '~/lib/utils/dom_utils'; import { DEBOUNCE_DROPDOWN_DELAY } from '~/vue_shared/components/sidebar/labels_select_widget/constants'; +import toast from '~/vue_shared/plugins/global_toast'; +import { s__ } from '~/locale'; import Sidebar from '~/right_sidebar'; import { CopyAsGFM } from '../markdown/copy_as_gfm'; import { @@ -21,6 +23,15 @@ export default class ShortcutsIssuable extends Shortcuts { constructor() { super(); + this.inMemoryButton = document.createElement('button'); + this.clipboardInstance = new ClipboardJS(this.inMemoryButton); + this.clipboardInstance.on('success', () => { + toast(s__('GlobalShortcuts|Copied source branch name to clipboard.')); + }); + this.clipboardInstance.on('error', () => { + toast(s__('GlobalShortcuts|Unable to copy the source branch name at this time.')); + }); + Mousetrap.bind(keysFor(ISSUE_MR_CHANGE_ASSIGNEE), () => ShortcutsIssuable.openSidebarDropdown('assignee'), ); @@ -32,7 +43,7 @@ export default class ShortcutsIssuable extends Shortcuts { ); Mousetrap.bind(keysFor(ISSUABLE_COMMENT_OR_REPLY), ShortcutsIssuable.replyWithSelectedText); Mousetrap.bind(keysFor(ISSUABLE_EDIT_DESCRIPTION), ShortcutsIssuable.editIssue); - Mousetrap.bind(keysFor(MR_COPY_SOURCE_BRANCH_NAME), ShortcutsIssuable.copyBranchName); + Mousetrap.bind(keysFor(MR_COPY_SOURCE_BRANCH_NAME), () => this.copyBranchName()); /** * We're attaching a global focus event listener on document for @@ -153,17 +164,14 @@ export default class ShortcutsIssuable extends Shortcuts { return false; } - static copyBranchName() { - // There are two buttons - one that is shown when the sidebar - // is expanded, and one that is shown when it's collapsed. - const allCopyBtns = Array.from(document.querySelectorAll('.js-source-branch-copy')); + async copyBranchName() { + const button = document.querySelector('.js-source-branch-copy'); + const branchName = button?.dataset.clipboardText; - // Select whichever button is currently visible so that - // the "Copied" tooltip is shown when a click is simulated. - const visibleBtn = allCopyBtns.find(isElementVisible); + if (branchName) { + this.inMemoryButton.dataset.clipboardText = branchName; - if (visibleBtn) { - clickCopyToClipboardButton(visibleBtn); + this.inMemoryButton.dispatchEvent(new CustomEvent('click')); } } } diff --git a/app/assets/javascripts/boards/components/board_card_move_to_position.vue b/app/assets/javascripts/boards/components/board_card_move_to_position.vue index a0cc3756fc4..ff938219475 100644 --- a/app/assets/javascripts/boards/components/board_card_move_to_position.vue +++ b/app/assets/javascripts/boards/components/board_card_move_to_position.vue @@ -48,21 +48,12 @@ export default { listHasNextPage() { return this.pageInfoByListId[this.list.id]?.hasNextPage; }, - firstItemInListId() { - return this.listItems[0]?.id; - }, lengthOfListItemsInBoard() { return this.listItems?.length; }, - lastItemInTheListId() { - return this.listItems[this.lengthOfListItemsInBoard - 1]?.id; - }, itemIdentifier() { return `${this.item.id}-${this.item.iid}-${this.index}`; }, - showMoveToEndOfList() { - return !this.listHasNextPage; - }, isFirstItemInList() { return this.index === 0; }, @@ -80,9 +71,8 @@ export default { if (this.isFirstItemInList) { return; } - const moveAfterId = this.firstItemInListId; this.moveToPosition({ - moveAfterId, + positionInList: 0, }); }, moveToEnd() { @@ -93,20 +83,20 @@ export default { if (this.isLastItemInList) { return; } - const moveBeforeId = this.lastItemInTheListId; this.moveToPosition({ - moveBeforeId, + positionInList: -1, }); }, - moveToPosition({ moveAfterId, moveBeforeId }) { + moveToPosition({ positionInList }) { this.moveItem({ itemId: this.item.id, itemIid: this.item.iid, itemPath: this.item.referencePath, fromListId: this.list.id, toListId: this.list.id, - moveAfterId, - moveBeforeId, + positionInList, + atIndex: this.index, + allItemsLoadedInList: !this.listHasNextPage, }); }, }, @@ -117,7 +107,6 @@ export default {
- + {{ $options.i18n.moveToStartText }} - + {{ $options.i18n.moveToEndText }}
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue index 93835519033..edf1a5ee7e6 100644 --- a/app/assets/javascripts/boards/components/board_list.vue +++ b/app/assets/javascripts/boards/components/board_list.vue @@ -66,7 +66,7 @@ export default { }, }, computed: { - ...mapState(['pageInfoByListId', 'listsFlags', 'filterParams']), + ...mapState(['pageInfoByListId', 'listsFlags', 'filterParams', 'isUpdateIssueOrderInProgress']), ...mapGetters(['isEpicBoard']), listItemsCount() { return this.isEpicBoard ? this.list.epicsCount : this.boardList?.issuesCount; @@ -132,6 +132,9 @@ export default { return this.canMoveIssue ? options : {}; }, + disableScrollingWhenMutationInProgress() { + return this.hasNextPage && this.isUpdateIssueOrderInProgress; + }, }, watch: { boardItems() { @@ -285,9 +288,13 @@ export default { v-bind="treeRootOptions" :data-board="list.id" :data-board-type="list.listType" - :class="{ 'bg-danger-100': boardItemsSizeExceedsMax }" + :class="{ + 'bg-danger-100': boardItemsSizeExceedsMax, + 'gl-overflow-hidden': disableScrollingWhenMutationInProgress, + 'gl-overflow-y-auto': !disableScrollingWhenMutationInProgress, + }" draggable=".board-card" - class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-3 gl-pt-0 gl-overflow-y-auto gl-overflow-x-hidden" + class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-3 gl-pt-0 gl-overflow-x-hidden" data-testid="tree-root-wrapper" @start="handleDragOnStart" @end="handleDragOnEnd" diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index f84274104b2..c2e346da606 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -479,16 +479,25 @@ export default { toListId, moveBeforeId, moveAfterId, + positionInList, + allItemsLoadedInList, } = moveData; commit(types.REMOVE_BOARD_ITEM_FROM_LIST, { itemId, listId: fromListId }); + if (reordering && !allItemsLoadedInList && positionInList === -1) { + return; + } + if (reordering) { commit(types.ADD_BOARD_ITEM_TO_LIST, { itemId, listId: toListId, moveBeforeId, moveAfterId, + positionInList, + atIndex: originalIndex, + allItemsLoadedInList, }); return; @@ -500,6 +509,7 @@ export default { listId: toListId, moveBeforeId, moveAfterId, + positionInList, }); } @@ -553,7 +563,15 @@ export default { updateIssueOrder: async ({ commit, dispatch, state }, { moveData, mutationVariables = {} }) => { try { - const { itemId, fromListId, toListId, moveBeforeId, moveAfterId, itemNotInToList } = moveData; + const { + itemId, + fromListId, + toListId, + moveBeforeId, + moveAfterId, + itemNotInToList, + positionInList, + } = moveData; const { fullBoardId, filterParams, @@ -562,6 +580,8 @@ export default { }, } = state; + commit(types.MUTATE_ISSUE_IN_PROGRESS, true); + const { data } = await gqlClient.mutate({ mutation: issueMoveListMutation, variables: { @@ -572,6 +592,7 @@ export default { toListId: getIdFromGraphQLId(toListId), moveBeforeId: moveBeforeId ? getIdFromGraphQLId(moveBeforeId) : undefined, moveAfterId: moveAfterId ? getIdFromGraphQLId(moveAfterId) : undefined, + positionInList, // 'mutationVariables' allows EE code to pass in extra parameters. ...mutationVariables, }, @@ -643,7 +664,9 @@ export default { } commit(types.MUTATE_ISSUE_SUCCESS, { issue: data.issueMoveList.issue }); + commit(types.MUTATE_ISSUE_IN_PROGRESS, false); } catch { + commit(types.MUTATE_ISSUE_IN_PROGRESS, false); commit( types.SET_ERROR, s__('Boards|An error occurred while moving the issue. Please try again.'), diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js index 43268f21f96..0e496677b7b 100644 --- a/app/assets/javascripts/boards/stores/mutation_types.js +++ b/app/assets/javascripts/boards/stores/mutation_types.js @@ -44,3 +44,4 @@ export const ADD_LIST_TO_HIGHLIGHTED_LISTS = 'ADD_LIST_TO_HIGHLIGHTED_LISTS'; export const REMOVE_LIST_FROM_HIGHLIGHTED_LISTS = 'REMOVE_LIST_FROM_HIGHLIGHTED_LISTS'; export const RESET_BOARD_ITEM_SELECTION = 'RESET_BOARD_ITEM_SELECTION'; export const SET_ERROR = 'SET_ERROR'; +export const MUTATE_ISSUE_IN_PROGRESS = 'MUTATE_ISSUE_IN_PROGRESS'; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index 26a98a645b3..44abb2030c7 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -20,17 +20,28 @@ export const removeItemFromList = ({ state, listId, itemId }) => { updateListItemsCount({ state, listId, value: -1 }); }; -export const addItemToList = ({ state, listId, itemId, moveBeforeId, moveAfterId, atIndex }) => { +export const addItemToList = ({ + state, + listId, + itemId, + moveBeforeId, + moveAfterId, + atIndex, + positionInList, +}) => { const listIssues = state.boardItemsByListId[listId]; let newIndex = atIndex || 0; + const moveToStartOrLast = positionInList !== undefined; if (moveBeforeId) { newIndex = listIssues.indexOf(moveBeforeId) + 1; } else if (moveAfterId) { newIndex = listIssues.indexOf(moveAfterId); + } else if (moveToStartOrLast) { + newIndex = positionInList === -1 ? listIssues.length : 0; } listIssues.splice(newIndex, 0, itemId); Vue.set(state.boardItemsByListId, listId, listIssues); - updateListItemsCount({ state, listId, value: 1 }); + updateListItemsCount({ state, listId, value: moveToStartOrLast ? 0 : 1 }); }; export default { @@ -205,12 +216,34 @@ export default { Vue.set(state.boardItems, issue.id, formatIssue(issue)); }, + [mutationTypes.MUTATE_ISSUE_IN_PROGRESS](state, isLoading) { + state.isUpdateIssueOrderInProgress = isLoading; + }, + [mutationTypes.ADD_BOARD_ITEM_TO_LIST]: ( state, - { itemId, listId, moveBeforeId, moveAfterId, atIndex, inProgress = false }, + { + itemId, + listId, + moveBeforeId, + moveAfterId, + atIndex, + positionInList, + allItemsLoadedInList, + inProgress = false, + }, ) => { Vue.set(state.listsFlags, listId, { ...state.listsFlags, addItemToListInProgress: inProgress }); - addItemToList({ state, listId, itemId, moveBeforeId, moveAfterId, atIndex }); + addItemToList({ + state, + listId, + itemId, + moveBeforeId, + moveAfterId, + atIndex, + positionInList, + allItemsLoadedInList, + }); }, [mutationTypes.REMOVE_BOARD_ITEM_FROM_LIST]: (state, { itemId, listId }) => { diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js index b62c032b921..bf3f777ea7d 100644 --- a/app/assets/javascripts/boards/stores/state.js +++ b/app/assets/javascripts/boards/stores/state.js @@ -40,4 +40,5 @@ export default () => ({ }, // TODO: remove after ce/ee split of board_content.vue isShowingEpicsSwimlanes: false, + isUpdateIssueOrderInProgress: false, }); diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb index 0374d076da8..0223ad3818c 100644 --- a/app/models/ci/bridge.rb +++ b/app/models/ci/bridge.rb @@ -77,7 +77,7 @@ module Ci %i[pipeline project ref tag options name allow_failure stage stage_idx yaml_variables when description needs_attributes - scheduling_type ci_stage].freeze + scheduling_type ci_stage partition_id].freeze end def inherit_status_from_downstream!(pipeline) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index a08fdf3652a..60a1d8b4b53 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -217,7 +217,8 @@ module Ci allow_failure stage stage_idx trigger_request yaml_variables when environment coverage_regex description tag_list protected needs_attributes - job_variables_attributes resource_group scheduling_type ci_stage].freeze + job_variables_attributes resource_group scheduling_type + ci_stage partition_id].freeze end end diff --git a/app/models/ci/partition.rb b/app/models/ci/partition.rb new file mode 100644 index 00000000000..d773038df01 --- /dev/null +++ b/app/models/ci/partition.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +module Ci + class Partition < Ci::ApplicationRecord + end +end diff --git a/app/views/profiles/notifications/_email_settings.html.haml b/app/views/profiles/notifications/_email_settings.html.haml index b4db99a8bd4..c4de33dcd9e 100644 --- a/app/views/profiles/notifications/_email_settings.html.haml +++ b/app/views/profiles/notifications/_email_settings.html.haml @@ -1,6 +1,6 @@ - form = local_assigns.fetch(:form) .form-group - = form.label :notification_email, class: "label-bold" + = form.label :notification_email, _('Notification Email'), class: "label-bold" = form.select :notification_email, @user.public_verified_emails, { include_blank: _('Use primary email (%{email})') % { email: @user.email }, selected: @user.notification_email }, class: "select2", disabled: local_assigns.fetch(:email_change_disabled, nil) .help-block = local_assigns.fetch(:help_text, nil) diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index 26c9b2f0ee1..0f4b130a774 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -25,7 +25,7 @@ = gitlab_ui_form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications gl-mt-3' } do |f| = render_if_exists 'profiles/notifications/email_settings', form: f - = label_tag :global_notification_level, "Global notification level", class: "label-bold" + = label_tag :global_notification_level, _('Global notification level'), class: "label-bold" %br .clearfix .form-group.float-left.global-notification-setting diff --git a/config/metrics/counts_all/20210216182006_source_code_pushes.yml b/config/metrics/counts_all/20210216182006_source_code_pushes.yml index 5c3c70f2496..3e4ef3ec76c 100644 --- a/config/metrics/counts_all/20210216182006_source_code_pushes.yml +++ b/config/metrics/counts_all/20210216182006_source_code_pushes.yml @@ -12,7 +12,7 @@ time_frame: all data_source: redis instrumentation_class: RedisMetric options: - counter_class: SourceCodeCounter + prefix: source_code event: pushes distribution: - ce diff --git a/db/docs/ci_partitions.yml b/db/docs/ci_partitions.yml new file mode 100644 index 00000000000..8dfa31f05f9 --- /dev/null +++ b/db/docs/ci_partitions.yml @@ -0,0 +1,9 @@ +--- +table_name: ci_partitions +classes: +- Ci::Partition +feature_categories: +- continuous_integration +description: Database partitioning metadata for CI tables +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96856 +milestone: '15.4' diff --git a/db/migrate/20220902065314_create_ci_partitions.rb b/db/migrate/20220902065314_create_ci_partitions.rb new file mode 100644 index 00000000000..1a8a4f172f8 --- /dev/null +++ b/db/migrate/20220902065314_create_ci_partitions.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class CreateCiPartitions < Gitlab::Database::Migration[2.0] + def change + create_table :ci_partitions do |t| + t.timestamps_with_timezone null: false + end + end +end diff --git a/db/migrate/20220902065316_create_default_partition_record.rb b/db/migrate/20220902065316_create_default_partition_record.rb new file mode 100644 index 00000000000..6493fb23d4c --- /dev/null +++ b/db/migrate/20220902065316_create_default_partition_record.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class CreateDefaultPartitionRecord < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + restrict_gitlab_migration gitlab_schema: :gitlab_ci + + def up + execute(<<~SQL) + INSERT INTO "ci_partitions" ("id", "created_at", "updated_at") + VALUES (100, now(), now()); + SQL + + reset_pk_sequence!('ci_partitions') + end + + def down + execute(<<~SQL) + DELETE FROM "ci_partitions" WHERE "ci_partitions"."id" = 100; + SQL + end +end diff --git a/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb b/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb new file mode 100644 index 00000000000..6257164b44e --- /dev/null +++ b/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddPartitionIdToCiBuilds < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + # rubocop:disable Migration/AddColumnsToWideTables + def change + add_column :ci_builds, :partition_id, :bigint, default: 100, null: false + end + # rubocop:enable Migration/AddColumnsToWideTables +end diff --git a/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb b/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb new file mode 100644 index 00000000000..e04ea99539f --- /dev/null +++ b/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddPartitionIdToCiBuildsMetadata < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :ci_builds_metadata, :partition_id, :bigint, default: 100, null: false + end +end diff --git a/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb b/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb new file mode 100644 index 00000000000..1d9eeb0330e --- /dev/null +++ b/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddPartitionIdToCiJobArtifacts < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :ci_job_artifacts, :partition_id, :bigint, default: 100, null: false + end +end diff --git a/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb b/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb new file mode 100644 index 00000000000..bb3e7c27ee8 --- /dev/null +++ b/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddPartitionIdToCiPipelines < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :ci_pipelines, :partition_id, :bigint, default: 100, null: false + end +end diff --git a/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb b/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb new file mode 100644 index 00000000000..0ddbf491ee9 --- /dev/null +++ b/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddPartitionIdToCiStages < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :ci_stages, :partition_id, :bigint, default: 100, null: false + end +end diff --git a/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb b/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb new file mode 100644 index 00000000000..14f17b371b4 --- /dev/null +++ b/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddPartitionIdToCiPipelineVariables < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def change + add_column :ci_pipeline_variables, :partition_id, :bigint, default: 100, null: false + end +end diff --git a/db/schema_migrations/20220902065314 b/db/schema_migrations/20220902065314 new file mode 100644 index 00000000000..8197a41403d --- /dev/null +++ b/db/schema_migrations/20220902065314 @@ -0,0 +1 @@ +d1ca445a17c742d435cba3d898e61242a3df9c92caeadecba147fce858d8cb80 \ No newline at end of file diff --git a/db/schema_migrations/20220902065316 b/db/schema_migrations/20220902065316 new file mode 100644 index 00000000000..e9c3598206e --- /dev/null +++ b/db/schema_migrations/20220902065316 @@ -0,0 +1 @@ +910d87fbab226671b8e12b236be43970f6b2a3083f30df9586b3f8edf779f4af \ No newline at end of file diff --git a/db/schema_migrations/20220902065317 b/db/schema_migrations/20220902065317 new file mode 100644 index 00000000000..fa60ee97fef --- /dev/null +++ b/db/schema_migrations/20220902065317 @@ -0,0 +1 @@ +11c65391a6744d7d7c303c6593dafa8e6dca392675974a2a1df2c164afbd4fe1 \ No newline at end of file diff --git a/db/schema_migrations/20220902065558 b/db/schema_migrations/20220902065558 new file mode 100644 index 00000000000..2886e656d41 --- /dev/null +++ b/db/schema_migrations/20220902065558 @@ -0,0 +1 @@ +cce779cc52b2bb175ccd3d07ac6a7df3711ae362fa0a5004bfc58fa1eb440e1f \ No newline at end of file diff --git a/db/schema_migrations/20220902065611 b/db/schema_migrations/20220902065611 new file mode 100644 index 00000000000..365cb0f6194 --- /dev/null +++ b/db/schema_migrations/20220902065611 @@ -0,0 +1 @@ +8ec0cc23559ba1b83042bed4abf8c47487ecb999fa66e602fbf4a9edac0569ec \ No newline at end of file diff --git a/db/schema_migrations/20220902065623 b/db/schema_migrations/20220902065623 new file mode 100644 index 00000000000..cf75e086f31 --- /dev/null +++ b/db/schema_migrations/20220902065623 @@ -0,0 +1 @@ +4f2076138e65849d60cf093f140afa1abaa7beea4d6c95048e6743168a7f17a9 \ No newline at end of file diff --git a/db/schema_migrations/20220902065635 b/db/schema_migrations/20220902065635 new file mode 100644 index 00000000000..bd131598d78 --- /dev/null +++ b/db/schema_migrations/20220902065635 @@ -0,0 +1 @@ +49a86fa87974f2c0cdc5a38726ab792f70c43e7f215495323d0999fd9f6e45f6 \ No newline at end of file diff --git a/db/schema_migrations/20220902065647 b/db/schema_migrations/20220902065647 new file mode 100644 index 00000000000..31ee9352fe6 --- /dev/null +++ b/db/schema_migrations/20220902065647 @@ -0,0 +1 @@ +812f25371d731d03bd4727328ad0daaf954595e24a314dd5f1adccdc3a4532c4 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 419a328269c..fe6c6f295c4 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -12632,6 +12632,7 @@ CREATE TABLE ci_builds ( scheduling_type smallint, id bigint NOT NULL, stage_id bigint, + partition_id bigint DEFAULT 100 NOT NULL, CONSTRAINT check_1e2fbd1b39 CHECK ((lock_version IS NOT NULL)) ); @@ -12658,7 +12659,8 @@ CREATE TABLE ci_builds_metadata ( build_id bigint NOT NULL, id bigint NOT NULL, runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL, - id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL + id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL, + partition_id bigint DEFAULT 100 NOT NULL ); CREATE SEQUENCE ci_builds_metadata_id_seq @@ -12823,6 +12825,7 @@ CREATE TABLE ci_job_artifacts ( job_id bigint NOT NULL, locked smallint DEFAULT 2, original_filename text, + partition_id bigint DEFAULT 100 NOT NULL, CONSTRAINT check_27f0f6dbab CHECK ((file_store IS NOT NULL)), CONSTRAINT check_85573000db CHECK ((char_length(original_filename) <= 512)) ); @@ -12928,6 +12931,21 @@ CREATE SEQUENCE ci_namespace_monthly_usages_id_seq ALTER SEQUENCE ci_namespace_monthly_usages_id_seq OWNED BY ci_namespace_monthly_usages.id; +CREATE TABLE ci_partitions ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL +); + +CREATE SEQUENCE ci_partitions_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE ci_partitions_id_seq OWNED BY ci_partitions.id; + CREATE TABLE ci_pending_builds ( id bigint NOT NULL, build_id bigint NOT NULL, @@ -13071,7 +13089,8 @@ CREATE TABLE ci_pipeline_variables ( encrypted_value_iv character varying, pipeline_id integer NOT NULL, variable_type smallint DEFAULT 1 NOT NULL, - raw boolean DEFAULT true NOT NULL + raw boolean DEFAULT true NOT NULL, + partition_id bigint DEFAULT 100 NOT NULL ); CREATE SEQUENCE ci_pipeline_variables_id_seq @@ -13113,6 +13132,7 @@ CREATE TABLE ci_pipelines ( external_pull_request_id bigint, ci_ref_id bigint, locked smallint DEFAULT 1 NOT NULL, + partition_id bigint DEFAULT 100 NOT NULL, CONSTRAINT check_d7e99a025e CHECK ((lock_version IS NOT NULL)) ); @@ -13417,6 +13437,7 @@ CREATE TABLE ci_stages ( lock_version integer DEFAULT 0, "position" integer, id bigint NOT NULL, + partition_id bigint DEFAULT 100 NOT NULL, CONSTRAINT check_81b431e49b CHECK ((lock_version IS NOT NULL)) ); @@ -23241,6 +23262,8 @@ ALTER TABLE ONLY ci_namespace_mirrors ALTER COLUMN id SET DEFAULT nextval('ci_na ALTER TABLE ONLY ci_namespace_monthly_usages ALTER COLUMN id SET DEFAULT nextval('ci_namespace_monthly_usages_id_seq'::regclass); +ALTER TABLE ONLY ci_partitions ALTER COLUMN id SET DEFAULT nextval('ci_partitions_id_seq'::regclass); + ALTER TABLE ONLY ci_pending_builds ALTER COLUMN id SET DEFAULT nextval('ci_pending_builds_id_seq'::regclass); ALTER TABLE ONLY ci_pipeline_artifacts ALTER COLUMN id SET DEFAULT nextval('ci_pipeline_artifacts_id_seq'::regclass); @@ -24976,6 +24999,9 @@ ALTER TABLE ONLY ci_namespace_mirrors ALTER TABLE ONLY ci_namespace_monthly_usages ADD CONSTRAINT ci_namespace_monthly_usages_pkey PRIMARY KEY (id); +ALTER TABLE ONLY ci_partitions + ADD CONSTRAINT ci_partitions_pkey PRIMARY KEY (id); + ALTER TABLE ONLY ci_pending_builds ADD CONSTRAINT ci_pending_builds_pkey PRIMARY KEY (id); diff --git a/doc/api/members.md b/doc/api/members.md index b0992aafb7e..3ffe94e6f99 100644 --- a/doc/api/members.md +++ b/doc/api/members.md @@ -76,8 +76,7 @@ Example response: }, "expires_at": "2012-10-22T14:13:35Z", "access_level": 30, - "group_saml_identity": null, - "membership_state": "active" + "group_saml_identity": null }, { "id": 2, @@ -102,8 +101,7 @@ Example response: "extern_uid":"ABC-1234567890", "provider": "group_saml", "saml_provider_id": 10 - }, - "membership_state": "active" + } } ] ``` @@ -163,8 +161,7 @@ Example response: }, "expires_at": "2012-10-22T14:13:35Z", "access_level": 30, - "group_saml_identity": null, - "membership_state": "active" + "group_saml_identity": null }, { "id": 2, @@ -189,8 +186,7 @@ Example response: "extern_uid":"ABC-1234567890", "provider": "group_saml", "saml_provider_id": 10 - }, - "membership_state": "active" + } }, { "id": 3, @@ -210,8 +206,7 @@ Example response: }, "expires_at": "2012-11-22T14:13:35Z", "access_level": 30, - "group_saml_identity": null, - "membership_state": "active" + "group_saml_identity": null } ] ``` @@ -257,8 +252,7 @@ Example response: "web_url": "http://192.168.1.8:3000/root" }, "expires_at": null, - "group_saml_identity": null, - "membership_state": "active" + "group_saml_identity": null } ``` @@ -305,8 +299,7 @@ Example response: }, "email": "john@example.com", "expires_at": null, - "group_saml_identity": null, - "membership_state": "active" + "group_saml_identity": null } ``` @@ -370,7 +363,6 @@ Example response: "web_url": "http://192.168.1.8:3000/root", "last_activity_on": "2021-01-27", "membership_type": "group_member", - "membership_state": "active", "removable": true, "created_at": "2021-01-03T12:16:02.000Z" }, @@ -384,7 +376,6 @@ Example response: "email": "john@example.com", "last_activity_on": "2021-01-25", "membership_type": "group_member", - "membership_state": "active", "removable": true, "created_at": "2021-01-04T18:46:42.000Z" }, @@ -397,7 +388,6 @@ Example response: "web_url": "http://192.168.1.8:3000/root", "last_activity_on": "2021-01-20", "membership_type": "group_invite", - "membership_state": "awaiting", "removable": false, "created_at": "2021-01-09T07:12:31.000Z" } diff --git a/doc/ci/mobile_devops.md b/doc/ci/mobile_devops.md new file mode 100644 index 00000000000..6eb56434a1b --- /dev/null +++ b/doc/ci/mobile_devops.md @@ -0,0 +1,42 @@ +--- +stage: none +group: unassigned +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +type: reference +--- + +# Mobile DevOps + +GitLab Mobile DevOps is a collection of features and tools designed for mobile developers +and teams to automate their build and release process using GitLab CI/CD. Mobile DevOps +is an experimental feature developed by [GitLab Incubation Engineering](https://about.gitlab.com/handbook/engineering/incubation/). + +Mobile DevOps is still in development, but you can: + +- [Request a feature](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=feature_request). +- [Report a bug](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=report_bug). +- [Share feedback](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=general_feedback). + +## Code Signing + +[Project-level Secure Files](secure_files/index.md) makes it easier to manage key stores, provision profiles, +and signing certificates directly in a GitLab project. + +For a guided walkthrough of this feature, watch the [video demo](https://youtu.be/O7FbJu3H2YM). + +## Review Apps for Mobile + +You can use [Review Apps](review_apps/index.md) to preview changes directly from a merge request. +Review Apps for Mobile brings that capability to mobile developers through an integration +with [Appetize](https://appetize.io/). + +Watch a [video walkthrough](https://youtu.be/X15mI19TXa4) of this feature, or visit the +[setup instructions](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/readme/-/issues/15) +to get started. + +## Mobile SAST + +You can use [Static Application Security Testing (SAST)](../user/application_security/sast/index.md) +to run static analyzers on code to check for known security vulnerabilities. Mobile SAST +expands this functionality for mobile teams with an [experimental SAST feature](../user/application_security/sast/index.md#experimental-features) +based on [Mobile Security Framework (MobSF)](https://github.com/MobSF/Mobile-Security-Framework-MobSF). diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md index 8c04992fd67..4ef58fefcb9 100644 --- a/doc/development/service_ping/implement.md +++ b/doc/development/service_ping/implement.md @@ -272,7 +272,7 @@ Events are handled by counter classes in the `Gitlab::UsageDataCounters` namespa 1. Listed in [`Gitlab::UsageDataCounters::COUNTERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters.rb#L5) to be then included in `Gitlab::UsageData`. -1. Specified in the metric definition using the `RedisMetric` instrumentation class as a `counter_class` option to be picked up using the [metric instrumentation](metrics_instrumentation.md) framework. Refer to the [Redis metrics](metrics_instrumentation.md#redis-metrics) documentation for an example implementation. +1. Specified in the metric definition using the `RedisMetric` instrumentation class by their `prefix` option to be picked up using the [metric instrumentation](metrics_instrumentation.md) framework. Refer to the [Redis metrics](metrics_instrumentation.md#redis-metrics) documentation for an example implementation. Inheriting classes are expected to override `KNOWN_EVENTS` and `PREFIX` constants to build event names and associated metrics. For example, for prefix `issues` and events array `%w[create, update, delete]`, three metrics will be added to the Service Ping payload: `counts.issues_create`, `counts.issues_update` and `counts.issues_delete`. diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md index ee0d701a5bb..860434ab2ad 100644 --- a/doc/development/service_ping/metrics_instrumentation.md +++ b/doc/development/service_ping/metrics_instrumentation.md @@ -154,14 +154,16 @@ end You can use Redis metrics to track events not kept in the database, for example, a count of how many times the search bar has been used. -[Example of a merge request that adds a `Redis` metric](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66582). +[Example of a merge request that adds a `Redis` metric](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97009). + +Please note that `RedisMetric` class can only be used as the `instrumentation_class` for Redis metrics with simple counters classes (classes that only inherit `BaseCounter` and set `PREFIX` and `KNOWN_EVENTS` constants). In case the counter class has additional logic included in it, a new `instrumentation_class`, inheriting from `RedisMetric`, needs to be created. This new class needs to include the additional logic from the counter class. Count unique values for `source_code_pushes` event. Required options: - `event`: the event name. -- `counter_class`: one of the counter classes from the `Gitlab::UsageDataCounters` namespace; it should implement `read` method or inherit it from `BaseCounter`. +- `prefix`: the value of the `PREFIX` constant used in the counter classes from the `Gitlab::UsageDataCounters` namespace. ```yaml time_frame: all @@ -169,7 +171,7 @@ data_source: redis instrumentation_class: 'RedisMetric' options: event: pushes - counter_class: SourceCodeCounter + prefix: source_code ``` ### Availability-restrained Redis metrics @@ -200,7 +202,7 @@ data_source: redis instrumentation_class: 'MergeUsageCountRedisMetric' options: event: pushes - counter_class: SourceCodeCounter + prefix: source_code ``` ## Redis HyperLogLog metrics diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml index 93fe5871a9b..1c2d04561b4 100644 --- a/lib/gitlab/database/gitlab_schemas.yml +++ b/lib/gitlab/database/gitlab_schemas.yml @@ -91,6 +91,7 @@ ci_job_artifact_states: :gitlab_ci ci_minutes_additional_packs: :gitlab_ci ci_namespace_monthly_usages: :gitlab_ci ci_namespace_mirrors: :gitlab_ci +ci_partitions: :gitlab_ci ci_pending_builds: :gitlab_ci ci_pipeline_artifacts: :gitlab_ci ci_pipeline_chat_data: :gitlab_ci diff --git a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb index a25bad2436b..c9449f10cc2 100644 --- a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb +++ b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb @@ -11,37 +11,41 @@ module Gitlab # instrumentation_class: RedisMetric # options: # event: pushes - # counter_class: SourceCodeCounter + # prefix: source_code # class RedisMetric < BaseMetric + include Gitlab::UsageDataCounters::RedisCounter + def initialize(time_frame:, options: {}) super raise ArgumentError, "'event' option is required" unless metric_event.present? - raise ArgumentError, "'counter class' option is required" unless counter_class.present? + raise ArgumentError, "'prefix' option is required" unless prefix.present? end def metric_event options[:event] end - def counter_class_name - options[:counter_class] - end - - def counter_class - "Gitlab::UsageDataCounters::#{counter_class_name}".constantize + def prefix + options[:prefix] end def value redis_usage_data do - counter_class.read(metric_event) + total_count(redis_key) end end def suggested_name Gitlab::Usage::Metrics::NameSuggestion.for(:redis) end + + private + + def redis_key + "USAGE_#{prefix}_#{metric_event}".upcase + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index c2a4609123d..0742b7bba2b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -17962,6 +17962,9 @@ msgstr "" msgid "Global Shortcuts" msgstr "" +msgid "Global notification level" +msgstr "" + msgid "Global notification settings" msgstr "" @@ -18049,6 +18052,12 @@ msgstr "" msgid "GlobalSearch|project" msgstr "" +msgid "GlobalShortcuts|Copied source branch name to clipboard." +msgstr "" + +msgid "GlobalShortcuts|Unable to copy the source branch name at this time." +msgstr "" + msgid "Globally-allowed IP ranges" msgstr "" @@ -26698,6 +26707,9 @@ msgstr "" msgid "Nothing to preview." msgstr "" +msgid "Notification Email" +msgstr "" + msgid "Notification events" msgstr "" diff --git a/package.json b/package.json index 230f0a4d8f7..286a6a72d28 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "@gitlab/at.js": "1.5.7", "@gitlab/favicon-overlay": "2.0.0", "@gitlab/svgs": "3.3.0", - "@gitlab/ui": "43.9.3", + "@gitlab/ui": "43.13.0", "@gitlab/visual-review-tools": "1.7.3", "@gitlab/web-ide": "0.0.1-dev-20220815034418", "@rails/actioncable": "6.1.4-7", @@ -197,7 +197,7 @@ "yaml": "^2.0.0-10" }, "devDependencies": { - "@gitlab/eslint-plugin": "16.0.0", + "@gitlab/eslint-plugin": "17.0.0", "@gitlab/stylelint-config": "4.1.0", "@graphql-eslint/eslint-plugin": "3.10.7", "@testing-library/dom": "^7.16.2", diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 4092f639eae..4aeafed5712 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -31,9 +31,14 @@ RSpec.describe 'Database schema' do boards: %w[milestone_id iteration_id], chat_names: %w[chat_id team_id user_id], chat_teams: %w[team_id], - ci_builds: %w[erased_by_id trigger_request_id], + ci_builds: %w[erased_by_id trigger_request_id partition_id], + ci_builds_metadata: %w[partition_id], + ci_job_artifacts: %w[partition_id], ci_namespace_monthly_usages: %w[namespace_id], + ci_pipeline_variables: %w[partition_id], + ci_pipelines: %w[partition_id], ci_runner_projects: %w[runner_id], + ci_stages: %w[partition_id], ci_trigger_requests: %w[commit_id], cluster_providers_aws: %w[security_group_id vpc_id access_key_id], cluster_providers_gcp: %w[gcp_project_id operation_id], diff --git a/spec/frontend/boards/components/board_card_move_to_position_spec.js b/spec/frontend/boards/components/board_card_move_to_position_spec.js index 01bad53d9e1..7254b9486ef 100644 --- a/spec/frontend/boards/components/board_card_move_to_position_spec.js +++ b/spec/frontend/boards/components/board_card_move_to_position_spec.js @@ -1,8 +1,8 @@ +import { shallowMount } from '@vue/test-utils'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; -import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue'; import { mockList, mockIssue2, mockIssue, mockIssue3, mockIssue4 } from 'jest/boards/mock_data'; import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; @@ -19,6 +19,7 @@ describe('Board Card Move to position', () => { let trackingSpy; let store; let dispatch; + const itemIndex = 1; const createStoreOptions = () => { const state = { @@ -42,7 +43,7 @@ describe('Board Card Move to position', () => { }; const createComponent = (propsData) => { - wrapper = shallowMountExtended(BoardCardMoveToPosition, { + wrapper = shallowMount(BoardCardMoveToPosition, { store, propsData: { item: mockIssue2, @@ -66,7 +67,6 @@ describe('Board Card Move to position', () => { wrapper.destroy(); }); - const findEllipsesButton = () => wrapper.findByTestId('move-card-dropdown'); const findMoveToPositionDropdown = () => wrapper.findComponent(GlDropdown); const findDropdownItems = () => findMoveToPositionDropdown().findAllComponents(GlDropdownItem); const findDropdownItemAtIndex = (index) => findDropdownItems().at(index); @@ -74,7 +74,7 @@ describe('Board Card Move to position', () => { describe('Dropdown', () => { describe('Dropdown button', () => { it('has an icon with vertical ellipsis', () => { - expect(findEllipsesButton().exists()).toBe(true); + expect(findMoveToPositionDropdown().exists()).toBe(true); expect(findMoveToPositionDropdown().props('icon')).toBe('ellipsis_v'); }); @@ -82,24 +82,11 @@ describe('Board Card Move to position', () => { findMoveToPositionDropdown().vm.$emit('click'); expect(findDropdownItems()).toHaveLength(dropdownOptions.length); }); - - it('is opened on the click of vertical ellipsis and has 1 dropdown items when number of list items > 10', () => { - wrapper.destroy(); - - createComponent({ - list: { - ...mockList, - id: 'gid://gitlab/List/2', - }, - }); - findMoveToPositionDropdown().vm.$emit('click'); - expect(findDropdownItems()).toHaveLength(1); - }); }); describe('Dropdown options', () => { beforeEach(() => { - createComponent({ index: 1 }); + createComponent({ index: itemIndex }); trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); dispatch = jest.spyOn(store, 'dispatch').mockImplementation(() => {}); }); @@ -109,13 +96,13 @@ describe('Board Card Move to position', () => { }); it.each` - dropdownIndex | dropdownLabel | trackLabel | moveAfterId | moveBeforeId - ${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${'move_to_start'} | ${mockIssue.id} | ${undefined} - ${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${'move_to_end'} | ${undefined} | ${mockIssue4.id} + dropdownIndex | dropdownLabel | trackLabel | positionInList + ${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${'move_to_start'} | ${0} + ${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${'move_to_end'} | ${-1} `( 'on click of dropdown index $dropdownIndex with label $dropdownLabel should call moveItem action with tracking label $trackLabel', - async ({ dropdownIndex, dropdownLabel, trackLabel, moveAfterId, moveBeforeId }) => { - await findEllipsesButton().vm.$emit('click'); + async ({ dropdownIndex, dropdownLabel, trackLabel, positionInList }) => { + await findMoveToPositionDropdown().vm.$emit('click'); expect(findDropdownItemAtIndex(dropdownIndex).text()).toBe(dropdownLabel); await findDropdownItemAtIndex(dropdownIndex).vm.$emit('click', { @@ -134,9 +121,10 @@ describe('Board Card Move to position', () => { itemId: mockIssue2.id, itemIid: mockIssue2.iid, itemPath: mockIssue2.referencePath, - moveBeforeId, - moveAfterId, + positionInList, toListId: mockList.id, + allItemsLoadedInList: true, + atIndex: itemIndex, }); }, ); diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js index e48b946ff1b..e919300228a 100644 --- a/spec/frontend/boards/stores/actions_spec.js +++ b/spec/frontend/boards/stores/actions_spec.js @@ -1056,6 +1056,8 @@ describe('moveIssueCard and undoMoveIssueCard', () => { originalIndex = 0, moveBeforeId = undefined, moveAfterId = undefined, + allItemsLoadedInList = true, + listPosition = undefined, } = {}) => { state = { boardLists: { @@ -1065,12 +1067,28 @@ describe('moveIssueCard and undoMoveIssueCard', () => { boardItems: { [itemId]: originalIssue }, boardItemsByListId: { [fromListId]: [123] }, }; - params = { itemId, fromListId, toListId, moveBeforeId, moveAfterId }; + params = { + itemId, + fromListId, + toListId, + moveBeforeId, + moveAfterId, + listPosition, + allItemsLoadedInList, + }; moveMutations = [ { type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: fromListId } }, { type: types.ADD_BOARD_ITEM_TO_LIST, - payload: { itemId, listId: toListId, moveBeforeId, moveAfterId }, + payload: { + itemId, + listId: toListId, + moveBeforeId, + moveAfterId, + listPosition, + allItemsLoadedInList, + atIndex: originalIndex, + }, }, ]; undoMutations = [ @@ -1365,10 +1383,18 @@ describe('updateIssueOrder', () => { { moveData }, state, [ + { + type: types.MUTATE_ISSUE_IN_PROGRESS, + payload: true, + }, { type: types.MUTATE_ISSUE_SUCCESS, payload: { issue: rawIssue }, }, + { + type: types.MUTATE_ISSUE_IN_PROGRESS, + payload: false, + }, ], [], ); @@ -1389,6 +1415,14 @@ describe('updateIssueOrder', () => { { moveData }, state, [ + { + type: types.MUTATE_ISSUE_IN_PROGRESS, + payload: true, + }, + { + type: types.MUTATE_ISSUE_IN_PROGRESS, + payload: false, + }, { type: types.SET_ERROR, payload: 'An error occurred while moving the issue. Please try again.', diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js index 1606ca09d8f..87a183c0441 100644 --- a/spec/frontend/boards/stores/mutations_spec.js +++ b/spec/frontend/boards/stores/mutations_spec.js @@ -513,6 +513,31 @@ describe('Board Store Mutations', () => { listState: [mockIssue2.id, mockIssue.id], }, ], + [ + 'to the top of the list', + { + payload: { + itemId: mockIssue2.id, + listId: mockList.id, + positionInList: 0, + atIndex: 1, + }, + listState: [mockIssue2.id, mockIssue.id], + }, + ], + [ + 'to the bottom of the list when the list is fully loaded', + { + payload: { + itemId: mockIssue2.id, + listId: mockList.id, + positionInList: -1, + atIndex: 0, + allItemsLoadedInList: true, + }, + listState: [mockIssue.id, mockIssue2.id], + }, + ], ])(`inserts an item into a list %s`, (_, { payload, listState }) => { mutations.ADD_BOARD_ITEM_TO_LIST(state, payload); diff --git a/spec/frontend/ide/stores/modules/commit/actions_spec.js b/spec/frontend/ide/stores/modules/commit/actions_spec.js index d65039e89cc..4e8467de759 100644 --- a/spec/frontend/ide/stores/modules/commit/actions_spec.js +++ b/spec/frontend/ide/stores/modules/commit/actions_spec.js @@ -210,7 +210,7 @@ describe('IDE commit module actions', () => { branch, }); store.state.openFiles.forEach((entry) => { - expect(entry.changed).toBeFalsy(); + expect(entry.changed).toBe(false); }); }); diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb index 831f775ec9a..e228a0a7d72 100644 --- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb +++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb @@ -11,18 +11,18 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git let(:expected_value) { 4 } - it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', counter_class: 'SourceCodeCounter' } } + it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', prefix: 'source_code' } } it 'raises an exception if event option is not present' do - expect { described_class.new(counter_class: 'SourceCodeCounter') }.to raise_error(ArgumentError) + expect { described_class.new(prefix: 'source_code') }.to raise_error(ArgumentError) end - it 'raises an exception if counter_class option is not present' do + it 'raises an exception if prefix option is not present' do expect { described_class.new(event: 'pushes') }.to raise_error(ArgumentError) end describe 'children classes' do - let(:options) { { event: 'pushes', counter_class: 'SourceCodeCounter' } } + let(:options) { { event: 'pushes', prefix: 'source_code' } } context 'availability not defined' do subject { Class.new(described_class).new(time_frame: nil, options: options) } diff --git a/yarn.lock b/yarn.lock index 7ecbe845056..954169a5f33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1027,10 +1027,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/at.js/-/at.js-1.5.7.tgz#1ee6f838cc4410a1d797770934df91d90df8179e" integrity sha512-c6ySRK/Ma7lxwpIVbSAF3P+xiTLrNTGTLRx4/pHK111AdFxwgUwrYF6aVZFXvmG65jHOJHoa0eQQ21RW6rm0Rg== -"@gitlab/eslint-plugin@16.0.0": - version "16.0.0" - resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-16.0.0.tgz#83b71bb3f749c6e52138d2c1c17ac623e7b2e3db" - integrity sha512-2n7geoRPkeMAq4GCqyvFzcTgcSrTM7pdCOxfcqIeuTmh/PFGhh+m7YC+YC4enhGOCN8lo08buLZhXkSgWiHSqA== +"@gitlab/eslint-plugin@17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-17.0.0.tgz#5451fbbad96b09d812af2afb247f6602fe0be6c6" + integrity sha512-c+sJtjzYl+KGPtZScU8Mji9seJw7dSEn31APyYEYTyWp72yMsFvXmg46txT2QCz+ueZlqk0/C2IQmgfe6fLcBw== dependencies: "@babel/core" "^7.17.0" "@babel/eslint-parser" "^7.17.0" @@ -1064,10 +1064,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.3.0.tgz#99b044484fcf3d5a6431281e320e2405540ff5a9" integrity sha512-S8Hqf+ms8aNrSgmci9SVoIyj/0qQnizU5uV5vUPAOwiufMDFDyI5qfcgn4EYZ6mnju3LiO+ReSL/PPTD4qNgHA== -"@gitlab/ui@43.9.3": - version "43.9.3" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-43.9.3.tgz#2dd91b14da769a873e45ffe07b5863f6c47211ba" - integrity sha512-TONSf+6UJYWTVs5qnItR1uLZ/0kBE8jGN8aLOVv4CDAsORvln0ZxtcZvMTCFp76YEtzXLkMUfvm7ZngQ26tIiA== +"@gitlab/ui@43.13.0": + version "43.13.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-43.13.0.tgz#7e4e7d41287cfba8a46dbdd3c8ba998a853a9ad2" + integrity sha512-y0BrVKsqRBEQMrsJseakBeMrFHVMTg7DVMa3tbdkKkrruV8SYOsX8wLrv20taDhiMlceKRB8lF5gLTPPHLwCGA== dependencies: "@popperjs/core" "^2.11.2" bootstrap-vue "2.20.1"