diff options
Diffstat (limited to 'app')
13 files changed, 145 insertions, 27 deletions
diff --git a/app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_trigger.vue b/app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_trigger.vue index a58b6e62254..eb5d1d39142 100644 --- a/app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_trigger.vue +++ b/app/assets/javascripts/add_context_commits_modal/components/add_context_commits_modal_trigger.vue @@ -38,7 +38,7 @@ export default { :class="[ { 'gl-ml-5': !contextCommitsEmpty, - 'gl-mt-5': !commitsEmpty && contextCommitsEmpty, + 'gl-mt-1': !commitsEmpty && contextCommitsEmpty, }, ]" :variant="commitsEmpty ? 'confirm' : 'default'" diff --git a/app/assets/javascripts/authentication/password/components/password_input.vue b/app/assets/javascripts/authentication/password/components/password_input.vue index fa9a7782b74..6e3af96cf33 100644 --- a/app/assets/javascripts/authentication/password/components/password_input.vue +++ b/app/assets/javascripts/authentication/password/components/password_input.vue @@ -15,27 +15,27 @@ export default { title: { type: String, required: false, - default: '', + default: null, }, id: { type: String, required: false, - default: '', + default: null, }, minimumPasswordLength: { type: String, required: false, - default: '', + default: null, }, qaSelector: { type: String, required: false, - default: '', + default: null, }, testid: { type: String, required: false, - default: '', + default: null, }, autocomplete: { type: String, diff --git a/app/assets/javascripts/projects/settings/components/access_dropdown.vue b/app/assets/javascripts/projects/settings/components/access_dropdown.vue index 08a1c586f69..a2e4827cbfa 100644 --- a/app/assets/javascripts/projects/settings/components/access_dropdown.vue +++ b/app/assets/javascripts/projects/settings/components/access_dropdown.vue @@ -63,6 +63,11 @@ export default { required: false, default: () => [], }, + items: { + type: Array, + required: false, + default: () => [], + }, }, data() { return { @@ -143,11 +148,37 @@ export default { query: debounce(function debouncedSearch() { return this.getData(); }, 500), + items(items) { + this.setDataForSave(items); + }, }, created() { this.getData({ initial: true }); }, methods: { + setDataForSave(items) { + this.selected = items.reduce( + (selected, item) => { + if (item.group_id) { + selected[LEVEL_TYPES.GROUP].push({ id: item.group_id, ...item }); + } else if (item.user_id) { + selected[LEVEL_TYPES.USER].push({ id: item.user_id, ...item }); + } else if (item.access_level) { + const level = this.accessLevelsData.find(({ id }) => item.access_level === id); + selected[LEVEL_TYPES.ROLE].push(level); + } else if (item.deploy_key_id) { + selected[LEVEL_TYPES.DEPLOY_KEY].push({ id: item.deploy_key_id, ...item }); + } + return selected; + }, + { + [LEVEL_TYPES.GROUP]: [], + [LEVEL_TYPES.USER]: [], + [LEVEL_TYPES.ROLE]: [], + [LEVEL_TYPES.DEPLOY_KEY]: [], + }, + ); + }, focusInput() { this.$refs.search.focusInput(); }, diff --git a/app/assets/javascripts/search/index.js b/app/assets/javascripts/search/index.js index 1e4b1e36514..f5684cebbf9 100644 --- a/app/assets/javascripts/search/index.js +++ b/app/assets/javascripts/search/index.js @@ -15,7 +15,7 @@ export const initSearchApp = () => { const store = createStore({ query, navigation, - useNewNavigation: gon.use_new_navigation, + useSidebarNavigation: gon.use_new_navigation, }); initTopbar(store); diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue index 317145d4cd1..7046a57058d 100644 --- a/app/assets/javascripts/search/sidebar/components/app.vue +++ b/app/assets/javascripts/search/sidebar/components/app.vue @@ -1,7 +1,7 @@ <script> import { mapState, mapGetters } from 'vuex'; -import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue'; -import ScopeNewNavigation from '~/search/sidebar/components/scope_new_navigation.vue'; +import ScopeLegacyNavigation from '~/search/sidebar/components/scope_legacy_navigation.vue'; +import ScopeSidebarNavigation from '~/search/sidebar/components/scope_sidebar_navigation.vue'; import SidebarPortal from '~/super_sidebar/components/sidebar_portal.vue'; import { SCOPE_ISSUES, SCOPE_MERGE_REQUESTS, SCOPE_BLOB } from '../constants'; import ResultsFilters from './results_filters.vue'; @@ -11,13 +11,14 @@ export default { name: 'GlobalSearchSidebar', components: { ResultsFilters, - ScopeNavigation, - ScopeNewNavigation, + ScopeLegacyNavigation, + ScopeSidebarNavigation, LanguageFilter, SidebarPortal, }, computed: { - ...mapState(['urlQuery', 'useNewNavigation']), + // useSidebarNavigation refers to whether the new left sidebar navigation is enabled + ...mapState(['useSidebarNavigation']), ...mapGetters(['currentScope']), showIssueAndMergeFilters() { return this.currentScope === SCOPE_ISSUES || this.currentScope === SCOPE_MERGE_REQUESTS; @@ -25,7 +26,9 @@ export default { showBlobFilter() { return this.currentScope === SCOPE_BLOB; }, - showOldNavigation() { + showScopeNavigation() { + // showLegacyNavigation refers to whether the scope navigation should be shown + // while the legacy navigation is being used and there are no search results the scope navigation has to be hidden return Boolean(this.currentScope); }, }, @@ -33,18 +36,18 @@ export default { </script> <template> - <section v-if="useNewNavigation"> + <section v-if="useSidebarNavigation"> <sidebar-portal> - <scope-new-navigation /> + <scope-sidebar-navigation /> <results-filters v-if="showIssueAndMergeFilters" /> <language-filter v-if="showBlobFilter" /> </sidebar-portal> </section> <section - v-else + v-else-if="showScopeNavigation" class="search-sidebar gl-display-flex gl-flex-direction-column gl-md-mr-5 gl-mb-6 gl-mt-5" > - <scope-navigation /> + <scope-legacy-navigation /> <results-filters v-if="showIssueAndMergeFilters" /> <language-filter v-if="showBlobFilter" /> </section> diff --git a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_legacy_navigation.vue index fc41baee831..e682369d60b 100644 --- a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue +++ b/app/assets/javascripts/search/sidebar/components/scope_legacy_navigation.vue @@ -8,7 +8,7 @@ import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../con import { slugifyWithUnderscore } from '../../../lib/utils/text_utility'; export default { - name: 'ScopeNavigation', + name: 'ScopeLegacyNavigation', i18n: { countOverLimitLabel: s__('GlobalSearch|Result count is over limit.'), }, diff --git a/app/assets/javascripts/search/sidebar/components/scope_new_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue index 86b7cc577a6..3707e152e47 100644 --- a/app/assets/javascripts/search/sidebar/components/scope_new_navigation.vue +++ b/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue @@ -6,7 +6,7 @@ import NavItem from '~/super_sidebar/components/nav_item.vue'; import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants'; export default { - name: 'ScopeNewNavigation', + name: 'ScopeSidebarNavigation', i18n: { countOverLimitLabel: s__('GlobalSearch|Result count is over limit.'), }, diff --git a/app/assets/javascripts/search/store/index.js b/app/assets/javascripts/search/store/index.js index 634f8f7a7fa..2478518c157 100644 --- a/app/assets/javascripts/search/store/index.js +++ b/app/assets/javascripts/search/store/index.js @@ -7,11 +7,11 @@ import createState from './state'; Vue.use(Vuex); -export const getStoreConfig = ({ query, navigation, useNewNavigation }) => ({ +export const getStoreConfig = (storeInitValues) => ({ actions, getters, mutations, - state: createState({ query, navigation, useNewNavigation }), + state: createState(storeInitValues), }); const createStore = (config) => new Vuex.Store(getStoreConfig(config)); diff --git a/app/assets/javascripts/search/store/state.js b/app/assets/javascripts/search/store/state.js index a62b6728819..c897d4108a8 100644 --- a/app/assets/javascripts/search/store/state.js +++ b/app/assets/javascripts/search/store/state.js @@ -1,7 +1,7 @@ import { cloneDeep } from 'lodash'; import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from './constants'; -const createState = ({ query, navigation, useNewNavigation }) => ({ +const createState = ({ query, navigation, useSidebarNavigation }) => ({ urlQuery: cloneDeep(query), query, groups: [], @@ -14,7 +14,7 @@ const createState = ({ query, navigation, useNewNavigation }) => ({ }, sidebarDirty: false, navigation, - useNewNavigation, + useSidebarNavigation, aggregations: { error: false, fetching: false, diff --git a/app/graphql/types/ci/catalog/resource_type.rb b/app/graphql/types/ci/catalog/resource_type.rb index 7958a0ba558..b90ae111a3d 100644 --- a/app/graphql/types/ci/catalog/resource_type.rb +++ b/app/graphql/types/ci/catalog/resource_type.rb @@ -37,6 +37,10 @@ module Types description: 'Number of times the catalog resource has been forked.', alpha: { milestone: '16.1' } + field :root_namespace, Types::NamespaceType, null: true, + description: 'Root namespace of the catalog resource.', + alpha: { milestone: '16.1' } + def web_path ::Gitlab::Routing.url_helpers.project_path(object.project) end @@ -44,6 +48,29 @@ module Types def forks_count BatchLoader::GraphQL.wrap(object.forks_count) end + + def root_namespace + BatchLoader::GraphQL.for(object.project_id).batch do |project_ids, loader| + projects = Project.id_in(project_ids) + + # This preloader uses traversal_ids to obtain Group-type root namespaces. + # It also preloads each project's immediate parent namespace, which effectively + # preloads the User-type root namespaces since they cannot be nested (parent == root). + Preloaders::ProjectRootAncestorPreloader.new(projects, :group).execute + root_namespaces = projects.map(&:root_ancestor) + + # NamespaceType requires the `:read_namespace` ability. We must preload the policy for + # Group-type namespaces to avoid N+1 queries caused by the authorization requests. + group_root_namespaces = root_namespaces.select { |n| n.type == ::Group.sti_name } + Preloaders::GroupPolicyPreloader.new(group_root_namespaces, current_user).execute + + # For User-type namespaces, the authorization request requires preloading the owner objects. + user_root_namespaces = root_namespaces.select { |n| n.type == ::Namespaces::UserNamespace.sti_name } + ActiveRecord::Associations::Preloader.new(records: user_root_namespaces, associations: :owner).call + + projects.each { |project| loader.call(project.id, project.root_ancestor) } + end + end end # rubocop: enable Graphql/AuthorizeTypes end diff --git a/app/services/packages/ml_model/create_package_file_service.rb b/app/services/packages/ml_model/create_package_file_service.rb new file mode 100644 index 00000000000..574f70940fc --- /dev/null +++ b/app/services/packages/ml_model/create_package_file_service.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module Packages + module MlModel + class CreatePackageFileService < BaseService + def execute + ::Packages::Package.transaction do + create_package_file(find_or_create_package) + end + end + + private + + def find_or_create_package + package_params = { + name: params[:package_name], + version: params[:package_version], + build: params[:build], + status: params[:status] + } + + package = ::Packages::MlModel::FindOrCreatePackageService + .new(project, current_user, package_params) + .execute + + package.update_column(:status, params[:status]) if params[:status] && params[:status] != package.status + + package.create_build_infos!(params[:build]) + + package + end + + def create_package_file(package) + file_params = { + file: params[:file], + size: params[:file].size, + file_sha256: params[:file].sha256, + file_name: params[:file_name], + build: params[:build] + } + + ::Packages::CreatePackageFileService.new(package, file_params).execute + end + end + end +end diff --git a/app/services/packages/ml_model/find_or_create_package_service.rb b/app/services/packages/ml_model/find_or_create_package_service.rb new file mode 100644 index 00000000000..cab99e1b008 --- /dev/null +++ b/app/services/packages/ml_model/find_or_create_package_service.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Packages + module MlModel + class FindOrCreatePackageService < ::Packages::CreatePackageService + def execute + find_or_create_package!(::Packages::Package.package_types['ml_model']) + end + end + end +end diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index 5d693ac603d..a0f47f375f7 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -8,10 +8,10 @@ - hidden = @hidden_commit_count - commits.chunk { |c| c.committed_date.in_time_zone.to_date }.each do |day, daily_commits| - %li.js-commit-header.gl-mt-7.gl-pb-2.gl-border-b{ data: { day: day } } + %li.js-commit-header.gl-py-2.gl-border-b{ data: { day: day } } %span.day.font-weight-bold= l(day, format: '%b %d, %Y') - %li.commits-row{ data: { day: day } } + %li.commits-row.gl-mb-6{ data: { day: day } } %ul.content-list.commit-list.flex-list - if Feature.enabled?(:cached_commits, project) = render partial: 'projects/commits/commit', collection: daily_commits, locals: { project: project, ref: ref, merge_request: merge_request }, cached: ->(commit) { commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) } @@ -19,13 +19,13 @@ = render partial: 'projects/commits/commit', collection: daily_commits, locals: { project: project, ref: ref, merge_request: merge_request } - if context_commits.present? - %li.js-commit-header.gl-mt-7.gl-pb-2.gl-border-b + %li.js-commit-header.gl-py-2.gl-border-b %span.font-weight-bold= n_("%d previously merged commit", "%d previously merged commits", context_commits.count) % context_commits.count - if can_update_merge_request = render Pajamas::ButtonComponent.new(button_options: { class: 'gl-ml-3 add-review-item-modal-trigger', data: { context_commits_empty: 'false' } }) do = _('Add/remove') - %li.commits-row + %li.commits-row.gl-mb-6 %ul.content-list.commit-list.flex-list - if Feature.enabled?(:cached_commits, project) = render partial: 'projects/commits/commit', collection: context_commits, locals: { project: project, ref: ref, merge_request: merge_request }, cached: ->(commit) { commit_partial_cache_key(commit, ref: ref, merge_request: merge_request, request: request) } |