From e09e832fe3b40bda499856c09dfac0a501e8392f Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 7 Mar 2025 09:09:20 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- GITALY_SERVER_VERSION | 2 +- GITLAB_SHELL_VERSION | 2 +- .../javascripts/diffs/components/app.vue | 31 +- .../diffs/components/compare_versions.vue | 15 +- app/assets/javascripts/diffs/constants.js | 2 +- app/assets/javascripts/diffs/store/actions.js | 13 - .../diffs/store/modules/diff_state.js | 1 - .../javascripts/diffs/store/mutation_types.js | 1 - .../javascripts/diffs/store/mutations.js | 3 - .../javascripts/diffs/stores/file_browser.js | 26 ++ .../diffs/stores/legacy_diffs/actions.js | 13 - .../diffs/stores/legacy_diffs/index.js | 1 - .../diffs/stores/legacy_diffs/mutations.js | 3 - .../pages/projects/issues/show/index.js | 25 +- .../javascripts/rapid_diffs/app/index.js | 2 + .../rapid_diffs/streaming_error.js | 11 + .../whats_new/components/feature.vue | 8 + .../design_preview/design_details.vue | 2 - .../design_preview/design_toolbar.vue | 8 +- .../streaming_error_component.html.haml | 1 + .../rapid_diffs/streaming_error_component.rb | 9 + app/controllers/concerns/stream_diffs.rb | 5 +- app/services/ml/create_experiment_service.rb | 2 +- .../ml/create_model_version_service.rb | 2 +- ..._root_namespace_cluster_agent_mappings.yml | 10 - ...pment_namespace_cluster_agent_mappings.yml | 15 + ...l => namespace_cluster_agent_mappings.yml} | 11 +- ...ackages_debian_project_component_files.yml | 1 - .../packages_debian_project_components.yml | 12 +- db/docs/packages_maven_metadata.yml | 12 +- ...lopment_namespace_cluster_agent_mapping.rb | 13 + ..._namespace_cluster_agent_mappings_again.rb | 36 -- ...nt_namespace_cluster_agent_mapping_view.rb | 14 + ..._project_components_project_id_not_null.rb | 14 + ...ages_maven_metadata_project_id_not_null.rb | 14 + ...ora_daily_metrics_project_id_date_index.rb | 20 + db/schema_migrations/20240711035245 | 1 - db/schema_migrations/20250219055827 | 1 + db/schema_migrations/20250224063503 | 1 + db/schema_migrations/20250225043936 | 1 + db/schema_migrations/20250225044336 | 1 + db/schema_migrations/20250304084537 | 1 + db/structure.sql | 66 +-- doc/api/graphql/reference/_index.md | 59 ++- doc/user/gitlab_duo_chat/examples.md | 1 - lib/api/api.rb | 4 - .../virtual_registries/packages/endpoint.rb | 109 ----- .../packages/maven/cache/entry.rb | 31 -- .../packages/maven/registry.rb | 15 - .../packages/maven/upstream.rb | 15 - lib/api/ml/mlflow/runs.rb | 6 +- .../packages/maven/cache/entries.rb | 122 ------ .../packages/maven/endpoints.rb | 165 ------- .../packages/maven/registries.rb | 153 ------- .../packages/maven/upstreams.rb | 193 --------- ...l_root_namespace_cluster_agent_mappings.rb | 16 - lib/gitlab/database.rb | 4 +- lib/web_ide/settings/default_settings.rb | 4 +- lib/web_ide/settings_sync.rb | 15 +- locale/gitlab.pot | 15 + .../streaming_error_component_spec.rb | 14 + .../virtual_registries/packages/maven_spec.rb | 98 ----- spec/frontend/diffs/components/app_spec.js | 67 ++- .../diffs/components/compare_versions_spec.js | 11 +- spec/frontend/diffs/store/actions_spec.js | 28 -- spec/frontend/diffs/store/mutations_spec.js | 10 - .../diffs/stores/file_browser_spec.js | 37 ++ .../diffs/stores/legacy_diffs/actions_spec.js | 28 -- .../stores/legacy_diffs/mutations_spec.js | 8 - spec/frontend/rapid_diffs/app/app_spec.js | 2 + .../rapid_diffs/streaming_error_spec.js | 28 ++ .../design_preview/design_toolbar_spec.js | 4 +- .../packages/maven/cache/entry_spec.rb | 14 - .../packages/maven/registry_spec.rb | 11 - .../packages/maven/upstream_spec.rb | 11 - .../lib/web_ide/extension_marketplace_spec.rb | 2 +- .../extension_marketplace_validator_spec.rb | 2 +- .../settings/settings_initializer_spec.rb | 2 +- .../settings/settings_integration_spec.rb | 8 +- spec/lib/web_ide/settings_sync_spec.rb | 9 + ...space_cluster_agent_mappings_again_spec.rb | 28 -- .../packages/maven/cache/entries_spec.rb | 200 --------- .../packages/maven/endpoints_spec.rb | 315 -------------- .../packages/maven/registries_spec.rb | 332 --------------- .../packages/maven/upstreams_spec.rb | 401 ------------------ .../ml/create_experiment_service_spec.rb | 11 + .../ml/create_model_version_service_spec.rb | 30 ++ .../requests/api/maven_vreg_shared_context.rb | 49 --- .../maven_packages_shared_examples.rb | 43 -- 89 files changed, 520 insertions(+), 2642 deletions(-) create mode 100644 app/assets/javascripts/diffs/stores/file_browser.js create mode 100644 app/assets/javascripts/rapid_diffs/streaming_error.js create mode 100644 app/components/rapid_diffs/streaming_error_component.html.haml create mode 100644 app/components/rapid_diffs/streaming_error_component.rb delete mode 100644 db/docs/batched_background_migrations/backfill_root_namespace_cluster_agent_mappings.yml create mode 100644 db/docs/deleted_tables/remote_development_namespace_cluster_agent_mappings.yml rename db/docs/{remote_development_namespace_cluster_agent_mappings.yml => namespace_cluster_agent_mappings.yml} (62%) create mode 100644 db/migrate/20250219055827_rename_remote_development_namespace_cluster_agent_mapping.rb delete mode 100644 db/post_migrate/20240711035245_queue_backfill_root_namespace_cluster_agent_mappings_again.rb create mode 100644 db/post_migrate/20250224063503_delete_remote_development_namespace_cluster_agent_mapping_view.rb create mode 100644 db/post_migrate/20250225043936_add_packages_debian_project_components_project_id_not_null.rb create mode 100644 db/post_migrate/20250225044336_add_packages_maven_metadata_project_id_not_null.rb create mode 100644 db/post_migrate/20250304084537_add_dora_daily_metrics_project_id_date_index.rb delete mode 100644 db/schema_migrations/20240711035245 create mode 100644 db/schema_migrations/20250219055827 create mode 100644 db/schema_migrations/20250224063503 create mode 100644 db/schema_migrations/20250225043936 create mode 100644 db/schema_migrations/20250225044336 create mode 100644 db/schema_migrations/20250304084537 delete mode 100644 lib/api/concerns/virtual_registries/packages/endpoint.rb delete mode 100644 lib/api/entities/virtual_registries/packages/maven/cache/entry.rb delete mode 100644 lib/api/entities/virtual_registries/packages/maven/registry.rb delete mode 100644 lib/api/entities/virtual_registries/packages/maven/upstream.rb delete mode 100644 lib/api/virtual_registries/packages/maven/cache/entries.rb delete mode 100644 lib/api/virtual_registries/packages/maven/endpoints.rb delete mode 100644 lib/api/virtual_registries/packages/maven/registries.rb delete mode 100644 lib/api/virtual_registries/packages/maven/upstreams.rb delete mode 100644 lib/gitlab/background_migration/backfill_root_namespace_cluster_agent_mappings.rb create mode 100644 spec/components/rapid_diffs/streaming_error_component_spec.rb delete mode 100644 spec/features/api/virtual_registries/packages/maven_spec.rb create mode 100644 spec/frontend/diffs/stores/file_browser_spec.js create mode 100644 spec/frontend/rapid_diffs/streaming_error_spec.js delete mode 100644 spec/lib/api/entities/virtual_registries/packages/maven/cache/entry_spec.rb delete mode 100644 spec/lib/api/entities/virtual_registries/packages/maven/registry_spec.rb delete mode 100644 spec/lib/api/entities/virtual_registries/packages/maven/upstream_spec.rb delete mode 100644 spec/migrations/20240711035245_queue_backfill_root_namespace_cluster_agent_mappings_again_spec.rb delete mode 100644 spec/requests/api/virtual_registries/packages/maven/cache/entries_spec.rb delete mode 100644 spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb delete mode 100644 spec/requests/api/virtual_registries/packages/maven/registries_spec.rb delete mode 100644 spec/requests/api/virtual_registries/packages/maven/upstreams_spec.rb delete mode 100644 spec/support/shared_contexts/requests/api/maven_vreg_shared_context.rb delete mode 100644 spec/support/shared_examples/requests/api/virtual_registries/maven_packages_shared_examples.rb diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 193cf7ca6f8..27a907dec8a 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -6bd8110489a44adeacff1897a757d8ea52bf4f87 +ee1bf60a8952f6e9b4f3bf7627ac671ef1427cad diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index b1a8b7b864e..648c04b679f 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -14.40.0 +14.41.0 diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index c2aea4a7074..82f5eb829bd 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -4,6 +4,7 @@ import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import { debounce, throttle } from 'lodash'; // eslint-disable-next-line no-restricted-imports import { mapState, mapGetters, mapActions } from 'vuex'; +import { mapState as mapPiniaState, mapActions as mapPiniaActions } from 'pinia'; import FindingsDrawer from 'ee_component/diffs/components/shared/findings_drawer.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { convertToGraphQLId } from '~/graphql_shared/utils'; @@ -18,7 +19,7 @@ import { import { createAlert } from '~/alert'; import { InternalEvents } from '~/tracking'; import { helpPagePath } from '~/helpers/help_page_helper'; -import { parseBoolean, handleLocationHash } from '~/lib/utils/common_utils'; +import { parseBoolean, handleLocationHash, getCookie } from '~/lib/utils/common_utils'; import { BV_HIDE_TOOLTIP, DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import { Mousetrap } from '~/lib/mousetrap'; import { updateHistory, getLocationHash } from '~/lib/utils/url_utility'; @@ -27,9 +28,9 @@ import { __ } from '~/locale'; import notesEventHub from '~/notes/event_hub'; import { DynamicScroller, DynamicScrollerItem } from 'vendor/vue-virtual-scroller'; import getMRCodequalityAndSecurityReports from 'ee_else_ce/diffs/components/graphql/get_mr_codequality_and_security_reports.query.graphql'; +import { useFileBrowser } from '~/diffs/stores/file_browser'; import { sortFindingsByFile } from '../utils/sort_findings_by_file'; import { - MR_TREE_SHOW_KEY, ALERT_OVERFLOW_HIDDEN, ALERT_MERGE_CONFLICT, ALERT_COLLAPSED_FILES, @@ -44,6 +45,7 @@ import { TRACKING_MULTIPLE_FILES_MODE, EVT_MR_PREPARED, EVT_DISCUSSIONS_ASSIGNED, + FILE_BROWSER_VISIBLE, } from '../constants'; import { isCollapsed } from '../utils/diff_file'; @@ -243,7 +245,6 @@ export default { 'showWhitespace', 'targetBranchName', 'branchName', - 'showTreeList', 'addedLines', 'removedLines', ]), @@ -259,6 +260,7 @@ export default { ]), ...mapGetters(['isNotesFetched', 'getNoteableData']), ...mapGetters('findingsDrawer', ['activeDrawer']), + ...mapPiniaState(useFileBrowser, ['fileBrowserVisible']), diffs() { if (!this.viewDiffsFileByFile) { return this.diffFiles; @@ -316,7 +318,7 @@ export default { return convertToGraphQLId('MergeRequest', this.getNoteableData.id); }, renderFileTree() { - return this.renderDiffFiles && this.showTreeList; + return this.renderDiffFiles && this.fileBrowserVisible; }, hideTooltips() { const hide = () => { @@ -455,12 +457,10 @@ export default { 'setCurrentFileHash', 'setHighlightedRow', 'goToFile', - 'setShowTreeList', 'navigateToDiffFileIndex', 'setFileByFile', 'disableVirtualScroller', 'fetchLinkedFile', - 'toggleTreeList', 'expandAllFiles', 'collapseAllFiles', 'setDiffViewType', @@ -468,6 +468,7 @@ export default { 'goToFile', ]), ...mapActions('findingsDrawer', ['setDrawer']), + ...mapPiniaActions(useFileBrowser, ['setFileBrowserVisibility', 'toggleFileBrowserVisibility']), closeDrawer() { this.setDrawer({}); }, @@ -684,16 +685,14 @@ export default { } }, setTreeDisplay() { - const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY); - let showTreeList = true; - - if (storedTreeShow !== null) { - showTreeList = parseBoolean(storedTreeShow); - } else if (!bp.isDesktop() || (!this.isBatchLoading && this.flatBlobsList.length <= 1)) { - showTreeList = false; + const hideBrowserPreference = getCookie(FILE_BROWSER_VISIBLE); + if (hideBrowserPreference) { + this.setFileBrowserVisibility(parseBoolean(hideBrowserPreference)); + } else { + const shouldHide = + !bp.isDesktop() || (!this.isBatchLoading && this.flatBlobsList.length <= 1); + this.setFileBrowserVisibility(!shouldHide); } - - return this.setShowTreeList({ showTreeList, saving: false }); }, async scrollVirtualScrollerToFileHash(hash) { const index = this.diffFiles.findIndex((f) => f.file_hash === hash); @@ -741,7 +740,7 @@ export default { } }, fileTreeToggled() { - this.toggleTreeList(); + this.toggleFileBrowserVisibility(); this.adjustView(); }, isDiffViewActive(item) { diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue index a0b507e07e2..32a5ac46b0a 100644 --- a/app/assets/javascripts/diffs/components/compare_versions.vue +++ b/app/assets/javascripts/diffs/components/compare_versions.vue @@ -10,6 +10,7 @@ import { } from '@gitlab/ui'; // eslint-disable-next-line no-restricted-imports import { mapActions, mapGetters, mapState } from 'vuex'; +import { mapActions as mapPiniaActions, mapState as mapPiniaState } from 'pinia'; import { __ } from '~/locale'; import { setUrlParams } from '~/lib/utils/url_utility'; import { @@ -20,6 +21,7 @@ import { } from '~/behaviors/shortcuts/keybindings'; import { shouldDisableShortcuts } from '~/behaviors/shortcuts/shortcuts_toggle'; import { sanitize } from '~/lib/dompurify'; +import { useFileBrowser } from '~/diffs/stores/file_browser'; import CompareDropdownLayout from './compare_dropdown_layout.vue'; export default { @@ -47,12 +49,13 @@ export default { 'diffCompareDropdownTargetVersions', 'diffCompareDropdownSourceVersions', ]), - ...mapState('diffs', ['commit', 'showTreeList', 'startVersion', 'latestVersionPath']), + ...mapState('diffs', ['commit', 'startVersion', 'latestVersionPath']), + ...mapPiniaState(useFileBrowser, ['fileBrowserVisible']), toggleFileBrowserShortcutKey() { return shouldDisableShortcuts() ? null : keysFor(MR_TOGGLE_FILE_BROWSER)[0]; }, toggleFileBrowserTitle() { - return this.showTreeList ? __('Hide file browser') : __('Show file browser'); + return this.fileBrowserVisible ? __('Hide file browser') : __('Show file browser'); }, toggleFileBrowserTooltip() { const description = this.toggleFileBrowserTitle; @@ -113,7 +116,7 @@ export default { }, }, methods: { - ...mapActions('diffs', ['setShowTreeList']), + ...mapPiniaActions(useFileBrowser, ['toggleFileBrowserVisibility']), ...mapActions('diffs', ['moveToNeighboringCommit']), }, }; @@ -130,10 +133,10 @@ export default { data-testid="file-tree-button" :aria-label="toggleFileBrowserTitle" :aria-keyshortcuts="toggleFileBrowserShortcutKey" - :selected="showTreeList" - @click="setShowTreeList({ showTreeList: !showTreeList })" + :selected="fileBrowserVisible" + @click="toggleFileBrowserVisibility" > - +
{{ __('Viewing commit') }} diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js index 77ca5f2009a..209b8400226 100644 --- a/app/assets/javascripts/diffs/constants.js +++ b/app/assets/javascripts/diffs/constants.js @@ -28,7 +28,7 @@ export const LENGTH_OF_AVATAR_TOOLTIP = 17; export const DIFF_FILE_SYMLINK_MODE = '120000'; export const DIFF_FILE_DELETED_MODE = '0'; -export const MR_TREE_SHOW_KEY = 'mr_tree_show'; +export const FILE_BROWSER_VISIBLE = 'file_browser_visible'; export const TREE_TYPE = 'tree'; export const TREE_LIST_STORAGE_KEY = 'mr_diff_tree_list'; diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 0517a146b50..c7086352079 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -29,7 +29,6 @@ import { import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME, - MR_TREE_SHOW_KEY, TREE_LIST_STORAGE_KEY, OLD_LINE_KEY, NEW_LINE_KEY, @@ -718,18 +717,6 @@ export const scrollToFile = ({ state, commit, getters }, { path }) => { } }; -export const setShowTreeList = ({ commit }, { showTreeList, saving = true }) => { - commit(types.SET_SHOW_TREE_LIST, showTreeList); - - if (saving) { - localStorage.setItem(MR_TREE_SHOW_KEY, showTreeList); - } -}; - -export const toggleTreeList = ({ state, commit }) => { - commit(types.SET_SHOW_TREE_LIST, !state.showTreeList); -}; - export const openDiffFileCommentForm = ({ commit, getters }, formData) => { const form = getters.getCommentFormForDiffFile(formData.fileHash); diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js index 763c6084491..2a7efbbb74b 100644 --- a/app/assets/javascripts/diffs/store/modules/diff_state.js +++ b/app/assets/javascripts/diffs/store/modules/diff_state.js @@ -25,7 +25,6 @@ export default () => ({ diffViewType: INLINE_DIFF_VIEW_TYPE, tree: [], treeEntries: {}, - showTreeList: true, currentDiffFileId: '', projectPath: '', viewedDiffFileIds: {}, diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js index 2621aeaacd6..ec30ddb368e 100644 --- a/app/assets/javascripts/diffs/store/mutation_types.js +++ b/app/assets/javascripts/diffs/store/mutation_types.js @@ -23,7 +23,6 @@ export const REMOVE_LINE_DISCUSSIONS_FOR_FILE = 'REMOVE_LINE_DISCUSSIONS_FOR_FIL export const TOGGLE_FOLDER_OPEN = 'TOGGLE_FOLDER_OPEN'; export const SET_FOLDER_OPEN = 'SET_FOLDER_OPEN'; export const TREE_ENTRY_DIFF_LOADING = 'TREE_ENTRY_DIFF_LOADING'; -export const SET_SHOW_TREE_LIST = 'SET_SHOW_TREE_LIST'; export const SET_CURRENT_DIFF_FILE = 'SET_CURRENT_DIFF_FILE'; export const SET_DIFF_FILE_VIEWED = 'SET_DIFF_FILE_VIEWED'; export const SET_LINKED_FILE_HASH = 'SET_LINKED_FILE_HASH'; diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 9f219284375..4a80d3bbb67 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -297,9 +297,6 @@ export default { [types.TREE_ENTRY_DIFF_LOADING](state, { path, loading = true }) { state.treeEntries[path].diffLoading = loading; }, - [types.SET_SHOW_TREE_LIST](state, showTreeList) { - state.showTreeList = showTreeList; - }, [types.SET_CURRENT_DIFF_FILE](state, fileId) { state.currentDiffFileId = fileId; }, diff --git a/app/assets/javascripts/diffs/stores/file_browser.js b/app/assets/javascripts/diffs/stores/file_browser.js new file mode 100644 index 00000000000..8c59f8e2614 --- /dev/null +++ b/app/assets/javascripts/diffs/stores/file_browser.js @@ -0,0 +1,26 @@ +import { defineStore } from 'pinia'; +import { getCookie, parseBoolean, setCookie } from '~/lib/utils/common_utils'; +import { FILE_BROWSER_VISIBLE } from '~/diffs/constants'; + +export const useFileBrowser = defineStore('fileBrowser', { + state() { + return { + fileBrowserVisible: true, + }; + }, + actions: { + setFileBrowserVisibility(visible) { + this.fileBrowserVisible = visible; + }, + toggleFileBrowserVisibility() { + this.fileBrowserVisible = !this.fileBrowserVisible; + setCookie(FILE_BROWSER_VISIBLE, this.fileBrowserVisible); + }, + initFileBrowserVisibility() { + const visibilityPreference = getCookie(FILE_BROWSER_VISIBLE); + if (visibilityPreference) { + this.fileBrowserVisible = parseBoolean(visibilityPreference); + } + }, + }, +}); diff --git a/app/assets/javascripts/diffs/stores/legacy_diffs/actions.js b/app/assets/javascripts/diffs/stores/legacy_diffs/actions.js index 2b1ebb674b4..6235adeb87a 100644 --- a/app/assets/javascripts/diffs/stores/legacy_diffs/actions.js +++ b/app/assets/javascripts/diffs/stores/legacy_diffs/actions.js @@ -30,7 +30,6 @@ import { useNotes } from '~/notes/store/legacy_notes'; import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME, - MR_TREE_SHOW_KEY, TREE_LIST_STORAGE_KEY, OLD_LINE_KEY, NEW_LINE_KEY, @@ -718,18 +717,6 @@ export function scrollToFile({ path }) { } } -export function setShowTreeList({ showTreeList, saving = true }) { - this[types.SET_SHOW_TREE_LIST](showTreeList); - - if (saving) { - localStorage.setItem(MR_TREE_SHOW_KEY, showTreeList); - } -} - -export function toggleTreeList() { - this[types.SET_SHOW_TREE_LIST](!this.showTreeList); -} - export function openDiffFileCommentForm(formData) { const form = this.getCommentFormForDiffFile(formData.fileHash); diff --git a/app/assets/javascripts/diffs/stores/legacy_diffs/index.js b/app/assets/javascripts/diffs/stores/legacy_diffs/index.js index 481af7c825a..fcabf732d13 100644 --- a/app/assets/javascripts/diffs/stores/legacy_diffs/index.js +++ b/app/assets/javascripts/diffs/stores/legacy_diffs/index.js @@ -36,7 +36,6 @@ export const useLegacyDiffs = defineStore('legacyDiffs', { diffViewType: INLINE_DIFF_VIEW_TYPE, tree: [], treeEntries: {}, - showTreeList: true, currentDiffFileId: '', projectPath: '', viewedDiffFileIds: {}, diff --git a/app/assets/javascripts/diffs/stores/legacy_diffs/mutations.js b/app/assets/javascripts/diffs/stores/legacy_diffs/mutations.js index f3af0c55f9d..c2ba48d62fb 100644 --- a/app/assets/javascripts/diffs/stores/legacy_diffs/mutations.js +++ b/app/assets/javascripts/diffs/stores/legacy_diffs/mutations.js @@ -293,9 +293,6 @@ export default { [types.TREE_ENTRY_DIFF_LOADING]({ path, loading = true }) { this.treeEntries[path].diffLoading = loading; }, - [types.SET_SHOW_TREE_LIST](showTreeList) { - this.showTreeList = showTreeList; - }, [types.SET_CURRENT_DIFF_FILE](fileId) { this.currentDiffFileId = fileId; }, diff --git a/app/assets/javascripts/pages/projects/issues/show/index.js b/app/assets/javascripts/pages/projects/issues/show/index.js index a2ff45b454a..81b42f62b4e 100644 --- a/app/assets/javascripts/pages/projects/issues/show/index.js +++ b/app/assets/javascripts/pages/projects/issues/show/index.js @@ -1,3 +1,24 @@ -import { initShow } from '~/issues'; +import { issuableInitialDataById, isLegacyIssueType } from '~/issues/show/utils/issuable_data'; -initShow(); +const initLegacyIssuePage = async () => { + const [{ initShow }] = await Promise.all([import('~/issues')]); + initShow(); +}; + +const initWorkItemPage = async () => { + const [{ initWorkItemsRoot }] = await Promise.all([import('~/work_items')]); + + initWorkItemsRoot({ workItemType: 'issue' }); +}; + +const issuableData = issuableInitialDataById('js-issuable-app'); + +if ( + !isLegacyIssueType(issuableData) && + gon.features.workItemsViewPreference && + gon.current_user_use_work_items_view +) { + initWorkItemPage(); +} else { + initLegacyIssuePage(); +} diff --git a/app/assets/javascripts/rapid_diffs/app/index.js b/app/assets/javascripts/rapid_diffs/app/index.js index 81a2616b4c0..53e63859f55 100644 --- a/app/assets/javascripts/rapid_diffs/app/index.js +++ b/app/assets/javascripts/rapid_diffs/app/index.js @@ -4,6 +4,7 @@ import { DiffFile } from '~/rapid_diffs/diff_file'; import { DiffFileMounted } from '~/rapid_diffs/diff_file_mounted'; import { useDiffsList } from '~/rapid_diffs/stores/diffs_list'; import { initFileBrowser } from '~/rapid_diffs/app/init_file_browser'; +import { StreamingError } from '~/rapid_diffs/streaming_error'; // This facade interface joins together all the bits and pieces of Rapid Diffs: DiffFile, Settings, File browser, etc. // It's a unified entrypoint for Rapid Diffs and all external communications should happen through this interface. @@ -30,6 +31,7 @@ class RapidDiffsFacade { #registerCustomElements() { customElements.define('diff-file', this.DiffFileImplementation); customElements.define('diff-file-mounted', DiffFileMounted); + customElements.define('streaming-error', StreamingError); } } diff --git a/app/assets/javascripts/rapid_diffs/streaming_error.js b/app/assets/javascripts/rapid_diffs/streaming_error.js new file mode 100644 index 00000000000..916832fc6ff --- /dev/null +++ b/app/assets/javascripts/rapid_diffs/streaming_error.js @@ -0,0 +1,11 @@ +import { createAlert } from '~/alert'; +import { __ } from '~/locale'; + +export class StreamingError extends HTMLElement { + connectedCallback() { + const message = __(`Could not fetch all changes. Try reloading the page.`); + // eslint-disable-next-line no-console + console.error(`Failed to stream diffs: ${this.getAttribute('message')}`); + createAlert({ message }); + } +} diff --git a/app/assets/javascripts/whats_new/components/feature.vue b/app/assets/javascripts/whats_new/components/feature.vue index 3daabcc2798..1900d072a3e 100644 --- a/app/assets/javascripts/whats_new/components/feature.vue +++ b/app/assets/javascripts/whats_new/components/feature.vue @@ -3,6 +3,7 @@ import { GlBadge, GlIcon, GlLink, GlButton } from '@gitlab/ui'; import { localeDateFormat, newDate } from '~/lib/utils/datetime_utility'; import SafeHtml from '~/vue_shared/directives/safe_html'; +import { slugify } from '~/lib/utils/text_utility'; export default { components: { @@ -27,6 +28,9 @@ export default { } return localeDateFormat.asDate.format(newDate(this.feature.published_at)); }, + descriptionId() { + return `${slugify(this.feature.name)}-feature-description`; + }, }, }; @@ -40,6 +44,8 @@ export default { class="gl-block" data-testid="whats-new-image-link" data-track-action="click_whats_new_item" + :aria-hidden="true" + tabindex="-1" :data-track-label="feature.name" :data-track-property="feature.documentation_link" > @@ -76,6 +82,7 @@ export default {
@@ -85,6 +92,7 @@ export default { data-track-action="click_whats_new_item" :data-track-label="feature.name" :data-track-property="feature.documentation_link" + :aria-describedby="descriptionId" > {{ __('Learn more') }} diff --git a/app/assets/javascripts/work_items/components/design_management/design_preview/design_details.vue b/app/assets/javascripts/work_items/components/design_management/design_preview/design_details.vue index 21ecf14ec42..1e5c8e56752 100644 --- a/app/assets/javascripts/work_items/components/design_management/design_preview/design_details.vue +++ b/app/assets/javascripts/work_items/components/design_management/design_preview/design_details.vue @@ -100,7 +100,6 @@ export default { prevCurrentUserTodos: null, maxScale: DEFAULT_MAX_SCALE, workItemId: '', - workItemTitle: '', isSidebarOpen: true, }; }, @@ -360,7 +359,6 @@ export default { >

- {{ workItemTitle }} + {{ design.issue.title }} - + {{ design.filename }} e Gitlab::AppLogger.error("Error streaming diffs: #{e.message}") - response.stream.write e.message + error_component = ::RapidDiffs::StreamingErrorComponent.new(message: e.message) + response.stream.write error_component.render_in(view_context) ensure response.stream.close end diff --git a/app/services/ml/create_experiment_service.rb b/app/services/ml/create_experiment_service.rb index ed312b6e965..8c49856fe15 100644 --- a/app/services/ml/create_experiment_service.rb +++ b/app/services/ml/create_experiment_service.rb @@ -2,7 +2,7 @@ module Ml class CreateExperimentService - def initialize(project, experiment_name, user = nil) + def initialize(project, experiment_name, user) @project = project @name = experiment_name @user = user diff --git a/app/services/ml/create_model_version_service.rb b/app/services/ml/create_model_version_service.rb index d9641b1e60f..11e07ff3537 100644 --- a/app/services/ml/create_model_version_service.rb +++ b/app/services/ml/create_model_version_service.rb @@ -45,7 +45,7 @@ module Ml ServiceResponse.success(message: [], payload: { model_version: @model_version }) end - rescue ActiveRecord::RecordInvalid => e + rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique => e ServiceResponse.error(message: [e.message], payload: { model_version: nil }) rescue ModelVersionCreationError => e ServiceResponse.error(message: e.errors, payload: { model_version: nil }) diff --git a/db/docs/batched_background_migrations/backfill_root_namespace_cluster_agent_mappings.yml b/db/docs/batched_background_migrations/backfill_root_namespace_cluster_agent_mappings.yml deleted file mode 100644 index 29551160222..00000000000 --- a/db/docs/batched_background_migrations/backfill_root_namespace_cluster_agent_mappings.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -migration_job_name: BackfillRootNamespaceClusterAgentMappings -description: Creates mappings between root namespaces and all cluster agents - within these namespaces that have remote development enabled -feature_category: remote_development -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/159051 -milestone: '17.2' -queued_migration_version: 20240711035245 -# Replace with the approximate date you think it's best to ensure the completion of this BBM. -finalized_by: 20241023210409 diff --git a/db/docs/deleted_tables/remote_development_namespace_cluster_agent_mappings.yml b/db/docs/deleted_tables/remote_development_namespace_cluster_agent_mappings.yml new file mode 100644 index 00000000000..fcb2a5e200b --- /dev/null +++ b/db/docs/deleted_tables/remote_development_namespace_cluster_agent_mappings.yml @@ -0,0 +1,15 @@ +--- +table_name: remote_development_namespace_cluster_agent_mappings +classes: + - RemoteDevelopment::RemoteDevelopmentNamespaceClusterAgentMapping +feature_categories: + - workspaces +description: Moved to deleted because it got renamed to namespace_cluster_agent_mappings +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145101 +milestone: '16.10' +gitlab_schema: gitlab_main_cell +sharding_key: + namespace_id: namespaces +table_size: small +removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181926 +removed_in_milestone: '17.10' diff --git a/db/docs/remote_development_namespace_cluster_agent_mappings.yml b/db/docs/namespace_cluster_agent_mappings.yml similarity index 62% rename from db/docs/remote_development_namespace_cluster_agent_mappings.yml rename to db/docs/namespace_cluster_agent_mappings.yml index 21a94c0092b..465929161c0 100644 --- a/db/docs/remote_development_namespace_cluster_agent_mappings.yml +++ b/db/docs/namespace_cluster_agent_mappings.yml @@ -1,14 +1,13 @@ --- -table_name: remote_development_namespace_cluster_agent_mappings +table_name: namespace_cluster_agent_mappings classes: -- RemoteDevelopment::RemoteDevelopmentNamespaceClusterAgentMapping +- RemoteDevelopment::NamespaceClusterAgentMapping feature_categories: - workspaces description: This table records associations between Namespaces and Cluster Agents - as a part of supporting group-cluster agent association in the context of Remote - Development Workspaces -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145101 -milestone: '16.10' + as a part of supporting group-cluster agent association in the context of Workspaces +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/181926 +milestone: '17.10' gitlab_schema: gitlab_main_cell sharding_key: namespace_id: namespaces diff --git a/db/docs/packages_debian_project_component_files.yml b/db/docs/packages_debian_project_component_files.yml index e79d4657684..9bb4574b9a8 100644 --- a/db/docs/packages_debian_project_component_files.yml +++ b/db/docs/packages_debian_project_component_files.yml @@ -17,5 +17,4 @@ desired_sharding_key: table: packages_debian_project_components sharding_key: project_id belongs_to: component - awaiting_backfill_on_parent: true table_size: small diff --git a/db/docs/packages_debian_project_components.yml b/db/docs/packages_debian_project_components.yml index 4a03a0dcf3f..0d457441cf3 100644 --- a/db/docs/packages_debian_project_components.yml +++ b/db/docs/packages_debian_project_components.yml @@ -8,14 +8,6 @@ description: Debian package project-level distribution components introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51732 milestone: '13.9' gitlab_schema: gitlab_main_cell -desired_sharding_key: - project_id: - references: projects - backfill_via: - parent: - foreign_key: distribution_id - table: packages_debian_project_distributions - sharding_key: project_id - belongs_to: distribution -desired_sharding_key_migration_job_name: BackfillPackagesDebianProjectComponentsProjectId table_size: small +sharding_key: + project_id: projects diff --git a/db/docs/packages_maven_metadata.yml b/db/docs/packages_maven_metadata.yml index b27e84bb0db..d8e2adf28d2 100644 --- a/db/docs/packages_maven_metadata.yml +++ b/db/docs/packages_maven_metadata.yml @@ -8,14 +8,6 @@ description: Maven package metadata introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/6607 milestone: '11.3' gitlab_schema: gitlab_main_cell -desired_sharding_key: - project_id: - references: projects - backfill_via: - parent: - foreign_key: package_id - table: packages_packages - sharding_key: project_id - belongs_to: package -desired_sharding_key_migration_job_name: BackfillPackagesMavenMetadataProjectId table_size: small +sharding_key: + project_id: projects diff --git a/db/migrate/20250219055827_rename_remote_development_namespace_cluster_agent_mapping.rb b/db/migrate/20250219055827_rename_remote_development_namespace_cluster_agent_mapping.rb new file mode 100644 index 00000000000..4d99ae53743 --- /dev/null +++ b/db/migrate/20250219055827_rename_remote_development_namespace_cluster_agent_mapping.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class RenameRemoteDevelopmentNamespaceClusterAgentMapping < Gitlab::Database::Migration[2.2] + milestone '17.10' + + def up + rename_table_safely(:remote_development_namespace_cluster_agent_mappings, :namespace_cluster_agent_mappings) + end + + def down + undo_rename_table_safely(:remote_development_namespace_cluster_agent_mappings, :namespace_cluster_agent_mappings) + end +end diff --git a/db/post_migrate/20240711035245_queue_backfill_root_namespace_cluster_agent_mappings_again.rb b/db/post_migrate/20240711035245_queue_backfill_root_namespace_cluster_agent_mappings_again.rb deleted file mode 100644 index ec0c4cd76b0..00000000000 --- a/db/post_migrate/20240711035245_queue_backfill_root_namespace_cluster_agent_mappings_again.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -# See https://docs.gitlab.com/ee/development/migration_style_guide.html -# for more information on how to write migrations for GitLab. - -class QueueBackfillRootNamespaceClusterAgentMappingsAgain < Gitlab::Database::Migration[2.2] - milestone '17.2' - - # Configure the `gitlab_schema` to perform data manipulation (DML). - # Visit: https://docs.gitlab.com/ee/development/database/migrations_for_multiple_databases.html - restrict_gitlab_migration gitlab_schema: :gitlab_main_cell - MIGRATION = "BackfillRootNamespaceClusterAgentMappings" - DELAY_INTERVAL = 2.minutes - BATCH_SIZE = 1000 - SUB_BATCH_SIZE = 100 - - # Add dependent 'batched_background_migrations.queued_migration_version' values. - # DEPENDENT_BATCHED_BACKGROUND_MIGRATIONS = [] - - def up - delete_batched_background_migration(MIGRATION, :remote_development_agent_configs, :id, []) - - queue_batched_background_migration( - MIGRATION, - :remote_development_agent_configs, - :id, - job_interval: DELAY_INTERVAL, - batch_size: BATCH_SIZE, - sub_batch_size: SUB_BATCH_SIZE - ) - end - - def down - delete_batched_background_migration(MIGRATION, :remote_development_agent_configs, :id, []) - end -end diff --git a/db/post_migrate/20250224063503_delete_remote_development_namespace_cluster_agent_mapping_view.rb b/db/post_migrate/20250224063503_delete_remote_development_namespace_cluster_agent_mapping_view.rb new file mode 100644 index 00000000000..16e31f84d20 --- /dev/null +++ b/db/post_migrate/20250224063503_delete_remote_development_namespace_cluster_agent_mapping_view.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class DeleteRemoteDevelopmentNamespaceClusterAgentMappingView < Gitlab::Database::Migration[2.2] + milestone '17.10' + disable_ddl_transaction! + + def up + finalize_table_rename(:remote_development_namespace_cluster_agent_mappings, :namespace_cluster_agent_mappings) + end + + def down + undo_finalize_table_rename(:remote_development_namespace_cluster_agent_mappings, :namespace_cluster_agent_mappings) + end +end diff --git a/db/post_migrate/20250225043936_add_packages_debian_project_components_project_id_not_null.rb b/db/post_migrate/20250225043936_add_packages_debian_project_components_project_id_not_null.rb new file mode 100644 index 00000000000..3935fea5582 --- /dev/null +++ b/db/post_migrate/20250225043936_add_packages_debian_project_components_project_id_not_null.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class AddPackagesDebianProjectComponentsProjectIdNotNull < Gitlab::Database::Migration[2.2] + milestone '17.10' + disable_ddl_transaction! + + def up + add_not_null_constraint :packages_debian_project_components, :project_id + end + + def down + remove_not_null_constraint :packages_debian_project_components, :project_id + end +end diff --git a/db/post_migrate/20250225044336_add_packages_maven_metadata_project_id_not_null.rb b/db/post_migrate/20250225044336_add_packages_maven_metadata_project_id_not_null.rb new file mode 100644 index 00000000000..a4c57bb5590 --- /dev/null +++ b/db/post_migrate/20250225044336_add_packages_maven_metadata_project_id_not_null.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class AddPackagesMavenMetadataProjectIdNotNull < Gitlab::Database::Migration[2.2] + milestone '17.10' + disable_ddl_transaction! + + def up + add_not_null_constraint :packages_maven_metadata, :project_id + end + + def down + remove_not_null_constraint :packages_maven_metadata, :project_id + end +end diff --git a/db/post_migrate/20250304084537_add_dora_daily_metrics_project_id_date_index.rb b/db/post_migrate/20250304084537_add_dora_daily_metrics_project_id_date_index.rb new file mode 100644 index 00000000000..8311d063ec9 --- /dev/null +++ b/db/post_migrate/20250304084537_add_dora_daily_metrics_project_id_date_index.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddDoraDailyMetricsProjectIdDateIndex < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + + milestone '17.10' + + INDEX_NAME = 'index_dora_daily_metrics_on_project_id_and_date' + OLD_INDEX_NAME = 'index_dora_daily_metrics_on_project_id' + + def up + add_concurrent_index(:dora_daily_metrics, [:project_id, :date], name: INDEX_NAME) + remove_concurrent_index_by_name(:dora_daily_metrics, OLD_INDEX_NAME) + end + + def down + add_concurrent_index(:dora_daily_metrics, :project_id, name: OLD_INDEX_NAME) + remove_concurrent_index_by_name(:dora_daily_metrics, INDEX_NAME) + end +end diff --git a/db/schema_migrations/20240711035245 b/db/schema_migrations/20240711035245 deleted file mode 100644 index e5581098857..00000000000 --- a/db/schema_migrations/20240711035245 +++ /dev/null @@ -1 +0,0 @@ -5bd102fd3ff8463e40c4467317d3f146511ef683945e7c2cabaf8bff7abd3d04 \ No newline at end of file diff --git a/db/schema_migrations/20250219055827 b/db/schema_migrations/20250219055827 new file mode 100644 index 00000000000..388e56e8551 --- /dev/null +++ b/db/schema_migrations/20250219055827 @@ -0,0 +1 @@ +166459c440ed6e868c66ddc3b79a5aae27b20d2bc21e489123b6676f8cd97841 \ No newline at end of file diff --git a/db/schema_migrations/20250224063503 b/db/schema_migrations/20250224063503 new file mode 100644 index 00000000000..85f846fcfe0 --- /dev/null +++ b/db/schema_migrations/20250224063503 @@ -0,0 +1 @@ +dbdfbe9e0140762dcb606ee83ee5ac6ee6f2f0c4b9a60979f73394a9f81ec2e6 \ No newline at end of file diff --git a/db/schema_migrations/20250225043936 b/db/schema_migrations/20250225043936 new file mode 100644 index 00000000000..b1170f34b8a --- /dev/null +++ b/db/schema_migrations/20250225043936 @@ -0,0 +1 @@ +247a50b157e3bf43bc9a70ae74b1f2be18dfec57f9796cac6813a459a6f0cfee \ No newline at end of file diff --git a/db/schema_migrations/20250225044336 b/db/schema_migrations/20250225044336 new file mode 100644 index 00000000000..717fbe6dce8 --- /dev/null +++ b/db/schema_migrations/20250225044336 @@ -0,0 +1 @@ +0bd459ee0a6ad163f1e2e6ff856328f6898174736af667ec2c9f96ed18cffb1f \ No newline at end of file diff --git a/db/schema_migrations/20250304084537 b/db/schema_migrations/20250304084537 new file mode 100644 index 00000000000..75458872294 --- /dev/null +++ b/db/schema_migrations/20250304084537 @@ -0,0 +1 @@ +c13b43a5493f93febb1d54451741f1e39113a5b8b1d6959408ef4390eba92388 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index e2f601274b1..58f5d22415f 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -16913,6 +16913,24 @@ CREATE TABLE namespace_ci_cd_settings ( allow_stale_runner_pruning boolean DEFAULT false NOT NULL ); +CREATE TABLE namespace_cluster_agent_mappings ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + namespace_id bigint NOT NULL, + cluster_agent_id bigint NOT NULL, + creator_id bigint +); + +CREATE SEQUENCE namespace_cluster_agent_mappings_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE namespace_cluster_agent_mappings_id_seq OWNED BY namespace_cluster_agent_mappings.id; + CREATE TABLE namespace_commit_emails ( id bigint NOT NULL, user_id bigint NOT NULL, @@ -18100,7 +18118,8 @@ CREATE TABLE packages_debian_project_components ( distribution_id bigint NOT NULL, name text NOT NULL, project_id bigint, - CONSTRAINT check_517559f298 CHECK ((char_length(name) <= 255)) + CONSTRAINT check_517559f298 CHECK ((char_length(name) <= 255)), + CONSTRAINT check_6c727037a7 CHECK ((project_id IS NOT NULL)) ); CREATE SEQUENCE packages_debian_project_components_id_seq @@ -18247,7 +18266,8 @@ CREATE TABLE packages_maven_metadata ( app_name character varying NOT NULL, app_version character varying, path character varying(512) NOT NULL, - project_id bigint + project_id bigint, + CONSTRAINT check_bf287ce98c CHECK ((project_id IS NOT NULL)) ); CREATE SEQUENCE packages_maven_metadata_id_seq @@ -20745,24 +20765,6 @@ CREATE SEQUENCE releases_id_seq ALTER SEQUENCE releases_id_seq OWNED BY releases.id; -CREATE TABLE remote_development_namespace_cluster_agent_mappings ( - id bigint NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - namespace_id bigint NOT NULL, - cluster_agent_id bigint NOT NULL, - creator_id bigint -); - -CREATE SEQUENCE remote_development_namespace_cluster_agent_mappings_id_seq - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - -ALTER SEQUENCE remote_development_namespace_cluster_agent_mappings_id_seq OWNED BY remote_development_namespace_cluster_agent_mappings.id; - CREATE TABLE remote_mirrors ( id bigint NOT NULL, project_id bigint, @@ -25891,6 +25893,8 @@ ALTER TABLE ONLY namespace_admin_notes ALTER COLUMN id SET DEFAULT nextval('name ALTER TABLE ONLY namespace_bans ALTER COLUMN id SET DEFAULT nextval('namespace_bans_id_seq'::regclass); +ALTER TABLE ONLY namespace_cluster_agent_mappings ALTER COLUMN id SET DEFAULT nextval('namespace_cluster_agent_mappings_id_seq'::regclass); + ALTER TABLE ONLY namespace_commit_emails ALTER COLUMN id SET DEFAULT nextval('namespace_commit_emails_id_seq'::regclass); ALTER TABLE ONLY namespace_import_users ALTER COLUMN id SET DEFAULT nextval('namespace_import_users_id_seq'::regclass); @@ -26167,8 +26171,6 @@ ALTER TABLE ONLY release_links ALTER COLUMN id SET DEFAULT nextval('release_link ALTER TABLE ONLY releases ALTER COLUMN id SET DEFAULT nextval('releases_id_seq'::regclass); -ALTER TABLE ONLY remote_development_namespace_cluster_agent_mappings ALTER COLUMN id SET DEFAULT nextval('remote_development_namespace_cluster_agent_mappings_id_seq'::regclass); - ALTER TABLE ONLY remote_mirrors ALTER COLUMN id SET DEFAULT nextval('remote_mirrors_id_seq'::regclass); ALTER TABLE ONLY required_code_owners_sections ALTER COLUMN id SET DEFAULT nextval('required_code_owners_sections_id_seq'::regclass); @@ -28470,6 +28472,9 @@ ALTER TABLE ONLY namespace_bans ALTER TABLE ONLY namespace_ci_cd_settings ADD CONSTRAINT namespace_ci_cd_settings_pkey PRIMARY KEY (namespace_id); +ALTER TABLE ONLY namespace_cluster_agent_mappings + ADD CONSTRAINT namespace_cluster_agent_mappings_pkey PRIMARY KEY (id); + ALTER TABLE ONLY namespace_commit_emails ADD CONSTRAINT namespace_commit_emails_pkey PRIMARY KEY (id); @@ -29019,9 +29024,6 @@ ALTER TABLE releases ALTER TABLE ONLY releases ADD CONSTRAINT releases_pkey PRIMARY KEY (id); -ALTER TABLE ONLY remote_development_namespace_cluster_agent_mappings - ADD CONSTRAINT remote_development_namespace_cluster_agent_mappings_pkey PRIMARY KEY (id); - ALTER TABLE ONLY remote_mirrors ADD CONSTRAINT remote_mirrors_pkey PRIMARY KEY (id); @@ -31095,9 +31097,9 @@ CREATE UNIQUE INDEX i_duo_workflows_events_on_correlation_id ON duo_workflows_ev CREATE INDEX i_gitlab_subscription_histories_on_namespace_change_type_plan ON gitlab_subscription_histories USING btree (namespace_id, change_type, hosted_plan_id); -CREATE INDEX i_namespace_cluster_agent_mappings_on_cluster_agent_id ON remote_development_namespace_cluster_agent_mappings USING btree (cluster_agent_id); +CREATE INDEX i_namespace_cluster_agent_mappings_on_cluster_agent_id ON namespace_cluster_agent_mappings USING btree (cluster_agent_id); -CREATE INDEX i_namespace_cluster_agent_mappings_on_creator_id ON remote_development_namespace_cluster_agent_mappings USING btree (creator_id); +CREATE INDEX i_namespace_cluster_agent_mappings_on_creator_id ON namespace_cluster_agent_mappings USING btree (creator_id); CREATE INDEX i_organization_cluster_agent_mappings_on_creator_id ON organization_cluster_agent_mappings USING btree (creator_id); @@ -32981,7 +32983,7 @@ CREATE UNIQUE INDEX index_dora_configurations_on_project_id ON dora_configuratio CREATE UNIQUE INDEX index_dora_daily_metrics_on_environment_id_and_date ON dora_daily_metrics USING btree (environment_id, date); -CREATE INDEX index_dora_daily_metrics_on_project_id ON dora_daily_metrics USING btree (project_id); +CREATE INDEX index_dora_daily_metrics_on_project_id_and_date ON dora_daily_metrics USING btree (project_id, date); CREATE UNIQUE INDEX index_dora_performance_scores_on_project_id_and_date ON dora_performance_scores USING btree (project_id, date); @@ -36441,7 +36443,7 @@ CREATE UNIQUE INDEX unique_merge_request_metrics_by_merge_request_id ON merge_re CREATE INDEX unique_ml_model_versions_on_model_id_and_id ON ml_model_versions USING btree (model_id, id DESC); -CREATE UNIQUE INDEX unique_namespace_cluster_agent_mappings_for_agent_association ON remote_development_namespace_cluster_agent_mappings USING btree (namespace_id, cluster_agent_id); +CREATE UNIQUE INDEX unique_namespace_cluster_agent_mappings_for_agent_association ON namespace_cluster_agent_mappings USING btree (namespace_id, cluster_agent_id); CREATE UNIQUE INDEX unique_organizations_on_path_case_insensitive ON organizations USING btree (lower(path)); @@ -39016,7 +39018,7 @@ ALTER TABLE ONLY audit_events_amazon_s3_configurations ALTER TABLE ONLY issue_customer_relations_contacts ADD CONSTRAINT fk_0c0037f723 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE; -ALTER TABLE ONLY remote_development_namespace_cluster_agent_mappings +ALTER TABLE ONLY namespace_cluster_agent_mappings ADD CONSTRAINT fk_0c483ecb9d FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE; ALTER TABLE ONLY zoekt_replicas @@ -39067,7 +39069,7 @@ ALTER TABLE ONLY protected_environment_deploy_access_levels ALTER TABLE ONLY cluster_agent_migrations ADD CONSTRAINT fk_1211a345fb FOREIGN KEY (agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE; -ALTER TABLE ONLY remote_development_namespace_cluster_agent_mappings +ALTER TABLE ONLY namespace_cluster_agent_mappings ADD CONSTRAINT fk_124d8167c5 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL; ALTER TABLE ONLY cluster_agent_url_configurations @@ -40441,7 +40443,7 @@ ALTER TABLE ONLY ci_sources_pipelines ALTER TABLE ONLY packages_maven_metadata ADD CONSTRAINT fk_be88aed360 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE; -ALTER TABLE ONLY remote_development_namespace_cluster_agent_mappings +ALTER TABLE ONLY namespace_cluster_agent_mappings ADD CONSTRAINT fk_be8e9c740f FOREIGN KEY (cluster_agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE; ALTER TABLE ONLY oauth_device_grants diff --git a/doc/api/graphql/reference/_index.md b/doc/api/graphql/reference/_index.md index cee273920e4..8883225401f 100644 --- a/doc/api/graphql/reference/_index.md +++ b/doc/api/graphql/reference/_index.md @@ -4024,6 +4024,7 @@ Input type: `CreateComplianceRequirementInput` | ---- | ---- | ----------- | | `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | | `complianceFrameworkId` | [`ComplianceManagementFrameworkID!`](#compliancemanagementframeworkid) | Global ID of the compliance framework of the new requirement. | +| `controls` | [`[ComplianceRequirementsControlInput!]`](#compliancerequirementscontrolinput) | Controls to add to the compliance requirement. | | `params` | [`ComplianceRequirementInput!`](#compliancerequirementinput) | Parameters to update the compliance requirement with. | #### Fields @@ -26427,7 +26428,7 @@ GPG signature for a signed commit. | `descendantGroupsCount` | [`Int!`](#int) | Count of direct descendant groups of this group. | | `description` | [`String`](#string) | Description of the namespace. | | `descriptionHtml` | [`String`](#string) | GitLab Flavored Markdown rendering of `description`. | -| `dora` | [`Dora`](#dora) | Group's DORA metrics. | +| `dora` | [`GroupDora`](#groupdora) | Group's DORA metrics. | | `duoFeaturesEnabled` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 16.10. **Status**: Experiment. Indicates whether GitLab Duo features are enabled for the group. | | `emailsDisabled` | [`Boolean`](#boolean) | Indicates if a group has email notifications disabled. | | `emailsEnabled` | [`Boolean`](#boolean) | Indicates if a group has email notifications enabled. | @@ -28175,6 +28176,58 @@ Represents an external destination to stream group level audit events. | ---- | ---- | ----------- | | `egressNodes` | [`EgressNodeConnection`](#egressnodeconnection) | Data nodes. (see [Connections](#connections)) | +### `GroupDora` + +All information related to group DORA metrics. + +#### Fields with arguments + +##### `GroupDora.metrics` + +DORA metrics for the current group or project. + +Returns [`[DoraMetric!]`](#dorametric). + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `endDate` | [`Date`](#date) | Date range to end at. Default is the current date. | +| `environmentTiers` | [`[DeploymentTier!]`](#deploymenttier) | Deployment tiers of the environments to return. Defaults to `[PRODUCTION]`. | +| `interval` | [`DoraMetricBucketingInterval`](#dorametricbucketinginterval) | How the metric should be aggregated. Defaults to `DAILY`. In the case of `ALL`, the `date` field in the response will be `null`. | +| `startDate` | [`Date`](#date) | Date range to start from. Default is 3 months ago. | + +##### `GroupDora.projects` + +Projects within this group with at least 1 DORA metric for given period. + +Returns [`ProjectConnection!`](#projectconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#pagination-arguments): +`before: String`, `after: String`, `first: Int`, and `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `complianceFrameworkFilters` | [`ComplianceFrameworkFilters`](#complianceframeworkfilters) | Filters applied when selecting a compliance framework. | +| `endDate` | [`Date!`](#date) | Date range to end DORA lookup at. | +| `hasCodeCoverage` | [`Boolean`](#boolean) | Returns only the projects which have code coverage. | +| `hasVulnerabilities` | [`Boolean`](#boolean) | Returns only the projects which have vulnerabilities. | +| `ids` | [`[ID!]`](#id) | Filter projects by IDs. | +| `includeArchived` | [`Boolean`](#boolean) | Include also archived projects. | +| `includeSiblingProjects` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 17.2. **Status**: Experiment. Include also projects from parent group. | +| `includeSubgroups` | [`Boolean`](#boolean) | Include also subgroup projects. | +| `notAimedForDeletion` | [`Boolean`](#boolean) | Include projects that are not aimed for deletion. | +| `sbomComponentId` | [`ID`](#id) | Return only the projects related to the specified SBOM component. | +| `search` | [`String`](#string) | Search project with most similar names or paths. | +| `sort` | [`NamespaceProjectSort`](#namespaceprojectsort) | Sort projects by the criteria. | +| `startDate` | [`Date!`](#date) | Date range to start DORA lookup from. | +| `withIssuesEnabled` | [`Boolean`](#boolean) | Return only projects with issues enabled. | +| `withMergeRequestsEnabled` | [`Boolean`](#boolean) | Return only projects with merge requests enabled. | +| `withNamespaceDomainPages` | [`Boolean`](#boolean) | Return only projects that use the namespace domain for pages projects. | + ### `GroupMember` Represents a Group Membership. @@ -46915,9 +46968,9 @@ Attributes for defining a CI/CD variable. | Name | Type | Description | | ---- | ---- | ----------- | | `controlType` | [`String`](#string) | Type of the compliance control. | -| `expression` | [`String`](#string) | Expression of the compliance control. | +| `expression` | [`String!`](#string) | Expression of the compliance control. | | `externalUrl` | [`String`](#string) | URL of the external control. | -| `name` | [`String`](#string) | New name for the compliance requirement control. | +| `name` | [`String!`](#string) | New name for the compliance requirement control. | | `secretToken` | [`String`](#string) | Secret token for an external control. | ### `ComplianceStandardsAdherenceInput` diff --git a/doc/user/gitlab_duo_chat/examples.md b/doc/user/gitlab_duo_chat/examples.md index ec3320015e6..c52bc16e55a 100644 --- a/doc/user/gitlab_duo_chat/examples.md +++ b/doc/user/gitlab_duo_chat/examples.md @@ -344,7 +344,6 @@ Programming languages that require compiling the source code may throw cryptic e The availability of this feature is controlled by a feature flag. For more information, see the history. -GitLab.com customers must contact their Customer Success Manager to enable this feature. {{< /alert >}} diff --git a/lib/api/api.rb b/lib/api/api.rb index e24fd3cf5af..421c9b86cc8 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -361,10 +361,6 @@ module API mount ::API::Users mount ::API::UserCounts mount ::API::UserRunners - mount ::API::VirtualRegistries::Packages::Maven::Registries - mount ::API::VirtualRegistries::Packages::Maven::Upstreams - mount ::API::VirtualRegistries::Packages::Maven::Cache::Entries - mount ::API::VirtualRegistries::Packages::Maven::Endpoints mount ::API::WebCommits mount ::API::Wikis diff --git a/lib/api/concerns/virtual_registries/packages/endpoint.rb b/lib/api/concerns/virtual_registries/packages/endpoint.rb deleted file mode 100644 index adff635b930..00000000000 --- a/lib/api/concerns/virtual_registries/packages/endpoint.rb +++ /dev/null @@ -1,109 +0,0 @@ -# frozen_string_literal: true - -module API - module Concerns - module VirtualRegistries - module Packages - module Endpoint - extend ActiveSupport::Concern - - NO_BROWSER_EXECUTION_RESPONSE_HEADERS = { 'Content-Security-Policy' => "default-src 'none'" }.freeze - MAJOR_BROWSERS = %i[webkit firefox ie edge opera chrome].freeze - WEB_BROWSER_ERROR_MESSAGE = 'This endpoint is not meant to be accessed by a web browser.' - UPSTREAM_GID_HEADER = 'X-Gitlab-Virtual-Registry-Upstream-Global-Id' - MAX_FILE_SIZE = 5.gigabytes - - included do - helpers do - def require_non_web_browser! - browser = ::Browser.new(request.user_agent) - bad_request!(WEB_BROWSER_ERROR_MESSAGE) if MAJOR_BROWSERS.any? { |b| browser.method(:"#{b}?").call } - end - - def send_successful_response_from(service_response:) - action, action_params = service_response.to_h.values_at(:action, :action_params) - case action - when :workhorse_upload_url - workhorse_upload_url(**action_params.slice(:url, :upstream)) - when :download_file - extra_response_headers = download_file_extra_response_headers(action_params: action_params) - present_carrierwave_file!( - action_params[:file], - supports_direct_download: extra_response_headers.blank?, - content_type: action_params[:content_type], - content_disposition: 'inline', - extra_response_headers: extra_response_headers - ) - when :download_digest - content_type 'text/plain' - env['api.format'] = :binary # to return data as-is - body action_params[:digest] - end - end - - def send_error_response_from!(service_response:) - case service_response.reason - when :unauthorized - unauthorized! - when :file_not_found_on_upstreams, :digest_not_found_in_cache_entries - not_found!(service_response.message) - else - bad_request!(service_response.message) - end - end - - def workhorse_upload_url(url:, upstream:) - allow_localhost = Gitlab.dev_or_test_env? || - Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services? - allowed_uris = ObjectStoreSettings.enabled_endpoint_uris - send_workhorse_headers( - Gitlab::Workhorse.send_dependency( - upstream.headers, - url, - response_headers: NO_BROWSER_EXECUTION_RESPONSE_HEADERS, - allow_localhost: allow_localhost, - allowed_uris: allowed_uris, - ssrf_filter: true, - upload_config: { - headers: { UPSTREAM_GID_HEADER => upstream.to_global_id.to_s }, - authorized_upload_response: authorized_upload_response - } - ) - ) - end - - def authorized_upload_response - ::VirtualRegistries::Cache::EntryUploader.workhorse_authorize( - has_length: true, - maximum_size: MAX_FILE_SIZE, - use_final_store_path: true, - final_store_path_config: { - override_path: upstream.object_storage_key_for(registry_id: registry.id) - } - ) - end - - def send_workhorse_headers(headers) - header(*headers) - env['api.format'] = :binary - content_type 'application/octet-stream' - status :ok - body '' - end - - def ok_empty_response - status :ok - env['api.format'] = :binary # to return data as-is - body '' - end - end - - after_validation do - require_non_web_browser! - end - end - end - end - end - end -end diff --git a/lib/api/entities/virtual_registries/packages/maven/cache/entry.rb b/lib/api/entities/virtual_registries/packages/maven/cache/entry.rb deleted file mode 100644 index c8c3b67694c..00000000000 --- a/lib/api/entities/virtual_registries/packages/maven/cache/entry.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module API - module Entities - module VirtualRegistries - module Packages - module Maven - module Cache - class Entry < Grape::Entity - expose :id do |cache_entry, _options| - Base64.urlsafe_encode64("#{cache_entry.upstream_id} #{cache_entry.relative_path}") - end - - expose :group_id, - :upstream_id, - :upstream_checked_at, - :file_md5, - :file_sha1, - :size, - :relative_path, - :upstream_etag, - :content_type, - :created_at, - :updated_at - end - end - end - end - end - end -end diff --git a/lib/api/entities/virtual_registries/packages/maven/registry.rb b/lib/api/entities/virtual_registries/packages/maven/registry.rb deleted file mode 100644 index 73f4e0c66c8..00000000000 --- a/lib/api/entities/virtual_registries/packages/maven/registry.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module API - module Entities - module VirtualRegistries - module Packages - module Maven - class Registry < Grape::Entity - expose :id, :group_id, :created_at, :updated_at - end - end - end - end - end -end diff --git a/lib/api/entities/virtual_registries/packages/maven/upstream.rb b/lib/api/entities/virtual_registries/packages/maven/upstream.rb deleted file mode 100644 index 0743b6feed3..00000000000 --- a/lib/api/entities/virtual_registries/packages/maven/upstream.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -module API - module Entities - module VirtualRegistries - module Packages - module Maven - class Upstream < Grape::Entity - expose :id, :group_id, :url, :cache_validity_hours, :created_at, :updated_at - end - end - end - end - end -end diff --git a/lib/api/ml/mlflow/runs.rb b/lib/api/ml/mlflow/runs.rb index 8315e63ea20..2935371f00c 100644 --- a/lib/api/ml/mlflow/runs.rb +++ b/lib/api/ml/mlflow/runs.rb @@ -14,6 +14,8 @@ module API check_api_write! unless request.get? || request.head? end + CANDIDATE_STATES = ::Ml::Candidate.statuses.keys.map(&:upcase).freeze + resource :runs do desc 'Creates a Run.' do success Entities::Ml::Mlflow::Run @@ -93,9 +95,9 @@ module API params do requires :run_id, type: String, desc: 'UUID of the candidate.' optional :status, type: String, - values: ::Ml::Candidate.statuses.keys.map(&:upcase), + values: CANDIDATE_STATES, desc: "Status of the run. Accepts: " \ - "#{::Ml::Candidate.statuses.keys.map(&:upcase)}." + "#{CANDIDATE_STATES}." optional :end_time, type: Integer, desc: 'Ending time of the run' end post 'update', urgency: :low do diff --git a/lib/api/virtual_registries/packages/maven/cache/entries.rb b/lib/api/virtual_registries/packages/maven/cache/entries.rb deleted file mode 100644 index f900b3f1f3b..00000000000 --- a/lib/api/virtual_registries/packages/maven/cache/entries.rb +++ /dev/null @@ -1,122 +0,0 @@ -# frozen_string_literal: true - -module API - module VirtualRegistries - module Packages - module Maven - module Cache - class Entries < ::API::Base - include ::API::Helpers::Authentication - include ::API::PaginationParams - - feature_category :virtual_registry - urgency :low - - authenticate_with do |accept| - accept.token_types(:personal_access_token).sent_through(:http_private_token_header) - accept.token_types(:deploy_token).sent_through(:http_deploy_token_header) - accept.token_types(:job_token).sent_through(:http_job_token_header) - end - - helpers do - include ::Gitlab::Utils::StrongMemoize - - def require_dependency_proxy_enabled! - not_found! unless ::Gitlab.config.dependency_proxy.enabled - end - - def upstream - ::VirtualRegistries::Packages::Maven::Upstream.find(params[:id]) - end - strong_memoize_attr :upstream - - def cache_entries - upstream.default_cache_entries.order_created_desc.search_by_relative_path(params[:search]) - end - - def cache_entry - ::VirtualRegistries::Packages::Maven::Cache::Entry - .default - .find_by_upstream_id_and_relative_path!(*declared_params[:id].split) - end - strong_memoize_attr :cache_entry - end - - after_validation do - not_found! unless Feature.enabled?(:virtual_registry_maven, current_user) - - require_dependency_proxy_enabled! - - authenticate! - end - - namespace 'virtual_registries/packages/maven' do - namespace :upstreams do - route_param :id, type: Integer, desc: 'The ID of the maven virtual registry upstream' do - namespace :cache_entries do - desc 'List maven virtual registry upstream cache entries' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in an experimental state. \ - This feature is behind the `virtual_registry_maven` feature flag.' - success ::API::Entities::VirtualRegistries::Packages::Maven::Cache::Entry - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - is_array true - hidden true - end - - params do - optional :search, type: String, desc: 'Search query', documentation: { example: 'foo/bar/mypkg' } - use :pagination - end - get do - authorize! :read_virtual_registry, upstream - - present paginate(cache_entries), - with: ::API::Entities::VirtualRegistries::Packages::Maven::Cache::Entry - end - end - end - end - - namespace :cache_entries do - desc 'Delete a maven virtual registry upstream cache entry' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in an experimental state. \ - This feature is behind the `virtual_registry_maven` feature flag.' - success code: 204 - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - params do - requires :id, type: String, coerce_with: Base64.method(:urlsafe_decode64), - desc: 'The base64 encoded upstream id and relative path of the cache entry', - documentation: { example: 'Zm9vL2Jhci9teXBrZy5wb20=' } - end - - delete '*id' do - authorize! :destroy_virtual_registry, cache_entry.upstream - - destroy_conditionally!(cache_entry) do |cache_entry| - render_validation_error!(cache_entry) unless cache_entry.mark_as_pending_destruction - end - end - end - end - end - end - end - end - end -end diff --git a/lib/api/virtual_registries/packages/maven/endpoints.rb b/lib/api/virtual_registries/packages/maven/endpoints.rb deleted file mode 100644 index 5437850785d..00000000000 --- a/lib/api/virtual_registries/packages/maven/endpoints.rb +++ /dev/null @@ -1,165 +0,0 @@ -# frozen_string_literal: true - -module API - module VirtualRegistries - module Packages - module Maven - class Endpoints < ::API::Base - include ::API::Helpers::Authentication - include ::API::Concerns::VirtualRegistries::Packages::Endpoint - include ::API::APIGuard - - feature_category :virtual_registry - urgency :low - - AUTHENTICATE_REALM_HEADER = 'WWW-Authenticate' - AUTHENTICATE_REALM_NAME = 'Basic realm="GitLab Virtual Registry"' - - SHA1_CHECKSUM_HEADER = 'x-checksum-sha1' - MD5_CHECKSUM_HEADER = 'x-checksum-md5' - - authenticate_with do |accept| - accept.token_types(:personal_access_token).sent_through(:http_private_token_header) - accept.token_types(:deploy_token).sent_through(:http_deploy_token_header) - accept.token_types(:job_token).sent_through(:http_job_token_header) - - accept.token_types( - :personal_access_token_with_username, - :deploy_token_with_username, - :job_token_with_username - ).sent_through(:http_basic_auth) - end - - allow_access_with_scope :read_virtual_registry - - helpers do - include ::Gitlab::Utils::StrongMemoize - - delegate :group, :upstream, :registry_upstream, to: :registry - - def require_dependency_proxy_enabled! - not_found! unless ::Gitlab.config.dependency_proxy.enabled - end - - def registry - ::VirtualRegistries::Packages::Maven::Registry.find(params[:id]) - end - strong_memoize_attr :registry - - def download_file_extra_response_headers(action_params:) - { - SHA1_CHECKSUM_HEADER => action_params[:file_sha1], - MD5_CHECKSUM_HEADER => action_params[:file_md5] - } - end - - # override from api helpers unauthorized! function - def unauthorized!(reason = nil) - header(AUTHENTICATE_REALM_HEADER, AUTHENTICATE_REALM_NAME) - super - end - - params :id_and_path do - requires :id, - type: Integer, - desc: 'The ID of the Maven virtual registry' - requires :path, - type: String, - file_path: true, - desc: 'Package path', - documentation: { example: 'foo/bar/mypkg/1.0-SNAPSHOT/mypkg-1.0-SNAPSHOT.jar' } - end - end - - after_validation do - not_found! unless Feature.enabled?(:virtual_registry_maven, current_user) - - require_dependency_proxy_enabled! - - authenticate! - end - - namespace 'virtual_registries/packages/maven/:id/*path' do - desc 'Download endpoint of the Maven virtual registry.' do - detail 'This feature was introduced in GitLab 17.3. \ - This feature is currently in experiment state. \ - This feature is behind the `virtual_registry_maven` feature flag.' - success [ - { code: 200 } - ] - failure [ - { code: 400, message: 'Bad request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not Found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - params do - use :id_and_path - end - get format: false do - service_response = ::VirtualRegistries::Packages::Maven::HandleFileRequestService.new( - registry: registry, - current_user: current_user, - params: { path: declared_params[:path] } - ).execute - - send_error_response_from!(service_response: service_response) if service_response.error? - send_successful_response_from(service_response: service_response) - end - - desc 'Workhorse upload endpoint of the Maven virtual registry. Only workhorse can access it.' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in experiment state. \ - This feature is behind the `virtual_registry_maven` feature flag.' - success [ - { code: 200 } - ] - failure [ - { code: 400, message: 'Bad request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not Found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - params do - use :id_and_path - requires :file, - type: ::API::Validations::Types::WorkhorseFile, - desc: 'The file being uploaded', - documentation: { type: 'file' } - end - post 'upload' do - require_gitlab_workhorse! - authorize!(:read_virtual_registry, registry) - - etag, content_type, upstream_gid = request.headers.fetch_values( - 'Etag', - ::Gitlab::Workhorse::SEND_DEPENDENCY_CONTENT_TYPE_HEADER, - UPSTREAM_GID_HEADER - ) { nil } - - # TODO: revisit this part when multiple upstreams are supported - # https://gitlab.com/gitlab-org/gitlab/-/issues/480461 - # coherence check - not_found!('Upstream') unless upstream == GlobalID::Locator.locate(upstream_gid) - - service_response = ::VirtualRegistries::Packages::Maven::Cache::Entries::CreateOrUpdateService.new( - upstream: upstream, - current_user: current_user, - params: declared_params.merge(etag: etag, content_type: content_type) - ).execute - - send_error_response_from!(service_response: service_response) if service_response.error? - ok_empty_response - end - end - end - end - end - end -end diff --git a/lib/api/virtual_registries/packages/maven/registries.rb b/lib/api/virtual_registries/packages/maven/registries.rb deleted file mode 100644 index 7ab9678411f..00000000000 --- a/lib/api/virtual_registries/packages/maven/registries.rb +++ /dev/null @@ -1,153 +0,0 @@ -# frozen_string_literal: true - -module API - module VirtualRegistries - module Packages - module Maven - class Registries < ::API::Base - include ::API::Helpers::Authentication - - feature_category :virtual_registry - urgency :low - - authenticate_with do |accept| - accept.token_types(:personal_access_token).sent_through(:http_private_token_header) - accept.token_types(:deploy_token).sent_through(:http_deploy_token_header) - accept.token_types(:job_token).sent_through(:http_job_token_header) - end - - helpers do - include ::Gitlab::Utils::StrongMemoize - - def group - find_group!(params[:id]) - end - strong_memoize_attr :group - - def registry - ::VirtualRegistries::Packages::Maven::Registry.find(params[:id]) - end - strong_memoize_attr :registry - - def policy_subject - ::VirtualRegistries::Packages::Policies::Group.new(group) - end - - def require_dependency_proxy_enabled! - not_found! unless ::Gitlab.config.dependency_proxy.enabled - end - end - - after_validation do - not_found! unless Feature.enabled?(:virtual_registry_maven, current_user) - - require_dependency_proxy_enabled! - - authenticate! - end - - resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - params do - requires :id, types: [String, Integer], desc: 'The group ID or full group path. Must be a top-level group' - end - - namespace ':id/-/virtual_registries/packages/maven/registries' do - desc 'Get the list of all maven virtual registries' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in an experimental state. \ - This feature is behind the `virtual_registry_maven` feature flag.' - success ::API::Entities::VirtualRegistries::Packages::Maven::Registry - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - - get do - authorize! :read_virtual_registry, policy_subject - - registries = ::VirtualRegistries::Packages::Maven::Registry.for_group(group) - - present registries, with: ::API::Entities::VirtualRegistries::Packages::Maven::Registry - end - - desc 'Create a new maven virtual registry' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in an experimental state. \ - This feature is behind the `virtual_registry_maven` feature flag.' - success ::API::Entities::VirtualRegistries::Packages::Maven::Registry - failure [ - { code: 400, message: 'Bad request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - - post do - authorize! :create_virtual_registry, policy_subject - - new_reg = ::VirtualRegistries::Packages::Maven::Registry.new(group:) - - render_validation_error!(new_reg) unless new_reg.save - - present new_reg, with: ::API::Entities::VirtualRegistries::Packages::Maven::Registry - end - end - end - - namespace 'virtual_registries/packages/maven/registries' do - route_param :id, type: Integer, desc: 'The ID of the maven virtual registry' do - desc 'Get a specific maven virtual registry' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in an experimental state. \ - This feature is behind the `virtual_registry_maven` feature flag.' - success ::API::Entities::VirtualRegistries::Packages::Maven::Registry - failure [ - { code: 400, message: 'Bad request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - get do - authorize! :read_virtual_registry, registry - - present registry, with: ::API::Entities::VirtualRegistries::Packages::Maven::Registry - end - - desc 'Delete a specific maven virtual registry' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in an experimental state. \ - This feature is behind the `virtual_registry_maven` feature flag.' - success code: 204 - failure [ - { code: 400, message: 'Bad request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' }, - { code: 412, message: 'Precondition Failed' } - ] - tags %w[maven_virtual_registries] - hidden true - end - delete do - authorize! :destroy_virtual_registry, registry - - destroy_conditionally!(registry) - end - end - end - end - end - end - end -end diff --git a/lib/api/virtual_registries/packages/maven/upstreams.rb b/lib/api/virtual_registries/packages/maven/upstreams.rb deleted file mode 100644 index 8bb1c901191..00000000000 --- a/lib/api/virtual_registries/packages/maven/upstreams.rb +++ /dev/null @@ -1,193 +0,0 @@ -# frozen_string_literal: true - -module API - module VirtualRegistries - module Packages - module Maven - class Upstreams < ::API::Base - include ::API::Helpers::Authentication - - feature_category :virtual_registry - urgency :low - - authenticate_with do |accept| - accept.token_types(:personal_access_token).sent_through(:http_private_token_header) - accept.token_types(:deploy_token).sent_through(:http_deploy_token_header) - accept.token_types(:job_token).sent_through(:http_job_token_header) - end - - helpers do - include ::Gitlab::Utils::StrongMemoize - - delegate :group, :registry_upstream, to: :registry - - def require_dependency_proxy_enabled! - not_found! unless Gitlab.config.dependency_proxy.enabled - end - - def registry - ::VirtualRegistries::Packages::Maven::Registry.find(params[:id]) - end - strong_memoize_attr :registry - - def upstream - ::VirtualRegistries::Packages::Maven::Upstream.find(params[:id]) - end - strong_memoize_attr :upstream - end - - after_validation do - not_found! unless Feature.enabled?(:virtual_registry_maven, current_user) - - require_dependency_proxy_enabled! - - authenticate! - end - - namespace 'virtual_registries/packages/maven' do - namespace :registries do - route_param :id, type: Integer, desc: 'The ID of the maven virtual registry' do - namespace :upstreams do - desc 'List all maven virtual registry upstreams' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in experiment state. \ - This feature behind the `virtual_registry_maven` feature flag.' - success code: 200 - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - get do - authorize! :read_virtual_registry, registry - - present [registry.upstream].compact, with: Entities::VirtualRegistries::Packages::Maven::Upstream - end - - desc 'Add a maven virtual registry upstream' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in experiment state. \ - This feature behind the `virtual_registry_maven` feature flag.' - success code: 201, model: ::API::Entities::VirtualRegistries::Packages::Maven::Upstream - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' }, - { code: 409, message: 'Conflict' } - ] - tags %w[maven_virtual_registries] - hidden true - end - params do - requires :url, type: String, desc: 'The URL of the maven virtual registry upstream', - allow_blank: false - optional :username, type: String, desc: 'The username of the maven virtual registry upstream' - optional :password, type: String, desc: 'The password of the maven virtual registry upstream' - optional :cache_validity_hours, type: Integer, desc: 'The cache validity in hours. Defaults to 24' - all_or_none_of :username, :password - end - post do - authorize! :create_virtual_registry, registry - - conflict!(_('Upstream already exists')) if registry.upstream - - new_upstream = registry.build_upstream(declared_params(include_missing: false).merge(group:)) - registry_upstream.group = group - - ApplicationRecord.transaction do - render_validation_error!(new_upstream) unless new_upstream.save - render_validation_error!(registry_upstream) unless registry_upstream.save - end - - present new_upstream, with: Entities::VirtualRegistries::Packages::Maven::Upstream - end - end - end - end - - namespace :upstreams do - route_param :id, type: Integer, desc: 'The ID of the maven virtual registry upstream' do - desc 'Get a specific maven virtual registry upstream' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in experiment state. \ - This feature behind the `virtual_registry_maven` feature flag.' - success ::API::Entities::VirtualRegistries::Packages::Maven::Upstream - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - get do - authorize! :read_virtual_registry, upstream - - present upstream, with: ::API::Entities::VirtualRegistries::Packages::Maven::Upstream - end - - desc 'Update a maven virtual registry upstream' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in experiment state. \ - This feature behind the `virtual_registry_maven` feature flag.' - success code: 200 - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - params do - with(allow_blank: false) do - optional :url, type: String, desc: 'The URL of the maven virtual registry upstream' - optional :username, type: String, desc: 'The username of the maven virtual registry upstream' - optional :password, type: String, desc: 'The password of the maven virtual registry upstream' - optional :cache_validity_hours, type: Integer, desc: 'The validity of the cache in hours' - end - - at_least_one_of :url, :username, :password, :cache_validity_hours - end - patch do - authorize! :update_virtual_registry, upstream - - render_validation_error!(upstream) unless upstream.update(declared_params(include_missing: false)) - - status :ok - end - - desc 'Delete a maven virtual registry upstream' do - detail 'This feature was introduced in GitLab 17.4. \ - This feature is currently in experiment state. \ - This feature behind the `virtual_registry_maven` feature flag.' - success code: 204 - failure [ - { code: 400, message: 'Bad Request' }, - { code: 401, message: 'Unauthorized' }, - { code: 403, message: 'Forbidden' }, - { code: 404, message: 'Not found' } - ] - tags %w[maven_virtual_registries] - hidden true - end - delete do - authorize! :destroy_virtual_registry, upstream - - destroy_conditionally!(upstream) - end - end - end - end - end - end - end - end -end diff --git a/lib/gitlab/background_migration/backfill_root_namespace_cluster_agent_mappings.rb b/lib/gitlab/background_migration/backfill_root_namespace_cluster_agent_mappings.rb deleted file mode 100644 index 3e143ba2534..00000000000 --- a/lib/gitlab/background_migration/backfill_root_namespace_cluster_agent_mappings.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module BackgroundMigration - # Backfills mappings between root namespaces and all agents within the root namespace - # that have remote development module enabled - # For more details, check: https://gitlab.com/gitlab-org/gitlab/-/issues/454411 - class BackfillRootNamespaceClusterAgentMappings < BatchedMigrationJob - feature_category :workspaces - - def perform; end - end - end -end - -Gitlab::BackgroundMigration::BackfillRootNamespaceClusterAgentMappings.prepend_mod diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb index e14cc2e80c2..9eeec0482ed 100644 --- a/lib/gitlab/database.rb +++ b/lib/gitlab/database.rb @@ -12,9 +12,7 @@ module Gitlab # TABLES_TO_BE_RENAMED = { # 'old_name' => 'new_name' # }.freeze - TABLES_TO_BE_RENAMED = { - 'remote_development_namespace_cluster_agent_mappings' => 'namespace_cluster_agent_mappings' - }.freeze + TABLES_TO_BE_RENAMED = {}.freeze # Minimum PostgreSQL version requirement per documentation: # https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements diff --git a/lib/web_ide/settings/default_settings.rb b/lib/web_ide/settings/default_settings.rb index e3d03cbe19d..19df3da3b06 100644 --- a/lib/web_ide/settings/default_settings.rb +++ b/lib/web_ide/settings/default_settings.rb @@ -10,10 +10,12 @@ module WebIde vscode_extension_marketplace: [ # See https://sourcegraph.com/github.com/microsoft/vscode@6979fb003bfa575848eda2d3966e872a9615427b/-/blob/src/vs/base/common/product.ts?L96 # for the original source of settings entries in the VS Code source code. + # See https://open-vsx.org/swagger-ui/index.html?urls.primaryName=VSCode%20Adapter# + # for OpenVSX Swagger API { service_url: "https://open-vsx.org/vscode/gallery", item_url: "https://open-vsx.org/vscode/item", - resource_url_template: "https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}", + resource_url_template: "https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}", control_url: "", nls_base_url: "", publisher_url: "" diff --git a/lib/web_ide/settings_sync.rb b/lib/web_ide/settings_sync.rb index 4f4a2e352e7..829c36c5320 100644 --- a/lib/web_ide/settings_sync.rb +++ b/lib/web_ide/settings_sync.rb @@ -7,10 +7,13 @@ module WebIde # "web_ide_https://open-vsx.org/vscode/gallery_https://open-vsx.org/vscode/item_" # This hash is used out in the wild, so we don't want to change it... # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/178491 - CURRENT_DEFAULT_SETTINGS_HASH = "2e0d3e8c1107f9ccc5ea" - - # The actual hash we calculate for default settings, but needs to be mapped to the CURRENT one for compatability - DEFAULT_SETTINGS_HASH = "e36c431c0e2e1ee82c86" + HASH_CONVERSION = { + # 2e0d3e8c1107f9ccc5ea is the hash of "web_ide_https://open-vsx.org/vscode/gallery_https://open-vsx.org/vscode/item_" + # e36c431c0e2e1ee82c86 is the hash of "web_ide_https://open-vsx.org/vscode/gallery_https://open-vsx.org/vscode/item_https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}" + # 55b10685e181429abe78 is the hash of "web_ide_https://open-vsx.org/vscode/gallery_https://open-vsx.org/vscode/item_https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}" + "e36c431c0e2e1ee82c86" => "2e0d3e8c1107f9ccc5ea", + "55b10685e181429abe78" => "2e0d3e8c1107f9ccc5ea" + }.freeze def settings_context_hash(extension_marketplace_settings:) return unless extension_marketplace_settings[:enabled] @@ -29,9 +32,7 @@ module WebIde private def optionally_transform_hash(hash) - return CURRENT_DEFAULT_SETTINGS_HASH if hash == DEFAULT_SETTINGS_HASH - - hash + HASH_CONVERSION.fetch(hash, hash) end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a9e0241107d..f4483e682b3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -17008,6 +17008,9 @@ msgstr "" msgid "Could not draw the lines for job relationships" msgstr "" +msgid "Could not fetch all changes. Try reloading the page." +msgstr "" + msgid "Could not fetch policy because existing policy YAML is invalid" msgstr "" @@ -22136,6 +22139,9 @@ msgstr "" msgid "DuoProTrial|You have successfully started a Duo Pro trial that will expire on %{exp_date}. To give members access to new GitLab Duo Pro features, %{assign_link_start}assign them%{assign_link_end} to GitLab Duo Pro seats." msgstr "" +msgid "Duplicate entries found for compliance controls for the requirement." +msgstr "" + msgid "Duplicate packages" msgstr "" @@ -24534,6 +24540,9 @@ msgstr "" msgid "Failed to add a resource link" msgstr "" +msgid "Failed to add compliance requirement control %{control_name}: %{error_message}" +msgstr "" + msgid "Failed to add emoji. Please try again" msgstr "" @@ -37476,6 +37485,9 @@ msgstr "" msgid "More options" msgstr "" +msgid "More than %{control_count} controls not allowed for a requirement." +msgstr "" + msgid "More than %{number_commits_distance} commits different with %{default_branch}" msgstr "" @@ -38891,6 +38903,9 @@ msgstr "" msgid "Not permitted to create compliance control" msgstr "" +msgid "Not permitted to create requirement" +msgstr "" + msgid "Not permitted to destroy framework" msgstr "" diff --git a/spec/components/rapid_diffs/streaming_error_component_spec.rb b/spec/components/rapid_diffs/streaming_error_component_spec.rb new file mode 100644 index 00000000000..918a7400662 --- /dev/null +++ b/spec/components/rapid_diffs/streaming_error_component_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe RapidDiffs::StreamingErrorComponent, type: :component, feature_category: :code_review_workflow do + it "renders component with message" do + result = render_component('Foo') + expect(result).to have_css('streaming-error[message="Foo"]') + end + + def render_component(message) + render_inline(described_class.new(message: message)) + end +end diff --git a/spec/features/api/virtual_registries/packages/maven_spec.rb b/spec/features/api/virtual_registries/packages/maven_spec.rb deleted file mode 100644 index af141cc7535..00000000000 --- a/spec/features/api/virtual_registries/packages/maven_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Virtual Registries Packages Maven', :api, :js, feature_category: :virtual_registry do - include_context 'file upload requests helpers' - include_context 'with a server running the dependency proxy' - - let_it_be(:group) { create(:group) } - let_it_be(:user) { create(:user, owner_of: group) } - let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } - let_it_be(:registry) { create(:virtual_registries_packages_maven_registry, group: group) } - - let_it_be(:external_server) do - handler = ->(env) do - if env['REQUEST_PATH'] == '/file' # rubocop:disable RSpec/AvoidConditionalStatements -- This is a lambda for the external server - [200, { 'Content-Type' => 'text/plain', 'ETag' => '"etag"' }, ['File contents']] - else - [404, {}, []] - end - end - - run_server(handler) - end - - let_it_be(:upstream) do - create(:virtual_registries_packages_maven_upstream, registry: registry) - end - - let(:api_path) { "/virtual_registries/packages/maven/#{registry.id}/file" } - let(:url) { capybara_url(api(api_path)) } - let(:authorization) do - ActionController::HttpAuthentication::Basic.encode_credentials(user.username, personal_access_token.token) - end - - subject(:request) { HTTParty.get(url, headers: { authorization: authorization }) } - - before do - upstream.update_column(:url, external_server.base_url) # avoids guard that rejects local urls - stub_config(dependency_proxy: { enabled: true }) - allow(Gitlab::CurrentSettings).to receive(:allow_local_requests_from_web_hooks_and_services?).and_return(true) - end - - context 'with no cache entry' do - it 'returns the file contents and create the cache entry' do - expect { request }.to change { upstream.cache_entries.count }.by(1) - end - end - - context 'with a cache entry' do - let_it_be_with_reload(:cache_entry) do - create( - :virtual_registries_packages_maven_cache_entry, - :upstream_checked, - upstream: upstream, - relative_path: '/file', - content_type: 'text/plain', - upstream_etag: '"etag"' - ) - end - - it 'returns the file contents from the cache' do - expect(::Gitlab::HTTP).not_to receive(:head) - expect { request }.not_to change { upstream.cache_entries.count } - expect(request.headers[::API::VirtualRegistries::Packages::Maven::Endpoints::SHA1_CHECKSUM_HEADER]) - .to be_an_instance_of(String) - expect(request.headers[::API::VirtualRegistries::Packages::Maven::Endpoints::MD5_CHECKSUM_HEADER]) - .to be_an_instance_of(String) - end - - context 'with a stale cache entry' do - before do - cache_entry.update_column(:upstream_checked_at, 2.days.ago) - end - - it 'returns the file contents and refresh the cache entry' do - expect(::Gitlab::HTTP).to receive(:head).and_call_original - - expect { request }.to not_change { upstream.cache_entries.count } - .and change { cache_entry.reload.upstream_checked_at } - end - - context 'with a wrong etag' do - before do - cache_entry.update_column(:upstream_etag, 'wrong') - end - - it 'returns the file contents and updates the cache entry' do - expect(::Gitlab::HTTP).to receive(:head).and_call_original - - expect { request }.to not_change { upstream.cache_entries.count } - .and change { cache_entry.reload.upstream_checked_at } - .and change { cache_entry.reload.upstream_etag }.from('wrong').to('"etag"') - end - end - end - end -end diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js index c485fdb8175..0233a3a9049 100644 --- a/spec/frontend/diffs/components/app_spec.js +++ b/spec/frontend/diffs/components/app_spec.js @@ -5,6 +5,7 @@ import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; +import { createTestingPinia } from '@pinia/testing'; import api from '~/api'; import getMRCodequalityAndSecurityReports from 'ee_else_ce/diffs/components/graphql/get_mr_codequality_and_security_reports.query.graphql'; import createMockApollo from 'helpers/mock_apollo_helper'; @@ -26,7 +27,7 @@ import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue'; import eventHub from '~/diffs/event_hub'; import notesEventHub from '~/notes/event_hub'; -import { EVT_DISCUSSIONS_ASSIGNED } from '~/diffs/constants'; +import { EVT_DISCUSSIONS_ASSIGNED, FILE_BROWSER_VISIBLE } from '~/diffs/constants'; import axios from '~/lib/utils/axios_utils'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; @@ -38,7 +39,8 @@ import { stubPerformanceWebAPI } from 'helpers/performance'; import { getDiffFileMock } from 'jest/diffs/mock_data/diff_file'; import waitForPromises from 'helpers/wait_for_promises'; import { diffMetadata } from 'jest/diffs/mock_data/diff_metadata'; -import { pinia } from '~/pinia/instance'; +import { removeCookie, setCookie } from '~/lib/utils/common_utils'; +import { useFileBrowser } from '~/diffs/stores/file_browser'; import { useMockInternalEventsTracking } from 'helpers/tracking_internal_events_helper'; import createDiffsStore from '../create_diffs_store'; import diffsMockData from '../mock_data/merge_request_diffs'; @@ -74,6 +76,7 @@ describe('diffs/components/app', () => { baseConfig = {}, actions = {}, } = {}) => { + const pinia = createTestingPinia({ stubActions: false }); fakeApollo = createMockApollo([ [getMRCodequalityAndSecurityReports, codeQualityAndSastQueryHandlerSuccess], ]); @@ -716,28 +719,26 @@ describe('diffs/components/app', () => { }); }); - describe('setTreeDisplay', () => { - afterEach(() => { - localStorage.removeItem('mr_tree_show'); + describe('file browser visibility', () => { + beforeEach(() => { + removeCookie(FILE_BROWSER_VISIBLE); }); - it('calls setShowTreeList when only 1 file', () => { + it('hides files browser with only 1 file', async () => { createComponent({ + props: { shouldShow: true }, extendStore: ({ state }) => { state.diffs.treeEntries = { 123: { type: 'blob', fileHash: '123' } }; }, }); - jest.spyOn(store, 'dispatch'); - wrapper.vm.setTreeDisplay(); + await waitForPromises(); - expect(store.dispatch).toHaveBeenCalledWith('diffs/setShowTreeList', { - showTreeList: false, - saving: false, - }); + expect(useFileBrowser().setFileBrowserVisibility).toHaveBeenCalledWith(false); }); - it('calls setShowTreeList with true when more than 1 file is in tree entries map', () => { + it('shows file browser with more than 1 file', async () => { createComponent({ + props: { shouldShow: true }, extendStore: ({ state }) => { state.diffs.treeEntries = { 111: { type: 'blob', fileHash: '111', path: '111.js' }, @@ -745,37 +746,31 @@ describe('diffs/components/app', () => { }; }, }); - jest.spyOn(store, 'dispatch'); + await waitForPromises(); - wrapper.vm.setTreeDisplay(); - - expect(store.dispatch).toHaveBeenCalledWith('diffs/setShowTreeList', { - showTreeList: true, - saving: false, - }); + expect(useFileBrowser().setFileBrowserVisibility).toHaveBeenCalledWith(true); }); it.each` - showTreeList + fileBrowserVisible ${true} ${false} - `('calls setShowTreeList with localstorage $showTreeList', ({ showTreeList }) => { - localStorage.setItem('mr_tree_show', showTreeList); + `( + 'sets browser visibility from cookie value: $fileBrowserVisible', + async ({ fileBrowserVisible }) => { + setCookie(FILE_BROWSER_VISIBLE, fileBrowserVisible); - createComponent({ - extendStore: ({ state }) => { - state.diffs.treeEntries['123'] = { sha: '123' }; - }, - }); - jest.spyOn(store, 'dispatch'); + createComponent({ + props: { shouldShow: true }, + extendStore: ({ state }) => { + state.diffs.treeEntries['123'] = { sha: '123' }; + }, + }); + await waitForPromises(); - wrapper.vm.setTreeDisplay(); - - expect(store.dispatch).toHaveBeenCalledWith('diffs/setShowTreeList', { - showTreeList, - saving: false, - }); - }); + expect(useFileBrowser().setFileBrowserVisibility).toHaveBeenCalledWith(fileBrowserVisible); + }, + ); }); describe('file-by-file', () => { diff --git a/spec/frontend/diffs/components/compare_versions_spec.js b/spec/frontend/diffs/components/compare_versions_spec.js index b2bfc238c6a..3718512bf72 100644 --- a/spec/frontend/diffs/components/compare_versions_spec.js +++ b/spec/frontend/diffs/components/compare_versions_spec.js @@ -1,12 +1,15 @@ import { GlAnimatedSidebarIcon } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; -import { nextTick } from 'vue'; +import Vue, { nextTick } from 'vue'; +import { createTestingPinia } from '@pinia/testing'; +import { PiniaVuePlugin } from 'pinia'; import getDiffWithCommit from 'test_fixtures/merge_request_diffs/with_commit.json'; import setWindowLocation from 'helpers/set_window_location_helper'; import { TEST_HOST } from 'helpers/test_constants'; import { trimText } from 'helpers/text_helper'; import CompareVersionsComponent from '~/diffs/components/compare_versions.vue'; import store from '~/mr_notes/stores'; +import { useFileBrowser } from '~/diffs/stores/file_browser'; import diffsMockData from '../mock_data/merge_request_diffs'; jest.mock('~/mr_notes/stores', () => jest.requireActual('helpers/mocks/mr_notes/stores')); @@ -18,6 +21,8 @@ beforeEach(() => { setWindowLocation(TEST_HOST); }); +Vue.use(PiniaVuePlugin); + describe('CompareVersions', () => { let wrapper; const targetBranchName = 'tmp-wine-dev'; @@ -28,6 +33,9 @@ describe('CompareVersions', () => { store.state.diffs.commit = { ...store.state.diffs.commit, ...commitArgs }; } + const pinia = createTestingPinia(); + // force Vue 2 mode by eager store creation + useFileBrowser(); wrapper = mount(CompareVersionsComponent, { propsData: { toggleFileTreeVisible: true, @@ -36,6 +44,7 @@ describe('CompareVersions', () => { mocks: { $store: store, }, + pinia, }); }; const findCompareSourceDropdown = () => wrapper.find('.mr-version-dropdown'); diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js index fab41854860..dd8ab33a6c6 100644 --- a/spec/frontend/diffs/store/actions_spec.js +++ b/spec/frontend/diffs/store/actions_spec.js @@ -1419,34 +1419,6 @@ describe('DiffsStoreActions', () => { }); }); - describe('setShowTreeList', () => { - it('commits toggle', () => { - return testAction( - diffActions.setShowTreeList, - { showTreeList: true }, - {}, - [{ type: types.SET_SHOW_TREE_LIST, payload: true }], - [], - ); - }); - - it('updates localStorage', () => { - jest.spyOn(localStorage, 'setItem').mockImplementation(() => {}); - - diffActions.setShowTreeList({ commit() {} }, { showTreeList: true }); - - expect(localStorage.setItem).toHaveBeenCalledWith('mr_tree_show', true); - }); - - it('does not update localStorage', () => { - jest.spyOn(localStorage, 'setItem').mockImplementation(() => {}); - - diffActions.setShowTreeList({ commit() {} }, { showTreeList: true, saving: false }); - - expect(localStorage.setItem).not.toHaveBeenCalled(); - }); - }); - describe('renderFileForDiscussionId', () => { const rootState = { notes: { diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js index a10979aacaf..a5ad95c2791 100644 --- a/spec/frontend/diffs/store/mutations_spec.js +++ b/spec/frontend/diffs/store/mutations_spec.js @@ -994,16 +994,6 @@ describe('DiffsStoreMutations', () => { }); }); - describe('SET_SHOW_TREE_LIST', () => { - it('sets showTreeList', () => { - const state = createState(); - - mutations[types.SET_SHOW_TREE_LIST](state, true); - - expect(state.showTreeList).toBe(true, 'Failed to toggle showTreeList to true'); - }); - }); - describe('SET_CURRENT_DIFF_FILE', () => { it('updates currentDiffFileId', () => { const state = createState(); diff --git a/spec/frontend/diffs/stores/file_browser_spec.js b/spec/frontend/diffs/stores/file_browser_spec.js new file mode 100644 index 00000000000..df225063cff --- /dev/null +++ b/spec/frontend/diffs/stores/file_browser_spec.js @@ -0,0 +1,37 @@ +import { createPinia, setActivePinia } from 'pinia'; +import { useFileBrowser } from '~/diffs/stores/file_browser'; +import { getCookie, setCookie } from '~/lib/utils/common_utils'; +import { FILE_BROWSER_VISIBLE } from '~/diffs/constants'; + +describe('FileBrowser store', () => { + beforeEach(() => { + setActivePinia(createPinia()); + }); + + describe('browser visibility', () => { + beforeEach(() => { + window.document.cookie = ''; + }); + + it('is visible by default', () => { + expect(useFileBrowser().fileBrowserVisible).toBe(true); + }); + + it('#setFileBrowserVisibility', () => { + useFileBrowser().setFileBrowserVisibility(false); + expect(useFileBrowser().fileBrowserVisible).toBe(false); + }); + + it('#toggleFileBrowserVisibility', () => { + useFileBrowser().toggleFileBrowserVisibility(); + expect(useFileBrowser().fileBrowserVisible).toBe(false); + expect(getCookie(FILE_BROWSER_VISIBLE)).toBe('false'); + }); + + it('#initFileBrowserVisibility', () => { + setCookie(FILE_BROWSER_VISIBLE, false); + useFileBrowser().initFileBrowserVisibility(); + expect(useFileBrowser().fileBrowserVisible).toBe(false); + }); + }); +}); diff --git a/spec/frontend/diffs/stores/legacy_diffs/actions_spec.js b/spec/frontend/diffs/stores/legacy_diffs/actions_spec.js index 67d9acd171e..b3df5129253 100644 --- a/spec/frontend/diffs/stores/legacy_diffs/actions_spec.js +++ b/spec/frontend/diffs/stores/legacy_diffs/actions_spec.js @@ -1457,34 +1457,6 @@ describe('legacyDiffs actions', () => { }); }); - describe('setShowTreeList', () => { - it('commits toggle', () => { - return testAction( - store.setShowTreeList, - { showTreeList: true }, - {}, - [{ type: store[types.SET_SHOW_TREE_LIST], payload: true }], - [], - ); - }); - - it('updates localStorage', () => { - jest.spyOn(localStorage, 'setItem').mockImplementation(() => {}); - - store.setShowTreeList({ showTreeList: true }); - - expect(localStorage.setItem).toHaveBeenCalledWith('mr_tree_show', true); - }); - - it('does not update localStorage', () => { - jest.spyOn(localStorage, 'setItem').mockImplementation(() => {}); - - store.setShowTreeList({ showTreeList: true, saving: false }); - - expect(localStorage.setItem).not.toHaveBeenCalled(); - }); - }); - describe('renderFileForDiscussionId', () => { const notesState = { discussions: [ diff --git a/spec/frontend/diffs/stores/legacy_diffs/mutations_spec.js b/spec/frontend/diffs/stores/legacy_diffs/mutations_spec.js index 2c14bb43a50..7eef0b4b6de 100644 --- a/spec/frontend/diffs/stores/legacy_diffs/mutations_spec.js +++ b/spec/frontend/diffs/stores/legacy_diffs/mutations_spec.js @@ -982,14 +982,6 @@ describe('DiffsStoreMutations', () => { }); }); - describe('SET_SHOW_TREE_LIST', () => { - it('sets showTreeList', () => { - store[types.SET_SHOW_TREE_LIST](true); - - expect(store.showTreeList).toBe(true, 'Failed to toggle showTreeList to true'); - }); - }); - describe('SET_CURRENT_DIFF_FILE', () => { it('updates currentDiffFileId', () => { store[types.SET_CURRENT_DIFF_FILE]('somefileid'); diff --git a/spec/frontend/rapid_diffs/app/app_spec.js b/spec/frontend/rapid_diffs/app/app_spec.js index b5293880a9b..df04dd4d1be 100644 --- a/spec/frontend/rapid_diffs/app/app_spec.js +++ b/spec/frontend/rapid_diffs/app/app_spec.js @@ -7,6 +7,7 @@ import { DiffFileMounted } from '~/rapid_diffs/diff_file_mounted'; import { useDiffsList } from '~/rapid_diffs/stores/diffs_list'; import { pinia } from '~/pinia/instance'; import { initFileBrowser } from '~/rapid_diffs/app/init_file_browser'; +import { StreamingError } from '~/rapid_diffs/streaming_error'; jest.mock('~/rapid_diffs/app/view_settings'); jest.mock('~/rapid_diffs/app/init_file_browser'); @@ -36,6 +37,7 @@ describe('Rapid Diffs App', () => { expect(initFileBrowser).toHaveBeenCalled(); expect(window.customElements.get('diff-file')).toBe(DiffFile); expect(window.customElements.get('diff-file-mounted')).toBe(DiffFileMounted); + expect(window.customElements.get('streaming-error')).toBe(StreamingError); }); it('streams remaining diffs', () => { diff --git a/spec/frontend/rapid_diffs/streaming_error_spec.js b/spec/frontend/rapid_diffs/streaming_error_spec.js new file mode 100644 index 00000000000..2cfcac913a4 --- /dev/null +++ b/spec/frontend/rapid_diffs/streaming_error_spec.js @@ -0,0 +1,28 @@ +import { StreamingError } from '~/rapid_diffs/streaming_error'; +import { createAlert } from '~/alert'; + +jest.mock('~/alert'); + +describe('DiffFile Web Component', () => { + const html = ` + + + `; + + const renderComponent = () => { + document.body.innerHTML = html; + }; + + beforeAll(() => { + customElements.define('streaming-error', StreamingError); + }); + + it('creates an alert', () => { + const spy = jest.spyOn(console, 'error').mockImplementation(() => {}); + renderComponent(); + expect(spy).toHaveBeenCalledWith('Failed to stream diffs: Foo'); + expect(createAlert).toHaveBeenCalledWith({ + message: 'Could not fetch all changes. Try reloading the page.', + }); + }); +}); diff --git a/spec/frontend/work_items/components/design_management/design_preview/design_toolbar_spec.js b/spec/frontend/work_items/components/design_management/design_preview/design_toolbar_spec.js index bb65cb2c3ef..e9923aeb40c 100644 --- a/spec/frontend/work_items/components/design_management/design_preview/design_toolbar_spec.js +++ b/spec/frontend/work_items/components/design_management/design_preview/design_toolbar_spec.js @@ -13,7 +13,6 @@ jest.mock('~/lib/utils/common_utils'); describe('DesignToolbar', () => { let wrapper; - const workItemTitle = 'Test title'; function createComponent({ isLoading = false, @@ -22,7 +21,6 @@ describe('DesignToolbar', () => { } = {}) { wrapper = shallowMountExtended(DesignToolbar, { propsData: { - workItemTitle, isLoading, design, isSidebarOpen: true, @@ -57,7 +55,7 @@ describe('DesignToolbar', () => { it('renders issue title and design filename', () => { createComponent(); - expect(findDesignTitle()).toContain(workItemTitle); + expect(findDesignTitle()).toContain('My precious issue'); expect(findDesignTitle()).toContain(mockDesign.filename); }); diff --git a/spec/lib/api/entities/virtual_registries/packages/maven/cache/entry_spec.rb b/spec/lib/api/entities/virtual_registries/packages/maven/cache/entry_spec.rb deleted file mode 100644 index 361d1e5becd..00000000000 --- a/spec/lib/api/entities/virtual_registries/packages/maven/cache/entry_spec.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe API::Entities::VirtualRegistries::Packages::Maven::Cache::Entry, feature_category: :virtual_registry do - let(:cache_entry) { build_stubbed(:virtual_registries_packages_maven_cache_entry) } - - subject { described_class.new(cache_entry).as_json } - - it 'has the expected attributes' do - is_expected.to include(:id, :group_id, :upstream_id, :upstream_checked_at, :created_at, :updated_at, - :file_md5, :file_sha1, :size, :relative_path, :upstream_etag, :content_type) - end -end diff --git a/spec/lib/api/entities/virtual_registries/packages/maven/registry_spec.rb b/spec/lib/api/entities/virtual_registries/packages/maven/registry_spec.rb deleted file mode 100644 index f1cf3f8c012..00000000000 --- a/spec/lib/api/entities/virtual_registries/packages/maven/registry_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe API::Entities::VirtualRegistries::Packages::Maven::Registry, feature_category: :virtual_registry do - let(:registry) { build_stubbed(:virtual_registries_packages_maven_registry) } - - subject { described_class.new(registry).as_json } - - it { is_expected.to include(:id, :group_id, :created_at, :updated_at) } -end diff --git a/spec/lib/api/entities/virtual_registries/packages/maven/upstream_spec.rb b/spec/lib/api/entities/virtual_registries/packages/maven/upstream_spec.rb deleted file mode 100644 index 442c9091ef5..00000000000 --- a/spec/lib/api/entities/virtual_registries/packages/maven/upstream_spec.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe API::Entities::VirtualRegistries::Packages::Maven::Upstream, feature_category: :virtual_registry do - let(:upstream) { build_stubbed(:virtual_registries_packages_maven_upstream) } - - subject { described_class.new(upstream).as_json } - - it { is_expected.to include(:id, :group_id, :url, :cache_validity_hours, :created_at, :updated_at) } -end diff --git a/spec/lib/web_ide/extension_marketplace_spec.rb b/spec/lib/web_ide/extension_marketplace_spec.rb index 95dd3e103ec..8f85e9090d9 100644 --- a/spec/lib/web_ide/extension_marketplace_spec.rb +++ b/spec/lib/web_ide/extension_marketplace_spec.rb @@ -14,7 +14,7 @@ RSpec.describe WebIde::ExtensionMarketplace, feature_category: :web_ide do item_url: 'https://open-vsx.org/vscode/item', service_url: 'https://open-vsx.org/vscode/gallery', resource_url_template: - 'https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}' + 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}' } end diff --git a/spec/lib/web_ide/settings/extension_marketplace_validator_spec.rb b/spec/lib/web_ide/settings/extension_marketplace_validator_spec.rb index 5ce2522db48..38c2dfc051d 100644 --- a/spec/lib/web_ide/settings/extension_marketplace_validator_spec.rb +++ b/spec/lib/web_ide/settings/extension_marketplace_validator_spec.rb @@ -7,7 +7,7 @@ RSpec.describe WebIde::Settings::ExtensionMarketplaceValidator, feature_category let(:service_url) { "https://open-vsx.org/vscode/gallery" } let(:item_url) { "https://open-vsx.org/vscode/item" } - let(:resource_url_template) { "https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}" } + let(:resource_url_template) { "https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}" } let(:vscode_extension_marketplace) do { service_url: service_url, diff --git a/spec/lib/web_ide/settings/settings_initializer_spec.rb b/spec/lib/web_ide/settings/settings_initializer_spec.rb index 34bdb4b2cc6..e22a16f55a4 100644 --- a/spec/lib/web_ide/settings/settings_initializer_spec.rb +++ b/spec/lib/web_ide/settings/settings_initializer_spec.rb @@ -26,7 +26,7 @@ RSpec.describe WebIde::Settings::SettingsInitializer, feature_category: :web_ide item_url: "https://open-vsx.org/vscode/item", nls_base_url: "", publisher_url: "", - resource_url_template: 'https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}', + resource_url_template: 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}', service_url: "https://open-vsx.org/vscode/gallery" }, vscode_extension_marketplace_metadata: { diff --git a/spec/lib/web_ide/settings/settings_integration_spec.rb b/spec/lib/web_ide/settings/settings_integration_spec.rb index 65ca3d43ebd..532d9ceb0b4 100644 --- a/spec/lib/web_ide/settings/settings_integration_spec.rb +++ b/spec/lib/web_ide/settings/settings_integration_spec.rb @@ -9,7 +9,7 @@ RSpec.describe ::WebIde::Settings, feature_category: :web_ide do # rubocop:disab { service_url: "https://open-vsx.org/vscode/gallery", item_url: "https://open-vsx.org/vscode/item", - resource_url_template: 'https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}', + resource_url_template: 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}', control_url: "", nls_base_url: "", publisher_url: "" @@ -72,7 +72,7 @@ RSpec.describe ::WebIde::Settings, feature_category: :web_ide do # rubocop:disab stub_env("GITLAB_WEB_IDE_VSCODE_EXTENSION_MARKETPLACE", '{"service_url":"https://OVERRIDE.org/vscode/gallery",' \ '"item_url":"https://OVERRIDE.org/vscode/item",' \ - '"resource_url_template":"https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}"}' + '"resource_url_template":"https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}"}' ) end @@ -81,7 +81,7 @@ RSpec.describe ::WebIde::Settings, feature_category: :web_ide do # rubocop:disab { service_url: "https://OVERRIDE.org/vscode/gallery", item_url: "https://OVERRIDE.org/vscode/item", - resource_url_template: "https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}" + resource_url_template: "https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}" } ) end @@ -102,7 +102,7 @@ RSpec.describe ::WebIde::Settings, feature_category: :web_ide do # rubocop:disab it "uses default value" do expected_value = { item_url: "https://open-vsx.org/vscode/item", - resource_url_template: "https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}", + resource_url_template: "https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}", service_url: "https://open-vsx.org/vscode/gallery", control_url: "", nls_base_url: "", diff --git a/spec/lib/web_ide/settings_sync_spec.rb b/spec/lib/web_ide/settings_sync_spec.rb index 15f49f0b1a7..71f8da8e551 100644 --- a/spec/lib/web_ide/settings_sync_spec.rb +++ b/spec/lib/web_ide/settings_sync_spec.rb @@ -25,6 +25,15 @@ RSpec.describe WebIde::SettingsSync, feature_category: :web_ide do expectation: 'c6620244fe72864fa8d8' }, "default vscode settings" => { + enabled: true, + vscode_settings: { + service_url: 'https://open-vsx.org/vscode/gallery', + item_url: 'https://open-vsx.org/vscode/item', + resource_url_template: 'https://open-vsx.org/vscode/unpkg/{publisher}/{name}/{version}/{path}' + }, + expectation: '2e0d3e8c1107f9ccc5ea' + }, + "default vscode settings (compatability)" => { enabled: true, vscode_settings: { service_url: 'https://open-vsx.org/vscode/gallery', diff --git a/spec/migrations/20240711035245_queue_backfill_root_namespace_cluster_agent_mappings_again_spec.rb b/spec/migrations/20240711035245_queue_backfill_root_namespace_cluster_agent_mappings_again_spec.rb deleted file mode 100644 index 49324582425..00000000000 --- a/spec/migrations/20240711035245_queue_backfill_root_namespace_cluster_agent_mappings_again_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require_migration! - -RSpec.describe QueueBackfillRootNamespaceClusterAgentMappingsAgain, migration: :gitlab_main_cell, - feature_category: :workspaces do - let!(:batched_migration) { described_class::MIGRATION } - - it 'schedules a new batched migration' do - reversible_migration do |migration| - migration.before -> { - expect(batched_migration).not_to have_scheduled_batched_migration - } - - migration.after -> { - expect(batched_migration).to have_scheduled_batched_migration( - table_name: :remote_development_agent_configs, - column_name: :id, - interval: described_class::DELAY_INTERVAL, - batch_size: described_class::BATCH_SIZE, - gitlab_schema: :gitlab_main_cell, - sub_batch_size: described_class::SUB_BATCH_SIZE - ) - } - end - end -end diff --git a/spec/requests/api/virtual_registries/packages/maven/cache/entries_spec.rb b/spec/requests/api/virtual_registries/packages/maven/cache/entries_spec.rb deleted file mode 100644 index b2069b269f1..00000000000 --- a/spec/requests/api/virtual_registries/packages/maven/cache/entries_spec.rb +++ /dev/null @@ -1,200 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe API::VirtualRegistries::Packages::Maven::Cache::Entries, :aggregate_failures, feature_category: :virtual_registry do - using RSpec::Parameterized::TableSyntax - include_context 'for maven virtual registry api setup' - - describe 'GET /api/v4/virtual_registries/packages/maven/upstreams/:id/cache_entries' do - let(:upstream_id) { upstream.id } - let(:url) { "/virtual_registries/packages/maven/upstreams/#{upstream_id}/cache_entries" } - - let_it_be(:processing_cache_entry) do - create( - :virtual_registries_packages_maven_cache_entry, - :processing, - upstream: upstream, - group: upstream.group, - relative_path: cache_entry.relative_path - ) - end - - subject(:api_request) { get api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - api_request - - expect(response).to have_gitlab_http_status(:ok) - expect(Gitlab::Json.parse(response.body)).to contain_exactly( - cache_entry - .as_json - .merge('id' => Base64.urlsafe_encode64("#{upstream.id} #{cache_entry.relative_path}")) - .except('object_storage_key', 'file', 'file_store', 'status', 'file_final_path') - ) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'with invalid upstream' do - where(:upstream_id, :status) do - non_existing_record_id | :not_found - 'foo' | :bad_request - '' | :bad_request - end - - with_them do - it_behaves_like 'returning response status', params[:status] - end - end - - context 'with a non-member user' do - let_it_be(:user) { create(:user) } - - where(:group_access_level, :status) do - 'PUBLIC' | :forbidden - 'INTERNAL' | :forbidden - 'PRIVATE' | :forbidden - end - - with_them do - before do - group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false)) - end - - it_behaves_like 'returning response status', params[:status] - end - end - - context 'for authentication' do - where(:token, :sent_as, :status) do - :personal_access_token | :header | :ok - :deploy_token | :header | :ok - :job_token | :header | :ok - end - - with_them do - let(:headers) { token_header(token) } - - it_behaves_like 'returning response status', params[:status] - end - end - - context 'for search param' do - let(:url) { "#{super()}?search=#{search}" } - let(:valid_search) { cache_entry.relative_path.slice(0, 5) } - - where(:search, :status) do - ref(:valid_search) | :ok - 'foo' | :empty - '' | :ok - nil | :ok - end - - with_them do - if params[:status] == :ok - it_behaves_like 'successful response' - else - it 'returns an empty array' do - api_request - - expect(json_response).to eq([]) - end - end - end - end - end - - describe 'DELETE /api/v4/virtual_registries/packages/maven/cache_entries/:id' do - let(:id) { Base64.urlsafe_encode64("#{upstream.id} #{cache_entry.relative_path}") } - let(:url) { "/virtual_registries/packages/maven/cache_entries/#{id}" } - - subject(:api_request) { delete api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - expect { api_request }.to change { - VirtualRegistries::Packages::Maven::Cache::Entry.last.status - }.from('default').to('pending_destruction') - - expect(response).to have_gitlab_http_status(:no_content) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'for different user roles' do - where(:user_role, :status) do - :owner | :no_content - :maintainer | :no_content - :developer | :forbidden - :reporter | :forbidden - :guest | :forbidden - end - - with_them do - before do - group.send(:"add_#{user_role}", user) - end - - if params[:status] == :no_content - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - end - - context 'for authentication' do - before_all do - group.add_maintainer(user) - end - - where(:token, :sent_as, :status) do - :personal_access_token | :header | :no_content - :deploy_token | :header | :forbidden - :job_token | :header | :no_content - end - - with_them do - let(:headers) { token_header(token) } - - if params[:status] == :no_content - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - end - - context 'when error occurs' do - before_all do - group.add_maintainer(user) - end - - before do - allow_next_found_instance_of(cache_entry.class) do |instance| - errors = ActiveModel::Errors.new(instance).tap { |e| e.add(:cache_entry, 'error message') } - allow(instance).to receive_messages(mark_as_pending_destruction: false, errors: errors) - end - end - - it 'returns an error' do - api_request - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to eq({ 'message' => { 'cache_entry' => ['error message'] } }) - end - end - end -end diff --git a/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb b/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb deleted file mode 100644 index eb88a1c3e13..00000000000 --- a/spec/requests/api/virtual_registries/packages/maven/endpoints_spec.rb +++ /dev/null @@ -1,315 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe API::VirtualRegistries::Packages::Maven::Endpoints, :aggregate_failures, feature_category: :virtual_registry do - using RSpec::Parameterized::TableSyntax - include_context 'for maven virtual registry api setup' - - describe 'GET /api/v4/virtual_registries/packages/maven/:id/*path' do - let_it_be(:path) { 'com/test/package/1.2.3/package-1.2.3.pom' } - - let(:url) { "/virtual_registries/packages/maven/#{registry.id}/#{path}" } - let(:service_response) do - ServiceResponse.success( - payload: { - action: :workhorse_upload_url, - action_params: { url: upstream.url_for(path), upstream: upstream } - } - ) - end - - let(:service_double) do - instance_double(::VirtualRegistries::Packages::Maven::HandleFileRequestService, execute: service_response) - end - - before do - allow(::VirtualRegistries::Packages::Maven::HandleFileRequestService) - .to receive(:new) - .with(registry: registry, current_user: user, params: { path: path }) - .and_return(service_double) - end - - subject(:request) do - get api(url), headers: headers - end - - shared_examples 'returning the workhorse send_dependency response' do - it 'returns a workhorse send_url response' do - expect(::VirtualRegistries::Cache::EntryUploader).to receive(:workhorse_authorize).with( - a_hash_including( - use_final_store_path: true, - final_store_path_config: { override_path: be_instance_of(String) } - ) - ).and_call_original - - expect(Gitlab::Workhorse).to receive(:send_dependency).with( - an_instance_of(Hash), - an_instance_of(String), - a_hash_including( - allow_localhost: true, - ssrf_filter: true, - allowed_uris: ObjectStoreSettings.enabled_endpoint_uris - ) - ).and_call_original - - request - - expect(response).to have_gitlab_http_status(:ok) - expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('send-dependency:') - expect(response.headers['Content-Type']).to eq('application/octet-stream') - expect(response.headers['Content-Length'].to_i).to eq(0) - expect(response.body).to eq('') - - send_data_type, send_data = workhorse_send_data - - expected_headers = upstream.headers.deep_stringify_keys.deep_transform_values do |value| - [value] - end - - expected_resp_headers = described_class::NO_BROWSER_EXECUTION_RESPONSE_HEADERS.deep_transform_values do |value| - [value] - end - - expected_upload_config = { - 'Headers' => { described_class::UPSTREAM_GID_HEADER => [upstream.to_global_id.to_s] }, - 'AuthorizedUploadResponse' => a_kind_of(Hash) - } - - expect(send_data_type).to eq('send-dependency') - expect(send_data['Url']).to be_present - expect(send_data['Headers']).to eq(expected_headers) - expect(send_data['ResponseHeaders']).to eq(expected_resp_headers) - expect(send_data['UploadConfig']).to include(expected_upload_config) - end - end - - it_behaves_like 'maven virtual registry authenticated endpoint', - success_shared_example_name: 'returning the workhorse send_dependency response' do - let(:headers) { {} } - end - - context 'with a valid user' do - let(:headers) { { 'Private-Token' => personal_access_token.token } } - - context 'with successful handle request service responses' do - let_it_be(:cache_entry) do - create( - :virtual_registries_packages_maven_cache_entry, - content_type: 'text/xml', - upstream: upstream, - group_id: upstream.group_id, - relative_path: "/#{path}" - ) - end - - before do - # reset the test stub to use the actual service - allow(::VirtualRegistries::Packages::Maven::HandleFileRequestService).to receive(:new).and_call_original - end - - context 'when the handle request service returns download_file' do - it 'returns the workhorse send_url response' do - request - - expect(response).to have_gitlab_http_status(:ok) - expect(response.media_type).to eq(cache_entry.content_type) - # this is a direct download from the file system, workhorse is not involved - expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to be_nil - end - end - - context 'when the handle request service returns download_digest' do - let(:path) { "#{super()}.sha1" } - - it 'returns the requested digest' do - request - - expect(response).to have_gitlab_http_status(:ok) - expect(response.media_type).to eq('text/plain') - expect(response.body).to eq(cache_entry.file_sha1) - end - end - end - - context 'with service response errors' do - where(:reason, :expected_status) do - :path_not_present | :bad_request - :unauthorized | :unauthorized - :no_upstreams | :bad_request - :file_not_found_on_upstreams | :not_found - :digest_not_found_in_cache_entries | :not_found - :upstream_not_available | :bad_request - :fips_unsupported_md5 | :bad_request - end - - with_them do - let(:service_response) do - ServiceResponse.error(message: 'error', reason: reason) - end - - it "returns a #{params[:expected_status]} response" do - request - - expect(response).to have_gitlab_http_status(expected_status) - expect(response.body).to include('error') unless expected_status == :unauthorized - end - end - end - - context 'with a web browser' do - described_class::MAJOR_BROWSERS.each do |browser| - context "when accessing with a #{browser} browser" do - before do - allow_next_instance_of(::Browser) do |b| - allow(b).to receive("#{browser}?").and_return(true) - end - end - - it 'returns a bad request response' do - request - - expect(response).to have_gitlab_http_status(:bad_request) - expect(response.body).to include(described_class::WEB_BROWSER_ERROR_MESSAGE) - end - end - end - end - - context 'for a invalid registry id' do - let(:url) { "/virtual_registries/packages/maven/#{non_existing_record_id}/#{path}" } - - it_behaves_like 'returning response status', :not_found - end - - context 'with a personal access token with only the read_virtual_registry scope' do - let(:personal_access_token) { create(:personal_access_token, user: user, scopes: ['read_virtual_registry']) } - - let(:headers) { { 'Private-Token' => personal_access_token.token } } - - before do - # read_virtual_registry is only available when the dependency proxy feature is enabled - stub_config(dependency_proxy: { enabled: true }) - end - - it_behaves_like 'returning the workhorse send_dependency response' - end - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - end - - context 'with no user' do - let(:headers) { {} } - - it 'returns unauthorized with the www-authenticate header' do - request - - expect(response).to have_gitlab_http_status(:unauthorized) - expect(response.headers[described_class::AUTHENTICATE_REALM_HEADER]) - .to eq(described_class::AUTHENTICATE_REALM_NAME) - end - end - - it_behaves_like 'maven virtual registry not authenticated user' - end - - describe 'POST /api/v4/virtual_registries/packages/maven/:id/*path/upload' do - include_context 'workhorse headers' - - let(:file_upload) { fixture_file_upload('spec/fixtures/packages/maven/my-app-1.0-20180724.124855-1.pom') } - let(:gid_header) { { described_class::UPSTREAM_GID_HEADER => upstream.to_global_id.to_s } } - let(:additional_headers) do - gid_header.merge(::Gitlab::Workhorse::SEND_DEPENDENCY_CONTENT_TYPE_HEADER => 'text/xml') - end - - let(:headers) { workhorse_headers.merge(additional_headers) } - - let_it_be(:path) { 'com/test/package/1.2.3/package-1.2.3.pom' } - let_it_be(:url) { "/virtual_registries/packages/maven/#{registry.id}/#{path}/upload" } - let_it_be(:processing_cache_entries) do - create( - :virtual_registries_packages_maven_cache_entry, - :processing, - upstream: upstream, - group: upstream.group, - relative_path: "/#{path}" - ) - end - - subject(:request) do - workhorse_finalize( - api(url), - file_key: :file, - headers: headers, - params: { - file: file_upload, - 'file.md5' => 'd8e8fca2dc0f896fd7cb4cb0031ba249', - 'file.sha1' => '4e1243bd22c66e76c2ba9eddc1f91394e57f9f83' - }, - send_rewritten_field: true - ) - end - - shared_examples 'returning successful response' do - it 'accepts the upload', :freeze_time do - expect { request }.to change { upstream.cache_entries.count }.by(1) - - expect(response).to have_gitlab_http_status(:ok) - expect(response.body).to eq('') - expect(upstream.default_cache_entries.search_by_relative_path(path).last).to have_attributes( - relative_path: "/#{path}", - upstream_etag: nil, - upstream_checked_at: Time.zone.now, - file_sha1: kind_of(String), - file_md5: kind_of(String) - ) - end - end - - it_behaves_like 'maven virtual registry authenticated endpoint', - success_shared_example_name: 'returning successful response' - - context 'with a valid user' do - let(:headers) { super().merge(token_header(:personal_access_token)) } - - context 'with no workhorse headers' do - let(:headers) { token_header(:personal_access_token).merge(gid_header) } - - it_behaves_like 'returning response status', :forbidden - end - - context 'with no permissions on registry' do - let_it_be(:user) { create(:user) } - - it_behaves_like 'returning response status', :forbidden - end - - context 'with an invalid upstream gid' do - let_it_be(:upstream) { build(:virtual_registries_packages_maven_upstream, id: non_existing_record_id) } - - it_behaves_like 'returning response status', :not_found - end - - context 'with an incoherent upstream gid' do - let_it_be(:upstream) { create(:virtual_registries_packages_maven_upstream) } - - it_behaves_like 'returning response status', :not_found - end - - context 'with a persistence error' do - before do - allow(::VirtualRegistries::Packages::Maven::Cache::Entry) - .to receive(:create_or_update_by!).and_raise(ActiveRecord::RecordInvalid) - end - - it_behaves_like 'returning response status', :bad_request - end - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - end - - it_behaves_like 'maven virtual registry not authenticated user' - end -end diff --git a/spec/requests/api/virtual_registries/packages/maven/registries_spec.rb b/spec/requests/api/virtual_registries/packages/maven/registries_spec.rb deleted file mode 100644 index de71971e8db..00000000000 --- a/spec/requests/api/virtual_registries/packages/maven/registries_spec.rb +++ /dev/null @@ -1,332 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe API::VirtualRegistries::Packages::Maven::Registries, :aggregate_failures, feature_category: :virtual_registry do - using RSpec::Parameterized::TableSyntax - include_context 'for maven virtual registry api setup' - - describe 'GET /api/v4/groups/:id/-/virtual_registries/packages/maven/registries' do - let(:group_id) { group.id } - let(:url) { "/groups/#{group_id}/-/virtual_registries/packages/maven/registries" } - - subject(:api_request) { get api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - api_request - - expect(response).to have_gitlab_http_status(:ok) - expect(Gitlab::Json.parse(response.body)).to contain_exactly(registry.as_json) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'with valid group_id' do - it_behaves_like 'successful response' - end - - context 'with invalid group_id' do - where(:group_id, :status) do - non_existing_record_id | :not_found - 'foo' | :not_found - '' | :not_found - end - - with_them do - it_behaves_like 'returning response status', params[:status] - end - end - - context 'with a non member user' do - let_it_be(:user) { create(:user) } - - where(:group_access_level, :status) do - 'PUBLIC' | :forbidden - 'INTERNAL' | :forbidden - 'PRIVATE' | :not_found - end - - with_them do - before do - group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false)) - end - - it_behaves_like 'returning response status', params[:status] - end - end - - context 'for authentication' do - where(:token, :sent_as, :status) do - :personal_access_token | :header | :ok - :deploy_token | :header | :ok - :job_token | :header | :ok - end - - with_them do - let(:headers) { token_header(token) } - - it_behaves_like 'returning response status', params[:status] - end - end - end - - describe 'POST /api/v4/groups/:id/-/virtual_registries/packages/maven/registries' do - let_it_be(:registry_class) { ::VirtualRegistries::Packages::Maven::Registry } - let(:group_id) { group.id } - let(:url) { "/groups/#{group_id}/-/virtual_registries/packages/maven/registries" } - - subject(:api_request) { post api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - expect { api_request }.to change { registry_class.count }.by(1) - - expect(response).to have_gitlab_http_status(:created) - expect(Gitlab::Json.parse(response.body)).to eq(registry_class.last.as_json) - end - end - - context 'with valid group_id' do - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - where(:user_role, :status) do - :owner | :created - :maintainer | :created - :developer | :forbidden - :reporter | :forbidden - :guest | :forbidden - end - - with_them do - before do - registry_class.for_group(group).delete_all - group.send(:"add_#{user_role}", user) - end - - if params[:status] == :created - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - - context 'with existing registry' do - before_all do - group.add_maintainer(user) - end - - it 'returns a bad request' do - api_request - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to eq({ 'message' => { 'group' => ['has already been taken'] } }) - end - end - - context 'for authentication' do - before_all do - group.add_maintainer(user) - end - - before do - registry_class.for_group(group).delete_all - end - - where(:token, :sent_as, :status) do - :personal_access_token | :header | :created - :deploy_token | :header | :forbidden - :job_token | :header | :created - end - - with_them do - let(:headers) { token_header(token) } - - it_behaves_like 'returning response status', params[:status] - end - end - end - - context 'with invalid group_id' do - before_all do - group.add_maintainer(user) - end - - where(:group_id, :status) do - non_existing_record_id | :not_found - 'foo' | :not_found - '' | :not_found - end - - with_them do - it_behaves_like 'returning response status', params[:status] - end - end - - context 'with subgroup' do - let(:subgroup) { create(:group, parent: group, visibility_level: group.visibility_level) } - - let(:group_id) { subgroup.id } - - before_all do - group.add_maintainer(user) - end - - it 'returns a bad request because it is not a top level group' do - api_request - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response).to eq({ 'message' => { 'group' => ['must be a top level Group'] } }) - end - end - end - - describe 'GET /api/v4/virtual_registries/packages/maven/registries/:id' do - let(:registry_id) { registry.id } - let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}" } - - subject(:api_request) { get api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - api_request - - expect(response).to have_gitlab_http_status(:ok) - expect(Gitlab::Json.parse(response.body)).to eq(registry.as_json) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'with valid registry_id' do - it_behaves_like 'successful response' - end - - context 'with invalid registry_id' do - where(:registry_id, :status) do - non_existing_record_id | :not_found - 'foo' | :bad_request - '' | :bad_request - end - - with_them do - it_behaves_like 'returning response status', params[:status] - end - end - - context 'with a non member user' do - let_it_be(:user) { create(:user) } - - where(:group_access_level, :status) do - 'PUBLIC' | :forbidden - 'INTERNAL' | :forbidden - 'PRIVATE' | :forbidden - end - with_them do - before do - group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false)) - end - - it_behaves_like 'returning response status', params[:status] - end - end - - context 'for authentication' do - where(:token, :sent_as, :status) do - :personal_access_token | :header | :ok - :deploy_token | :header | :ok - :job_token | :header | :ok - end - - with_them do - let(:headers) { token_header(token) } - - it_behaves_like 'returning response status', params[:status] - end - end - end - - describe 'DELETE /api/v4/virtual_registries/packages/maven/registries/:id' do - let(:registry_id) { registry.id } - let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}" } - - subject(:api_request) { delete api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - expect { api_request }.to change { ::VirtualRegistries::Packages::Maven::Registry.count }.by(-1) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'with valid registry_id' do - where(:user_role, :status) do - :owner | :no_content - :maintainer | :no_content - :developer | :forbidden - :reporter | :forbidden - :guest | :forbidden - end - - with_them do - before do - group.send(:"add_#{user_role}", user) - end - - if params[:status] == :no_content - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - end - - context 'with invalid registry_id' do - where(:registry_id, :status) do - non_existing_record_id | :not_found - 'foo' | :bad_request - '' | :not_found - end - - with_them do - it_behaves_like 'returning response status', params[:status] - end - end - - context 'for authentication' do - before_all do - group.add_maintainer(user) - end - - where(:token, :sent_as, :status) do - :personal_access_token | :header | :no_content - :deploy_token | :header | :forbidden - :job_token | :header | :no_content - end - - with_them do - let(:headers) { token_header(token) } - - it_behaves_like 'returning response status', params[:status] - end - end - end -end diff --git a/spec/requests/api/virtual_registries/packages/maven/upstreams_spec.rb b/spec/requests/api/virtual_registries/packages/maven/upstreams_spec.rb deleted file mode 100644 index 6092da87e4a..00000000000 --- a/spec/requests/api/virtual_registries/packages/maven/upstreams_spec.rb +++ /dev/null @@ -1,401 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe API::VirtualRegistries::Packages::Maven::Upstreams, :aggregate_failures, feature_category: :virtual_registry do - using RSpec::Parameterized::TableSyntax - include_context 'for maven virtual registry api setup' - - describe 'GET /api/v4/virtual_registries/packages/maven/registries/:id/upstreams' do - let(:registry_id) { registry.id } - let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}/upstreams" } - - subject(:api_request) { get api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - api_request - - expect(response).to have_gitlab_http_status(:ok) - expect(Gitlab::Json.parse(response.body)).to contain_exactly(registry.upstream.as_json) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'with valid registry' do - it_behaves_like 'successful response' - end - - context 'with invalid registry' do - where(:registry_id, :status) do - non_existing_record_id | :not_found - 'foo' | :bad_request - '' | :bad_request - end - - with_them do - it_behaves_like 'returning response status', params[:status] - end - end - - context 'with a non member user' do - let_it_be(:user) { create(:user) } - - where(:group_access_level, :status) do - 'PUBLIC' | :forbidden - 'INTERNAL' | :forbidden - 'PRIVATE' | :forbidden - end - - with_them do - before do - group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false)) - end - - it_behaves_like 'returning response status', params[:status] - end - end - - context 'for authentication' do - where(:token, :sent_as, :status) do - :personal_access_token | :header | :ok - :deploy_token | :header | :ok - :job_token | :header | :ok - end - - with_them do - let(:headers) { token_header(token) } - - it_behaves_like 'returning response status', params[:status] - end - end - end - - describe 'POST /api/v4/virtual_registries/packages/maven/registries/:id/upstreams' do - let(:registry_id) { registry.id } - let(:url) { "/virtual_registries/packages/maven/registries/#{registry_id}/upstreams" } - let(:params) { { url: 'http://example.com' } } - - subject(:api_request) { post api(url), headers: headers, params: params } - - shared_examples 'successful response' do - let(:upstream_model) { ::VirtualRegistries::Packages::Maven::Upstream } - - it 'returns a successful response' do - expect { api_request }.to change { upstream_model.count }.by(1) - .and change { ::VirtualRegistries::Packages::Maven::RegistryUpstream.count }.by(1) - - expect(response).to have_gitlab_http_status(:created) - expect(Gitlab::Json.parse(response.body)).to eq(upstream_model.last.as_json) - expect(upstream_model.last.cache_validity_hours).to eq( - params[:cache_validity_hours] || upstream_model.new.cache_validity_hours - ) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'with valid params' do - where(:user_role, :status) do - :owner | :created - :maintainer | :created - :developer | :forbidden - :reporter | :forbidden - :guest | :forbidden - end - - with_them do - before do - registry.upstream&.destroy! - group.send(:"add_#{user_role}", user) - end - - if params[:status] == :created - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - end - - context 'with invalid registry' do - where(:registry_id, :status) do - non_existing_record_id | :not_found - 'foo' | :bad_request - '' | :not_found - end - - with_them do - it_behaves_like 'returning response status', params[:status] - end - end - - context 'for params' do - where(:params, :status) do - { url: 'http://example.com', username: 'test', password: 'test', cache_validity_hours: 3 } | :created - { url: 'http://example.com', username: 'test', password: 'test' } | :created - { url: '', username: 'test', password: 'test' } | :bad_request - { url: 'http://example.com', username: 'test' } | :bad_request - {} | :bad_request - end - - before do - registry.upstream&.destroy! - end - - before_all do - group.add_maintainer(user) - end - - with_them do - if params[:status] == :created - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - end - - context 'with existing upstream' do - before_all do - group.add_maintainer(user) - create(:virtual_registries_packages_maven_upstream, registry: registry) - end - - it_behaves_like 'returning response status', :conflict - end - - context 'for authentication' do - before_all do - group.add_maintainer(user) - end - - before do - registry.upstream&.destroy! - end - - where(:token, :sent_as, :status) do - :personal_access_token | :header | :created - :deploy_token | :header | :forbidden - :job_token | :header | :created - end - - with_them do - let(:headers) { token_header(token) } - - if params[:status] == :created - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - end - end - - describe 'GET /api/v4/virtual_registries/packages/maven/upstreams/:id' do - let(:url) { "/virtual_registries/packages/maven/upstreams/#{upstream.id}" } - - subject(:api_request) { get api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - api_request - - expect(response).to have_gitlab_http_status(:ok) - expect(Gitlab::Json.parse(response.body)).to eq(registry.upstream.as_json) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'with valid params' do - it_behaves_like 'successful response' - end - - context 'with a non member user' do - let_it_be(:user) { create(:user) } - - where(:group_access_level, :status) do - 'PUBLIC' | :forbidden - 'INTERNAL' | :forbidden - 'PRIVATE' | :forbidden - end - - with_them do - before do - group.update!(visibility_level: Gitlab::VisibilityLevel.const_get(group_access_level, false)) - end - - it_behaves_like 'returning response status', params[:status] - end - end - - context 'for authentication' do - where(:token, :sent_as, :status) do - :personal_access_token | :header | :ok - :deploy_token | :header | :ok - :job_token | :header | :ok - end - - with_them do - let(:headers) { token_header(token) } - - it_behaves_like 'returning response status', params[:status] - end - end - end - - describe 'PATCH /api/v4/virtual_registries/packages/maven/upstreams/:id' do - let(:url) { "/virtual_registries/packages/maven/upstreams/#{upstream.id}" } - - subject(:api_request) { patch api(url), params: params, headers: headers } - - context 'with valid params' do - let(:params) { { url: 'http://example.com', username: 'test', password: 'test' } } - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - where(:user_role, :status) do - :owner | :ok - :maintainer | :ok - :developer | :forbidden - :reporter | :forbidden - :guest | :forbidden - end - - with_them do - before do - group.send(:"add_#{user_role}", user) - end - - it_behaves_like 'returning response status', params[:status] - end - - context 'for authentication' do - before_all do - group.add_maintainer(user) - end - - where(:token, :sent_as, :status) do - :personal_access_token | :header | :ok - :deploy_token | :header | :forbidden - :job_token | :header | :ok - end - - with_them do - let(:headers) { token_header(token) } - - it_behaves_like 'returning response status', params[:status] - end - end - end - - context 'for params' do - before_all do - group.add_maintainer(user) - end - - let(:params) do - { url: param_url, username: username, password: password, cache_validity_hours: cache_validity_hours }.compact - end - - where(:param_url, :username, :password, :cache_validity_hours, :status) do - nil | 'test' | 'test' | 3 | :ok - 'http://example.com' | nil | 'test' | 3 | :ok - 'http://example.com' | 'test' | nil | 3 | :ok - 'http://example.com' | 'test' | 'test' | nil | :ok - nil | nil | nil | 3 | :ok - 'http://example.com' | 'test' | 'test' | 3 | :ok - '' | 'test' | 'test' | 3 | :bad_request - 'http://example.com' | '' | 'test' | 3 | :bad_request - 'http://example.com' | 'test' | '' | 3 | :bad_request - 'http://example.com' | 'test' | 'test' | -1 | :bad_request - nil | nil | nil | nil | :bad_request - end - - with_them do - it_behaves_like 'returning response status', params[:status] - end - end - end - - describe 'DELETE /api/v4/virtual_registries/packages/maven/upstreams/:id' do - let(:url) { "/virtual_registries/packages/maven/upstreams/#{upstream.id}" } - - subject(:api_request) { delete api(url), headers: headers } - - shared_examples 'successful response' do - it 'returns a successful response' do - expect { api_request }.to change { ::VirtualRegistries::Packages::Maven::Upstream.count }.by(-1) - .and change { ::VirtualRegistries::Packages::Maven::RegistryUpstream.count }.by(-1) - end - end - - it { is_expected.to have_request_urgency(:low) } - - it_behaves_like 'disabled virtual_registry_maven feature flag' - it_behaves_like 'maven virtual registry disabled dependency proxy' - it_behaves_like 'maven virtual registry not authenticated user' - - context 'for different user roles' do - where(:user_role, :status) do - :owner | :no_content - :maintainer | :no_content - :developer | :forbidden - :reporter | :forbidden - :guest | :forbidden - end - - with_them do - before do - group.send(:"add_#{user_role}", user) - end - - if params[:status] == :no_content - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - end - - context 'for authentication' do - before_all do - group.add_maintainer(user) - end - - where(:token, :sent_as, :status) do - :personal_access_token | :header | :no_content - :deploy_token | :header | :forbidden - :job_token | :header | :no_content - end - - with_them do - let(:headers) { token_header(token) } - - if params[:status] == :no_content - it_behaves_like 'successful response' - else - it_behaves_like 'returning response status', params[:status] - end - end - end - end -end diff --git a/spec/services/ml/create_experiment_service_spec.rb b/spec/services/ml/create_experiment_service_spec.rb index 5fdca79cb33..2f3a15746f1 100644 --- a/spec/services/ml/create_experiment_service_spec.rb +++ b/spec/services/ml/create_experiment_service_spec.rb @@ -30,5 +30,16 @@ RSpec.describe ::Ml::CreateExperimentService, feature_category: :mlops do expect(create_experiment).to be_error end end + + context 'with invalid parameters' do + let(:name) { '' } + + it 'returns validation errors' do + response = create_experiment + + expect(response).to be_error + expect(response.message).to include("Name can't be blank") + end + end end end diff --git a/spec/services/ml/create_model_version_service_spec.rb b/spec/services/ml/create_model_version_service_spec.rb index 3fcaf548591..2ee9ec7ccd0 100644 --- a/spec/services/ml/create_model_version_service_spec.rb +++ b/spec/services/ml/create_model_version_service_spec.rb @@ -283,5 +283,35 @@ RSpec.describe ::Ml::CreateModelVersionService, feature_category: :mlops do expect(service).to be_error.and have_attributes(message: ["Run with eid not found"]) end end + + context 'when a RecordNotUnique error occurs' do + let_it_be(:pg_error) { 'PG::UniqueViolation: ERROR: duplicate key value violates unique constraint' } + + before do + allow_next_instance_of(::Ml::ModelVersion) do |model_version| + allow(model_version).to receive(:save).and_raise( + ActiveRecord::RecordNotUnique.new(pg_error) + ) + end + end + + it 'returns an error response with the exception message' do + expect(service).to be_error + expect(service.message).to include(pg_error) + expect(service.payload[:model_version]).to be_nil + end + + it 'does not create model version or package' do + expect { service }.to not_change { Ml::ModelVersion.count } + .and not_change { Packages::MlModel::Package.count } + end + + it 'does not track events or audit' do + service + + expect(Gitlab::InternalEvents).not_to have_received(:track_event) + expect(Gitlab::Audit::Auditor).not_to have_received(:audit) + end + end end end diff --git a/spec/support/shared_contexts/requests/api/maven_vreg_shared_context.rb b/spec/support/shared_contexts/requests/api/maven_vreg_shared_context.rb deleted file mode 100644 index 6787cbc267e..00000000000 --- a/spec/support/shared_contexts/requests/api/maven_vreg_shared_context.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_context 'for maven virtual registry api setup' do - include WorkhorseHelpers - include HttpBasicAuthHelpers - - let_it_be(:group) { create(:group) } - let_it_be_with_reload(:registry) { create(:virtual_registries_packages_maven_registry, group: group) } - let_it_be(:upstream) { create(:virtual_registries_packages_maven_upstream, registry: registry) } - let_it_be_with_reload(:cache_entry) do - create(:virtual_registries_packages_maven_cache_entry, upstream: upstream) - end - - let_it_be(:project) { create(:project, namespace: group) } - let_it_be(:user) { create(:user, owner_of: project) } - let_it_be(:job) { create(:ci_build, :running, user: user, project: project) } - let_it_be(:deploy_token) do - create(:deploy_token, :group, groups: [group], read_virtual_registry: true) - end - - let(:personal_access_token) { create(:personal_access_token, user: user) } - let(:headers) { token_header(:personal_access_token) } - - before do - stub_config(dependency_proxy: { enabled: true }) # not enabled by default - end - - def token_header(token) - case token - when :personal_access_token - { 'PRIVATE-TOKEN' => personal_access_token.token } - when :deploy_token - { 'Deploy-Token' => deploy_token.token } - when :job_token - { 'Job-Token' => job.token } - end - end - - def token_basic_auth(token) - case token - when :personal_access_token - user_basic_auth_header(user, personal_access_token) - when :deploy_token - deploy_token_basic_auth_header(deploy_token) - when :job_token - job_basic_auth_header(job) - end - end -end diff --git a/spec/support/shared_examples/requests/api/virtual_registries/maven_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/virtual_registries/maven_packages_shared_examples.rb deleted file mode 100644 index 697ce59cb0d..00000000000 --- a/spec/support/shared_examples/requests/api/virtual_registries/maven_packages_shared_examples.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_examples 'disabled virtual_registry_maven feature flag' do - before do - stub_feature_flags(virtual_registry_maven: false) - end - - it_behaves_like 'returning response status', :not_found -end - -RSpec.shared_examples 'maven virtual registry disabled dependency proxy' do - before do - stub_config(dependency_proxy: { enabled: false }) - end - - it_behaves_like 'returning response status', :not_found -end - -RSpec.shared_examples 'maven virtual registry not authenticated user' do - let(:headers) { {} } - - it_behaves_like 'returning response status', :unauthorized -end - -RSpec.shared_examples 'maven virtual registry authenticated endpoint' do |success_shared_example_name:| - %i[personal_access_token deploy_token job_token].each do |token_type| - context "with a #{token_type}" do - let_it_be(:user) { deploy_token } if token_type == :deploy_token - - context 'when sent by headers' do - let(:headers) { super().merge(token_header(token_type)) } - - it_behaves_like success_shared_example_name - end - - context 'when sent by basic auth' do - let(:headers) { super().merge(token_basic_auth(token_type)) } - - it_behaves_like success_shared_example_name - end - end - end -end