Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
e89a412ba3
commit
f791b9447b
19 changed files with 437 additions and 96 deletions
|
@ -186,18 +186,12 @@ RSpec/ExpectChange:
|
||||||
# Offense count: 47
|
# Offense count: 47
|
||||||
RSpec/ExpectGitlabTracking:
|
RSpec/ExpectGitlabTracking:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'ee/spec/controllers/projects/settings/operations_controller_spec.rb'
|
|
||||||
- 'ee/spec/requests/api/visual_review_discussions_spec.rb'
|
|
||||||
- 'ee/spec/services/epics/issue_promote_service_spec.rb'
|
|
||||||
- 'spec/controllers/groups/registry/repositories_controller_spec.rb'
|
|
||||||
- 'spec/controllers/projects/registry/repositories_controller_spec.rb'
|
- 'spec/controllers/projects/registry/repositories_controller_spec.rb'
|
||||||
- 'spec/controllers/projects/registry/tags_controller_spec.rb'
|
- 'spec/controllers/projects/registry/tags_controller_spec.rb'
|
||||||
- 'spec/controllers/projects/settings/operations_controller_spec.rb'
|
- 'spec/controllers/projects/settings/operations_controller_spec.rb'
|
||||||
- 'spec/controllers/registrations_controller_spec.rb'
|
|
||||||
- 'spec/lib/api/helpers_spec.rb'
|
- 'spec/lib/api/helpers_spec.rb'
|
||||||
- 'spec/requests/api/project_container_repositories_spec.rb'
|
- 'spec/requests/api/project_container_repositories_spec.rb'
|
||||||
- 'spec/support/shared_examples/controllers/trackable_shared_examples.rb'
|
- 'spec/support/shared_examples/controllers/trackable_shared_examples.rb'
|
||||||
- 'spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb'
|
|
||||||
- 'spec/support/shared_examples/requests/api/discussions_shared_examples.rb'
|
- 'spec/support/shared_examples/requests/api/discussions_shared_examples.rb'
|
||||||
- 'spec/support/shared_examples/requests/api/packages_shared_examples.rb'
|
- 'spec/support/shared_examples/requests/api/packages_shared_examples.rb'
|
||||||
- 'spec/support/shared_examples/requests/api/tracking_shared_examples.rb'
|
- 'spec/support/shared_examples/requests/api/tracking_shared_examples.rb'
|
||||||
|
|
|
@ -395,7 +395,7 @@ class Environment < ApplicationRecord
|
||||||
|
|
||||||
# Overrides ReactiveCaching default to activate limit checking behind a FF
|
# Overrides ReactiveCaching default to activate limit checking behind a FF
|
||||||
def reactive_cache_limit_enabled?
|
def reactive_cache_limit_enabled?
|
||||||
Feature.enabled?(:reactive_caching_limit_environment, project)
|
Feature.enabled?(:reactive_caching_limit_environment, project, default_enabled: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
5
changelogs/unreleased/270200-downtier-pat-apis.yml
Normal file
5
changelogs/unreleased/270200-downtier-pat-apis.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Move Personal Access Token API to Core
|
||||||
|
merge_request: 46145
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Make all Project Issue Boards API available even in CE
|
||||||
|
merge_request: 46137
|
||||||
|
author: Takuya Noguchi
|
||||||
|
type: changed
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: Limits the Deploy Boards data to 10 MB. This change is enabled by default behind
|
||||||
|
a feature flag
|
||||||
|
merge_request: 46043
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -4,4 +4,4 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34202
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/202633
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/202633
|
||||||
group: group::configure
|
group: group::configure
|
||||||
type: development
|
type: development
|
||||||
default_enabled: false
|
default_enabled: true
|
||||||
|
|
|
@ -3,5 +3,5 @@ name: suggest_pipeline
|
||||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45926
|
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45926
|
||||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267492
|
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267492
|
||||||
type: development
|
type: development
|
||||||
group: group::growth
|
group: group::expansion
|
||||||
default_enabled: true
|
default_enabled: true
|
||||||
|
|
|
@ -4,13 +4,14 @@ group: unassigned
|
||||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||||
---
|
---
|
||||||
|
|
||||||
# Personal access tokens API **(ULTIMATE)**
|
# Personal access tokens API
|
||||||
|
|
||||||
You can read more about [personal access tokens](../user/profile/personal_access_tokens.md#personal-access-tokens).
|
You can read more about [personal access tokens](../user/profile/personal_access_tokens.md#personal-access-tokens).
|
||||||
|
|
||||||
## List personal access tokens
|
## List personal access tokens
|
||||||
|
|
||||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227264) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3.
|
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227264) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.3.
|
||||||
|
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/270200) to [GitLab Core](https://about.gitlab.com/pricing/) in 13.6.
|
||||||
|
|
||||||
Get a list of personal access tokens.
|
Get a list of personal access tokens.
|
||||||
|
|
||||||
|
|
|
@ -237,6 +237,7 @@ module API
|
||||||
mount ::API::ProjectTemplates
|
mount ::API::ProjectTemplates
|
||||||
mount ::API::Terraform::State
|
mount ::API::Terraform::State
|
||||||
mount ::API::Terraform::StateVersion
|
mount ::API::Terraform::StateVersion
|
||||||
|
mount ::API::PersonalAccessTokens
|
||||||
mount ::API::ProtectedBranches
|
mount ::API::ProtectedBranches
|
||||||
mount ::API::ProtectedTags
|
mount ::API::ProtectedTags
|
||||||
mount ::API::Releases
|
mount ::API::Releases
|
||||||
|
|
|
@ -42,6 +42,43 @@ module API
|
||||||
authorize!(:read_board, user_project)
|
authorize!(:read_board, user_project)
|
||||||
present board, with: Entities::Board
|
present board, with: Entities::Board
|
||||||
end
|
end
|
||||||
|
|
||||||
|
desc 'Create a project board' do
|
||||||
|
detail 'This feature was introduced in 10.4'
|
||||||
|
success Entities::Board
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
requires :name, type: String, desc: 'The board name'
|
||||||
|
end
|
||||||
|
post '/' do
|
||||||
|
authorize!(:admin_board, board_parent)
|
||||||
|
|
||||||
|
create_board
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Update a project board' do
|
||||||
|
detail 'This feature was introduced in 11.0'
|
||||||
|
success Entities::Board
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
use :update_params
|
||||||
|
end
|
||||||
|
put '/:board_id' do
|
||||||
|
authorize!(:admin_board, board_parent)
|
||||||
|
|
||||||
|
update_board
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Delete a project board' do
|
||||||
|
detail 'This feature was introduced in 10.4'
|
||||||
|
success Entities::Board
|
||||||
|
end
|
||||||
|
|
||||||
|
delete '/:board_id' do
|
||||||
|
authorize!(:admin_board, board_parent)
|
||||||
|
|
||||||
|
delete_board
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
params do
|
params do
|
||||||
|
|
|
@ -10,6 +10,35 @@ module API
|
||||||
board_parent.boards.find(params[:board_id])
|
board_parent.boards.find(params[:board_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_board
|
||||||
|
forbidden! unless board_parent.multiple_issue_boards_available?
|
||||||
|
|
||||||
|
response =
|
||||||
|
::Boards::CreateService.new(board_parent, current_user, { name: params[:name] }).execute
|
||||||
|
|
||||||
|
present response.payload, with: Entities::Board
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_board
|
||||||
|
service = ::Boards::UpdateService.new(board_parent, current_user, declared_params(include_missing: false))
|
||||||
|
service.execute(board)
|
||||||
|
|
||||||
|
if board.valid?
|
||||||
|
present board, with: Entities::Board
|
||||||
|
else
|
||||||
|
bad_request!("Failed to save board #{board.errors.messages}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_board
|
||||||
|
forbidden! unless board_parent.multiple_issue_boards_available?
|
||||||
|
|
||||||
|
destroy_conditionally!(board) do |board|
|
||||||
|
service = ::Boards::DestroyService.new(board_parent, current_user)
|
||||||
|
service.execute(board)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def board_lists
|
def board_lists
|
||||||
board.destroyable_lists
|
board.destroyable_lists
|
||||||
end
|
end
|
||||||
|
@ -62,6 +91,12 @@ module API
|
||||||
params :list_creation_params do
|
params :list_creation_params do
|
||||||
requires :label_id, type: Integer, desc: 'The ID of an existing label'
|
requires :label_id, type: Integer, desc: 'The ID of an existing label'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
params :update_params do
|
||||||
|
# Configurable issue boards are not available in CE/EE Core.
|
||||||
|
# https://docs.gitlab.com/ee/user/project/issue_board.html#configurable-issue-boards
|
||||||
|
optional :name, type: String, desc: 'The board name'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@ module API
|
||||||
module Entities
|
module Entities
|
||||||
class Board < Grape::Entity
|
class Board < Grape::Entity
|
||||||
expose :id
|
expose :id
|
||||||
|
expose :name
|
||||||
expose :project, using: Entities::BasicProjectDetails
|
expose :project, using: Entities::BasicProjectDetails
|
||||||
|
|
||||||
expose :lists, using: Entities::List do |board|
|
expose :lists, using: Entities::List do |board|
|
||||||
|
|
59
lib/api/personal_access_tokens.rb
Normal file
59
lib/api/personal_access_tokens.rb
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module API
|
||||||
|
class PersonalAccessTokens < ::API::Base
|
||||||
|
include ::API::PaginationParams
|
||||||
|
|
||||||
|
desc 'Get all Personal Access Tokens' do
|
||||||
|
detail 'This feature was added in GitLab 13.3'
|
||||||
|
success Entities::PersonalAccessToken
|
||||||
|
end
|
||||||
|
params do
|
||||||
|
optional :user_id, type: Integer, desc: 'User ID'
|
||||||
|
|
||||||
|
use :pagination
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
authenticate!
|
||||||
|
restrict_non_admins! unless current_user.admin?
|
||||||
|
end
|
||||||
|
|
||||||
|
helpers do
|
||||||
|
def finder_params(current_user)
|
||||||
|
current_user.admin? ? { user: user(params[:user_id]) } : { user: current_user }
|
||||||
|
end
|
||||||
|
|
||||||
|
def user(user_id)
|
||||||
|
UserFinder.new(user_id).find_by_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def restrict_non_admins!
|
||||||
|
return if params[:user_id].blank?
|
||||||
|
|
||||||
|
unauthorized! unless Ability.allowed?(current_user, :read_user_personal_access_tokens, user(params[:user_id]))
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_token(id)
|
||||||
|
PersonalAccessToken.find(id) || not_found!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
resources :personal_access_tokens do
|
||||||
|
get do
|
||||||
|
tokens = PersonalAccessTokensFinder.new(finder_params(current_user), current_user).execute
|
||||||
|
|
||||||
|
present paginate(tokens), with: Entities::PersonalAccessToken
|
||||||
|
end
|
||||||
|
|
||||||
|
delete ':id' do
|
||||||
|
service = ::PersonalAccessTokens::RevokeService.new(
|
||||||
|
current_user,
|
||||||
|
{ token: find_token(params[:id]) }
|
||||||
|
).execute
|
||||||
|
|
||||||
|
service.success? ? no_content! : bad_request!(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -64,12 +64,11 @@ RSpec.describe Groups::Registry::RepositoriesController do
|
||||||
context 'html format' do
|
context 'html format' do
|
||||||
let(:format) { :html }
|
let(:format) { :html }
|
||||||
|
|
||||||
it 'show index page' do
|
it 'show index page', :snowplow do
|
||||||
expect(Gitlab::Tracking).not_to receive(:event)
|
|
||||||
|
|
||||||
subject
|
subject
|
||||||
|
|
||||||
expect(response).to have_gitlab_http_status(:ok)
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect_no_snowplow_event
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
# rubocop: disable RSpec/FactoriesInMigrationSpecs
|
# rubocop: disable RSpec/FactoriesInMigrationSpecs
|
||||||
RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover, :aggregate_failures do
|
||||||
let(:test_dir) { FileUploader.options['storage_path'] }
|
let(:test_dir) { FileUploader.options['storage_path'] }
|
||||||
let(:filename) { 'image.png' }
|
let(:filename) { 'image.png' }
|
||||||
|
|
||||||
|
@ -67,27 +67,35 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'migrates the file correctly' do
|
shared_examples 'migrates the file correctly' do |remote|
|
||||||
before do
|
it 'creates a new upload record correctly, updates the legacy upload note so that it references the file in the markdown, removes the attachment from the note model, removes the file, moves legacy uploads to the correct location, removes the upload record' do
|
||||||
described_class.new(legacy_upload).execute
|
expect(File.exist?(legacy_upload.absolute_path)).to be_truthy unless remote
|
||||||
end
|
|
||||||
|
described_class.new(legacy_upload).execute
|
||||||
|
|
||||||
it 'creates a new uplaod record correctly' do
|
|
||||||
expect(new_upload.secret).not_to be_nil
|
expect(new_upload.secret).not_to be_nil
|
||||||
expect(new_upload.path).to end_with("#{new_upload.secret}/image.png")
|
expect(new_upload.path).to end_with("#{new_upload.secret}/#{filename}")
|
||||||
expect(new_upload.model_id).to eq(project.id)
|
expect(new_upload.model_id).to eq(project.id)
|
||||||
expect(new_upload.model_type).to eq('Project')
|
expect(new_upload.model_type).to eq('Project')
|
||||||
expect(new_upload.uploader).to eq('FileUploader')
|
expect(new_upload.uploader).to eq('FileUploader')
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the legacy upload note so that it references the file in the markdown' do
|
expected_path = File.join('/uploads', new_upload.secret, filename)
|
||||||
expected_path = File.join('/uploads', new_upload.secret, 'image.png')
|
|
||||||
expected_markdown = "some note \n ![image](#{expected_path})"
|
expected_markdown = "some note \n ![image](#{expected_path})"
|
||||||
expect(note.reload.note).to eq(expected_markdown)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'removes the attachment from the note model' do
|
expect(note.reload.note).to eq(expected_markdown)
|
||||||
expect(note.reload.attachment.file).to be_nil
|
expect(note.attachment.file).to be_nil
|
||||||
|
|
||||||
|
if remote
|
||||||
|
expect(bucket.files.get(remote_file[:key])).to be_nil
|
||||||
|
connection = ::Fog::Storage.new(FileUploader.object_store_credentials)
|
||||||
|
expect(connection.get_object('uploads', new_upload.path)[:status]).to eq(200)
|
||||||
|
else
|
||||||
|
expect(File.exist?(legacy_upload.absolute_path)).to be_falsey
|
||||||
|
expected_path = File.join(test_dir, 'uploads', project.disk_path, new_upload.secret, filename)
|
||||||
|
expect(File.exist?(expected_path)).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
|
expect { legacy_upload.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -120,23 +128,6 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the upload is in local storage' do
|
context 'when the upload is in local storage' do
|
||||||
shared_examples 'legacy local file' do
|
|
||||||
it 'removes the file correctly' do
|
|
||||||
expect(File.exist?(legacy_upload.absolute_path)).to be_truthy
|
|
||||||
|
|
||||||
described_class.new(legacy_upload).execute
|
|
||||||
|
|
||||||
expect(File.exist?(legacy_upload.absolute_path)).to be_falsey
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'moves legacy uploads to the correct location' do
|
|
||||||
described_class.new(legacy_upload).execute
|
|
||||||
|
|
||||||
expected_path = File.join(test_dir, 'uploads', project.disk_path, new_upload.secret, filename)
|
|
||||||
expect(File.exist?(expected_path)).to be_truthy
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the upload file does not exist on the filesystem' do
|
context 'when the upload file does not exist on the filesystem' do
|
||||||
let(:legacy_upload) { create_upload(note, filename, false) }
|
let(:legacy_upload) { create_upload(note, filename, false) }
|
||||||
|
|
||||||
|
@ -201,15 +192,11 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
||||||
path: "uploads/-/system/note/attachment/#{note.id}/#{filename}", model: note, mount_point: nil)
|
path: "uploads/-/system/note/attachment/#{note.id}/#{filename}", model: note, mount_point: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'migrates the file correctly'
|
it_behaves_like 'migrates the file correctly', false
|
||||||
it_behaves_like 'legacy local file'
|
|
||||||
it_behaves_like 'legacy upload deletion'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the file can be handled correctly' do
|
context 'when the file can be handled correctly' do
|
||||||
it_behaves_like 'migrates the file correctly'
|
it_behaves_like 'migrates the file correctly', false
|
||||||
it_behaves_like 'legacy local file'
|
|
||||||
it_behaves_like 'legacy upload deletion'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -217,17 +204,13 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
||||||
context 'when the file belongs to a legacy project' do
|
context 'when the file belongs to a legacy project' do
|
||||||
let(:project) { legacy_project }
|
let(:project) { legacy_project }
|
||||||
|
|
||||||
it_behaves_like 'migrates the file correctly'
|
it_behaves_like 'migrates the file correctly', false
|
||||||
it_behaves_like 'legacy local file'
|
|
||||||
it_behaves_like 'legacy upload deletion'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the file belongs to a hashed project' do
|
context 'when the file belongs to a hashed project' do
|
||||||
let(:project) { hashed_project }
|
let(:project) { hashed_project }
|
||||||
|
|
||||||
it_behaves_like 'migrates the file correctly'
|
it_behaves_like 'migrates the file correctly', false
|
||||||
it_behaves_like 'legacy local file'
|
|
||||||
it_behaves_like 'legacy upload deletion'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -244,17 +227,13 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
||||||
context 'when the file belongs to a legacy project' do
|
context 'when the file belongs to a legacy project' do
|
||||||
let(:project) { legacy_project }
|
let(:project) { legacy_project }
|
||||||
|
|
||||||
it_behaves_like 'migrates the file correctly'
|
it_behaves_like 'migrates the file correctly', false
|
||||||
it_behaves_like 'legacy local file'
|
|
||||||
it_behaves_like 'legacy upload deletion'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the file belongs to a hashed project' do
|
context 'when the file belongs to a hashed project' do
|
||||||
let(:project) { hashed_project }
|
let(:project) { hashed_project }
|
||||||
|
|
||||||
it_behaves_like 'migrates the file correctly'
|
it_behaves_like 'migrates the file correctly', false
|
||||||
it_behaves_like 'legacy local file'
|
|
||||||
it_behaves_like 'legacy upload deletion'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -272,23 +251,6 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
||||||
stub_uploads_object_storage(FileUploader)
|
stub_uploads_object_storage(FileUploader)
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'legacy remote file' do
|
|
||||||
it 'removes the file correctly' do
|
|
||||||
# expect(bucket.files.get(remote_file[:key])).to be_nil
|
|
||||||
|
|
||||||
described_class.new(legacy_upload).execute
|
|
||||||
|
|
||||||
expect(bucket.files.get(remote_file[:key])).to be_nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'moves legacy uploads to the correct remote location' do
|
|
||||||
described_class.new(legacy_upload).execute
|
|
||||||
|
|
||||||
connection = ::Fog::Storage.new(FileUploader.object_store_credentials)
|
|
||||||
expect(connection.get_object('uploads', new_upload.path)[:status]).to eq(200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when the upload file does not exist on the filesystem' do
|
context 'when the upload file does not exist on the filesystem' do
|
||||||
it_behaves_like 'legacy upload deletion'
|
it_behaves_like 'legacy upload deletion'
|
||||||
end
|
end
|
||||||
|
@ -300,9 +262,7 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
||||||
|
|
||||||
let(:project) { legacy_project }
|
let(:project) { legacy_project }
|
||||||
|
|
||||||
it_behaves_like 'migrates the file correctly'
|
it_behaves_like 'migrates the file correctly', true
|
||||||
it_behaves_like 'legacy remote file'
|
|
||||||
it_behaves_like 'legacy upload deletion'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the file belongs to a hashed project' do
|
context 'when the file belongs to a hashed project' do
|
||||||
|
@ -312,9 +272,7 @@ RSpec.describe Gitlab::BackgroundMigration::LegacyUploadMover do
|
||||||
|
|
||||||
let(:project) { hashed_project }
|
let(:project) { hashed_project }
|
||||||
|
|
||||||
it_behaves_like 'migrates the file correctly'
|
it_behaves_like 'migrates the file correctly', true
|
||||||
it_behaves_like 'legacy remote file'
|
|
||||||
it_behaves_like 'legacy upload deletion'
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,7 +35,46 @@ RSpec.describe API::Boards do
|
||||||
|
|
||||||
it_behaves_like 'group and project boards', "/projects/:id/boards"
|
it_behaves_like 'group and project boards', "/projects/:id/boards"
|
||||||
|
|
||||||
describe "POST /projects/:id/boards/lists" do
|
describe "POST /projects/:id/boards" do
|
||||||
|
let(:url) { "/projects/#{board_parent.id}/boards" }
|
||||||
|
|
||||||
|
it 'creates a new issue board' do
|
||||||
|
post api(url, user), params: { name: 'foo' }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
expect(json_response['name']).to eq('foo')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'fails to create a new board' do
|
||||||
|
post api(url, user), params: { some_name: 'foo' }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
expect(json_response['error']).to eq('name is missing')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PUT /projects/:id/boards/:board_id" do
|
||||||
|
let(:url) { "/projects/#{board_parent.id}/boards/#{board.id}" }
|
||||||
|
|
||||||
|
it 'updates the issue board' do
|
||||||
|
put api(url, user), params: { name: 'changed board name' }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response['name']).to eq('changed board name')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /projects/:id/boards/:board_id" do
|
||||||
|
let(:url) { "/projects/#{board_parent.id}/boards/#{board.id}" }
|
||||||
|
|
||||||
|
it 'delete the issue board' do
|
||||||
|
delete api(url, user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /projects/:id/boards/:board_id/lists" do
|
||||||
let(:url) { "/projects/#{board_parent.id}/boards/#{board.id}/lists" }
|
let(:url) { "/projects/#{board_parent.id}/boards/#{board.id}/lists" }
|
||||||
|
|
||||||
it 'creates a new issue board list for group labels' do
|
it 'creates a new issue board list for group labels' do
|
||||||
|
@ -65,7 +104,7 @@ RSpec.describe API::Boards do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /groups/:id/boards/lists" do
|
describe "POST /groups/:id/boards/:board_id/lists" do
|
||||||
let_it_be(:group) { create(:group) }
|
let_it_be(:group) { create(:group) }
|
||||||
let_it_be(:board_parent) { create(:group, parent: group ) }
|
let_it_be(:board_parent) { create(:group, parent: group ) }
|
||||||
let(:url) { "/groups/#{board_parent.id}/boards/#{board.id}/lists" }
|
let(:url) { "/groups/#{board_parent.id}/boards/#{board.id}/lists" }
|
||||||
|
|
112
spec/requests/api/personal_access_tokens_spec.rb
Normal file
112
spec/requests/api/personal_access_tokens_spec.rb
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe API::PersonalAccessTokens do
|
||||||
|
let_it_be(:path) { '/personal_access_tokens' }
|
||||||
|
let_it_be(:token1) { create(:personal_access_token) }
|
||||||
|
let_it_be(:token2) { create(:personal_access_token) }
|
||||||
|
let_it_be(:current_user) { create(:user) }
|
||||||
|
|
||||||
|
describe 'GET /personal_access_tokens' do
|
||||||
|
context 'logged in as an Administrator' do
|
||||||
|
let_it_be(:current_user) { create(:admin) }
|
||||||
|
|
||||||
|
it 'returns all PATs by default' do
|
||||||
|
get api(path, current_user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response.count).to eq(PersonalAccessToken.all.count)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'filtered with user_id parameter' do
|
||||||
|
it 'returns only PATs belonging to that user' do
|
||||||
|
get api(path, current_user), params: { user_id: token1.user.id }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response.count).to eq(1)
|
||||||
|
expect(json_response.first['user_id']).to eq(token1.user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'logged in as a non-Administrator' do
|
||||||
|
let_it_be(:current_user) { create(:user) }
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
let_it_be(:token) { create(:personal_access_token, user: current_user)}
|
||||||
|
let_it_be(:other_token) { create(:personal_access_token, user: user) }
|
||||||
|
|
||||||
|
it 'returns all PATs belonging to the signed-in user' do
|
||||||
|
get api(path, current_user, personal_access_token: token)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response.count).to eq(1)
|
||||||
|
expect(json_response.map { |r| r['user_id'] }.uniq).to contain_exactly(current_user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'filtered with user_id parameter' do
|
||||||
|
it 'returns PATs belonging to the specific user' do
|
||||||
|
get api(path, current_user, personal_access_token: token), params: { user_id: current_user.id }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(json_response.count).to eq(1)
|
||||||
|
expect(json_response.map { |r| r['user_id'] }.uniq).to contain_exactly(current_user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is unauthorized if filtered by a user other than current_user' do
|
||||||
|
get api(path, current_user, personal_access_token: token), params: { user_id: user.id }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'not authenticated' do
|
||||||
|
it 'is forbidden' do
|
||||||
|
get api(path)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'DELETE /personal_access_tokens/:id' do
|
||||||
|
let(:path) { "/personal_access_tokens/#{token1.id}" }
|
||||||
|
|
||||||
|
context 'when current_user is an administrator', :enable_admin_mode do
|
||||||
|
let_it_be(:admin_user) { create(:admin) }
|
||||||
|
let_it_be(:admin_token) { create(:personal_access_token, user: admin_user) }
|
||||||
|
let_it_be(:admin_path) { "/personal_access_tokens/#{admin_token.id}" }
|
||||||
|
|
||||||
|
it 'revokes a different users token' do
|
||||||
|
delete api(path, admin_user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
|
expect(token1.reload.revoked?).to be true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'revokes their own token' do
|
||||||
|
delete api(admin_path, admin_user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when current_user is not an administrator' do
|
||||||
|
let_it_be(:user_token) { create(:personal_access_token, user: current_user) }
|
||||||
|
let_it_be(:user_token_path) { "/personal_access_tokens/#{user_token.id}" }
|
||||||
|
|
||||||
|
it 'fails revokes a different users token' do
|
||||||
|
delete api(path, current_user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:bad_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'revokes their own token' do
|
||||||
|
delete api(user_token_path, current_user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -117,15 +117,10 @@ RSpec.shared_examples 'discussions API' do |parent_type, noteable_type, id_name,
|
||||||
expect(response).to have_gitlab_http_status(:unauthorized)
|
expect(response).to have_gitlab_http_status(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'tracks a Notes::CreateService event' do
|
it 'tracks a Notes::CreateService event', :snowplow do
|
||||||
expect(Gitlab::Tracking).to receive(:event) do |category, action, data|
|
|
||||||
expect(category).to eq('Notes::CreateService')
|
|
||||||
expect(action).to eq('execute')
|
|
||||||
expect(data[:label]).to eq('note')
|
|
||||||
expect(data[:value]).to be_an(Integer)
|
|
||||||
end
|
|
||||||
|
|
||||||
post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user), params: { body: 'hi!' }
|
post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user), params: { body: 'hi!' }
|
||||||
|
|
||||||
|
expect_snowplow_event(category: 'Notes::CreateService', action: 'execute', label: 'note', value: anything)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with notes_create_service_tracking feature flag disabled' do
|
context 'with notes_create_service_tracking feature flag disabled' do
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.shared_examples 'multiple and scoped issue boards' do |route_definition|
|
||||||
|
let(:root_url) { route_definition.gsub(":id", board_parent.id.to_s) }
|
||||||
|
|
||||||
|
context 'multiple issue boards' do
|
||||||
|
before do
|
||||||
|
board_parent.add_reporter(user)
|
||||||
|
stub_licensed_features(multiple_group_issue_boards: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST #{route_definition}" do
|
||||||
|
it 'creates a board' do
|
||||||
|
post api(root_url, user), params: { name: "new board" }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:created)
|
||||||
|
|
||||||
|
expect(response).to match_response_schema('public_api/v4/board', dir: "ee")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PUT #{route_definition}/:board_id" do
|
||||||
|
let(:url) { "#{root_url}/#{board.id}" }
|
||||||
|
|
||||||
|
it 'updates a board' do
|
||||||
|
put api(url, user), params: { name: 'new name', weight: 4, labels: 'foo, bar' }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
|
||||||
|
expect(response).to match_response_schema('public_api/v4/board', dir: "ee")
|
||||||
|
|
||||||
|
board.reload
|
||||||
|
|
||||||
|
expect(board.name).to eq('new name')
|
||||||
|
expect(board.weight).to eq(4)
|
||||||
|
expect(board.labels.map(&:title)).to contain_exactly('foo', 'bar')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not remove missing attributes from the board' do
|
||||||
|
expect { put api(url, user), params: { name: 'new name' } }
|
||||||
|
.to not_change { board.reload.assignee }
|
||||||
|
.and not_change { board.reload.milestone }
|
||||||
|
.and not_change { board.reload.weight }
|
||||||
|
.and not_change { board.reload.labels.map(&:title).sort }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(response).to match_response_schema('public_api/v4/board', dir: "ee")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows removing optional attributes' do
|
||||||
|
put api(url, user), params: { name: 'new name', assignee_id: nil, milestone_id: nil, weight: nil, labels: nil }
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:ok)
|
||||||
|
expect(response).to match_response_schema('public_api/v4/board', dir: "ee")
|
||||||
|
|
||||||
|
board.reload
|
||||||
|
|
||||||
|
expect(board.name).to eq('new name')
|
||||||
|
expect(board.assignee).to be_nil
|
||||||
|
expect(board.milestone).to be_nil
|
||||||
|
expect(board.weight).to be_nil
|
||||||
|
expect(board.labels).to be_empty
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE #{route_definition}/:board_id" do
|
||||||
|
let(:url) { "#{root_url}/#{board.id}" }
|
||||||
|
|
||||||
|
it 'deletes a board' do
|
||||||
|
delete api(url, user)
|
||||||
|
|
||||||
|
expect(response).to have_gitlab_http_status(:no_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with the scoped_issue_board-feature available' do
|
||||||
|
it 'returns the milestone when the `scoped_issue_board` feature is enabled' do
|
||||||
|
stub_licensed_features(scoped_issue_board: true)
|
||||||
|
|
||||||
|
get api(root_url, user)
|
||||||
|
|
||||||
|
expect(json_response.first["milestone"]).not_to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'hides the milestone when the `scoped_issue_board` feature is disabled' do
|
||||||
|
stub_licensed_features(scoped_issue_board: false)
|
||||||
|
|
||||||
|
get api(root_url, user)
|
||||||
|
|
||||||
|
expect(json_response.first["milestone"]).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue