Commit 8a64580c authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 9c7c410a 86c8e69a
......@@ -52,7 +52,7 @@ class InvitesController < ApplicationController
end
def current_user_matches_invite?
current_user.verified_emails.include?(@member.invite_email)
current_user.verified_email?(@member.invite_email)
end
def member?
......
# frozen_string_literal: true
module Mutations
module MergeRequests
class AttentionRequired < Base
graphql_name 'MergeRequestAttentionRequired'
argument :user_id, ::Types::GlobalIDType[::User],
loads: Types::UserType,
required: true,
description: <<~DESC
User ID for the user that has their attention requested.
DESC
def resolve(project_path:, iid:, user:)
merge_request = authorized_find!(project_path: project_path, iid: iid)
result = ::MergeRequests::AttentionRequiredService.new(project: merge_request.project, current_user: current_user, merge_request: merge_request, user: user).execute
{
merge_request: merge_request,
errors: Array(result[:message])
}
end
end
end
end
......@@ -69,6 +69,7 @@ module Types
mount_mutation Mutations::MergeRequests::SetDraft, calls_gitaly: true
mount_mutation Mutations::MergeRequests::SetAssignees
mount_mutation Mutations::MergeRequests::ReviewerRereview
mount_mutation Mutations::MergeRequests::AttentionRequired, feature_flag: :mr_attention_requests
mount_mutation Mutations::Metrics::Dashboard::Annotations::Create
mount_mutation Mutations::Metrics::Dashboard::Annotations::Delete
mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true
......
......@@ -24,6 +24,7 @@ module TodosHelper
when Todo::UNMERGEABLE then 'Could not merge'
when Todo::DIRECTLY_ADDRESSED then "directly addressed #{todo_action_subject(todo)} on"
when Todo::MERGE_TRAIN_REMOVED then "Removed from Merge Train:"
when Todo::ATTENTION_REQUIRED then 'requested your attention on'
end
end
......
......@@ -12,14 +12,24 @@ module LooseForeignKeys
@delete_count_by_table = Hash.new { |h, k| h[k] = 0 }
@update_count_by_table = Hash.new { |h, k| h[k] = 0 }
@start_time = monotonic_time
@deletes_counter = Gitlab::Metrics.counter(
:loose_foreign_key_deletions,
'The number of loose foreign key deletions'
)
@updates_counter = Gitlab::Metrics.counter(
:loose_foreign_key_updates,
'The number of loose foreign key updates'
)
end
def add_deletions(table, count)
@delete_count_by_table[table] += count
@deletes_counter.increment({ table: table }, count)
end
def add_updates(table, count)
@update_count_by_table[table] += count
@updates_counter.increment({ table: table }, count)
end
def over_limit?
......
......@@ -1919,6 +1919,10 @@ class MergeRequest < ApplicationRecord
true
end
def find_assignee(user)
merge_request_assignees.find_by(user_id: user.id)
end
def find_reviewer(user)
merge_request_reviewers.find_by(user_id: user.id)
end
......
......@@ -114,18 +114,18 @@ class MergeRequestDiffCommit < ApplicationRecord
end
def author_name
commit_author.name
commit_author&.name
end
def author_email
commit_author.email
commit_author&.email
end
def committer_name
committer.name
committer&.name
end
def committer_email
committer.email
committer&.email
end
end
......@@ -142,6 +142,7 @@ class Namespace < ApplicationRecord
'COALESCE(SUM(ps.snippets_size), 0) AS snippets_size',
'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size',
'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size',
'COALESCE(SUM(ps.pipeline_artifacts_size), 0) AS pipeline_artifacts_size',
'COALESCE(SUM(ps.packages_size), 0) AS packages_size',
'COALESCE(SUM(ps.uploads_size), 0) AS uploads_size'
)
......
......@@ -18,6 +18,7 @@ class Todo < ApplicationRecord
DIRECTLY_ADDRESSED = 7
MERGE_TRAIN_REMOVED = 8 # This is an EE-only feature
REVIEW_REQUESTED = 9
ATTENTION_REQUIRED = 10
ACTION_NAMES = {
ASSIGNED => :assigned,
......@@ -28,7 +29,8 @@ class Todo < ApplicationRecord
APPROVAL_REQUIRED => :approval_required,
UNMERGEABLE => :unmergeable,
DIRECTLY_ADDRESSED => :directly_addressed,
MERGE_TRAIN_REMOVED => :merge_train_removed
MERGE_TRAIN_REMOVED => :merge_train_removed,
ATTENTION_REQUIRED => :attention_required
}.freeze
belongs_to :author, class_name: "User"
......@@ -189,6 +191,10 @@ class Todo < ApplicationRecord
action == REVIEW_REQUESTED
end
def attention_required?
action == ATTENTION_REQUIRED
end
def merge_train_removed?
action == MERGE_TRAIN_REMOVED
end
......
......@@ -6,13 +6,7 @@
# and existing service will use these one by one.
# After all are migrated, we can remove this class.
#
# New services should consider inheriting from:
#
# - BaseContainerService for services scoped by container (project or group)
# - BaseProjectService for services scoped to projects
# - BaseGroupService for services scoped to groups
#
# or, create a new base class and update this comment.
# For new services, please see https://docs.gitlab.com/ee/development/reusing_abstractions.html#service-classes
class BaseService
include BaseServiceUtility
include Gitlab::Experiment::Dsl
......
# frozen_string_literal: true
module MergeRequests
class AttentionRequiredService < MergeRequests::BaseService
attr_accessor :merge_request, :user
def initialize(project:, current_user:, merge_request:, user:)
super(project: project, current_user: current_user)
@merge_request = merge_request
@user = user
end
def execute
return error("Invalid permissions") unless can?(current_user, :update_merge_request, merge_request)
if reviewer || assignee
reviewer&.update(state: :attention_required)
assignee&.update(state: :attention_required)
notity_user
success
else
error("User is not a reviewer or assignee of the merge request")
end
end
private
def notity_user
todo_service.create_attention_required_todo(merge_request, current_user, user)
end
def assignee
merge_request.find_assignee(user)
end
def reviewer
merge_request.find_reviewer(user)
end
end
end
......@@ -217,6 +217,11 @@ class TodoService
create_todos(reviewers, attributes)
end
def create_attention_required_todo(target, author, users)
attributes = attributes_for_todo(target.project, target, author, Todo::ATTENTION_REQUIRED)
create_todos(users, attributes)
end
private
def create_todos(users, attributes)
......
......@@ -3228,6 +3228,29 @@ Input type: `MergeRequestAcceptInput`
| <a id="mutationmergerequestaccepterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequestacceptmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestAttentionRequired`
Available only when feature flag `mr_attention_requests` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
Input type: `MergeRequestAttentionRequiredInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequestattentionrequiredclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequestattentionrequirediid"></a>`iid` | [`String!`](#string) | IID of the merge request to mutate. |
| <a id="mutationmergerequestattentionrequiredprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the merge request to mutate is in. |
| <a id="mutationmergerequestattentionrequireduserid"></a>`userId` | [`UserID!`](#userid) | User ID for the user that has their attention requested. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationmergerequestattentionrequiredclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmergerequestattentionrequirederrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationmergerequestattentionrequiredmergerequest"></a>`mergeRequest` | [`MergeRequest`](#mergerequest) | Merge request after mutation. |
### `Mutation.mergeRequestCreate`
Input type: `MergeRequestCreateInput`
......
......@@ -100,13 +100,15 @@ GET /groups?statistics=true
"parent_id": null,
"created_at": "2020-01-15T12:36:29.590Z",
"statistics": {
"storage_size" : 363,
"repository_size" : 33,
"wiki_size" : 100,
"lfs_objects_size" : 123,
"job_artifacts_size" : 57,
"storage_size": 363,
"repository_size": 33,
"wiki_size": 100,
"lfs_objects_size": 123,
"job_artifacts_size": 57,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size" : 50
"snippets_size": 50,
"uploads_size": 0
}
}
]
......
......@@ -191,8 +191,10 @@ When the user is authenticated and `simple` is not set this returns something li
"wiki_size" : 0,
"lfs_objects_size": 0,
"job_artifacts_size": 0,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0
"snippets_size": 0,
"uploads_size": 0
},
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-client",
"_links": {
......@@ -303,8 +305,10 @@ When the user is authenticated and `simple` is not set this returns something li
"wiki_size" : 0,
"lfs_objects_size": 0,
"job_artifacts_size": 0,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0
"snippets_size": 0,
"uploads_size": 0
},
"container_registry_image_prefix": "registry.example.com/brightbox/puppet",
"_links": {
......@@ -469,8 +473,10 @@ GET /users/:user_id/projects
"wiki_size" : 0,
"lfs_objects_size": 0,
"job_artifacts_size": 0,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0
"snippets_size": 0,
"uploads_size": 0
},
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-client",
"_links": {
......@@ -581,8 +587,10 @@ GET /users/:user_id/projects
"wiki_size" : 0,
"lfs_objects_size": 0,
"job_artifacts_size": 0,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0
"snippets_size": 0,
"uploads_size": 0
},
"container_registry_image_prefix": "registry.example.com/brightbox/puppet",
"_links": {
......@@ -704,6 +712,10 @@ Example response:
"repository_size": 1038090,
"lfs_objects_size": 0,
"job_artifacts_size": 0
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0,
"uploads_size": 0
},
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-client",
"_links": {
......@@ -811,6 +823,10 @@ Example response:
"repository_size": 2066080,
"lfs_objects_size": 0,
"job_artifacts_size": 0
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0,
"uploads_size": 0
},
"container_registry_image_prefix": "registry.example.com/brightbox/puppet",
"_links": {
......@@ -978,8 +994,10 @@ GET /projects/:id
"wiki_size" : 0,
"lfs_objects_size": 0,
"job_artifacts_size": 0,
"pipeline_artifacts_size": 0,
"packages_size": 0,
"snippets_size": 0
"snippets_size": 0,
"uploads_size": 0
},
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-client",
"_links": {
......
......@@ -53,7 +53,10 @@ SELECT split_part("rs".path, '/', 1) as root_path,
COALESCE(SUM(ps.wiki_size), 0) AS wiki_size,
COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size,
COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size,
COALESCE(SUM(ps.packages_size), 0) AS packages_size
COALESCE(SUM(ps.pipeline_artifacts_size), 0) AS pipeline_artifacts_size,
COALESCE(SUM(ps.packages_size), 0) AS packages_size,
COALESCE(SUM(ps.snippets_size), 0) AS snippets_size,
COALESCE(SUM(ps.uploads_size), 0) AS uploads_size
FROM "projects"
INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'
INNER JOIN project_statistics ps ON ps.project_id = projects.id
......@@ -83,7 +86,10 @@ WITH refresh AS (
COALESCE(SUM(ps.wiki_size), 0) AS wiki_size,
COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size,
COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size,
COALESCE(SUM(ps.packages_size), 0) AS packages_size
COALESCE(SUM(ps.pipeline_artifacts_size), 0) AS pipeline_artifacts_size,
COALESCE(SUM(ps.packages_size), 0) AS packages_size,
COALESCE(SUM(ps.snippets_size), 0) AS snippets_size,
COALESCE(SUM(ps.uploads_size), 0) AS uploads_size
FROM "projects"
INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'
INNER JOIN project_statistics ps ON ps.project_id = projects.id
......@@ -94,7 +100,10 @@ SET storage_size = refresh.storage_size,
wiki_size = refresh.wiki_size,
lfs_objects_size = refresh.lfs_objects_size,
build_artifacts_size = refresh.build_artifacts_size,
packages_size = refresh.packages_size
pipeline_artifacts_size = refresh.pipeline_artifacts_size,
packages_size = refresh.packages_size,
snippets_size = refresh.snippets_size,
uploads_size = refresh.uploads_size
FROM refresh
INNER JOIN routes rs ON rs.path = refresh.root_path AND rs.source_type = 'Namespace'
WHERE namespace_storage_statistics.namespace_id = rs.source_id
......
......@@ -133,8 +133,20 @@ Everything in `lib/api`.
Everything that resides in `app/services`.
Services should consider inheriting from:
- `BaseContainerService` for services scoped by container (project or group)
- `BaseProjectService` for services scoped to projects
- `BaseGroupService` for services scoped to groups
or, create a new base class and update the list above.
Legacy classes inherited from `BaseService` for historical reasons.
In Service classes the use of `execute` and `#execute` is preferred over `call` and `#call`.
Classes that are not service objects should be [created elsewhere](directory_structure.md#use-namespaces-to-define-bounded-contexts, such as in `lib`.
#### ServiceResponse
Service classes usually have an `execute` method, which can return a
......
......@@ -47,16 +47,6 @@ Announced: 2021-08-22
## 15.0
### Legacy database configuration
The syntax of [GitLabs database](https://docs.gitlab.com/omnibus/settings/database.html)
configuration located in `database.yml` is changing and the legacy format is deprecated. The legacy format
supported using a single PostgreSQL adapter, whereas the new format is changing to support multiple databases. The `main:` database needs to be defined as a first configuration item.
This deprecation mainly impacts users compiling GitLab from source because Omnibus will handle this configuration automatically.
Announced: 2021-09-22
### Audit events for repository push events
Audit events for [repository events](https://docs.gitlab.com/ee/administration/audit_events.html#repository-push) are now deprecated and will be removed in GitLab 15.0.
......@@ -67,21 +57,31 @@ dramatically slow down GitLab instances. For this reason, they are being removed
Announced: 2021-09-22
### OmniAuth Kerberos gem
### GitLab Serverless
The `omniauth-kerberos` gem will be removed in our next major release, GitLab 15.0.
[GitLab Serverless](https://docs.gitlab.com/ee/user/project/clusters/serverless/) is a feature set to support Knative-based serverless development with automatic deployments and monitoring.
This gem has not been maintained and has very little usage. We therefore plan to remove support for this authentication method and recommend using the Kerberos [SPNEGO](https://en.wikipedia.org/wiki/SPNEGO) integration instead. You can follow the [upgrade instructions](https://docs.gitlab.com/ee/integration/kerberos.html#upgrading-from-password-based-to-ticket-based-kerberos-sign-ins) to upgrade from the `omniauth-kerberos` integration to the supported one.
We decided to remove the GitLab Serverless features as they never really resonated with our users. Besides, given the continuous development of Kubernetes and Knative, our current implementations do not even work with recent versions.
Note that we are not deprecating the Kerberos SPNEGO integration, only the old password-based Kerberos integration.
Announced: 2021-09-22
### Legacy database configuration
The syntax of [GitLabs database](https://docs.gitlab.com/omnibus/settings/database.html)
configuration located in `database.yml` is changing and the legacy format is deprecated. The legacy format
supported using a single PostgreSQL adapter, whereas the new format is changing to support multiple databases. The `main:` database needs to be defined as a first configuration item.
This deprecation mainly impacts users compiling GitLab from source because Omnibus will handle this configuration automatically.
Announced: 2021-09-22
### GitLab Serverless
### OmniAuth Kerberos gem
[GitLab Serverless](https://docs.gitlab.com/ee/user/project/clusters/serverless/) is a feature set to support Knative-based serverless development with automatic deployments and monitoring.
The `omniauth-kerberos` gem will be removed in our next major release, GitLab 15.0.
We decided to remove the GitLab Serverless features as they never really resonated with our users. Besides, given the continuous development of Kubernetes and Knative, our current implementations do not even work with recent versions.
This gem has not been maintained and has very little usage. We therefore plan to remove support for this authentication method and recommend using the Kerberos [SPNEGO](https://en.wikipedia.org/wiki/SPNEGO) integration instead. You can follow the [upgrade instructions](https://docs.gitlab.com/ee/integration/kerberos.html#upgrading-from-password-based-to-ticket-based-kerberos-sign-ins) to upgrade from the `omniauth-kerberos` integration to the supported one.
Note that we are not deprecating the Kerberos SPNEGO integration, only the old password-based Kerberos integration.
Announced: 2021-09-22
......
......@@ -31,7 +31,10 @@ module API
expose :wiki_size
expose :lfs_objects_size
expose :build_artifacts_size, as: :job_artifacts_size
expose :pipeline_artifacts_size
expose :packages_size
expose :snippets_size
expose :uploads_size
end
end
end
......
......@@ -9,8 +9,10 @@ module API
expose :wiki_size
expose :lfs_objects_size
expose :build_artifacts_size, as: :job_artifacts_size
expose :snippets_size
expose :pipeline_artifacts_size
expose :packages_size
expose :snippets_size
expose :uploads_size
end
end
end
......@@ -103,6 +103,20 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
expect(page).to have_content('You are already a member of this group.')
end
end
context 'when email case doesnt match', :js do
let(:invite_email) { 'User@example.com' }
let(:user) { create(:user, email: 'user@example.com') }
before do
sign_in(user)
visit invite_path(group_invite.raw_invite_token)
end
it 'accepts invite' do
expect(page).to have_content('You have been granted Developer access to group Owned.')
end
end
end
context 'when declining the invitation from invitation reminder email' do
......
......@@ -50,6 +50,26 @@ RSpec.describe LooseForeignKeys::ModificationTracker do
end
end
describe '#add_deletions' do
it 'increments a Prometheus counter' do
counter = Gitlab::Metrics.registry.get(:loose_foreign_key_deletions)
subject.add_deletions(:users, 4)
expect(counter.get(table: :users)).to eq(4)
end
end
describe '#add_updates' do
it 'increments a Prometheus counter' do
counter = Gitlab::Metrics.registry.get(:loose_foreign_key_updates)
subject.add_updates(:users, 4)
expect(counter.get(table: :users)).to eq(4)
end
end
describe '#stats' do
it 'exposes stats' do
freeze_time do
......
......@@ -568,26 +568,30 @@ RSpec.describe Namespace do
create(:project,
namespace: namespace,
statistics: build(:project_statistics,
namespace: namespace,
repository_size: 101,
wiki_size: 505,
lfs_objects_size: 202,
build_artifacts_size: 303,
packages_size: 404,
snippets_size: 605))
namespace: namespace,
repository_size: 101,
wiki_size: 505,
lfs_objects_size: 202,
build_artifacts_size: 303,
pipeline_artifacts_size: 707,
packages_size: 404,
snippets_size: 605,
uploads_size: 808))
end
let(:project2) do
create(:project,
namespace: namespace,
statistics: build(:project_statistics,
namespace: namespace,
repository_size: 10,
wiki_size: 50,
lfs_objects_size: 20,
build_artifacts_size: 30,
packages_size: 40,
snippets_size: 60))
namespace: namespace,
repository_size: 10,
wiki_size: 50,
lfs_objects_size: 20,
build_artifacts_size: 30,
pipeline_artifacts_size: 70,
packages_size: 40,
snippets_size: 60,
uploads_size: 80))
end
it "sums all project storage counters in the namespace" do
......@@ -595,13 +599,15 @@ RSpec.describe Namespace do
project2
statistics = described_class.with_statistics.find(namespace.id)
expect(statistics.storage_size).to eq 2330
expect(statistics.storage_size).to eq 3995
expect(statistics.repository_size).to eq 111
expect(statistics.wiki_size).to eq 555
expect(statistics.lfs_objects_size).to eq 222
expect(statistics.build_artifacts_size).to eq 333
expect(statistics.pipeline_artifacts_size).to eq 777
expect(statistics.packages_size).to eq 444
expect(statistics.snippets_size).to eq 665
expect(statistics.uploads_size).to eq 888
end
it "correctly handles namespaces without projects" do
......@@ -612,8 +618,10 @@ RSpec.describe Namespace do
expect(statistics.wiki_size).to eq 0
expect(statistics.lfs_objects_size).to eq 0
expect(statistics.build_artifacts_size).to eq 0
expect(statistics.pipeline_artifacts_size).to eq 0
expect(statistics.packages_size).to eq 0
expect(statistics.snippets_size).to eq 0
expect(statistics.uploads_size).to eq 0
end
end
......
......@@ -325,12 +325,14 @@ RSpec.describe ProjectStatistics do
lfs_objects_size: 3,
snippets_size: 2,
pipeline_artifacts_size: 3,
build_artifacts_size: 3,
packages_size: 6,
uploads_size: 5
)
statistics.reload
expect(statistics.storage_size).to eq 19
expect(statistics.storage_size).to eq 28
end
it 'works during wiki_size backfill' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'Setting attention required for reviewer' do
include GraphqlHelpers
let(:current_user) { create(:user) }
let(:merge_request) { create(:merge_request, reviewers: [user]) }
let(:project) { merge_request.project }
let(:user) { create(:user) }
let(:input) { { user_id: global_id_of(user) } }
let(:mutation) do
variables = {
project_path: project.full_path,
iid: merge_request.iid.to_s
}
graphql_mutation(:merge_request_attention_required, variables.merge(input),
<<-QL.strip_heredoc
clientMutationId
errors
QL
)
end
def mutation_response
graphql_mutation_response(:merge_request_attention_required)
end
def mutation_errors
mutation_response['errors']
end
before do
project.add_developer(current_user)
project.add_developer(user)
end
it 'returns an error if the user is not allowed to update the merge request' do
post_graphql_mutation(mutation, current_user: create(:user))
expect(graphql_errors).not_to be_empty
end
describe 'reviewer does not exist' do
let(:input) { { user_id: global_id_of(create(:user)) } }
it 'returns an error' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_errors).not_to be_empty
end
end
describe 'reviewer exists' do
it 'does not return an error' do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_errors).to be_empty
end
end
end
......@@ -319,12 +319,15 @@ RSpec.describe API::Groups do
it "includes statistics if requested" do
attributes = {
storage_size: 2392,
storage_size: 4093,
repository_size: 123,
wiki_size: 456,
lfs_objects_size: 234,
build_artifacts_size: 345,
snippets_size: 1234
pipeline_artifacts_size: 456,
packages_size: 567,
snippets_size: 1234,
uploads_size: 678
}.stringify_keys
exposed_attributes = attributes.dup
exposed_attributes['job_artifacts_size'] = exposed_attributes.delete('build_artifacts_size')
......
......@@ -359,7 +359,7 @@ RSpec.describe API::Projects do
statistics = json_response.find { |p| p['id'] == project.id }['statistics']
expect(statistics).to be_present
expect(statistics).to include('commit_count', 'storage_size', 'repository_size', 'wiki_size', 'lfs_objects_size', 'job_artifacts_size', 'snippets_size', 'packages_size')
expect(statistics).to include('commit_count', 'storage_size', 'repository_size', 'wiki_size', 'lfs_objects_size', 'job_artifacts_size', 'pipeline_artifacts_size', 'snippets_size', 'packages_size', 'uploads_size')
end
it "does not include license by default" do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe MergeRequests::AttentionRequiredService do
let(:current_user) { create(:user) }
let(:user) { create(:user) }
let(:assignee_user) { create(:user) }
let(:merge_request) { create(:merge_request, reviewers: [user], assignees: [assignee_user]) }
let(:reviewer) { merge_request.find_reviewer(user) }
let(:assignee) { merge_request.find_assignee(assignee_user) }
let(:project) { merge_request.project }
let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: user) }
let(:result) { service.execute }
let(:todo_service) { spy('todo service') }
before do
allow(service).to receive(:todo_service).and_return(todo_service)
project.add_developer(current_user)
project.add_developer(user)
end
describe '#execute' do
context 'invalid permissions' do
let(:service) { described_class.new(project: project, current_user: create(:user), merge_request: merge_request, user: user) }
it 'returns an error' do
expect(result[:status]).to eq :error
end
end
context 'reviewer does not exist' do
let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: create(:user)) }
it 'returns an error' do
expect(result[:status]).to eq :error
end
end
context 'reviewer exists' do
it 'returns success' do
expect(result[:status]).to eq :success
end
it 'updates reviewers state' do
service.execute
reviewer.reload
expect(reviewer.state).to eq 'attention_required'
end
it 'creates a new todo for the reviewer' do
expect(todo_service).to receive(:create_attention_required_todo).with(merge_request, current_user, user)
service.execute
end
end
context 'assignee exists' do
let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: assignee_user) }
it 'returns success' do
expect(result[:status]).to eq :success
end
it 'updates assignees state' do
service.execute
assignee.reload
expect(assignee.state).to eq 'attention_required'
end
it 'creates a new todo for the reviewer' do
expect(todo_service).to receive(:create_attention_required_todo).with(merge_request, current_user, assignee_user)
service.execute
end
end
context 'assignee is the same as reviewer' do
let(:merge_request) { create(:merge_request, reviewers: [user], assignees: [user]) }
let(:service) { described_class.new(project: project, current_user: current_user, merge_request: merge_request, user: user) }
let(:assignee) { merge_request.find_assignee(user) }
it 'updates reviewers and assignees state' do
service.execute
reviewer.reload
assignee.reload
expect(reviewer.state).to eq 'attention_required'
expect(assignee.state).to eq 'attention_required'
end
end
end
end
......@@ -1218,6 +1218,17 @@ RSpec.describe TodoService do
end
end
describe '#create_attention_required_todo' do
let(:target) { create(:merge_request, author: author, source_project: project) }
let(:user) { create(:user) }
it 'creates a todo for user' do
service.create_attention_required_todo(target, author, user)
should_create_todo(user: user, target: target, action: Todo::ATTENTION_REQUIRED)
end
end
def should_create_todo(attributes = {})
attributes.reverse_merge!(
project: project,
......
......@@ -20,9 +20,11 @@ module Deprecations
YAML.load_file(file)
end
deprecations = VersionSorter.sort(deprecations) { |d| d["removal_milestone"] }
deps = VersionSorter.sort(deprecations) { |d| d["removal_milestone"] }
milestones = deprecations.map { |d| d["removal_milestone"] }.uniq
deprecations = deps.sort_by { |d| d["name"] }
milestones = deps.map { |d| d["removal_milestone"] }.uniq
template = Rails.root.join("data/deprecations/templates/_deprecation_template.md.erb")
......
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