Commit 224f6cb1 authored by Giorgenes Gelatti's avatar Giorgenes Gelatti

Move package frontend to core

- Move controllers, views and specs to core.
- Enables packages in core for everyone.
- Remove Gitlab.ee? checks for package.
- Move packages_enabled in project entity to core
- Move project#has_package? specs to core
- Move project#has_package? to core
- Fix package js paths
parent c30e32ef
import Api from 'ee/api';
import Api from '~/api';
import createFlash from '~/flash';
import { FETCH_PACKAGE_VERSIONS_ERROR } from '../constants';
import * as types from './mutation_types';
......
import Api from 'ee/api';
import Api from '~/api';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import * as types from './mutation_types';
......
import initPackageList from 'ee/packages/list/packages_list_app_bundle';
import initPackageList from '~/packages/list/packages_list_app_bundle';
document.addEventListener('DOMContentLoaded', () => {
if (document.getElementById('js-vue-packages-list')) {
......
import initPackageList from 'ee/packages/list/packages_list_app_bundle';
import initPackageList from '~/packages/list/packages_list_app_bundle';
document.addEventListener('DOMContentLoaded', () => {
if (document.getElementById('js-vue-packages-list')) {
......
import initPackageDetail from 'ee/packages/details/';
import initPackageDetail from '~/packages/details/';
document.addEventListener('DOMContentLoaded', initPackageDetail);
......@@ -7,7 +7,7 @@ module Groups
private
def verify_packages_enabled!
render_404 unless group.packages_feature_available?
render_404 unless group.packages_feature_enabled?
end
end
end
......@@ -392,6 +392,7 @@ class ProjectsController < Projects::ApplicationController
:initialize_with_readme,
:autoclose_referenced_issues,
:suggestion_commit_message,
:packages_enabled,
:service_desk_enabled,
project_feature_attributes: %i[
......
......@@ -28,6 +28,7 @@ module GroupsHelper
def group_packages_nav_link_paths
%w[
groups/packages#index
groups/container_registries#index
]
end
......@@ -157,6 +158,15 @@ module GroupsHelper
groups.to_json
end
def group_packages_nav?
group_packages_list_nav? ||
group_container_registry_nav?
end
def group_packages_list_nav?
@group.packages_feature_enabled?
end
private
def get_group_sidebar_links
......
# frozen_string_literal: true
module PackagesHelper
def package_sort_path(options = {})
"#{request.path}?#{options.to_param}"
end
def nuget_package_registry_url(project_id)
expose_url(api_v4_projects_packages_nuget_index_path(id: project_id, format: '.json'))
end
def package_registry_instance_url(registry_type)
expose_url("api/#{::API::API.version}/packages/#{registry_type}")
end
def package_registry_project_url(project_id, registry_type = :maven)
project_api_path = expose_path(api_v4_projects_path(id: project_id))
package_registry_project_path = "#{project_api_path}/packages/#{registry_type}"
expose_url(package_registry_project_path)
end
def package_from_presenter(package)
presenter = ::Packages::Detail::PackagePresenter.new(package)
presenter.detail_view.to_json
end
def pypi_registry_url(project_id)
full_url = expose_url(api_v4_projects_packages_pypi_simple_package_name_path({ id: project_id, package_name: '' }, true))
full_url.sub!('://', '://__token__:<your_personal_token>@')
end
def packages_coming_soon_enabled?(resource)
::Feature.enabled?(:packages_coming_soon, resource) && ::Gitlab.dev_env_or_com?
end
def packages_coming_soon_data(resource)
return unless packages_coming_soon_enabled?(resource)
{
project_path: ::Gitlab.com? ? 'gitlab-org/gitlab' : 'gitlab-org/gitlab-test',
suggested_contributions: help_page_path('user/packages/index', anchor: 'suggested-contributions')
}
end
def packages_list_data(type, resource)
{
resource_id: resource.id,
page_type: type,
empty_list_help_url: help_page_path('administration/packages/index'),
empty_list_illustration: image_path('illustrations/no-packages.svg'),
coming_soon_json: packages_coming_soon_data(resource).to_json
}
end
end
......@@ -429,9 +429,19 @@ module ProjectsHelper
apply_external_nav_tabs(nav_tabs, project)
nav_tabs += package_nav_tabs(project, current_user)
nav_tabs
end
def package_nav_tabs(project, current_user)
[].tap do |tabs|
if ::Gitlab.config.packages.enabled && can?(current_user, :read_package, project)
tabs << :packages
end
end
end
def apply_external_nav_tabs(nav_tabs, project)
nav_tabs << :external_issue_tracker if project.external_issue_tracker
nav_tabs << :external_wiki if project.external_wiki
......@@ -584,6 +594,7 @@ module ProjectsHelper
def project_permissions_settings(project)
feature = project.project_feature
{
packagesEnabled: !!project.packages_enabled,
visibilityLevel: project.visibility_level,
requestAccessEnabled: !!project.request_access_enabled,
issuesAccessLevel: feature.issues_access_level,
......@@ -604,6 +615,8 @@ module ProjectsHelper
def project_permissions_panel_data(project)
{
packagesAvailable: ::Gitlab.config.packages.enabled,
packagesHelpPath: help_page_path('user/packages/index'),
currentSettings: project_permissions_settings(project),
canDisableEmails: can_disable_emails?(project, current_user),
canChangeVisibilityLevel: can_change_visibility_level?(project, current_user),
......
......@@ -581,6 +581,47 @@ module SortingHelper
def sort_value_expire_date
'expired_asc'
end
def packages_sort_options_hash
{
sort_value_recently_created => sort_title_created_date,
sort_value_oldest_created => sort_title_created_date,
sort_value_name => sort_title_name,
sort_value_name_desc => sort_title_name,
sort_value_version_desc => sort_title_version,
sort_value_version_asc => sort_title_version,
sort_value_type_desc => sort_title_type,
sort_value_type_asc => sort_title_type,
sort_value_project_name_desc => sort_title_project_name,
sort_value_project_name_asc => sort_title_project_name
}
end
def packages_reverse_sort_order_hash
{
sort_value_recently_created => sort_value_oldest_created,
sort_value_oldest_created => sort_value_recently_created,
sort_value_name => sort_value_name_desc,
sort_value_name_desc => sort_value_name,
sort_value_version_desc => sort_value_version_asc,
sort_value_version_asc => sort_value_version_desc,
sort_value_type_desc => sort_value_type_asc,
sort_value_type_asc => sort_value_type_desc,
sort_value_project_name_desc => sort_value_project_name_asc,
sort_value_project_name_asc => sort_value_project_name_desc
}
end
def packages_sort_option_title(sort_value)
packages_sort_options_hash[sort_value] || sort_title_created_date
end
def packages_sort_direction_button(sort_value)
reverse_sort = packages_reverse_sort_order_hash[sort_value]
url = package_sort_path(sort: reverse_sort)
sort_direction_button(url, reverse_sort, sort_value)
end
end
SortingHelper.prepend_if_ee('::EE::SortingHelper')
......@@ -45,7 +45,7 @@ class Packages::PackageFile < ApplicationRecord
end
def download_path
Gitlab::Routing.url_helpers.download_project_package_file_path(project, self) if ::Gitlab.ee?
Gitlab::Routing.url_helpers.download_project_package_file_path(project, self)
end
def local?
......
- if group_container_registry_nav?
= nav_link(controller: 'groups/registry/repositories') do
= link_to group_container_registries_path(@group), title: _('Container Registry') do
- packages_link = group_packages_list_nav? ? group_packages_path(@group) : group_container_registries_path(@group)
- if group_packages_nav?
= nav_link(controller: ['groups/packages', 'groups/registry/repositories']) do
= link_to packages_link, title: _('Packages') do
.nav-icon-container
= sprite_icon('package')
%span.nav-item-name
= _('Packages & Registries')
%ul.sidebar-sub-level-items
= nav_link(controller: 'groups/registry/repositories', html_options: { class: "fly-out-top-item" } ) do
= link_to group_container_registries_path(@group), title: _('Container Registry') do
= nav_link(controller: [:packages, :repositories], html_options: { class: "fly-out-top-item" } ) do
= link_to packages_link, title: _('Packages & Registries') do
%strong.fly-out-top-item-name
= _('Packages & Registries')
%li.divider.fly-out-top-item
- if group_packages_list_nav?
= nav_link(controller: 'groups/packages') do
= link_to group_packages_path(@group), title: _('Packages') do
%span= _('Package Registry')
- if group_container_registry_nav?
= nav_link(controller: 'groups/registry/repositories') do
= link_to group_container_registries_path(@group), title: _('Container Registry') do
%span= _('Container Registry')
- if project_nav_tab? :container_registry
= nav_link controller: :repositories do
= link_to project_container_registry_index_path(@project) do
- packages_link = project_nav_tab?(:packages) ? project_packages_path(@project) : project_container_registry_index_path(@project)
- if (project_nav_tab?(:packages) || project_nav_tab?(:container_registry))
= nav_link controller: [:packages, :repositories] do
= link_to packages_link, data: { qa_selector: 'packages_link' } do
.nav-icon-container
= sprite_icon('package')
%span.nav-item-name
= _('Packages & Registries')
%ul.sidebar-sub-level-items
= nav_link(controller: :repositories, html_options: { class: "fly-out-top-item" } ) do
= link_to project_container_registry_index_path(@project) do
= nav_link(controller: [:packages, :repositories], html_options: { class: "fly-out-top-item" } ) do
= link_to packages_link do
%strong.fly-out-top-item-name
= _('Packages & Registries')
%li.divider.fly-out-top-item
- if project_nav_tab? :packages
= nav_link controller: :packages do
= link_to project_packages_path(@project), title: _('Package Registry') do
%span= _('Package Registry')
- if project_nav_tab? :container_registry
= nav_link controller: :repositories do
= link_to project_container_registry_index_path(@project), class: 'shortcuts-container-registry', title: _('Container Registry') do
%span= _('Container Registry')
---
title: Package feature moved to core
merge_request: 36667
author:
type: changed
......@@ -55,6 +55,8 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
post :toggle_subscription, on: :member
end
resources :packages, only: [:index]
resources :milestones, constraints: { id: %r{[^/]+} } do
member do
get :merge_requests
......
......@@ -30,6 +30,13 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :artifacts, only: [:index, :destroy]
resources :packages, only: [:index, :show, :destroy], module: :packages
resources :package_files, only: [], module: :packages do
member do
get :download
end
end
resources :jobs, only: [:index, :show], constraints: { id: /\d+/ } do
collection do
resources :artifacts, only: [] do
......
......@@ -39,7 +39,7 @@ module EE
adjourned_deletion_for_projects_and_groups: :deletion_adjourned_period,
required_ci_templates: :required_instance_ci_template,
disable_name_update_for_users: :updating_name_disabled_for_users,
packages: :npm_package_requests_forwarding,
package_forwarding: :npm_package_requests_forwarding,
default_branch_protection_restriction_in_groups: :group_owners_can_manage_default_branch_protection
}.each do |license_feature, attribute_name|
if License.feature_available?(license_feature)
......@@ -60,10 +60,6 @@ module EE
attrs << :maintenance_mode_message
end
if License.feature_available?(:package_forwarding)
attrs << :npm_package_requests_forwarding
end
attrs
end
......
......@@ -77,7 +77,6 @@ module EE
reset_approvals_on_push
ci_cd_only
use_custom_template
packages_enabled
require_password_to_approve
group_with_project_templates_id
]
......
......@@ -42,14 +42,9 @@ module EE
]
end
override :group_packages_nav?
def group_packages_nav?
group_packages_list_nav? ||
group_dependency_proxy_nav? ||
group_container_registry_nav?
end
def group_packages_list_nav?
@group.packages_feature_available?
super || group_dependency_proxy_nav?
end
def group_dependency_proxy_nav?
......
# frozen_string_literal: true
module EE
module PackagesHelper
def package_sort_path(options = {})
"#{request.path}?#{options.to_param}"
end
def nuget_package_registry_url(project_id)
expose_url(api_v4_projects_packages_nuget_index_path(id: project_id, format: '.json'))
end
def package_registry_instance_url(registry_type)
expose_url("api/#{::API::API.version}/packages/#{registry_type}")
end
def package_registry_project_url(project_id, registry_type = :maven)
project_api_path = expose_path(api_v4_projects_path(id: project_id))
package_registry_project_path = "#{project_api_path}/packages/#{registry_type}"
expose_url(package_registry_project_path)
end
def package_from_presenter(package)
presenter = ::Packages::Detail::PackagePresenter.new(package)
presenter.detail_view.to_json
end
def pypi_registry_url(project_id)
full_url = expose_url(api_v4_projects_packages_pypi_simple_package_name_path({ id: project_id, package_name: '' }, true))
full_url.sub!('://', '://__token__:<your_personal_token>@')
end
def packages_coming_soon_enabled?(resource)
::Feature.enabled?(:packages_coming_soon, resource) && ::Gitlab.dev_env_or_com?
end
def packages_coming_soon_data(resource)
return unless packages_coming_soon_enabled?(resource)
{
project_path: ::Gitlab.com? ? 'gitlab-org/gitlab' : 'gitlab-org/gitlab-test',
suggested_contributions: help_page_path('user/packages/index', anchor: 'suggested-contributions')
}
end
def packages_list_data(type, resource)
{
resource_id: resource.id,
page_type: type,
empty_list_help_url: help_page_path('administration/packages/index'),
empty_list_illustration: image_path('illustrations/no-packages.svg'),
coming_soon_json: packages_coming_soon_data(resource).to_json
}
end
end
end
......@@ -31,10 +31,6 @@ module EE
nav_tabs += get_project_security_nav_tabs(project, current_user)
if ::Gitlab.config.packages.enabled && can?(current_user, :read_package, project)
nav_tabs << :packages
end
if can?(current_user, :read_code_review_analytics, project)
nav_tabs << :code_review
end
......@@ -61,21 +57,6 @@ module EE
tab_ability_map
end
override :project_permissions_settings
def project_permissions_settings(project)
super.merge(
packagesEnabled: !!project.packages_enabled
)
end
override :project_permissions_panel_data
def project_permissions_panel_data(project)
super.merge(
packagesAvailable: ::Gitlab.config.packages.enabled,
packagesHelpPath: help_page_path('user/packages/index')
)
end
override :default_url_to_repo
def default_url_to_repo(project = @project)
case default_clone_protocol
......
......@@ -64,47 +64,6 @@ module EE
end
end
def packages_sort_options_hash
{
sort_value_recently_created => sort_title_created_date,
sort_value_oldest_created => sort_title_created_date,
sort_value_name => sort_title_name,
sort_value_name_desc => sort_title_name,
sort_value_version_desc => sort_title_version,
sort_value_version_asc => sort_title_version,
sort_value_type_desc => sort_title_type,
sort_value_type_asc => sort_title_type,
sort_value_project_name_desc => sort_title_project_name,
sort_value_project_name_asc => sort_title_project_name
}
end
def packages_reverse_sort_order_hash
{
sort_value_recently_created => sort_value_oldest_created,
sort_value_oldest_created => sort_value_recently_created,
sort_value_name => sort_value_name_desc,
sort_value_name_desc => sort_value_name,
sort_value_version_desc => sort_value_version_asc,
sort_value_version_asc => sort_value_version_desc,
sort_value_type_desc => sort_value_type_asc,
sort_value_type_asc => sort_value_type_desc,
sort_value_project_name_desc => sort_value_project_name_asc,
sort_value_project_name_asc => sort_value_project_name_desc
}
end
def packages_sort_option_title(sort_value)
packages_sort_options_hash[sort_value] || sort_title_created_date
end
def packages_sort_direction_button(sort_value)
reverse_sort = packages_reverse_sort_order_hash[sort_value]
url = package_sort_path(sort: reverse_sort)
sort_direction_button(url, reverse_sort, sort_value)
end
# Creates a button with the opposite ordering for the current field in UI.
def sort_order_button(sort)
opposite_sorting_param = epics_ordering_options_hash[sort] || epics_ordering_options_hash.key(sort)
......
......@@ -303,10 +303,6 @@ module EE
end
end
def packages_feature_available?
packages_feature_enabled? && feature_available?(:packages)
end
def dependency_proxy_feature_available?
::Gitlab.config.dependency_proxy.enabled && feature_available?(:dependency_proxy)
end
......
- packages_link = project_nav_tab?(:packages) ? project_packages_path(@project) : project_container_registry_index_path(@project)
- if (project_nav_tab?(:packages) || project_nav_tab?(:container_registry))
= nav_link controller: [:packages, :repositories] do
= link_to packages_link, data: { qa_selector: 'packages_link' } do
.nav-icon-container
= sprite_icon('package')
%span.nav-item-name
= _('Packages & Registries')
%ul.sidebar-sub-level-items
= nav_link(controller: [:packages, :repositories], html_options: { class: "fly-out-top-item" } ) do
= link_to packages_link do
%strong.fly-out-top-item-name
= _('Packages & Registries')
%li.divider.fly-out-top-item
- if project_nav_tab? :packages
= nav_link controller: :packages do
= link_to project_packages_path(@project), title: _('Package Registry') do
%span= _('Package Registry')
- if project_nav_tab? :container_registry
= nav_link controller: :repositories do
= link_to project_container_registry_index_path(@project), class: 'shortcuts-container-registry', title: _('Container Registry') do
%span= _('Container Registry')
......@@ -176,7 +176,6 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :roadmap, only: [:show], controller: 'roadmap'
resource :dependency_proxy, only: [:show, :update]
resources :packages, only: [:index]
post '/restore' => '/groups#restore', as: :restore
end
......
......@@ -15,13 +15,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :requirements, only: [:index]
end
resources :packages, only: [:index, :show, :destroy], module: :packages
resources :package_files, only: [], module: :packages do
member do
get :download
end
end
resources :feature_flags, param: :iid do
resources :feature_flag_issues, only: [:index, :create, :destroy], as: 'issues', path: 'issues'
end
......
......@@ -24,7 +24,6 @@ module EE
expose :mirror_overwrites_diverged_branches, if: ->(project, _) { project.mirror? }
expose :external_authorization_classification_label,
if: ->(_, _) { License.feature_available?(:external_authorization_service_api_management) }
expose :packages_enabled
expose :marked_for_deletion_at, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) }
expose :marked_for_deletion_on, if: ->(project, _) { project.feature_available?(:adjourned_deletion_for_projects_and_groups) } do |project, _|
project.marked_for_deletion_at
......
......@@ -32,7 +32,6 @@ module EE
optional :only_mirror_protected_branches, type: Grape::API::Boolean, desc: 'Only mirror protected branches'
optional :mirror_overwrites_diverged_branches, type: Grape::API::Boolean, desc: 'Pull mirror overwrites diverged branches'
optional :import_url, type: String, desc: 'URL from which the project is imported'
optional :packages_enabled, type: Grape::API::Boolean, desc: 'Enable project packages feature'
optional :fallback_approvals_required, type: Integer, desc: 'Overall approvals required when no rule is present'
end
end
......@@ -50,7 +49,6 @@ module EE
:approvals_before_merge,
:external_authorization_classification_label,
:import_url,
:packages_enabled,
:fallback_approvals_required
]
end
......
......@@ -11,12 +11,6 @@ RSpec.describe 'Project navbar' do
let_it_be(:project) { create(:project, :repository) }
before do
insert_after_sub_nav_item(
_('Labels'),
within: _('Issues'),
new_sub_nav_item_name: _('Service Desk')
)
insert_package_nav(_('Operations'))
project.add_maintainer(user)
......
......@@ -142,14 +142,6 @@ RSpec.describe API::Projects do
end
end
describe 'packages_enabled attribute' do
it 'is exposed' do
get api("/projects/#{project.id}", user)
expect(json_response).to have_key 'packages_enabled'
end
end
describe 'compliance_frameworks attribute' do
context 'when compliance_framework feature is available' do
context 'when project has a compliance framework' do
......@@ -800,22 +792,6 @@ RSpec.describe API::Projects do
end
end
describe 'updating packages_enabled attribute' do
it 'is enabled by default' do
expect(project.packages_enabled).to be true
end
context 'without the need for a license' do
it 'disables project packages feature' do
put(api("/projects/#{project.id}", user), params: { packages_enabled: false })
expect(response).to have_gitlab_http_status(:ok)
expect(project.reload.packages_enabled).to be false
expect(json_response['packages_enabled']).to eq(false)
end
end
end
describe 'updating approvals_before_merge attribute' do
context 'when authenticated as project owner' do
it 'updates approvals_before_merge' do
......
......@@ -221,58 +221,6 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe 'Packages' do
let(:user) { create(:user) }
let_it_be(:package_menu_name) { 'Packages & Registries' }
let_it_be(:package_entry_name) { 'Package Registry' }
before do
project.team.add_developer(user)
sign_in(user)
stub_container_registry_config(enabled: true)
end
context 'when packages is enabled' do
it 'packages link is visible' do
render
expect(rendered).to have_link(package_menu_name, href: project_packages_path(project))
end
it 'packages list link is visible' do
render
expect(rendered).to have_link(package_entry_name, href: project_packages_path(project))
end
it 'container registry link is visible' do
render
expect(rendered).to have_link('Container Registry', href: project_container_registry_index_path(project))
end
end
context 'when container registry is disabled' do
before do
stub_container_registry_config(enabled: false)
end
it 'packages top level and list link are visible' do
render
expect(rendered).to have_link(package_menu_name, href: project_packages_path(project))
expect(rendered).to have_link(package_entry_name, href: project_packages_path(project))
end
it 'container registry link is not visible' do
render
expect(rendered).not_to have_link('Container Registry', href: project_container_registry_index_path(project))
end
end
end
describe 'Settings > Operations' do
it 'is not visible when no valid license' do
allow(view).to receive(:can?).and_return(true)
......
......@@ -13,10 +13,8 @@ module API
expose :_links do
expose :web_path do |package|
if ::Gitlab.ee?
::Gitlab::Routing.url_helpers.project_package_path(package.project, package)
end
end
expose :delete_api_path, if: can_destroy(:package, &:project) do |package|
expose_url api_v4_projects_packages_path(package_id: package.id, id: package.project_id)
......
......@@ -35,6 +35,7 @@ module API
end
end
expose :packages_enabled
expose :empty_repo?, as: :empty_repo
expose :archived?, as: :archived
expose :visibility
......
......@@ -61,6 +61,7 @@ module API
optional :auto_devops_deploy_strategy, type: String, values: %w(continuous manual timed_incremental), desc: 'Auto Deploy strategy'
optional :autoclose_referenced_issues, type: Boolean, desc: 'Flag indication if referenced issues auto-closing is enabled'
optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins'
optional :packages_enabled, type: Boolean, desc: 'Enable project packages feature'
end
params :optional_project_params_ee do
......@@ -137,6 +138,7 @@ module API
:suggestion_commit_message,
:repository_storage,
:compliance_framework_setting,
:packages_enabled,
:service_desk_enabled,
# TODO: remove in API v5, replaced by *_access_level
......
......@@ -277,6 +277,11 @@ module QA
autoload :Show, 'qa/page/project/job/show'
end
module Packages
autoload :Index, 'qa/page/project/packages/index'
autoload :Show, 'qa/page/project/packages/show'
end
module Settings
autoload :Advanced, 'qa/page/project/settings/advanced'
autoload :Main, 'qa/page/project/settings/main'
......@@ -315,6 +320,7 @@ module QA
autoload :Repository, 'qa/page/project/sub_menus/repository'
autoload :Settings, 'qa/page/project/sub_menus/settings'
autoload :Project, 'qa/page/project/sub_menus/project'
autoload :Packages, 'qa/page/project/sub_menus/packages'
end
module Issue
......
......@@ -101,7 +101,6 @@ module QA
autoload :Menu, 'qa/ee/page/project/menu'
module SubMenus
autoload :Packages, 'qa/ee/page/project/sub_menus/packages'
autoload :SecurityCompliance, 'qa/ee/page/project/sub_menus/security_compliance'
autoload :Repository, 'qa/ee/page/project/sub_menus/repository'
autoload :Settings, 'qa/ee/page/project/sub_menus/settings'
......@@ -140,11 +139,6 @@ module QA
end
end
module Packages
autoload :Index, 'qa/ee/page/project/packages/index'
autoload :Show, 'qa/ee/page/project/packages/show'
end
module Pipeline
autoload :Show, 'qa/ee/page/project/pipeline/show'
end
......
......@@ -13,7 +13,6 @@ module QA
base.class_eval do
prepend QA::Page::Project::SubMenus::Common
prepend SubMenus::SecurityCompliance
prepend SubMenus::Packages
prepend SubMenus::Project
prepend SubMenus::Repository
prepend SubMenus::Settings
......
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module Packages
class Index < QA::Page::Base
view 'ee/app/views/projects/packages/packages/_legacy_package_list.html.haml' do
element :package_row
element :package_link
end
def click_package(name)
click_element(:package_link, text: name)
end
def has_package?(name)
has_element?(:package_link, text: name)
end
def has_no_package?(name)
has_no_element?(:package_link, text: name)
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module Packages
class Show < QA::Page::Base
view 'ee/app/assets/javascripts/packages/details/components/app.vue' do
element :delete_button
element :delete_modal_button
element :package_information_content
end
def has_package_info?(name, version)
has_element?(:package_information_content, text: /#{name}.*#{version}/)
end
def click_delete
click_element(:delete_button)
wait_for_animated_element(:delete_modal_button)
click_element(:delete_modal_button)
end
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module EE
module Page
module Project
module SubMenus
module Packages
extend QA::Page::PageConcern
def self.prepended(base)
base.class_eval do
view 'ee/app/views/layouts/nav/sidebar/_project_packages_link.html.haml' do
element :packages_link
end
end
end
def click_packages_link
within_sidebar do
click_element :packages_link
end
end
end
end
end
end
end
end
......@@ -11,6 +11,7 @@ module QA
include SubMenus::Operations
include SubMenus::Repository
include SubMenus::Settings
include SubMenus::Packages
view 'app/views/layouts/nav/sidebar/_project.html.haml' do
element :activity_link
......
# frozen_string_literal: true
module QA
module Page
module Project
module Packages
class Index < QA::Page::Base
view 'app/views/projects/packages/packages/_legacy_package_list.html.haml' do
element :package_row
element :package_link
end
def click_package(name)
click_element(:package_link, text: name)
end
def has_package?(name)
has_element?(:package_link, text: name)
end
def has_no_package?(name)
has_no_element?(:package_link, text: name)
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Project
module Packages
class Show < QA::Page::Base
view 'app/assets/javascripts/packages/details/components/app.vue' do
element :delete_button
element :delete_modal_button
element :package_information_content
end
def has_package_info?(name, version)
has_element?(:package_information_content, text: /#{name}.*#{version}/)
end
def click_delete
click_element(:delete_button)
wait_for_animated_element(:delete_modal_button)
click_element(:delete_modal_button)
end
end
end
end
end
end
# frozen_string_literal: true
module QA
module Page
module Project
module SubMenus
module Packages
extend QA::Page::PageConcern
def self.included(base)
super
base.class_eval do
view 'app/views/layouts/nav/sidebar/_project_packages_link.html.haml' do
element :packages_link
end
end
end
def click_packages_link
within_sidebar do
click_element :packages_link
end
end
end
end
end
end
end
......@@ -186,11 +186,7 @@ RSpec.describe 'Edit Project Settings' do
click_button "Save changes"
end
if ::Gitlab.ee?
expect(find(".sharing-permissions")).to have_selector(".project-feature-toggle.is-disabled", count: 4)
else
expect(find(".sharing-permissions")).to have_selector(".project-feature-toggle.is-disabled", count: 3)
end
end
it "shows empty features project homepage" do
......
......@@ -12,17 +12,6 @@ RSpec.describe 'Project navbar' do
let_it_be(:project) { create(:project, :repository) }
before do
# TODO - This can be moved into 'project navbar structure' shared
# context when service desk feature gets moved to core.
# More information in: https://gitlab.com/gitlab-org/gitlab/-/issues/215364
if Gitlab.ee?
insert_after_sub_nav_item(
_('Labels'),
within: _('Issues'),
new_sub_nav_item_name: _('Service Desk')
)
end
insert_package_nav(_('Operations'))
project.add_maintainer(user)
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import PackageActivity from 'ee/packages/details/components/activity.vue';
import PackageActivity from '~/packages/details/components/activity.vue';
import {
npmPackage,
mavenPackage as packageWithoutBuildInfo,
......
......@@ -2,20 +2,20 @@ import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import { GlEmptyState, GlModal } from '@gitlab/ui';
import Tracking from '~/tracking';
import * as getters from 'ee/packages/details/store/getters';
import PackagesApp from 'ee/packages/details/components/app.vue';
import PackageTitle from 'ee/packages/details/components/package_title.vue';
import PackageInformation from 'ee/packages/details/components/information.vue';
import NpmInstallation from 'ee/packages/details/components/npm_installation.vue';
import MavenInstallation from 'ee/packages/details/components/maven_installation.vue';
import * as SharedUtils from 'ee/packages/shared/utils';
import { TrackingActions } from 'ee/packages/shared/constants';
import PackagesListLoader from 'ee/packages/shared/components/packages_list_loader.vue';
import PackageListRow from 'ee/packages/shared/components/package_list_row.vue';
import ConanInstallation from 'ee/packages/details/components/conan_installation.vue';
import NugetInstallation from 'ee/packages/details/components/nuget_installation.vue';
import PypiInstallation from 'ee/packages/details/components/pypi_installation.vue';
import DependencyRow from 'ee/packages/details/components/dependency_row.vue';
import * as getters from '~/packages/details/store/getters';
import PackagesApp from '~/packages/details/components/app.vue';
import PackageTitle from '~/packages/details/components/package_title.vue';
import PackageInformation from '~/packages/details/components/information.vue';
import NpmInstallation from '~/packages/details/components/npm_installation.vue';
import MavenInstallation from '~/packages/details/components/maven_installation.vue';
import * as SharedUtils from '~/packages/shared/utils';
import { TrackingActions } from '~/packages/shared/constants';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
import PackageListRow from '~/packages/shared/components/package_list_row.vue';
import ConanInstallation from '~/packages/details/components/conan_installation.vue';
import NugetInstallation from '~/packages/details/components/nuget_installation.vue';
import PypiInstallation from '~/packages/details/components/pypi_installation.vue';
import DependencyRow from '~/packages/details/components/dependency_row.vue';
import {
conanPackage,
mavenPackage,
......
import { mount } from '@vue/test-utils';
import CodeInstruction from 'ee/packages/details/components/code_instruction.vue';
import { TrackingLabels } from 'ee/packages/details/constants';
import CodeInstruction from '~/packages/details/components/code_instruction.vue';
import { TrackingLabels } from '~/packages/details/constants';
import Tracking from '~/tracking';
describe('Package code instruction', () => {
......
import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import ConanInstallation from 'ee/packages/details/components/conan_installation.vue';
import ConanInstallation from '~/packages/details/components/conan_installation.vue';
import { conanPackage as packageEntity } from '../../mock_data';
import { registryUrl as conanPath } from '../mock_data';
import { GlTabs } from '@gitlab/ui';
......
import { shallowMount } from '@vue/test-utils';
import DependencyRow from 'ee/packages/details/components/dependency_row.vue';
import DependencyRow from '~/packages/details/components/dependency_row.vue';
import { dependencyLinks } from '../../mock_data';
describe('DependencyRow', () => {
......
import { shallowMount } from '@vue/test-utils';
import PackageInformation from 'ee/packages/details/components/information.vue';
import PackageInformation from '~/packages/details/components/information.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { GlLink } from '@gitlab/ui';
......
import { mount } from '@vue/test-utils';
import InstallationTabs from 'ee/packages/details/components/installation_tabs.vue';
import InstallationTabs from '~/packages/details/components/installation_tabs.vue';
import Tracking from '~/tracking';
import { TrackingActions } from 'ee/packages/details/constants';
import { TrackingActions } from '~/packages/details/constants';
describe('InstallationTabs', () => {
let wrapper;
......
import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import MavenInstallation from 'ee/packages/details/components/maven_installation.vue';
import MavenInstallation from '~/packages/details/components/maven_installation.vue';
import { registryUrl as mavenPath } from '../mock_data';
import { mavenPackage as packageEntity } from '../../mock_data';
import { GlTabs } from '@gitlab/ui';
......
import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import NpmInstallation from 'ee/packages/details/components/npm_installation.vue';
import NpmInstallation from '~/packages/details/components/npm_installation.vue';
import { npmPackage as packageEntity } from '../../mock_data';
import { registryUrl as nugetPath } from '../mock_data';
import { GlTabs } from '@gitlab/ui';
......
import Vuex from 'vuex';
import { mount, createLocalVue } from '@vue/test-utils';
import NugetInstallation from 'ee/packages/details/components/nuget_installation.vue';
import NugetInstallation from '~/packages/details/components/nuget_installation.vue';
import { nugetPackage as packageEntity } from '../../mock_data';
import { registryUrl as nugetPath } from '../mock_data';
import { GlTabs } from '@gitlab/ui';
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import PackageTitle from 'ee/packages/details/components/package_title.vue';
import PackageTags from 'ee/packages/shared/components/package_tags.vue';
import PackageTitle from '~/packages/details/components/package_title.vue';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import {
conanPackage,
mavenFiles,
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import PypiInstallation from 'ee/packages/details/components/pypi_installation.vue';
import InstallationTabs from 'ee/packages/details/components/installation_tabs.vue';
import PypiInstallation from '~/packages/details/components/pypi_installation.vue';
import InstallationTabs from '~/packages/details/components/installation_tabs.vue';
import { pypiPackage as packageEntity } from '../../mock_data';
import { GlTabs } from '@gitlab/ui';
......
import Api from 'ee/api';
import Api from '~/api';
import createFlash from '~/flash';
import fetchPackageVersions from 'ee/packages/details/store/actions';
import * as types from 'ee/packages/details/store/mutation_types';
import { FETCH_PACKAGE_VERSIONS_ERROR } from 'ee/packages/details/constants';
import fetchPackageVersions from '~/packages/details/store/actions';
import * as types from '~/packages/details/store/mutation_types';
import { FETCH_PACKAGE_VERSIONS_ERROR } from '~/packages/details/constants';
import testAction from 'helpers/vuex_action_helper';
import { npmPackage as packageEntity } from '../../mock_data';
jest.mock('~/flash.js');
jest.mock('ee/api.js');
jest.mock('~/api.js');
describe('Actions Package details store', () => {
describe('fetchPackageVersions', () => {
......
......@@ -13,7 +13,7 @@ import {
nugetSetupCommand,
pypiPipCommand,
pypiSetupCommand,
} from 'ee/packages/details/store/getters';
} from '~/packages/details/store/getters';
import {
conanPackage,
npmPackage,
......@@ -29,8 +29,8 @@ import {
registryUrl,
pypiSetupCommandStr,
} from '../mock_data';
import { generateConanRecipe } from 'ee/packages/details/utils';
import { NpmManager } from 'ee/packages/details/constants';
import { generateConanRecipe } from '~/packages/details/utils';
import { NpmManager } from '~/packages/details/constants';
describe('Getters PackageDetails Store', () => {
let state;
......
import mutations from 'ee/packages/details/store/mutations';
import * as types from 'ee/packages/details/store/mutation_types';
import mutations from '~/packages/details/store/mutations';
import * as types from '~/packages/details/store/mutation_types';
import { npmPackage as packageEntity } from '../../mock_data';
describe('Mutations package details Store', () => {
......
import { generateConanRecipe, generatePackageInfo } from 'ee/packages/details/utils';
import { generateConanRecipe, generatePackageInfo } from '~/packages/details/utils';
import { conanPackage, mavenPackage, npmPackage, nugetPackage } from '../mock_data';
import {
generateConanInformation,
......
import * as comingSoon from 'ee/packages/list/coming_soon/helpers';
import * as comingSoon from '~/packages/list/coming_soon/helpers';
import { fakeIssues, asGraphQLResponse, asViewModel } from './mock_data';
jest.mock('ee/api.js');
jest.mock('~/api.js');
describe('Coming Soon Helpers', () => {
const [noLabels, acceptingMergeRequestLabel, workflowLabel] = fakeIssues;
......
import { GlEmptyState, GlSkeletonLoader, GlLabel } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import ComingSoon from 'ee/packages/list/coming_soon/packages_coming_soon.vue';
import { TrackingActions } from 'ee/packages/shared/constants';
import ComingSoon from '~/packages/list/coming_soon/packages_coming_soon.vue';
import { TrackingActions } from '~/packages/shared/constants';
import { asViewModel } from './mock_data';
import Tracking from '~/tracking';
import VueApollo, { ApolloQuery } from 'vue-apollo';
jest.mock('ee/packages/list/coming_soon/helpers.js');
jest.mock('~/packages/list/coming_soon/helpers.js');
const localVue = createLocalVue();
localVue.use(VueApollo);
......
import Vuex from 'vuex';
import { GlSearchBoxByClick } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import PackagesFilter from 'ee/packages/list/components/packages_filter.vue';
import PackagesFilter from '~/packages/list/components/packages_filter.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlEmptyState, GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
import PackageListApp from 'ee/packages/list/components/packages_list_app.vue';
import PackageListApp from '~/packages/list/components/packages_list_app.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
......
......@@ -3,11 +3,11 @@ import { last } from 'lodash';
import { GlTable, GlPagination, GlModal } from '@gitlab/ui';
import Tracking from '~/tracking';
import { mount, createLocalVue } from '@vue/test-utils';
import PackagesList from 'ee/packages/list/components/packages_list.vue';
import PackagesListLoader from 'ee/packages/shared/components/packages_list_loader.vue';
import PackagesListRow from 'ee/packages/shared/components/package_list_row.vue';
import * as SharedUtils from 'ee/packages/shared/utils';
import { TrackingActions } from 'ee/packages/shared/constants';
import PackagesList from '~/packages/list/components/packages_list.vue';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
import PackagesListRow from '~/packages/shared/components/package_list_row.vue';
import * as SharedUtils from '~/packages/shared/utils';
import { TrackingActions } from '~/packages/shared/constants';
import stubChildren from 'helpers/stub_children';
import { packageList } from '../../mock_data';
......
import Vuex from 'vuex';
import { GlSorting } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils';
import PackagesSort from 'ee/packages/list/components/packages_sort.vue';
import PackagesSort from '~/packages/list/components/packages_sort.vue';
import stubChildren from 'helpers/stub_children';
const localVue = createLocalVue();
......
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import Api from 'ee/api';
import Api from '~/api';
import createFlash from '~/flash';
import * as actions from 'ee/packages/list/stores/actions';
import * as types from 'ee/packages/list/stores/mutation_types';
import {
MISSING_DELETE_PATH_ERROR,
DELETE_PACKAGE_ERROR_MESSAGE,
} from 'ee/packages/list/constants';
import * as actions from '~/packages/list/stores/actions';
import * as types from '~/packages/list/stores/mutation_types';
import { MISSING_DELETE_PATH_ERROR, DELETE_PACKAGE_ERROR_MESSAGE } from '~/packages/list/constants';
import testAction from 'helpers/vuex_action_helper';
jest.mock('~/flash.js');
jest.mock('ee/api.js');
jest.mock('~/api.js');
describe('Actions Package list store', () => {
const headers = 'bar';
......
import getList from 'ee/packages/list/stores/getters';
import getList from '~/packages/list/stores/getters';
import { packageList } from '../../mock_data';
describe('Getters registry list store', () => {
......
import mutations from 'ee/packages/list/stores/mutations';
import * as types from 'ee/packages/list/stores/mutation_types';
import createState from 'ee/packages/list/stores/state';
import mutations from '~/packages/list/stores/mutations';
import * as types from '~/packages/list/stores/mutation_types';
import createState from '~/packages/list/stores/state';
import * as commonUtils from '~/lib/utils/common_utils';
import { npmPackage, mavenPackage } from '../../mock_data';
......
import { getNewPaginationPage } from 'ee/packages/list/utils';
import { getNewPaginationPage } from '~/packages/list/utils';
describe('Packages list utils', () => {
describe('packageTypeDisplay', () => {
......
import { mount, shallowMount } from '@vue/test-utils';
import PackagesListRow from 'ee/packages/shared/components/package_list_row.vue';
import PackageTags from 'ee/packages/shared/components/package_tags.vue';
import PackagesListRow from '~/packages/shared/components/package_list_row.vue';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import { packageList } from '../../mock_data';
describe('packages_list_row', () => {
......
import { mount } from '@vue/test-utils';
import PackageTags from 'ee/packages/shared/components/package_tags.vue';
import PackageTags from '~/packages/shared/components/package_tags.vue';
import { mockTags } from '../../mock_data';
describe('PackageTags', () => {
......
import { mount } from '@vue/test-utils';
import PackagesListLoader from 'ee/packages/shared/components/packages_list_loader.vue';
import PackagesListLoader from '~/packages/shared/components/packages_list_loader.vue';
describe('PackagesListLoader', () => {
let wrapper;
......
import { shallowMount } from '@vue/test-utils';
import PublishMethod from 'ee/packages/shared/components/publish_method.vue';
import PublishMethod from '~/packages/shared/components/publish_method.vue';
import { packageList } from '../../mock_data';
describe('publish_method', () => {
......
......@@ -3,8 +3,8 @@ import {
beautifyPath,
getPackageTypeLabel,
getCommitLink,
} from 'ee/packages/shared/utils';
import { PackageType, TrackingCategories } from 'ee/packages/shared/constants';
} from '~/packages/shared/utils';
import { PackageType, TrackingCategories } from '~/packages/shared/constants';
import { packageList } from '../mock_data';
describe('Packages shared utils', () => {
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe EE::PackagesHelper do
RSpec.describe PackagesHelper do
let_it_be(:base_url) { "#{Gitlab.config.gitlab.url}/api/v4/" }
let_it_be(:project) { create(:project) }
......
......@@ -474,6 +474,46 @@ RSpec.describe Project do
end
end
describe '#has_packages?' do
let(:project) { create(:project, :public) }
subject { project.has_packages?(package_type) }
shared_examples 'returning true examples' do
let!(:package) { create("#{package_type}_package", project: project) }
it { is_expected.to be true }
end
shared_examples 'returning false examples' do
it { is_expected.to be false }
end
context 'with maven packages' do
it_behaves_like 'returning true examples' do
let(:package_type) { :maven }
end
end
context 'with npm packages' do
it_behaves_like 'returning true examples' do
let(:package_type) { :npm }
end
end
context 'with conan packages' do
it_behaves_like 'returning true examples' do
let(:package_type) { :conan }
end
end
context 'with no package type' do
it_behaves_like 'returning false examples' do
let(:package_type) { nil }
end
end
end
describe '#ci_pipelines' do
let(:project) { create(:project) }
......
......@@ -1586,6 +1586,7 @@ RSpec.describe API::Projects do
expect(json_response['ci_default_git_depth']).to eq(project.ci_default_git_depth)
expect(json_response['merge_method']).to eq(project.merge_method.to_s)
expect(json_response['readme_url']).to eq(project.readme_url)
expect(json_response).to have_key 'packages_enabled'
end
it 'returns a group link with expiration date' do
......@@ -2339,6 +2340,22 @@ RSpec.describe API::Projects do
expect(project_member).to be_persisted
end
describe 'updating packages_enabled attribute' do
it 'is enabled by default' do
expect(project.packages_enabled).to be true
end
context 'without the need for a license' do
it 'disables project packages feature' do
put(api("/projects/#{project.id}", user), params: { packages_enabled: false })
expect(response).to have_gitlab_http_status(:ok)
expect(project.reload.packages_enabled).to be false
expect(json_response['packages_enabled']).to eq(false)
end
end
end
it 'returns 400 when nothing sent' do
project_param = {}
......
......@@ -19,11 +19,7 @@ module NavbarStructureHelper
hash[:nav_sub_items].insert(index + 1, new_sub_nav_item_name)
end
# TODO - This can be moved into 'project navbar structure' shared
# context when package feature gets moved to core.
# More information in: https://gitlab.com/gitlab-org/gitlab/-/issues/221259
def insert_package_nav(within)
if ::Gitlab.ee?
insert_after_nav_item(
within,
new_nav_item: {
......@@ -32,26 +28,12 @@ module NavbarStructureHelper
}
)
end
end
# TODO - This ee? condition can be removed
# when package feature gets moved to core.
# More information in: https://gitlab.com/gitlab-org/gitlab/-/issues/221259
def insert_container_nav(within)
if ::Gitlab.ee?
insert_after_sub_nav_item(
_('Package Registry'),
within: _('Packages & Registries'),
new_sub_nav_item_name: _('Container Registry')
)
else
insert_after_nav_item(
within,
new_nav_item: {
nav_item: _('Packages & Registries'),
nav_sub_items: [_('Container Registry')]
}
)
end
end
end
......@@ -47,6 +47,58 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe 'Packages' do
let(:user) { create(:user) }
let_it_be(:package_menu_name) { 'Packages & Registries' }
let_it_be(:package_entry_name) { 'Package Registry' }
before do
project.team.add_developer(user)
sign_in(user)
stub_container_registry_config(enabled: true)
end
context 'when packages is enabled' do
it 'packages link is visible' do
render
expect(rendered).to have_link(package_menu_name, href: project_packages_path(project))
end
it 'packages list link is visible' do
render
expect(rendered).to have_link(package_entry_name, href: project_packages_path(project))
end
it 'container registry link is visible' do
render
expect(rendered).to have_link('Container Registry', href: project_container_registry_index_path(project))
end
end
context 'when container registry is disabled' do
before do
stub_container_registry_config(enabled: false)
end
it 'packages top level and list link are visible' do
render
expect(rendered).to have_link(package_menu_name, href: project_packages_path(project))
expect(rendered).to have_link(package_entry_name, href: project_packages_path(project))
end
it 'container registry link is not visible' do
render
expect(rendered).not_to have_link('Container Registry', href: project_container_registry_index_path(project))
end
end
end
describe 'releases entry' do
it 'renders releases link' do
render
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment