Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0fc7ea6bc8
commit
e09e832fe3
|
|
@ -1 +1 @@
|
|||
6bd8110489a44adeacff1897a757d8ea52bf4f87
|
||||
ee1bf60a8952f6e9b4f3bf7627ac671ef1427cad
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
14.40.0
|
||||
14.41.0
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
>
|
||||
<gl-animated-sidebar-icon :is-on="showTreeList" />
|
||||
<gl-animated-sidebar-icon :is-on="fileBrowserVisible" />
|
||||
</gl-button>
|
||||
<div v-if="commit">
|
||||
{{ __('Viewing commit') }}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ export default () => ({
|
|||
diffViewType: INLINE_DIFF_VIEW_TYPE,
|
||||
tree: [],
|
||||
treeEntries: {},
|
||||
showTreeList: true,
|
||||
currentDiffFileId: '',
|
||||
projectPath: '',
|
||||
viewedDiffFileIds: {},
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ export const useLegacyDiffs = defineStore('legacyDiffs', {
|
|||
diffViewType: INLINE_DIFF_VIEW_TYPE,
|
||||
tree: [],
|
||||
treeEntries: {},
|
||||
showTreeList: true,
|
||||
currentDiffFileId: '',
|
||||
projectPath: '',
|
||||
viewedDiffFileIds: {},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
|
|
@ -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`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -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 {
|
|||
</gl-badge>
|
||||
</div>
|
||||
<div
|
||||
:id="descriptionId"
|
||||
v-safe-html:[$options.safeHtmlConfig]="feature.description"
|
||||
class="gl-pt-3 gl-leading-20"
|
||||
></div>
|
||||
|
|
@ -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') }} <gl-icon name="arrow-right" />
|
||||
</gl-button>
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ export default {
|
|||
prevCurrentUserTodos: null,
|
||||
maxScale: DEFAULT_MAX_SCALE,
|
||||
workItemId: '',
|
||||
workItemTitle: '',
|
||||
isSidebarOpen: true,
|
||||
};
|
||||
},
|
||||
|
|
@ -360,7 +359,6 @@ export default {
|
|||
>
|
||||
<div class="gl-relative gl-flex gl-grow gl-flex-col gl-overflow-hidden">
|
||||
<design-toolbar
|
||||
:work-item-title="workItemTitle"
|
||||
:design="design"
|
||||
:design-filename="$route.params.id"
|
||||
:is-loading="isLoading"
|
||||
|
|
|
|||
|
|
@ -31,10 +31,6 @@ export default {
|
|||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
props: {
|
||||
workItemTitle: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
design: {
|
||||
type: Object,
|
||||
required: true,
|
||||
|
|
@ -86,9 +82,9 @@ export default {
|
|||
<gl-skeleton-loader v-if="isLoading" :lines="1" />
|
||||
<h2 v-else class="gl-m-0 gl-flex gl-items-center gl-overflow-hidden gl-text-base">
|
||||
<span class="gl-truncate gl-text-heading gl-no-underline">
|
||||
{{ workItemTitle }}
|
||||
{{ design.issue.title }}
|
||||
</span>
|
||||
<gl-icon name="chevron-right" class="gl-shrink-0" variant="disabled" />
|
||||
<gl-icon name="chevron-right" class="gl-shrink-0" variant="subtle" />
|
||||
<span class="gl-truncate gl-font-normal">{{ design.filename }}</span>
|
||||
<imported-badge
|
||||
v-if="design.imported"
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
%streaming-error{ message: @message.to_s }
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RapidDiffs
|
||||
class StreamingErrorComponent < ViewComponent::Base
|
||||
def initialize(message:)
|
||||
@message = message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -13,9 +13,12 @@ module StreamDiffs
|
|||
offset = { offset_index: params.permit(:offset)[:offset].to_i }
|
||||
|
||||
stream_diff_files(streaming_diff_options.merge(offset))
|
||||
rescue ActionController::Live::ClientDisconnected
|
||||
# Ignored
|
||||
rescue StandardError => 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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'
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
5bd102fd3ff8463e40c4467317d3f146511ef683945e7c2cabaf8bff7abd3d04
|
||||
|
|
@ -0,0 +1 @@
|
|||
166459c440ed6e868c66ddc3b79a5aae27b20d2bc21e489123b6676f8cd97841
|
||||
|
|
@ -0,0 +1 @@
|
|||
dbdfbe9e0140762dcb606ee83ee5ac6ee6f2f0c4b9a60979f73394a9f81ec2e6
|
||||
|
|
@ -0,0 +1 @@
|
|||
247a50b157e3bf43bc9a70ae74b1f2be18dfec57f9796cac6813a459a6f0cfee
|
||||
|
|
@ -0,0 +1 @@
|
|||
0bd459ee0a6ad163f1e2e6ff856328f6898174736af667ec2c9f96ed18cffb1f
|
||||
|
|
@ -0,0 +1 @@
|
|||
c13b43a5493f93febb1d54451741f1e39113a5b8b1d6959408ef4390eba92388
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4024,6 +4024,7 @@ Input type: `CreateComplianceRequirementInput`
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationcreatecompliancerequirementclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationcreatecompliancerequirementcomplianceframeworkid"></a>`complianceFrameworkId` | [`ComplianceManagementFrameworkID!`](#compliancemanagementframeworkid) | Global ID of the compliance framework of the new requirement. |
|
||||
| <a id="mutationcreatecompliancerequirementcontrols"></a>`controls` | [`[ComplianceRequirementsControlInput!]`](#compliancerequirementscontrolinput) | Controls to add to the compliance requirement. |
|
||||
| <a id="mutationcreatecompliancerequirementparams"></a>`params` | [`ComplianceRequirementInput!`](#compliancerequirementinput) | Parameters to update the compliance requirement with. |
|
||||
|
||||
#### Fields
|
||||
|
|
@ -26427,7 +26428,7 @@ GPG signature for a signed commit.
|
|||
| <a id="groupdescendantgroupscount"></a>`descendantGroupsCount` | [`Int!`](#int) | Count of direct descendant groups of this group. |
|
||||
| <a id="groupdescription"></a>`description` | [`String`](#string) | Description of the namespace. |
|
||||
| <a id="groupdescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | GitLab Flavored Markdown rendering of `description`. |
|
||||
| <a id="groupdora"></a>`dora` | [`Dora`](#dora) | Group's DORA metrics. |
|
||||
| <a id="groupdora"></a>`dora` | [`GroupDora`](#groupdora) | Group's DORA metrics. |
|
||||
| <a id="groupduofeaturesenabled"></a>`duoFeaturesEnabled` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 16.10. **Status**: Experiment. Indicates whether GitLab Duo features are enabled for the group. |
|
||||
| <a id="groupemailsdisabled"></a>`emailsDisabled` | [`Boolean`](#boolean) | Indicates if a group has email notifications disabled. |
|
||||
| <a id="groupemailsenabled"></a>`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.
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="groupdatatransferegressnodes"></a>`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 |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="groupdorametricsenddate"></a>`endDate` | [`Date`](#date) | Date range to end at. Default is the current date. |
|
||||
| <a id="groupdorametricsenvironmenttiers"></a>`environmentTiers` | [`[DeploymentTier!]`](#deploymenttier) | Deployment tiers of the environments to return. Defaults to `[PRODUCTION]`. |
|
||||
| <a id="groupdorametricsinterval"></a>`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`. |
|
||||
| <a id="groupdorametricsstartdate"></a>`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 |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="groupdoraprojectscomplianceframeworkfilters"></a>`complianceFrameworkFilters` | [`ComplianceFrameworkFilters`](#complianceframeworkfilters) | Filters applied when selecting a compliance framework. |
|
||||
| <a id="groupdoraprojectsenddate"></a>`endDate` | [`Date!`](#date) | Date range to end DORA lookup at. |
|
||||
| <a id="groupdoraprojectshascodecoverage"></a>`hasCodeCoverage` | [`Boolean`](#boolean) | Returns only the projects which have code coverage. |
|
||||
| <a id="groupdoraprojectshasvulnerabilities"></a>`hasVulnerabilities` | [`Boolean`](#boolean) | Returns only the projects which have vulnerabilities. |
|
||||
| <a id="groupdoraprojectsids"></a>`ids` | [`[ID!]`](#id) | Filter projects by IDs. |
|
||||
| <a id="groupdoraprojectsincludearchived"></a>`includeArchived` | [`Boolean`](#boolean) | Include also archived projects. |
|
||||
| <a id="groupdoraprojectsincludesiblingprojects"></a>`includeSiblingProjects` {{< icon name="warning-solid" >}} | [`Boolean`](#boolean) | **Introduced** in GitLab 17.2. **Status**: Experiment. Include also projects from parent group. |
|
||||
| <a id="groupdoraprojectsincludesubgroups"></a>`includeSubgroups` | [`Boolean`](#boolean) | Include also subgroup projects. |
|
||||
| <a id="groupdoraprojectsnotaimedfordeletion"></a>`notAimedForDeletion` | [`Boolean`](#boolean) | Include projects that are not aimed for deletion. |
|
||||
| <a id="groupdoraprojectssbomcomponentid"></a>`sbomComponentId` | [`ID`](#id) | Return only the projects related to the specified SBOM component. |
|
||||
| <a id="groupdoraprojectssearch"></a>`search` | [`String`](#string) | Search project with most similar names or paths. |
|
||||
| <a id="groupdoraprojectssort"></a>`sort` | [`NamespaceProjectSort`](#namespaceprojectsort) | Sort projects by the criteria. |
|
||||
| <a id="groupdoraprojectsstartdate"></a>`startDate` | [`Date!`](#date) | Date range to start DORA lookup from. |
|
||||
| <a id="groupdoraprojectswithissuesenabled"></a>`withIssuesEnabled` | [`Boolean`](#boolean) | Return only projects with issues enabled. |
|
||||
| <a id="groupdoraprojectswithmergerequestsenabled"></a>`withMergeRequestsEnabled` | [`Boolean`](#boolean) | Return only projects with merge requests enabled. |
|
||||
| <a id="groupdoraprojectswithnamespacedomainpages"></a>`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 |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="compliancerequirementscontrolinputcontroltype"></a>`controlType` | [`String`](#string) | Type of the compliance control. |
|
||||
| <a id="compliancerequirementscontrolinputexpression"></a>`expression` | [`String`](#string) | Expression of the compliance control. |
|
||||
| <a id="compliancerequirementscontrolinputexpression"></a>`expression` | [`String!`](#string) | Expression of the compliance control. |
|
||||
| <a id="compliancerequirementscontrolinputexternalurl"></a>`externalUrl` | [`String`](#string) | URL of the external control. |
|
||||
| <a id="compliancerequirementscontrolinputname"></a>`name` | [`String`](#string) | New name for the compliance requirement control. |
|
||||
| <a id="compliancerequirementscontrolinputname"></a>`name` | [`String!`](#string) | New name for the compliance requirement control. |
|
||||
| <a id="compliancerequirementscontrolinputsecrettoken"></a>`secretToken` | [`String`](#string) | Secret token for an external control. |
|
||||
|
||||
### `ComplianceStandardsAdherenceInput`
|
||||
|
|
|
|||
|
|
@ -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 >}}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: ""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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: [
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
import { StreamingError } from '~/rapid_diffs/streaming_error';
|
||||
import { createAlert } from '~/alert';
|
||||
|
||||
jest.mock('~/alert');
|
||||
|
||||
describe('DiffFile Web Component', () => {
|
||||
const html = `
|
||||
<streaming-error message="Foo">
|
||||
</streaming-error>
|
||||
`;
|
||||
|
||||
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.',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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: "",
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue