Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
839e879bcf
commit
75a4eaade0
|
@ -522,7 +522,7 @@ export default {
|
|||
:file="file"
|
||||
:reviewed="fileReviews[index]"
|
||||
:is-first-file="index === 0"
|
||||
:is-last-file="index === diffs.length - 1"
|
||||
:is-last-file="index === diffFilesLength - 1"
|
||||
:help-page-path="helpPagePath"
|
||||
:can-current-user-fork="canCurrentUserFork"
|
||||
:view-diffs-file-by-file="viewDiffsFileByFile"
|
||||
|
|
|
@ -57,7 +57,10 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<label class="dropdown-input pt-3 pb-3 mb-0 border-bottom block position-relative" @click.stop>
|
||||
<label
|
||||
class="dropdown-input gl-pt-3 gl-pb-5 gl-mb-0 gl-border-b-1 gl-border-b-solid gl-display-block"
|
||||
@click.stop
|
||||
>
|
||||
<input
|
||||
ref="searchInput"
|
||||
v-model="search"
|
||||
|
|
|
@ -75,7 +75,10 @@ export default {
|
|||
|
||||
<template>
|
||||
<div>
|
||||
<label class="dropdown-input pt-3 pb-3 mb-0 border-bottom block" @click.stop>
|
||||
<label
|
||||
class="dropdown-input gl-pt-3 gl-pb-5 gl-mb-0 gl-border-b-1 gl-border-b-solid gl-display-block"
|
||||
@click.stop
|
||||
>
|
||||
<tokened-input
|
||||
v-model="search"
|
||||
:tokens="searchTokens"
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
<script>
|
||||
import Tab from '~/vue_shared/components/tabs/tab.vue';
|
||||
import Tabs from '~/vue_shared/components/tabs/tabs';
|
||||
import { GlTab, GlTabs } from '@gitlab/ui';
|
||||
import BranchesSearchList from './branches/search_list.vue';
|
||||
import MergeRequestSearchList from './merge_requests/list.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Tabs,
|
||||
Tab,
|
||||
GlTab,
|
||||
GlTabs,
|
||||
BranchesSearchList,
|
||||
MergeRequestSearchList,
|
||||
},
|
||||
|
@ -23,20 +22,14 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="ide-nav-form p-0">
|
||||
<tabs v-if="showMergeRequests" stop-propagation>
|
||||
<tab active>
|
||||
<template #title>
|
||||
{{ __('Branches') }}
|
||||
</template>
|
||||
<gl-tabs v-if="showMergeRequests">
|
||||
<gl-tab :title="__('Branches')">
|
||||
<branches-search-list />
|
||||
</tab>
|
||||
<tab>
|
||||
<template #title>
|
||||
{{ __('Merge Requests') }}
|
||||
</template>
|
||||
</gl-tab>
|
||||
<gl-tab :title="__('Merge Requests')">
|
||||
<merge-request-search-list />
|
||||
</tab>
|
||||
</tabs>
|
||||
</gl-tab>
|
||||
</gl-tabs>
|
||||
<branches-search-list v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// props can't be updated, so we map it to data where we can
|
||||
localActive: this.active,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
active() {
|
||||
this.localActive = this.active;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.isTab = true;
|
||||
},
|
||||
updated() {
|
||||
if (this.$parent) {
|
||||
this.$parent.$forceUpdate();
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="{
|
||||
active: localActive,
|
||||
}"
|
||||
class="tab-pane"
|
||||
role="tabpanel"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
|
@ -1,76 +0,0 @@
|
|||
export default {
|
||||
props: {
|
||||
stopPropagation: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex: 0,
|
||||
tabs: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.updateTabs();
|
||||
},
|
||||
methods: {
|
||||
updateTabs() {
|
||||
this.tabs = this.$children.filter((child) => child.isTab);
|
||||
this.currentIndex = this.tabs.findIndex((tab) => tab.localActive);
|
||||
},
|
||||
setTab(e, index) {
|
||||
if (this.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.tabs[this.currentIndex].localActive = false;
|
||||
this.tabs[index].localActive = true;
|
||||
|
||||
this.currentIndex = index;
|
||||
},
|
||||
},
|
||||
render(h) {
|
||||
const navItems = this.tabs.map((tab, i) =>
|
||||
h(
|
||||
'li',
|
||||
{
|
||||
key: i,
|
||||
},
|
||||
[
|
||||
h(
|
||||
'a',
|
||||
{
|
||||
class: tab.localActive ? 'active' : null,
|
||||
attrs: {
|
||||
href: '#',
|
||||
},
|
||||
on: {
|
||||
click: (e) => this.setTab(e, i),
|
||||
},
|
||||
},
|
||||
tab.$slots.title || tab.title,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
const nav = h(
|
||||
'ul',
|
||||
{
|
||||
class: 'nav-links tab-links',
|
||||
},
|
||||
[navItems],
|
||||
);
|
||||
const content = h(
|
||||
'div',
|
||||
{
|
||||
class: ['tab-content'],
|
||||
},
|
||||
[this.$slots.default],
|
||||
);
|
||||
|
||||
return h('div', {}, [[nav], content]);
|
||||
},
|
||||
};
|
|
@ -1043,8 +1043,7 @@ $ide-commit-header-height: 48px;
|
|||
.input-icon {
|
||||
right: auto;
|
||||
left: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ module Packages
|
|||
module FinderHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
InvalidPackageTypeError = Class.new(StandardError)
|
||||
InvalidStatusError = Class.new(StandardError)
|
||||
|
||||
private
|
||||
|
||||
def packages_visible_to_user(user, within_group:)
|
||||
|
@ -25,5 +28,35 @@ module Packages
|
|||
::Project.in_namespace(namespace_ids)
|
||||
.public_or_visible_to_user(user, ::Gitlab::Access::REPORTER)
|
||||
end
|
||||
|
||||
def package_type
|
||||
params[:package_type].presence
|
||||
end
|
||||
|
||||
def filter_by_package_type(packages)
|
||||
return packages unless package_type
|
||||
raise InvalidPackageTypeError unless ::Packages::Package.package_types.key?(package_type)
|
||||
|
||||
packages.with_package_type(package_type)
|
||||
end
|
||||
|
||||
def filter_by_package_name(packages)
|
||||
return packages unless params[:package_name].present?
|
||||
|
||||
packages.search_by_name(params[:package_name])
|
||||
end
|
||||
|
||||
def filter_with_version(packages)
|
||||
return packages if params[:include_versionless].present?
|
||||
|
||||
packages.has_version
|
||||
end
|
||||
|
||||
def filter_by_status(packages)
|
||||
return packages.displayable unless params[:status].present?
|
||||
raise InvalidStatusError unless Package.statuses.key?(params[:status])
|
||||
|
||||
packages.with_status(params[:status])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
module Packages
|
||||
class GroupPackagesFinder
|
||||
attr_reader :current_user, :group, :params
|
||||
|
||||
InvalidPackageTypeError = Class.new(StandardError)
|
||||
include ::Packages::FinderHelper
|
||||
|
||||
def initialize(current_user, group, params = { exclude_subgroups: false, order_by: 'created_at', sort: 'asc' })
|
||||
@current_user = current_user
|
||||
|
@ -20,6 +18,8 @@ module Packages
|
|||
|
||||
private
|
||||
|
||||
attr_reader :current_user, :group, :params
|
||||
|
||||
def packages_for_group_projects
|
||||
packages = ::Packages::Package
|
||||
.including_build_info
|
||||
|
@ -32,6 +32,7 @@ module Packages
|
|||
packages = filter_with_version(packages)
|
||||
packages = filter_by_package_type(packages)
|
||||
packages = filter_by_package_name(packages)
|
||||
packages = filter_by_status(packages)
|
||||
packages
|
||||
end
|
||||
|
||||
|
@ -46,10 +47,6 @@ module Packages
|
|||
.with_feature_available_for_user(:repository, current_user)
|
||||
end
|
||||
|
||||
def package_type
|
||||
params[:package_type].presence
|
||||
end
|
||||
|
||||
def groups
|
||||
return [group] if exclude_subgroups?
|
||||
|
||||
|
@ -59,24 +56,5 @@ module Packages
|
|||
def exclude_subgroups?
|
||||
params[:exclude_subgroups]
|
||||
end
|
||||
|
||||
def filter_by_package_type(packages)
|
||||
return packages unless package_type
|
||||
raise InvalidPackageTypeError unless Package.package_types.key?(package_type)
|
||||
|
||||
packages.with_package_type(package_type)
|
||||
end
|
||||
|
||||
def filter_by_package_name(packages)
|
||||
return packages unless params[:package_name].present?
|
||||
|
||||
packages.search_by_name(params[:package_name])
|
||||
end
|
||||
|
||||
def filter_with_version(packages)
|
||||
return packages if params[:include_versionless].present?
|
||||
|
||||
packages.has_version
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Packages
|
||||
class PackagesFinder
|
||||
attr_reader :params, :project
|
||||
include ::Packages::FinderHelper
|
||||
|
||||
def initialize(project, params = {})
|
||||
@project = project
|
||||
|
@ -21,29 +21,14 @@ module Packages
|
|||
packages = filter_with_version(packages)
|
||||
packages = filter_by_package_type(packages)
|
||||
packages = filter_by_package_name(packages)
|
||||
packages = filter_by_status(packages)
|
||||
packages = order_packages(packages)
|
||||
packages
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filter_with_version(packages)
|
||||
return packages if params[:include_versionless].present?
|
||||
|
||||
packages.has_version
|
||||
end
|
||||
|
||||
def filter_by_package_type(packages)
|
||||
return packages unless params[:package_type]
|
||||
|
||||
packages.with_package_type(params[:package_type])
|
||||
end
|
||||
|
||||
def filter_by_package_name(packages)
|
||||
return packages unless params[:package_name]
|
||||
|
||||
packages.search_by_name(params[:package_name])
|
||||
end
|
||||
attr_reader :params, :project
|
||||
|
||||
def order_packages(packages)
|
||||
packages.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
|
||||
|
|
|
@ -69,6 +69,8 @@ class Packages::Package < ApplicationRecord
|
|||
composer: 6, generic: 7, golang: 8, debian: 9,
|
||||
rubygems: 10 }
|
||||
|
||||
enum status: { default: 0, hidden: 1, processing: 2 }
|
||||
|
||||
scope :with_name, ->(name) { where(name: name) }
|
||||
scope :with_name_like, ->(name) { where(arel_table[:name].matches(name)) }
|
||||
scope :with_normalized_pypi_name, ->(name) { where("LOWER(regexp_replace(name, '[-_.]+', '-', 'g')) = ?", name.downcase) }
|
||||
|
@ -76,6 +78,8 @@ class Packages::Package < ApplicationRecord
|
|||
scope :with_version, ->(version) { where(version: version) }
|
||||
scope :without_version_like, -> (version) { where.not(arel_table[:version].matches(version)) }
|
||||
scope :with_package_type, ->(package_type) { where(package_type: package_type) }
|
||||
scope :with_status, ->(status) { where(status: status) }
|
||||
scope :displayable, -> { with_status(:default) }
|
||||
scope :including_build_info, -> { includes(pipelines: :user) }
|
||||
scope :including_project_route, -> { includes(project: { namespace: :route }) }
|
||||
scope :including_tags, -> { includes(:tags) }
|
||||
|
|
|
@ -9,7 +9,9 @@ module Packages
|
|||
.packages
|
||||
.with_package_type(package_type)
|
||||
.safe_find_or_create_by!(name: name, version: version) do |package|
|
||||
package.status = params[:status] if params[:status]
|
||||
package.creator = package_creator
|
||||
|
||||
add_build_info(package)
|
||||
end
|
||||
end
|
||||
|
@ -29,8 +31,9 @@ module Packages
|
|||
{
|
||||
creator: package_creator,
|
||||
name: params[:name],
|
||||
version: params[:version]
|
||||
}.merge(attrs)
|
||||
version: params[:version],
|
||||
status: params[:status]
|
||||
}.compact.merge(attrs)
|
||||
end
|
||||
|
||||
def package_creator
|
||||
|
|
|
@ -15,13 +15,16 @@ module Packages
|
|||
package_params = {
|
||||
name: params[:package_name],
|
||||
version: params[:package_version],
|
||||
build: params[:build]
|
||||
build: params[:build],
|
||||
status: params[:status]
|
||||
}
|
||||
|
||||
package = ::Packages::Generic::FindOrCreatePackageService
|
||||
.new(project, current_user, package_params)
|
||||
.execute
|
||||
|
||||
package.update_column(:status, params[:status]) if params[:status] && params[:status] != package.status
|
||||
|
||||
package.build_infos.safe_find_or_create_by!(pipeline: params[:build].pipeline) if params[:build].present?
|
||||
package
|
||||
end
|
||||
|
|
|
@ -42,6 +42,7 @@ module Packages
|
|||
package_params = {
|
||||
name: package_name,
|
||||
path: params[:path],
|
||||
status: params[:status],
|
||||
version: version
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add status attribute to packages and ability to set 'hidden' for generic packages
|
||||
merge_request: 53385
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Convert IDE nav form tab to GlTab
|
||||
merge_request: 54274
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddStatusToPackagesPackages < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def change
|
||||
add_column :packages_packages, :status, :smallint, default: 0, null: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddStatusIndexToPackagesPackages < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_packages_packages_on_project_id_and_status'
|
||||
|
||||
def up
|
||||
add_concurrent_index :packages_packages, [:project_id, :status], name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :packages_packages, name: INDEX_NAME
|
||||
end
|
||||
end
|
|
@ -0,0 +1 @@
|
|||
cb9f4b4e627cbc163653fc01c0542ef0ce87139b376c55bbaa4d7ab23e9b8973
|
|
@ -0,0 +1 @@
|
|||
5c661c453922181b350b8551d9a8f9b097e568459a2c2d128e41d9aefb026ab5
|
|
@ -15244,7 +15244,8 @@ CREATE TABLE packages_packages (
|
|||
name character varying NOT NULL,
|
||||
version character varying,
|
||||
package_type smallint NOT NULL,
|
||||
creator_id integer
|
||||
creator_id integer,
|
||||
status smallint DEFAULT 0 NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE packages_packages_id_seq
|
||||
|
@ -22886,6 +22887,8 @@ CREATE INDEX index_packages_packages_on_project_id_and_created_at ON packages_pa
|
|||
|
||||
CREATE INDEX index_packages_packages_on_project_id_and_package_type ON packages_packages USING btree (project_id, package_type);
|
||||
|
||||
CREATE INDEX index_packages_packages_on_project_id_and_status ON packages_packages USING btree (project_id, status);
|
||||
|
||||
CREATE INDEX index_packages_packages_on_project_id_and_version ON packages_packages USING btree (project_id, version);
|
||||
|
||||
CREATE INDEX index_packages_project_id_name_partial_for_nuget ON packages_packages USING btree (project_id, name) WHERE (((name)::text <> 'NuGet.Temporary.Package'::text) AND (version IS NOT NULL) AND (package_type = 4));
|
||||
|
|
|
@ -29,6 +29,7 @@ GET /projects/:id/packages
|
|||
| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm`, `pypi`, `composer`, `nuget`, or `golang`. (_Introduced in GitLab 12.9_)
|
||||
| `package_name` | string | no | Filter the project packages with a fuzzy search by name. (_Introduced in GitLab 12.9_)
|
||||
| `include_versionless` | boolean | no | When set to true, versionless packages are included in the response. (_Introduced in GitLab 13.8_)
|
||||
| `status` | string | no | Filter the returned packages by status. One of `default` (default), `hidden`, or `processing`. (_Introduced in GitLab 13.9_)
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/:id/packages"
|
||||
|
@ -70,6 +71,9 @@ Example response:
|
|||
|
||||
By default, the `GET` request returns 20 results, because the API is [paginated](README.md#pagination).
|
||||
|
||||
Although you can filter packages by status, working with packages that have a `processing` status
|
||||
can result in malformed data or broken packages.
|
||||
|
||||
### Within a group
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18871) in GitLab 12.5.
|
||||
|
@ -90,6 +94,7 @@ GET /groups/:id/packages
|
|||
| `package_type` | string | no | Filter the returned packages by type. One of `conan`, `maven`, `npm`, `pypi`, `composer`, `nuget`, or `golang`. (_Introduced in GitLab 12.9_) |
|
||||
| `package_name` | string | no | Filter the project packages with a fuzzy search by name. (_[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30980) in GitLab 13.0_)
|
||||
| `include_versionless` | boolean | no | When set to true, versionless packages are included in the response. (_Introduced in GitLab 13.8_)
|
||||
| `status` | string | no | Filter the returned packages by status. One of `default` (default), `hidden`, or `processing`. (_Introduced in GitLab 13.9_)
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/packages?exclude_subgroups=false"
|
||||
|
@ -166,6 +171,9 @@ The `_links` object contains the following properties:
|
|||
- `web_path`: The path which you can visit in GitLab and see the details of the package.
|
||||
- `delete_api_path`: The API path to delete the package. Only available if the request user has permission to do so.
|
||||
|
||||
Although you can filter packages by status, working with packages that have a `processing` status
|
||||
can result in malformed data or broken packages.
|
||||
|
||||
## Get a project package
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9667) in GitLab 11.9.
|
||||
|
|
|
@ -41,7 +41,7 @@ toggle the list of the milestone bars.
|
|||
> - Filtering roadmaps by milestone is enabled on GitLab.com.
|
||||
> - Filtering roadmaps by milestone is recommended for production use.
|
||||
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-filtering-roadmaps-by-milestone). **(PREMIUM SELF)**
|
||||
> - Filtering by epic confidentiality [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218624) in GitLab 13.8.
|
||||
> - Filtering by epic confidentiality [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218624) in GitLab 13.9.
|
||||
|
||||
WARNING:
|
||||
Filtering roadmaps by milestone might not be available to you. Check the **version history** note above for details.
|
||||
|
|
|
@ -49,6 +49,7 @@ PUT /projects/:id/packages/generic/:package_name/:package_version/:file_name
|
|||
| `package_name` | string | yes | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`).
|
||||
| `package_version` | string | yes | The package version. It can contain only numbers (`0-9`), and dots (`.`). Must be in the format of `X.Y.Z`, i.e. should match `/\A\d+\.\d+\.\d+\z/` regular expression.
|
||||
| `file_name` | string | yes | The filename. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`).
|
||||
| `status` | string | no | The package status. It can be `default` (default) or `hidden`. Hidden packages do not appear in the UI or [package API list endpoints](../../../api/packages.md).
|
||||
|
||||
Provide the file context in the request body.
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ module API
|
|||
file_name: API::NO_SLASH_URL_PART_REGEX
|
||||
}.freeze
|
||||
|
||||
ALLOWED_STATUSES = %w[default hidden].freeze
|
||||
|
||||
feature_category :package_registry
|
||||
|
||||
before do
|
||||
|
@ -35,6 +37,7 @@ module API
|
|||
requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.generic_package_name_regex, file_path: true
|
||||
requires :package_version, type: String, desc: 'Package version', regexp: Gitlab::Regex.generic_package_version_regex
|
||||
requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.generic_package_file_name_regex, file_path: true
|
||||
optional :status, type: String, values: ALLOWED_STATUSES, desc: 'Package status'
|
||||
end
|
||||
|
||||
put 'authorize' do
|
||||
|
@ -49,6 +52,7 @@ module API
|
|||
requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.generic_package_name_regex, file_path: true
|
||||
requires :package_version, type: String, desc: 'Package version', regexp: Gitlab::Regex.generic_package_version_regex
|
||||
requires :file_name, type: String, desc: 'Package file name', regexp: Gitlab::Regex.generic_package_file_name_regex, file_path: true
|
||||
optional :status, type: String, values: ALLOWED_STATUSES, desc: 'Package status'
|
||||
requires :file, type: ::API::Validations::Types::WorkhorseFile, desc: 'The package file to be published (generated by Multipart middleware)'
|
||||
end
|
||||
|
||||
|
|
|
@ -33,12 +33,14 @@ module API
|
|||
desc: 'Return packages with this name'
|
||||
optional :include_versionless, type: Boolean,
|
||||
desc: 'Returns packages without a version'
|
||||
optional :status, type: String, values: Packages::Package.statuses.keys,
|
||||
desc: 'Return packages with specified status'
|
||||
end
|
||||
get ':id/packages' do
|
||||
packages = Packages::GroupPackagesFinder.new(
|
||||
current_user,
|
||||
user_group,
|
||||
declared(params).slice(:exclude_subgroups, :order_by, :sort, :package_type, :package_name, :include_versionless)
|
||||
declared(params).slice(:exclude_subgroups, :order_by, :sort, :package_type, :package_name, :include_versionless, :status)
|
||||
).execute
|
||||
|
||||
present paginate(packages), with: ::API::Entities::Package, user: current_user, group: true
|
||||
|
|
|
@ -32,11 +32,13 @@ module API
|
|||
desc: 'Return packages with this name'
|
||||
optional :include_versionless, type: Boolean,
|
||||
desc: 'Returns packages without a version'
|
||||
optional :status, type: String, values: Packages::Package.statuses.keys,
|
||||
desc: 'Return packages with specified status'
|
||||
end
|
||||
get ':id/packages' do
|
||||
packages = ::Packages::PackagesFinder.new(
|
||||
user_project,
|
||||
declared_params.slice(:order_by, :sort, :package_type, :package_name, :include_versionless)
|
||||
declared_params.slice(:order_by, :sort, :package_type, :package_name, :include_versionless, :status)
|
||||
).execute
|
||||
|
||||
present paginate(packages), with: ::API::Entities::Package, user: current_user
|
||||
|
|
|
@ -7,9 +7,6 @@ module Gitlab
|
|||
class UrlBlocker
|
||||
BlockedUrlError = Class.new(StandardError)
|
||||
|
||||
GETADDRINFO_TIMEOUT_SECONDS = 15
|
||||
private_constant :GETADDRINFO_TIMEOUT_SECONDS
|
||||
|
||||
class << self
|
||||
# Validates the given url according to the constraints specified by arguments.
|
||||
#
|
||||
|
@ -113,7 +110,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def get_address_info(uri, dns_rebind_protection)
|
||||
Addrinfo.getaddrinfo(uri.hostname, get_port(uri), nil, :STREAM, timeout: GETADDRINFO_TIMEOUT_SECONDS).map do |addr|
|
||||
Addrinfo.getaddrinfo(uri.hostname, get_port(uri), nil, :STREAM).map do |addr|
|
||||
addr.ipv6_v4mapped? ? addr.ipv6_to_ipv4 : addr
|
||||
end
|
||||
rescue SocketError
|
||||
|
|
|
@ -18536,6 +18536,9 @@ msgstr ""
|
|||
msgid "Merge request (MR) approvals"
|
||||
msgstr ""
|
||||
|
||||
msgid "Merge request approval settings have been updated."
|
||||
msgstr ""
|
||||
|
||||
msgid "Merge request approvals"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -6,6 +6,15 @@ FactoryBot.define do
|
|||
name { 'my/company/app/my-app' }
|
||||
sequence(:version) { |n| "1.#{n}-SNAPSHOT" }
|
||||
package_type { :maven }
|
||||
status { :default }
|
||||
|
||||
trait :hidden do
|
||||
status { :hidden }
|
||||
end
|
||||
|
||||
trait :processing do
|
||||
status { :processing }
|
||||
end
|
||||
|
||||
factory :maven_package do
|
||||
maven_metadatum
|
||||
|
|
|
@ -147,6 +147,7 @@ RSpec.describe Packages::GroupPackagesFinder do
|
|||
end
|
||||
|
||||
it_behaves_like 'concerning versionless param'
|
||||
it_behaves_like 'concerning package statuses'
|
||||
end
|
||||
|
||||
context 'group has package of all types' do
|
||||
|
|
|
@ -82,5 +82,6 @@ RSpec.describe ::Packages::PackagesFinder do
|
|||
end
|
||||
|
||||
it_behaves_like 'concerning versionless param'
|
||||
it_behaves_like 'concerning package statuses'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import mountComponent from 'helpers/vue_mount_component_helper';
|
||||
import Tab from '~/vue_shared/components/tabs/tab.vue';
|
||||
|
||||
describe('Tab component', () => {
|
||||
const Component = Vue.extend(Tab);
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
vm = mountComponent(Component);
|
||||
});
|
||||
|
||||
it('sets localActive to equal active', (done) => {
|
||||
vm.active = true;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.localActive).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('sets active class', (done) => {
|
||||
vm.active = true;
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.classList).toContain('active');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,61 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import Tab from '~/vue_shared/components/tabs/tab.vue';
|
||||
import Tabs from '~/vue_shared/components/tabs/tabs';
|
||||
|
||||
describe('Tabs component', () => {
|
||||
let vm;
|
||||
|
||||
beforeEach(() => {
|
||||
vm = new Vue({
|
||||
components: {
|
||||
Tabs,
|
||||
Tab,
|
||||
},
|
||||
render(h) {
|
||||
return h('div', [
|
||||
h('tabs', [
|
||||
h('tab', { attrs: { title: 'Testing', active: true } }, 'First tab'),
|
||||
h('tab', [h('template', { slot: 'title' }, 'Test slot'), 'Second tab']),
|
||||
]),
|
||||
]);
|
||||
},
|
||||
}).$mount();
|
||||
|
||||
return vm.$nextTick();
|
||||
});
|
||||
|
||||
describe('tab links', () => {
|
||||
it('renders links for tabs', () => {
|
||||
expect(vm.$el.querySelectorAll('a').length).toBe(2);
|
||||
});
|
||||
|
||||
it('renders link titles from props', () => {
|
||||
expect(vm.$el.querySelector('a').textContent).toContain('Testing');
|
||||
});
|
||||
|
||||
it('renders link titles from slot', () => {
|
||||
expect(vm.$el.querySelectorAll('a')[1].textContent).toContain('Test slot');
|
||||
});
|
||||
|
||||
it('renders active class', () => {
|
||||
expect(vm.$el.querySelector('a').classList).toContain('active');
|
||||
});
|
||||
|
||||
it('updates active class on click', () => {
|
||||
vm.$el.querySelectorAll('a')[1].click();
|
||||
|
||||
return vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('a').classList).not.toContain('active');
|
||||
expect(vm.$el.querySelectorAll('a')[1].classList).toContain('active');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('content', () => {
|
||||
it('renders content panes', () => {
|
||||
expect(vm.$el.querySelectorAll('.tab-pane').length).toBe(2);
|
||||
expect(vm.$el.querySelectorAll('.tab-pane')[0].textContent).toContain('First tab');
|
||||
expect(vm.$el.querySelectorAll('.tab-pane')[1].textContent).toContain('Second tab');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -160,34 +160,6 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when resolving runs into a timeout' do
|
||||
let(:import_url) { 'http://example.com' }
|
||||
|
||||
subject { described_class.validate!(import_url, dns_rebind_protection: dns_rebind_protection) }
|
||||
|
||||
before do
|
||||
stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
|
||||
allow(Addrinfo).to receive(:getaddrinfo).and_raise(SocketError)
|
||||
end
|
||||
|
||||
context 'with dns rebinding enabled' do
|
||||
let(:dns_rebind_protection) { true }
|
||||
|
||||
it 'raises an error due to DNS timeout' do
|
||||
expect { subject }.to raise_error(described_class::BlockedUrlError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with dns rebinding disabled' do
|
||||
let(:dns_rebind_protection) { false }
|
||||
|
||||
it_behaves_like 'validates URI and hostname' do
|
||||
let(:expected_uri) { import_url }
|
||||
let(:expected_hostname) { nil }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#blocked_url?' do
|
||||
|
|
|
@ -4,6 +4,8 @@ require 'spec_helper'
|
|||
RSpec.describe Packages::Package, type: :model do
|
||||
include SortingHelper
|
||||
|
||||
it_behaves_like 'having unique enum values'
|
||||
|
||||
describe 'relationships' do
|
||||
it { is_expected.to belong_to(:project) }
|
||||
it { is_expected.to belong_to(:creator) }
|
||||
|
@ -605,6 +607,28 @@ RSpec.describe Packages::Package, type: :model do
|
|||
|
||||
it { is_expected.to match_array([pypi_package]) }
|
||||
end
|
||||
|
||||
describe '.displayable' do
|
||||
let_it_be(:hidden_package) { create(:maven_package, :hidden) }
|
||||
let_it_be(:processing_package) { create(:maven_package, :processing) }
|
||||
|
||||
subject { described_class.displayable }
|
||||
|
||||
it 'does not include hidden packages', :aggregate_failures do
|
||||
is_expected.not_to include(hidden_package)
|
||||
is_expected.not_to include(processing_package)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.with_status' do
|
||||
let_it_be(:hidden_package) { create(:maven_package, :hidden) }
|
||||
|
||||
subject { described_class.with_status(:hidden) }
|
||||
|
||||
it 'returns packages with specified status' do
|
||||
is_expected.to match_array([hidden_package])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.select_distinct_name' do
|
||||
|
|
|
@ -281,6 +281,7 @@ RSpec.describe API::GenericPackages do
|
|||
|
||||
package = project.packages.generic.last
|
||||
expect(package.name).to eq('mypackage')
|
||||
expect(package.status).to eq('default')
|
||||
expect(package.version).to eq('0.0.1')
|
||||
|
||||
if should_set_build_info
|
||||
|
@ -293,6 +294,39 @@ RSpec.describe API::GenericPackages do
|
|||
expect(package_file.file_name).to eq('myfile.tar.gz')
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a status' do
|
||||
context 'valid status' do
|
||||
let(:params) { super().merge(status: 'hidden') }
|
||||
|
||||
it 'assigns the status to the package' do
|
||||
headers = workhorse_headers.merge(auth_header)
|
||||
|
||||
upload_file(params, headers)
|
||||
|
||||
aggregate_failures do
|
||||
expect(response).to have_gitlab_http_status(:created)
|
||||
|
||||
package = project.packages.find_by(name: 'mypackage')
|
||||
expect(package).to be_hidden
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'invalid status' do
|
||||
let(:params) { super().merge(status: 'processing') }
|
||||
|
||||
it 'rejects the package' do
|
||||
headers = workhorse_headers.merge(auth_header)
|
||||
|
||||
upload_file(params, headers)
|
||||
|
||||
aggregate_failures do
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when valid personal access token is used' do
|
||||
|
|
|
@ -144,6 +144,7 @@ RSpec.describe API::GroupPackages do
|
|||
end
|
||||
|
||||
it_behaves_like 'with versionless packages'
|
||||
it_behaves_like 'with status param'
|
||||
it_behaves_like 'does not cause n^2 queries'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -120,6 +120,7 @@ RSpec.describe API::ProjectPackages do
|
|||
end
|
||||
|
||||
it_behaves_like 'with versionless packages'
|
||||
it_behaves_like 'with status param'
|
||||
it_behaves_like 'does not cause n^2 queries'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,6 +43,7 @@ RSpec.describe Packages::Composer::CreatePackageService do
|
|||
end
|
||||
|
||||
it_behaves_like 'assigns build to package'
|
||||
it_behaves_like 'assigns status to package'
|
||||
end
|
||||
|
||||
context 'with a tag' do
|
||||
|
@ -66,6 +67,7 @@ RSpec.describe Packages::Composer::CreatePackageService do
|
|||
end
|
||||
|
||||
it_behaves_like 'assigns build to package'
|
||||
it_behaves_like 'assigns status to package'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ RSpec.describe Packages::Conan::CreatePackageService do
|
|||
|
||||
it_behaves_like 'assigns the package creator'
|
||||
it_behaves_like 'assigns build to package'
|
||||
it_behaves_like 'assigns status to package'
|
||||
end
|
||||
|
||||
context 'invalid params' do
|
||||
|
|
|
@ -13,6 +13,8 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
|
|||
let(:temp_file) { Tempfile.new("test") }
|
||||
let(:file) { UploadedFile.new(temp_file.path, sha256: sha256) }
|
||||
let(:package) { create(:generic_package, project: project) }
|
||||
let(:package_service) { double }
|
||||
|
||||
let(:params) do
|
||||
{
|
||||
package_name: 'mypackage',
|
||||
|
@ -23,31 +25,34 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
|
|||
}
|
||||
end
|
||||
|
||||
let(:package_params) do
|
||||
{
|
||||
name: params[:package_name],
|
||||
version: params[:package_version],
|
||||
build: params[:build],
|
||||
status: nil
|
||||
}
|
||||
end
|
||||
|
||||
subject { described_class.new(project, user, params).execute }
|
||||
|
||||
before do
|
||||
FileUtils.touch(temp_file)
|
||||
expect(::Packages::Generic::FindOrCreatePackageService).to receive(:new).with(project, user, package_params).and_return(package_service)
|
||||
expect(package_service).to receive(:execute).and_return(package)
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm_f(temp_file)
|
||||
end
|
||||
|
||||
it 'creates package file' do
|
||||
package_service = double
|
||||
package_params = {
|
||||
name: params[:package_name],
|
||||
version: params[:package_version],
|
||||
build: params[:build]
|
||||
}
|
||||
expect(::Packages::Generic::FindOrCreatePackageService).to receive(:new).with(project, user, package_params).and_return(package_service)
|
||||
expect(package_service).to receive(:execute).and_return(package)
|
||||
|
||||
it 'creates package file', :aggregate_failures do
|
||||
expect { subject }.to change { package.package_files.count }.by(1)
|
||||
.and change { Packages::PackageFileBuildInfo.count }.by(1)
|
||||
|
||||
package_file = package.package_files.last
|
||||
aggregate_failures do
|
||||
expect(package_file.package.status).to eq('default')
|
||||
expect(package_file.package).to eq(package)
|
||||
expect(package_file.file_name).to eq('myfile.tar.gz.1')
|
||||
expect(package_file.size).to eq(file.size)
|
||||
|
@ -55,6 +60,21 @@ RSpec.describe Packages::Generic::CreatePackageFileService do
|
|||
end
|
||||
end
|
||||
|
||||
context 'with a status' do
|
||||
let(:params) { super().merge(status: 'hidden') }
|
||||
let(:package_params) { super().merge(status: 'hidden') }
|
||||
|
||||
it 'updates an existing packages status' do
|
||||
expect { subject }.to change { package.package_files.count }.by(1)
|
||||
.and change { Packages::PackageFileBuildInfo.count }.by(1)
|
||||
|
||||
package_file = package.package_files.last
|
||||
aggregate_failures do
|
||||
expect(package_file.package.status).to eq('hidden')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'assigns build to package file'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -36,10 +36,11 @@ RSpec.describe Packages::Maven::FindOrCreatePackageService do
|
|||
expect(pkg.version).to eq(version)
|
||||
end
|
||||
|
||||
context 'with a build' do
|
||||
context 'with optional attributes' do
|
||||
subject { service.execute.payload[:package] }
|
||||
|
||||
it_behaves_like 'assigns build to package'
|
||||
it_behaves_like 'assigns status to package'
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ RSpec.describe Packages::Npm::CreatePackageService do
|
|||
let(:params) { super().merge(build: job) }
|
||||
|
||||
it_behaves_like 'assigns build to package'
|
||||
it_behaves_like 'assigns status to package'
|
||||
|
||||
it 'creates a package file build info' do
|
||||
expect { subject }.to change { Packages::PackageFileBuildInfo.count }.by(1)
|
||||
|
|
|
@ -32,5 +32,6 @@ RSpec.describe Packages::Nuget::CreatePackageService do
|
|||
|
||||
it_behaves_like 'assigns the package creator'
|
||||
it_behaves_like 'assigns build to package'
|
||||
it_behaves_like 'assigns status to package'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -52,6 +52,7 @@ RSpec.describe Packages::Pypi::CreatePackageService do
|
|||
end
|
||||
|
||||
it_behaves_like 'assigns build to package'
|
||||
it_behaves_like 'assigns status to package'
|
||||
|
||||
context 'with an existing package' do
|
||||
before do
|
||||
|
|
|
@ -12,38 +12,19 @@ module DnsHelpers
|
|||
end
|
||||
|
||||
def stub_all_dns!
|
||||
allow(Addrinfo).to receive(:getaddrinfo).and_return([])
|
||||
allow(Addrinfo).to receive(:getaddrinfo).with(anything, anything, nil, :STREAM).and_return([])
|
||||
allow(Addrinfo).to receive(:getaddrinfo).with(anything, anything, nil, :STREAM, anything, anything).and_return([])
|
||||
end
|
||||
|
||||
def stub_invalid_dns!
|
||||
invalid_addresses = %r{
|
||||
\A
|
||||
(?:
|
||||
foobar\.\w |
|
||||
(?:\d{1,3}\.){4,}\d{1,3}
|
||||
)
|
||||
\z
|
||||
}ix
|
||||
|
||||
allow(Addrinfo).to receive(:getaddrinfo)
|
||||
.with(invalid_addresses, any_args)
|
||||
.and_raise(SocketError, 'getaddrinfo: Name or service not known')
|
||||
allow(Addrinfo).to receive(:getaddrinfo).with(/\Afoobar\.\w|(\d{1,3}\.){4,}\d{1,3}\z/i, anything, nil, :STREAM) do
|
||||
raise SocketError.new("getaddrinfo: Name or service not known")
|
||||
end
|
||||
end
|
||||
|
||||
def permit_local_dns!
|
||||
local_addresses = %r{
|
||||
\A
|
||||
(?:
|
||||
(?:127|10)\.0\.0\.\d{1,3} |
|
||||
(?:192\.168|172\.16)\.\d{1,3}\.\d{1,3} |
|
||||
0\.0\.0\.0 |
|
||||
localhost
|
||||
)
|
||||
\z
|
||||
}ix
|
||||
|
||||
allow(Addrinfo).to receive(:getaddrinfo)
|
||||
.with(local_addresses, any_args)
|
||||
.and_call_original
|
||||
local_addresses = /\A(127|10)\.0\.0\.\d{1,3}|(192\.168|172\.16)\.\d{1,3}\.\d{1,3}|0\.0\.0\.0|localhost\z/i
|
||||
allow(Addrinfo).to receive(:getaddrinfo).with(local_addresses, anything, nil, :STREAM).and_call_original
|
||||
allow(Addrinfo).to receive(:getaddrinfo).with(local_addresses, anything, nil, :STREAM, anything, anything, any_args).and_call_original
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,13 +18,14 @@ module StubRequests
|
|||
end
|
||||
|
||||
def stub_dns(url, ip_address:, port: 80)
|
||||
url = URI(url)
|
||||
url = parse_url(url)
|
||||
socket = Socket.sockaddr_in(port, ip_address)
|
||||
addr = Addrinfo.new(socket)
|
||||
|
||||
# See Gitlab::UrlBlocker
|
||||
allow(Addrinfo).to receive(:getaddrinfo)
|
||||
.with(url.hostname, url.port, any_args)
|
||||
.and_return([addr])
|
||||
.with(url.hostname, url.port, nil, :STREAM)
|
||||
.and_return([addr])
|
||||
end
|
||||
|
||||
def stub_all_dns(url, ip_address:)
|
||||
|
@ -33,14 +34,22 @@ module StubRequests
|
|||
socket = Socket.sockaddr_in(port, ip_address)
|
||||
addr = Addrinfo.new(socket)
|
||||
|
||||
# See Gitlab::UrlBlocker
|
||||
allow(Addrinfo).to receive(:getaddrinfo).and_call_original
|
||||
allow(Addrinfo).to receive(:getaddrinfo)
|
||||
.with(url.hostname, any_args)
|
||||
.with(url.hostname, anything, nil, :STREAM)
|
||||
.and_return([addr])
|
||||
end
|
||||
|
||||
def stubbed_hostname(url, hostname: IP_ADDRESS_STUB)
|
||||
url = URI(url)
|
||||
url = parse_url(url)
|
||||
url.hostname = hostname
|
||||
url.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_url(url)
|
||||
url.is_a?(URI) ? url : URI(url)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,3 +17,23 @@ RSpec.shared_examples 'concerning versionless param' do
|
|||
it { is_expected.not_to include(versionless_package) }
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'concerning package statuses' do
|
||||
let_it_be(:hidden_package) { create(:maven_package, :hidden, project: project) }
|
||||
|
||||
context 'hidden packages' do
|
||||
it { is_expected.not_to include(hidden_package) }
|
||||
end
|
||||
|
||||
context 'with status param' do
|
||||
let(:params) { { status: :hidden } }
|
||||
|
||||
it { is_expected.to match_array([hidden_package]) }
|
||||
end
|
||||
|
||||
context 'with invalid status param' do
|
||||
let(:params) { { status: 'invalid_status' } }
|
||||
|
||||
it { expect { subject }.to raise_exception(described_class::InvalidStatusError) }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,6 +40,19 @@ RSpec.shared_examples 'assigns the package creator' do
|
|||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'assigns status to package' do
|
||||
context 'with status param' do
|
||||
let_it_be(:status) { 'hidden' }
|
||||
let(:params) { super().merge(status: status) }
|
||||
|
||||
it 'assigns the status to the package' do
|
||||
package = subject
|
||||
|
||||
expect(package.status).to eq(status)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'returns packages' do |container_type, user_type|
|
||||
context "for #{user_type}" do
|
||||
before do
|
||||
|
@ -263,3 +276,41 @@ RSpec.shared_examples 'with versionless packages' do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.shared_examples 'with status param' do
|
||||
context 'hidden packages' do
|
||||
let!(:hidden_package) { create(:maven_package, :hidden, project: project) }
|
||||
|
||||
shared_examples 'not including the hidden package' do
|
||||
it 'does not return the package' do
|
||||
subject
|
||||
|
||||
expect(json_response.map { |package| package['id'] }).not_to include(hidden_package.id)
|
||||
end
|
||||
end
|
||||
|
||||
context 'no status param' do
|
||||
it_behaves_like 'not including the hidden package'
|
||||
end
|
||||
|
||||
context 'with hidden status param' do
|
||||
let(:params) { super().merge(status: 'hidden') }
|
||||
|
||||
it 'returns the package' do
|
||||
subject
|
||||
|
||||
expect(json_response.map { |package| package['id'] }).to include(hidden_package.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'bad status param' do
|
||||
let(:params) { super().merge(status: 'invalid') }
|
||||
|
||||
it 'returns the package' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue