Commit 45b4df3e authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 7421e6f9
......@@ -3,7 +3,7 @@
* Render modal to confirm rollback/redeploy.
*/
import _ from 'underscore';
import { escape as esc } from 'lodash';
import { GlModal } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
......@@ -30,7 +30,7 @@ export default {
: s__('Environments|Rollback environment %{name}?');
return sprintf(title, {
name: _.escape(this.environment.name),
name: esc(this.environment.name),
});
},
......@@ -50,10 +50,10 @@ export default {
},
modalText() {
const linkStart = `<a class="commit-sha mr-0" href="${_.escape(this.commitUrl)}">`;
const commitId = _.escape(this.commitShortSha);
const linkStart = `<a class="commit-sha mr-0" href="${esc(this.commitUrl)}">`;
const commitId = esc(this.commitShortSha);
const linkEnd = '</a>';
const name = _.escape(this.name);
const name = esc(this.name);
const body = this.environment.isLastDeployment
? s__(
'Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?',
......
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
import _ from 'underscore';
import { isEmpty } from 'lodash';
import { GlTooltipDirective } from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
......@@ -79,7 +79,7 @@ export default {
* @returns {Boolean}
*/
hasLastDeploymentKey() {
if (this.model && this.model.last_deployment && !_.isEmpty(this.model.last_deployment)) {
if (this.model && this.model.last_deployment && !isEmpty(this.model.last_deployment)) {
return true;
}
return false;
......@@ -390,8 +390,8 @@ export default {
deploymentHasUser() {
return (
this.model &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.user)
!isEmpty(this.model.last_deployment) &&
!isEmpty(this.model.last_deployment.user)
);
},
......@@ -404,8 +404,8 @@ export default {
deploymentUser() {
if (
this.model &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.user)
!isEmpty(this.model.last_deployment) &&
!isEmpty(this.model.last_deployment.user)
) {
return this.model.last_deployment.user;
}
......@@ -431,8 +431,8 @@ export default {
shouldRenderBuildName() {
return (
!this.isFolder &&
!_.isEmpty(this.model.last_deployment) &&
!_.isEmpty(this.model.last_deployment.deployable)
!isEmpty(this.model.last_deployment) &&
!isEmpty(this.model.last_deployment.deployable)
);
},
......@@ -473,7 +473,7 @@ export default {
shouldRenderDeploymentID() {
return (
!this.isFolder &&
!_.isEmpty(this.model.last_deployment) &&
!isEmpty(this.model.last_deployment) &&
this.model.last_deployment.iid !== undefined
);
},
......
......@@ -3,7 +3,7 @@
* Render environments table.
*/
import { GlLoadingIcon } from '@gitlab/ui';
import _ from 'underscore';
import { flow, reverse, sortBy } from 'lodash/fp';
import environmentTableMixin from 'ee_else_ce/environments/mixins/environments_table_mixin';
import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
......@@ -102,13 +102,13 @@ export default {
* 4. Reverse (last deployment descending, name ascending),
* 5. Put folders first.
*/
return _.chain(environments)
.sortBy(env => (env.isFolder ? env.folderName : env.name))
.reverse()
.sortBy(env => (env.last_deployment ? env.last_deployment.created_at : '0000'))
.reverse()
.sortBy(env => (env.isFolder ? -1 : 1))
.value();
return flow(
sortBy(env => (env.isFolder ? env.folderName : env.name)),
reverse,
sortBy(env => (env.last_deployment ? env.last_deployment.created_at : '0000')),
reverse,
sortBy(env => (env.isFolder ? -1 : 1)),
)(environments);
},
},
};
......
/**
* Common code between environmets app and folder view
*/
import _ from 'underscore';
import { isEqual, isFunction, omitBy } from 'lodash';
import Visibility from 'visibilityjs';
import EnvironmentsStore from 'ee_else_ce/environments/stores/environments_store';
import Poll from '../../lib/utils/poll';
......@@ -54,7 +54,7 @@ export default {
const response = this.filterNilValues(resp.config.params);
const request = this.filterNilValues(this.requestData);
if (_.isEqual(response, request)) {
if (isEqual(response, request)) {
this.store.storeAvailableCount(resp.data.available_count);
this.store.storeStoppedCount(resp.data.stopped_count);
this.store.storeEnvironments(resp.data.environments);
......@@ -64,7 +64,7 @@ export default {
},
filterNilValues(obj) {
return _.omit(obj, value => _.isUndefined(value) || _.isNull(value));
return omitBy(obj, value => value === undefined || value === null);
},
/**
......@@ -109,7 +109,7 @@ export default {
.then(() => this.fetchEnvironments())
.catch(err => {
this.isLoading = false;
Flash(_.isFunction(errorMessage) ? errorMessage(err.response.data) : errorMessage);
Flash(isFunction(errorMessage) ? errorMessage(err.response.data) : errorMessage);
});
}
},
......
......@@ -44,7 +44,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:vue_issuable_sidebar, project.group)
push_frontend_feature_flag(:save_issuable_health_status, project.group)
push_frontend_feature_flag(:save_issuable_health_status, project.group, default_enabled: true)
end
before_action only: :show do
......
......@@ -6,20 +6,20 @@ module Types
authorize :read_statistics
field :commit_count, GraphQL::INT_TYPE, null: false,
field :commit_count, GraphQL::FLOAT_TYPE, null: false,
description: 'Commit count of the project'
field :storage_size, GraphQL::INT_TYPE, null: false,
field :storage_size, GraphQL::FLOAT_TYPE, null: false,
description: 'Storage size of the project'
field :repository_size, GraphQL::INT_TYPE, null: false,
field :repository_size, GraphQL::FLOAT_TYPE, null: false,
description: 'Repository size of the project'
field :lfs_objects_size, GraphQL::INT_TYPE, null: false,
field :lfs_objects_size, GraphQL::FLOAT_TYPE, null: false,
description: 'Large File Storage (LFS) object size of the project'
field :build_artifacts_size, GraphQL::INT_TYPE, null: false,
field :build_artifacts_size, GraphQL::FLOAT_TYPE, null: false,
description: 'Build artifacts size of the project'
field :packages_size, GraphQL::INT_TYPE, null: false,
field :packages_size, GraphQL::FLOAT_TYPE, null: false,
description: 'Packages size of the project'
field :wiki_size, GraphQL::INT_TYPE, null: true,
field :wiki_size, GraphQL::FLOAT_TYPE, null: true,
description: 'Wiki size of the project'
end
end
......@@ -6,11 +6,11 @@ module Types
authorize :read_statistics
field :storage_size, GraphQL::INT_TYPE, null: false, description: 'The total storage in bytes'
field :repository_size, GraphQL::INT_TYPE, null: false, description: 'The Git repository size in bytes'
field :lfs_objects_size, GraphQL::INT_TYPE, null: false, description: 'The LFS objects size in bytes'
field :build_artifacts_size, GraphQL::INT_TYPE, null: false, description: 'The CI artifacts size in bytes'
field :packages_size, GraphQL::INT_TYPE, null: false, description: 'The packages size in bytes'
field :wiki_size, GraphQL::INT_TYPE, null: false, description: 'The wiki size in bytes'
field :storage_size, GraphQL::FLOAT_TYPE, null: false, description: 'The total storage in bytes'
field :repository_size, GraphQL::FLOAT_TYPE, null: false, description: 'The Git repository size in bytes'
field :lfs_objects_size, GraphQL::FLOAT_TYPE, null: false, description: 'The LFS objects size in bytes'
field :build_artifacts_size, GraphQL::FLOAT_TYPE, null: false, description: 'The CI artifacts size in bytes'
field :packages_size, GraphQL::FLOAT_TYPE, null: false, description: 'The packages size in bytes'
field :wiki_size, GraphQL::FLOAT_TYPE, null: false, description: 'The wiki size in bytes'
end
end
# frozen_string_literal: true
module ContainerRegistry
class Event
ALLOWED_ACTIONS = %w(push delete).freeze
PUSH_ACTION = 'push'
EVENT_TRACKING_CATEGORY = 'container_registry:notification'
attr_reader :event
def initialize(event)
@event = event
end
def supported?
action.in?(ALLOWED_ACTIONS)
end
def handle!
# no op
end
def track!
tracked_target = target_tag? ? :tag : :repository
tracking_action = "#{action}_#{tracked_target}"
if target_repository? && action_push? && !container_repository_exists?
tracking_action = "create_repository"
end
::Gitlab::Tracking.event(EVENT_TRACKING_CATEGORY, tracking_action)
end
private
def target_tag?
# There is no clear indication in the event structure when we delete a top-level manifest
# except existance of "tag" key
event['target'].has_key?('tag')
end
def target_repository?
!target_tag? && event['target'].has_key?('repository')
end
def action
event['action']
end
def action_push?
PUSH_ACTION == action
end
def container_repository_exists?
return unless container_registry_path
ContainerRepository.exists_by_path?(container_registry_path)
end
def container_registry_path
path = event.dig('target', 'repository')
return unless path
ContainerRegistry::Path.new(path)
end
end
end
::ContainerRegistry::Event.prepend_if_ee('EE::ContainerRegistry::Event')
......@@ -16,6 +16,13 @@ class ContainerRepository < ApplicationRecord
where(project_id: Project.for_group_and_its_subgroups(group).with_container_registry.select(:id))
end
def self.exists_by_path?(path)
where(
project: path.repository_project,
name: path.repository_name
).exists?
end
# rubocop: disable CodeReuse/ServiceClass
def registry
@registry ||= begin
......
......@@ -20,7 +20,6 @@
issue: { assignee_id: finder.assignee.try(:id),
milestone_id: finder.milestones.first.try(:id) }),
class: "btn btn-success",
title: _("New issue"),
id: "new_issue_link"
- if show_export_button
......
......@@ -48,7 +48,7 @@
- if can_create_issue
- if can_update_issue || can_report_spam
%li.divider
%li= link_to 'New issue', new_project_issue_path(@project), title: 'New issue', id: 'new_issue_link'
%li= link_to 'New issue', new_project_issue_path(@project), id: 'new_issue_link'
= render 'shared/issuable/close_reopen_button', issuable: @issue, can_update: can_update_issue, can_reopen: can_reopen_issue
......
......@@ -20,7 +20,7 @@
= _("To widen your search, change or remove filters above")
- if show_new_issue_link?(@project)
.text-center
= link_to _("New issue"), new_project_issue_path(@project), class: "btn btn-success", title: _("New issue"), id: "new_issue_body_link"
= link_to _("New issue"), new_project_issue_path(@project), class: "btn btn-success", id: "new_issue_body_link"
- elsif is_opened_state && opened_issues_count == 0 && closed_issues_count > 0
%h4.text-center
= _("There are no open issues")
......@@ -28,7 +28,7 @@
= _("To keep this project going, create a new issue")
- if show_new_issue_link?(@project)
.text-center
= link_to _("New issue"), new_project_issue_path(@project), class: "btn btn-success", title: _("New issue"), id: "new_issue_body_link"
= link_to _("New issue"), new_project_issue_path(@project), class: "btn btn-success", id: "new_issue_body_link"
- elsif is_closed_state && opened_issues_count > 0 && closed_issues_count == 0
%h4.text-center
= _("There are no closed issues")
......@@ -42,7 +42,7 @@
- if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: _('New issue'), type: :issues, with_feature_enabled: 'issues'
- else
= link_to _('New issue'), button_path, class: 'btn btn-success', title: _('New issue'), id: 'new_issue_link'
= link_to _('New issue'), button_path, class: 'btn btn-success', id: 'new_issue_link'
- if show_import_button
= render 'projects/issues/import_csv/button', type: :text
......
---
title: Add `discussion_locked` to Webhook
merge_request: 28018
author:
type: fixed
---
title: Remove feature flag 'export_fast_serialize' and 'export_fast_serialize_with_raw_json'
merge_request: 28037
author:
type: performance
---
title: >
#42671: Project and group storage statistics now support values up to 8 PiB (up from 4GiB)
merge_request: 23131
author: Matthias van de Meent
type: fixed
---
title: Remove new issue tooltip
merge_request: 28261
author: Victor Wu
type: other
......@@ -6695,37 +6695,37 @@ type ProjectStatistics {
"""
Build artifacts size of the project
"""
buildArtifactsSize: Int!
buildArtifactsSize: Float!
"""
Commit count of the project
"""
commitCount: Int!
commitCount: Float!
"""
Large File Storage (LFS) object size of the project
"""
lfsObjectsSize: Int!
lfsObjectsSize: Float!
"""
Packages size of the project
"""
packagesSize: Int!
packagesSize: Float!
"""
Repository size of the project
"""
repositorySize: Int!
repositorySize: Float!
"""
Storage size of the project
"""
storageSize: Int!
storageSize: Float!
"""
Wiki size of the project
"""
wikiSize: Int
wikiSize: Float
}
type Query {
......@@ -7059,32 +7059,32 @@ type RootStorageStatistics {
"""
The CI artifacts size in bytes
"""
buildArtifactsSize: Int!
buildArtifactsSize: Float!
"""
The LFS objects size in bytes
"""
lfsObjectsSize: Int!
lfsObjectsSize: Float!
"""
The packages size in bytes
"""
packagesSize: Int!
packagesSize: Float!
"""
The Git repository size in bytes
"""
repositorySize: Int!
repositorySize: Float!
"""
The total storage in bytes
"""
storageSize: Int!
storageSize: Float!
"""
The wiki size in bytes
"""
wikiSize: Int!
wikiSize: Float!
}
"""
......
......@@ -20104,7 +20104,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -20122,7 +20122,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -20140,7 +20140,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -20158,7 +20158,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -20176,7 +20176,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -20194,7 +20194,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -20209,7 +20209,7 @@
],
"type": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
},
"isDeprecated": false,
......@@ -21203,7 +21203,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -21221,7 +21221,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -21239,7 +21239,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -21257,7 +21257,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -21275,7 +21275,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......@@ -21293,7 +21293,7 @@
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"name": "Float",
"ofType": null
}
},
......
......@@ -979,13 +979,13 @@ Information about pagination in a connection.
| Name | Type | Description |
| --- | ---- | ---------- |
| `buildArtifactsSize` | Int! | Build artifacts size of the project |
| `commitCount` | Int! | Commit count of the project |
| `lfsObjectsSize` | Int! | Large File Storage (LFS) object size of the project |
| `packagesSize` | Int! | Packages size of the project |
| `repositorySize` | Int! | Repository size of the project |
| `storageSize` | Int! | Storage size of the project |
| `wikiSize` | Int | Wiki size of the project |
| `buildArtifactsSize` | Float! | Build artifacts size of the project |
| `commitCount` | Float! | Commit count of the project |
| `lfsObjectsSize` | Float! | Large File Storage (LFS) object size of the project |
| `packagesSize` | Float! | Packages size of the project |
| `repositorySize` | Float! | Repository size of the project |
| `storageSize` | Float! | Storage size of the project |
| `wikiSize` | Float | Wiki size of the project |
## RemoveAwardEmojiPayload
......@@ -1047,12 +1047,12 @@ Counts of requirements by their state.
| Name | Type | Description |
| --- | ---- | ---------- |
| `buildArtifactsSize` | Int! | The CI artifacts size in bytes |
| `lfsObjectsSize` | Int! | The LFS objects size in bytes |
| `packagesSize` | Int! | The packages size in bytes |
| `repositorySize` | Int! | The Git repository size in bytes |
| `storageSize` | Int! | The total storage in bytes |
| `wikiSize` | Int! | The wiki size in bytes |
| `buildArtifactsSize` | Float! | The CI artifacts size in bytes |
| `lfsObjectsSize` | Float! | The LFS objects size in bytes |
| `packagesSize` | Float! | The packages size in bytes |
| `repositorySize` | Float! | The Git repository size in bytes |
| `storageSize` | Float! | The total storage in bytes |
| `wikiSize` | Float! | The wiki size in bytes |
## SentryDetailedError
......
......@@ -252,7 +252,7 @@ X-Gitlab-Event: Tag Push Hook
}
```
### Issues events
### Issue events
Triggered when a new issue is created or an existing issue was updated/closed/reopened.
......@@ -267,10 +267,12 @@ X-Gitlab-Event: Issue Hook
```json
{
"object_kind": "issue",
"event_type": "issue",
"user": {
"name": "Administrator",
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon",
"email": "admin@example.com"
},
"project": {
"id": 1,
......@@ -284,17 +286,12 @@ X-Gitlab-Event: Issue Hook
"visibility_level":20,
"path_with_namespace":"gitlabhq/gitlab-test",
"default_branch":"master",
"ci_config_path": null,
"homepage":"http://example.com/gitlabhq/gitlab-test",
"url":"http://example.com/gitlabhq/gitlab-test.git",
"ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
"http_url":"http://example.com/gitlabhq/gitlab-test.git"
},
"repository": {
"name": "Gitlab Test",
"url": "http://example.com/gitlabhq/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
"homepage": "http://example.com/gitlabhq/gitlab-test"
},
"object_attributes": {
"id": 301,
"title": "New API: create/update/delete file",
......@@ -304,14 +301,45 @@ X-Gitlab-Event: Issue Hook
"project_id": 14,
"created_at": "2013-12-03T17:15:43Z",
"updated_at": "2013-12-03T17:15:43Z",
"position": 0,
"branch_name": null,
"updated_by_id": 1,
"last_edited_at": null,
"last_edited_by_id": null,
"relative_position": 0,
"description": "Create new API for manipulations with repository",
"milestone_id": null,
"state": "opened",
"state_id": 1,
"confidential": false,
"discussion_locked": true,
"due_date": null,
"moved_to_id": null,
"duplicated_to_id": null,
"time_estimate": 0,
"total_time_spent": 0,
"human_total_time_spent": null,
"human_time_estimate": null,
"weight": null,
"iid": 23,
"url": "http://example.com/diaspora/issues/23",
"action": "open"
"state": "opened",
"action": "open",
"labels": [{
"id": 206,
"title": "API",
"color": "#ffffff",
"project_id": 14,
"created_at": "2013-12-03T17:15:43Z",
"updated_at": "2013-12-03T17:15:43Z",
"template": false,
"description": "API related issues",
"type": "ProjectLabel",
"group_id": 41
}]
},
"repository": {
"name": "Gitlab Test",
"url": "http://example.com/gitlabhq/gitlab-test.git",
"description": "Aut reprehenderit ut est.",
"homepage": "http://example.com/gitlabhq/gitlab-test"
},
"assignees": [{
"name": "User1",
......
......@@ -182,12 +182,12 @@ To help you track the status of your issues, you can assign a status to each iss
#### Enable issue health status
This feature comes with the `:save_issuable_health_status` feature flag disabled by default. However, in some cases
this feature is incompatible with old configuration. To turn on the feature while configuration is
This feature comes with the `:save_issuable_health_status` feature flag enabled by default. However, in some cases
this feature is incompatible with old configuration. To turn off the feature while configuration is
migrated, ask a GitLab administrator with Rails console access to run the following command:
```ruby
Feature.enable(:save_issuable_health_status)
Feature.disable(:save_issuable_health_status)
```
## Other Issue actions
......
......@@ -121,6 +121,7 @@ module API
mount ::API::BroadcastMessages
mount ::API::Commits
mount ::API::CommitStatuses
mount ::API::ContainerRegistryEvent
mount ::API::DeployKeys
mount ::API::DeployTokens
mount ::API::Deployments
......
# frozen_string_literal: true
module API
class ContainerRegistryEvent < Grape::API
DOCKER_DISTRIBUTION_EVENTS_V1_JSON = 'application/vnd.docker.distribution.events.v1+json'
before { authenticate_registry_notification! }
resource :container_registry_event do
helpers do
def authenticate_registry_notification!
secret_token = Gitlab.config.registry.notification_secret
unauthorized! unless Devise.secure_compare(secret_token, headers['Authorization'])
end
end
# Docker Registry sends data in a body of the request as JSON string,
# by setting 'content_type' we make Grape to parse it automatically
content_type :json, DOCKER_DISTRIBUTION_EVENTS_V1_JSON
format :json
params do
requires :events, type: Array
end
# This endpoint is used by Docker Registry to push a set of event
# that took place recently.
post 'events' do
params['events'].each do |raw_event|
event = ::ContainerRegistry::Event.new(raw_event)
if event.supported?
event.handle!
event.track!
end
end
status :ok
end
end
end
end
......@@ -17,6 +17,7 @@ module Gitlab
confidential
created_at
description
discussion_locked
due_date
id
iid
......
......@@ -142,12 +142,7 @@ module Gitlab
# returned by database when no `ORDER` is specified
batch = batch.reorder(batch.klass.primary_key)
if Feature.enabled?(:export_fast_serialize_with_raw_json, default_enabled: true)
data.append(JSONBatchRelation.new(batch, options, preloads[key]).tap(&:raw_json))
else
batch = batch.preload(preloads[key]) if preloads&.key?(key)
data += batch.as_json(options)
end
data.append(JSONBatchRelation.new(batch, options, preloads[key]).tap(&:raw_json))
end
data
......
......@@ -6,13 +6,9 @@ module Gitlab
include Gitlab::ImportExport::CommandLineUtil
def serialize(exportable, relations_tree)
if Feature.enabled?(:export_fast_serialize, default_enabled: true)
Gitlab::ImportExport::FastHashSerializer
.new(exportable, relations_tree)
.execute
else
exportable.as_json(relations_tree)
end
Gitlab::ImportExport::FastHashSerializer
.new(exportable, relations_tree)
.execute
end
def save(tree, dir_path, filename)
......
......@@ -18,6 +18,7 @@ describe Gitlab::HookData::IssueBuilder do
confidential
created_at
description
discussion_locked
due_date
id
iid
......
......@@ -23,50 +23,6 @@ describe Gitlab::ImportExport::Group::TreeSaver do
expect(group_tree_saver.save).to be true
end
context ':export_fast_serialize feature flag checks' do
before do
expect(Gitlab::ImportExport::Reader).to receive(:new).with(shared: shared, config: group_config).and_return(reader)
expect(reader).to receive(:group_tree).and_return(group_tree)
end
let(:reader) { instance_double('Gitlab::ImportExport::Reader') }
let(:group_config) { Gitlab::ImportExport::Config.new(config: Gitlab::ImportExport.group_config_file).to_h }
let(:group_tree) do
{
include: [{ milestones: { include: [] } }],
preload: { milestones: nil }
}
end
context 'when :export_fast_serialize feature is enabled' do
let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) }
before do
stub_feature_flags(export_fast_serialize: true)
expect(Gitlab::ImportExport::FastHashSerializer).to receive(:new).with(group, group_tree).and_return(serializer)
end
it 'uses FastHashSerializer' do
expect(serializer).to receive(:execute)
group_tree_saver.save
end
end
context 'when :export_fast_serialize feature is disabled' do
before do
stub_feature_flags(export_fast_serialize: false)
end
it 'is serialized via built-in `as_json`' do
expect(group).to receive(:as_json).with(group_tree).and_call_original
group_tree_saver.save
end
end
end
# It is mostly duplicated in
# `spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb`
# except:
......
......@@ -8,35 +8,17 @@ describe Gitlab::ImportExport::LegacyRelationTreeSaver do
let(:tree) { {} }
describe '#serialize' do
context 'when :export_fast_serialize feature is enabled' do
let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) }
let(:serializer) { instance_double(Gitlab::ImportExport::FastHashSerializer) }
before do
stub_feature_flags(export_fast_serialize: true)
end
it 'uses FastHashSerializer' do
expect(Gitlab::ImportExport::FastHashSerializer)
.to receive(:new)
.with(exportable, tree)
.and_return(serializer)
it 'uses FastHashSerializer' do
expect(Gitlab::ImportExport::FastHashSerializer)
.to receive(:new)
.with(exportable, tree)
.and_return(serializer)
expect(serializer).to receive(:execute)
expect(serializer).to receive(:execute)
relation_tree_saver.serialize(exportable, tree)
end
end
context 'when :export_fast_serialize feature is disabled' do
before do
stub_feature_flags(export_fast_serialize: false)
end
it 'is serialized via built-in `as_json`' do
expect(exportable).to receive(:as_json).with(tree)
relation_tree_saver.serialize(exportable, tree)
end
relation_tree_saver.serialize(exportable, tree)
end
end
end
......@@ -25,51 +25,6 @@ describe Gitlab::ImportExport::Project::LegacyTreeSaver do
expect(project_tree_saver.save).to be true
end
context ':export_fast_serialize feature flag checks' do
before do
expect(Gitlab::ImportExport::Reader).to receive(:new).with(shared: shared).and_return(reader)
expect(reader).to receive(:project_tree).and_return(project_tree)
end
let(:serializer) { instance_double('Gitlab::ImportExport::FastHashSerializer') }
let(:reader) { instance_double('Gitlab::ImportExport::Reader') }
let(:project_tree) do
{
include: [{ issues: { include: [] } }],
preload: { issues: nil }
}
end
context 'when :export_fast_serialize feature is enabled' do
before do
stub_feature_flags(export_fast_serialize: true)
end
it 'uses FastHashSerializer' do
expect(Gitlab::ImportExport::FastHashSerializer)
.to receive(:new)
.with(project, project_tree)
.and_return(serializer)
expect(serializer).to receive(:execute)
project_tree_saver.save
end
end
context 'when :export_fast_serialize feature is disabled' do
before do
stub_feature_flags(export_fast_serialize: false)
end
it 'is serialized via built-in `as_json`' do
expect(project).to receive(:as_json).with(project_tree)
project_tree_saver.save
end
end
end
# It is mostly duplicated in
# `spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb`
# except:
......
# frozen_string_literal: true
require 'spec_helper'
describe ContainerRegistry::Event do
using RSpec::Parameterized::TableSyntax
let_it_be(:group) { create(:group, name: 'group') }
let_it_be(:project) { create(:project, name: 'test', namespace: group) }
describe '#supported?' do
let(:raw_event) { { 'action' => action } }
subject { described_class.new(raw_event).supported? }
where(:action, :supported) do
'delete' | true
'push' | true
'mount' | false
'pull' | false
end
with_them do
it { is_expected.to eq supported }
end
end
describe '#handle!' do
let(:raw_event) { { 'action' => 'push', 'target' => { 'mediaType' => ContainerRegistry::Client::DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE } } }
subject { described_class.new(raw_event).handle! }
it { is_expected.to eq nil }
end
describe '#track!' do
let_it_be(:container_repository) { create(:container_repository, name: 'container', project: project) }
let(:raw_event) { { 'action' => action, 'target' => target } }
subject { described_class.new(raw_event).track! }
context 'with a respository target' do
let(:target) do
{
'mediaType' => ContainerRegistry::Client::DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE,
'repository' => repository_path
}
end
where(:repository_path, :action, :tracking_action) do
'group/test/container' | 'push' | 'push_repository'
'group/test/container' | 'delete' | 'delete_repository'
'foo/bar' | 'push' | 'create_repository'
'foo/bar' | 'delete' | 'delete_repository'
end
with_them do
it 'creates a tracking event' do
expect(::Gitlab::Tracking).to receive(:event).with('container_registry:notification', tracking_action)
subject
end
end
end
context 'with a tag target' do
let(:target) do
{
'mediaType' => ContainerRegistry::Client::DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE,
'repository' => repository_path,
'tag' => 'latest'
}
end
where(:repository_path, :action, :tracking_action) do
'group/test/container' | 'push' | 'push_tag'
'group/test/container' | 'delete' | 'delete_tag'
'foo/bar' | 'push' | 'push_tag'
'foo/bar' | 'delete' | 'delete_tag'
end
with_them do
it 'creates a tracking event' do
expect(::Gitlab::Tracking).to receive(:event).with('container_registry:notification', tracking_action)
subject
end
end
end
end
end
......@@ -29,6 +29,18 @@ describe ContainerRepository do
end
end
describe '.exists_by_path?' do
it 'returns true for known container repository paths' do
path = ContainerRegistry::Path.new("#{project.full_path}/#{repository.name}")
expect(described_class.exists_by_path?(path)).to be_truthy
end
it 'returns false for unknown container repository paths' do
path = ContainerRegistry::Path.new('you/dont/know/me')
expect(described_class.exists_by_path?(path)).to be_falsey
end
end
describe '#tag' do
it 'has a test tag' do
expect(repository.tag('test')).not_to be_nil
......
# frozen_string_literal: true
require 'spec_helper'
describe API::ContainerRegistryEvent do
let(:secret_token) { 'secret_token' }
let(:events) { [{ action: 'push' }] }
let(:registry_headers) { { 'Content-Type' => ::API::ContainerRegistryEvent::DOCKER_DISTRIBUTION_EVENTS_V1_JSON } }
describe 'POST /container_registry_event/events' do
before do
allow(Gitlab.config.registry).to receive(:notification_secret) { secret_token }
end
subject do
post api('/container_registry_event/events'),
params: { events: events }.to_json,
headers: registry_headers.merge('Authorization' => secret_token)
end
it 'returns 200 status and events are passed to event handler' do
event = spy(:event)
allow(::ContainerRegistry::Event).to receive(:new).and_return(event)
expect(event).to receive(:supported?).and_return(true)
subject
expect(event).to have_received(:handle!).once
expect(event).to have_received(:track!).once
expect(response.status).to eq 200
end
it 'returns 401 error status when token is invalid' do
post api('/container_registry_event/events'),
params: { events: events }.to_json,
headers: registry_headers.merge('Authorization' => 'invalid_token')
expect(response.status).to eq 401
end
end
end
......@@ -6,7 +6,7 @@ describe 'rendering namespace statistics' do
include GraphqlHelpers
let(:namespace) { user.namespace }
let!(:statistics) { create(:namespace_root_storage_statistics, namespace: namespace, packages_size: 5.megabytes) }
let!(:statistics) { create(:namespace_root_storage_statistics, namespace: namespace, packages_size: 5.gigabytes) }
let(:user) { create(:user) }
let(:query) do
......@@ -26,7 +26,7 @@ describe 'rendering namespace statistics' do
post_graphql(query, current_user: user)
expect(graphql_data['namespace']['rootStorageStatistics']).not_to be_blank
expect(graphql_data['namespace']['rootStorageStatistics']['packagesSize']).to eq(5.megabytes)
expect(graphql_data['namespace']['rootStorageStatistics']['packagesSize']).to eq(5.gigabytes)
end
end
......
......@@ -6,7 +6,7 @@ describe 'rendering project statistics' do
include GraphqlHelpers
let(:project) { create(:project) }
let!(:project_statistics) { create(:project_statistics, project: project, packages_size: 5.megabytes) }
let!(:project_statistics) { create(:project_statistics, project: project, packages_size: 5.gigabytes) }
let(:user) { create(:user) }
let(:query) do
......@@ -28,7 +28,7 @@ describe 'rendering project statistics' do
it "includes the packages size if the user can read the statistics" do
post_graphql(query, current_user: user)
expect(graphql_data['project']['statistics']['packagesSize']).to eq(5.megabytes)
expect(graphql_data['project']['statistics']['packagesSize']).to eq(5.gigabytes)
end
context 'when the project is public' do
......
......@@ -3,9 +3,9 @@
require 'spec_helper'
describe API::ProjectTemplates do
let(:public_project) { create(:project, :public) }
let(:private_project) { create(:project, :private) }
let(:developer) { create(:user) }
let_it_be(:public_project) { create(:project, :public) }
let_it_be(:private_project) { create(:project, :private) }
let_it_be(:developer) { create(:user) }
before do
private_project.add_developer(developer)
......
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