Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
259aa13174
commit
d306437ae0
19 changed files with 571 additions and 119 deletions
|
@ -1 +1 @@
|
|||
f085d841e2f5571b260910a454215bc1a7687bf0
|
||||
9677be26e1a7f7e4f56bf4f7ba47bc0d16f40e16
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module WorkhorseImportExportUpload
|
||||
module WorkhorseAuthorization
|
||||
extend ActiveSupport::Concern
|
||||
include WorkhorseRequest
|
||||
|
||||
|
@ -12,10 +12,9 @@ module WorkhorseImportExportUpload
|
|||
def authorize
|
||||
set_workhorse_internal_api_content_type
|
||||
|
||||
authorized = ImportExportUploader.workhorse_authorize(
|
||||
authorized = uploader_class.workhorse_authorize(
|
||||
has_length: false,
|
||||
maximum_size: Gitlab::CurrentSettings.max_import_size.megabytes
|
||||
)
|
||||
maximum_size: Gitlab::CurrentSettings.max_attachment_size.megabytes.to_i)
|
||||
|
||||
render json: authorized
|
||||
rescue SocketError
|
||||
|
@ -27,7 +26,14 @@ module WorkhorseImportExportUpload
|
|||
def file_is_valid?(file)
|
||||
return false unless file.is_a?(::UploadedFile)
|
||||
|
||||
file_extension_whitelist.include?(File.extname(file.original_filename).downcase.delete('.'))
|
||||
end
|
||||
|
||||
def uploader_class
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def file_extension_whitelist
|
||||
ImportExportUploader::EXTENSION_WHITELIST
|
||||
.include?(File.extname(file.original_filename).delete('.'))
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Import::GitlabGroupsController < ApplicationController
|
||||
include WorkhorseImportExportUpload
|
||||
include WorkhorseAuthorization
|
||||
|
||||
before_action :ensure_group_import_enabled
|
||||
before_action :import_rate_limit, only: %i[create]
|
||||
|
@ -64,4 +64,8 @@ class Import::GitlabGroupsController < ApplicationController
|
|||
redirect_to new_group_path
|
||||
end
|
||||
end
|
||||
|
||||
def uploader_class
|
||||
ImportExportUploader
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Import::GitlabProjectsController < Import::BaseController
|
||||
include WorkhorseImportExportUpload
|
||||
include WorkhorseAuthorization
|
||||
|
||||
before_action :whitelist_query_limiting, only: [:create]
|
||||
before_action :verify_gitlab_project_import_enabled
|
||||
|
@ -45,4 +45,8 @@ class Import::GitlabProjectsController < Import::BaseController
|
|||
def whitelist_query_limiting
|
||||
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
|
||||
end
|
||||
|
||||
def uploader_class
|
||||
ImportExportUploader
|
||||
end
|
||||
end
|
||||
|
|
40
app/graphql/mutations/releases/delete.rb
Normal file
40
app/graphql/mutations/releases/delete.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Mutations
|
||||
module Releases
|
||||
class Delete < Base
|
||||
graphql_name 'ReleaseDelete'
|
||||
|
||||
field :release,
|
||||
Types::ReleaseType,
|
||||
null: true,
|
||||
description: 'The deleted release.'
|
||||
|
||||
argument :tag_name, GraphQL::STRING_TYPE,
|
||||
required: true, as: :tag,
|
||||
description: 'Name of the tag associated with the release to delete.'
|
||||
|
||||
authorize :destroy_release
|
||||
|
||||
def resolve(project_path:, tag:)
|
||||
project = authorized_find!(full_path: project_path)
|
||||
|
||||
params = { tag: tag }.with_indifferent_access
|
||||
|
||||
result = ::Releases::DestroyService.new(project, current_user, params).execute
|
||||
|
||||
if result[:status] == :success
|
||||
{
|
||||
release: result[:release],
|
||||
errors: []
|
||||
}
|
||||
else
|
||||
{
|
||||
release: nil,
|
||||
errors: [result[:message]]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -66,6 +66,7 @@ module Types
|
|||
mount_mutation Mutations::Notes::Destroy
|
||||
mount_mutation Mutations::Releases::Create
|
||||
mount_mutation Mutations::Releases::Update
|
||||
mount_mutation Mutations::Releases::Delete
|
||||
mount_mutation Mutations::Terraform::State::Delete
|
||||
mount_mutation Mutations::Terraform::State::Lock
|
||||
mount_mutation Mutations::Terraform::State::Unlock
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add GraphQL mutation to delete a release
|
||||
merge_request: 48364
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: import_requirements_csv
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48060
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284846
|
||||
milestone: '13.7'
|
||||
type: development
|
||||
group: group::product planning
|
||||
default_enabled: false
|
|
@ -14066,6 +14066,7 @@ type Mutation {
|
|||
prometheusIntegrationUpdate(input: PrometheusIntegrationUpdateInput!): PrometheusIntegrationUpdatePayload
|
||||
promoteToEpic(input: PromoteToEpicInput!): PromoteToEpicPayload
|
||||
releaseCreate(input: ReleaseCreateInput!): ReleaseCreatePayload
|
||||
releaseDelete(input: ReleaseDeleteInput!): ReleaseDeletePayload
|
||||
releaseUpdate(input: ReleaseUpdateInput!): ReleaseUpdatePayload
|
||||
removeAwardEmoji(input: RemoveAwardEmojiInput!): RemoveAwardEmojiPayload @deprecated(reason: "Use awardEmojiRemove. Deprecated in 13.2")
|
||||
removeProjectFromSecurityDashboard(input: RemoveProjectFromSecurityDashboardInput!): RemoveProjectFromSecurityDashboardPayload
|
||||
|
@ -18664,6 +18665,46 @@ type ReleaseCreatePayload {
|
|||
release: Release
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated input type of ReleaseDelete
|
||||
"""
|
||||
input ReleaseDeleteInput {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Full path of the project the release is associated with
|
||||
"""
|
||||
projectPath: ID!
|
||||
|
||||
"""
|
||||
Name of the tag associated with the release to delete.
|
||||
"""
|
||||
tagName: String!
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated return type of ReleaseDelete
|
||||
"""
|
||||
type ReleaseDeletePayload {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Errors encountered during execution of the mutation.
|
||||
"""
|
||||
errors: [String!]!
|
||||
|
||||
"""
|
||||
The deleted release.
|
||||
"""
|
||||
release: Release
|
||||
}
|
||||
|
||||
"""
|
||||
An edge in a connection.
|
||||
"""
|
||||
|
|
|
@ -41110,6 +41110,33 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "releaseDelete",
|
||||
"description": null,
|
||||
"args": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "ReleaseDeleteInput",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "ReleaseDeletePayload",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "releaseUpdate",
|
||||
"description": null,
|
||||
|
@ -54087,6 +54114,122 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "ReleaseDeleteInput",
|
||||
"description": "Autogenerated input type of ReleaseDelete",
|
||||
"fields": null,
|
||||
"inputFields": [
|
||||
{
|
||||
"name": "projectPath",
|
||||
"description": "Full path of the project the release is associated with",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "tagName",
|
||||
"description": "Name of the tag associated with the release to delete.",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "ReleaseDeletePayload",
|
||||
"description": "Autogenerated return type of ReleaseDelete",
|
||||
"fields": [
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "errors",
|
||||
"description": "Errors encountered during execution of the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"description": "The deleted release.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Release",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "ReleaseEdge",
|
||||
|
|
|
@ -2638,6 +2638,16 @@ Autogenerated return type of ReleaseCreate.
|
|||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `release` | Release | The release after mutation |
|
||||
|
||||
### ReleaseDeletePayload
|
||||
|
||||
Autogenerated return type of ReleaseDelete.
|
||||
|
||||
| Field | Type | Description |
|
||||
| ----- | ---- | ----------- |
|
||||
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
|
||||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `release` | Release | The deleted release. |
|
||||
|
||||
### ReleaseEvidence
|
||||
|
||||
Evidence for a release.
|
||||
|
|
|
@ -27254,6 +27254,9 @@ msgstr ""
|
|||
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
|
||||
msgstr ""
|
||||
|
||||
msgid "The uploaded file was invalid. Supported file extensions are %{extensions}."
|
||||
msgstr ""
|
||||
|
||||
msgid "The usage ping is disabled, and cannot be configured through this form."
|
||||
msgstr ""
|
||||
|
||||
|
@ -31656,6 +31659,9 @@ msgstr ""
|
|||
msgid "Your request for access has been queued for review."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your requirements are being imported. Once finished, you'll receive a confirmation email."
|
||||
msgstr ""
|
||||
|
||||
msgid "Your response has been recorded."
|
||||
msgstr ""
|
||||
|
||||
|
|
4
spec/fixtures/csv_uppercase.CSV
vendored
Normal file
4
spec/fixtures/csv_uppercase.CSV
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
TITLE,DESCRIPTION
|
||||
Issue in 中文,Test description
|
||||
"Hello","World"
|
||||
"Title with quote""",Description
|
|
92
spec/graphql/mutations/releases/delete_spec.rb
Normal file
92
spec/graphql/mutations/releases/delete_spec.rb
Normal file
|
@ -0,0 +1,92 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Mutations::Releases::Delete do
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
let_it_be(:non_project_member) { create(:user) }
|
||||
let_it_be(:developer) { create(:user) }
|
||||
let_it_be(:maintainer) { create(:user) }
|
||||
let_it_be(:tag) { 'v1.1.0'}
|
||||
let_it_be(:release) { create(:release, project: project, tag: tag) }
|
||||
|
||||
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
|
||||
|
||||
let(:mutation_arguments) do
|
||||
{
|
||||
project_path: project.full_path,
|
||||
tag: tag
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_developer(developer)
|
||||
project.add_maintainer(maintainer)
|
||||
end
|
||||
|
||||
shared_examples 'unauthorized or not found error' do
|
||||
it 'raises a Gitlab::Graphql::Errors::ResourceNotAvailable error' do
|
||||
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable, "The resource that you are attempting to access does not exist or you don't have permission to perform this action")
|
||||
end
|
||||
end
|
||||
|
||||
describe '#resolve' do
|
||||
subject(:resolve) do
|
||||
mutation.resolve(**mutation_arguments)
|
||||
end
|
||||
|
||||
context 'when the current user has access to create releases' do
|
||||
let(:current_user) { maintainer }
|
||||
|
||||
it 'deletes the release' do
|
||||
expect { subject }.to change { Release.count }.by(-1)
|
||||
end
|
||||
|
||||
it 'returns the deleted release' do
|
||||
expect(subject[:release].tag).to eq(tag)
|
||||
end
|
||||
|
||||
it 'does not remove the Git tag associated with the deleted release' do
|
||||
expect { subject }.not_to change { Project.find_by_id(project.id).repository.tag_count }
|
||||
end
|
||||
|
||||
it 'returns no errors' do
|
||||
expect(subject[:errors]).to eq([])
|
||||
end
|
||||
|
||||
context 'validation' do
|
||||
context 'when the release does not exist' do
|
||||
let(:mutation_arguments) { super().merge(tag: 'not-a-real-release') }
|
||||
|
||||
it 'returns the release as nil' do
|
||||
expect(subject[:release]).to be_nil
|
||||
end
|
||||
|
||||
it 'returns an errors-at-data message' do
|
||||
expect(subject[:errors]).to eq(['Release does not exist'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project does not exist' do
|
||||
let(:mutation_arguments) { super().merge(project_path: 'not/a/real/path') }
|
||||
|
||||
it_behaves_like 'unauthorized or not found error'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the current user doesn't have access to update releases" do
|
||||
context 'when the user is a developer' do
|
||||
let(:current_user) { developer }
|
||||
|
||||
it_behaves_like 'unauthorized or not found error'
|
||||
end
|
||||
|
||||
context 'when the user is a non-project member' do
|
||||
let(:current_user) { non_project_member }
|
||||
|
||||
it_behaves_like 'unauthorized or not found error'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
132
spec/requests/api/graphql/mutations/releases/delete_spec.rb
Normal file
132
spec/requests/api/graphql/mutations/releases/delete_spec.rb
Normal file
|
@ -0,0 +1,132 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Deleting a release' do
|
||||
include GraphqlHelpers
|
||||
include Presentable
|
||||
|
||||
let_it_be(:public_user) { create(:user) }
|
||||
let_it_be(:guest) { create(:user) }
|
||||
let_it_be(:reporter) { create(:user) }
|
||||
let_it_be(:developer) { create(:user) }
|
||||
let_it_be(:maintainer) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
let_it_be(:tag_name) { 'v1.1.0' }
|
||||
let_it_be(:release) { create(:release, project: project, tag: tag_name) }
|
||||
|
||||
let(:mutation_name) { :release_delete }
|
||||
|
||||
let(:project_path) { project.full_path }
|
||||
let(:mutation_arguments) do
|
||||
{
|
||||
projectPath: project_path,
|
||||
tagName: tag_name
|
||||
}
|
||||
end
|
||||
|
||||
let(:mutation) do
|
||||
graphql_mutation(mutation_name, mutation_arguments, <<~FIELDS)
|
||||
release {
|
||||
tagName
|
||||
}
|
||||
errors
|
||||
FIELDS
|
||||
end
|
||||
|
||||
let(:delete_release) { post_graphql_mutation(mutation, current_user: current_user) }
|
||||
let(:mutation_response) { graphql_mutation_response(mutation_name)&.with_indifferent_access }
|
||||
|
||||
before do
|
||||
project.add_guest(guest)
|
||||
project.add_reporter(reporter)
|
||||
project.add_developer(developer)
|
||||
project.add_maintainer(maintainer)
|
||||
end
|
||||
|
||||
shared_examples 'unauthorized or not found error' do
|
||||
it 'returns a top-level error with message' do
|
||||
delete_release
|
||||
|
||||
expect(mutation_response).to be_nil
|
||||
expect(graphql_errors.count).to eq(1)
|
||||
expect(graphql_errors.first['message']).to eq("The resource that you are attempting to access does not exist or you don't have permission to perform this action")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the current user has access to update releases' do
|
||||
let(:current_user) { maintainer }
|
||||
|
||||
it 'deletes the release' do
|
||||
expect { delete_release }.to change { Release.count }.by(-1)
|
||||
end
|
||||
|
||||
it 'returns the deleted release' do
|
||||
delete_release
|
||||
|
||||
expected_release = { tagName: tag_name }.with_indifferent_access
|
||||
|
||||
expect(mutation_response[:release]).to eq(expected_release)
|
||||
end
|
||||
|
||||
it 'does not remove the Git tag associated with the deleted release' do
|
||||
expect { delete_release }.not_to change { Project.find_by_id(project.id).repository.tag_count }
|
||||
end
|
||||
|
||||
it 'returns no errors' do
|
||||
delete_release
|
||||
|
||||
expect(mutation_response[:errors]).to eq([])
|
||||
end
|
||||
|
||||
context 'validation' do
|
||||
context 'when the release does not exist' do
|
||||
let_it_be(:tag_name) { 'not-a-real-release' }
|
||||
|
||||
it 'returns the release as null' do
|
||||
delete_release
|
||||
|
||||
expect(mutation_response[:release]).to be_nil
|
||||
end
|
||||
|
||||
it 'returns an errors-at-data message' do
|
||||
delete_release
|
||||
|
||||
expect(mutation_response[:errors]).to eq(['Release does not exist'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the project does not exist' do
|
||||
let(:project_path) { 'not/a/real/path' }
|
||||
|
||||
it_behaves_like 'unauthorized or not found error'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the current user doesn't have access to update releases" do
|
||||
context 'when the current user is a Developer' do
|
||||
let(:current_user) { developer }
|
||||
|
||||
it_behaves_like 'unauthorized or not found error'
|
||||
end
|
||||
|
||||
context 'when the current user is a Reporter' do
|
||||
let(:current_user) { reporter }
|
||||
|
||||
it_behaves_like 'unauthorized or not found error'
|
||||
end
|
||||
|
||||
context 'when the current user is a Guest' do
|
||||
let(:current_user) { guest }
|
||||
|
||||
it_behaves_like 'unauthorized or not found error'
|
||||
end
|
||||
|
||||
context 'when the current user is a public user' do
|
||||
let(:current_user) { public_user }
|
||||
|
||||
it_behaves_like 'unauthorized or not found error'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,6 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe Import::GitlabGroupsController do
|
||||
include WorkhorseHelpers
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
let(:import_path) { "#{Dir.tmpdir}/gitlab_groups_controller_spec" }
|
||||
let(:workhorse_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
|
||||
let(:workhorse_headers) do
|
||||
|
@ -28,8 +29,6 @@ RSpec.describe Import::GitlabGroupsController do
|
|||
describe 'POST create' do
|
||||
subject(:import_request) { upload_archive(file_upload, workhorse_headers, request_params) }
|
||||
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
let(:file) { File.join('spec', %w[fixtures group_export.tar.gz]) }
|
||||
let(:file_upload) { fixture_file_upload(file) }
|
||||
|
||||
|
@ -194,67 +193,10 @@ RSpec.describe Import::GitlabGroupsController do
|
|||
end
|
||||
|
||||
describe 'POST authorize' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
it_behaves_like 'handle uploads authorize request' do
|
||||
let(:uploader_class) { ImportExportUploader }
|
||||
|
||||
before do
|
||||
login_as(user)
|
||||
end
|
||||
|
||||
context 'when using a workhorse header' do
|
||||
subject(:authorize_request) { post authorize_import_gitlab_group_path, headers: workhorse_headers }
|
||||
|
||||
it 'authorizes the request' do
|
||||
authorize_request
|
||||
|
||||
expect(response).to have_gitlab_http_status :ok
|
||||
expect(response.media_type).to eq Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
|
||||
expect(json_response['TempPath']).to eq ImportExportUploader.workhorse_local_upload_path
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the request bypasses gitlab-workhorse' do
|
||||
subject(:authorize_request) { post authorize_import_gitlab_group_path }
|
||||
|
||||
it 'rejects the request' do
|
||||
expect { authorize_request }.to raise_error(JWT::DecodeError)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct upload is enabled' do
|
||||
subject(:authorize_request) { post authorize_import_gitlab_group_path, headers: workhorse_headers }
|
||||
|
||||
before do
|
||||
stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: true)
|
||||
end
|
||||
|
||||
it 'accepts the request and stores the files' do
|
||||
authorize_request
|
||||
|
||||
expect(response).to have_gitlab_http_status :ok
|
||||
expect(response.media_type).to eq Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
|
||||
expect(json_response).not_to have_key 'TempPath'
|
||||
|
||||
expect(json_response['RemoteObject'].keys)
|
||||
.to include('ID', 'GetURL', 'StoreURL', 'DeleteURL', 'MultipartUpload')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct upload is disabled' do
|
||||
subject(:authorize_request) { post authorize_import_gitlab_group_path, headers: workhorse_headers }
|
||||
|
||||
before do
|
||||
stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: false)
|
||||
end
|
||||
|
||||
it 'handles the local file' do
|
||||
authorize_request
|
||||
|
||||
expect(response).to have_gitlab_http_status :ok
|
||||
expect(response.media_type).to eq Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE
|
||||
|
||||
expect(json_response['TempPath']).to eq ImportExportUploader.workhorse_local_upload_path
|
||||
expect(json_response['RemoteObject']).to be_nil
|
||||
end
|
||||
subject { post authorize_import_gitlab_group_path, headers: workhorse_headers }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -84,56 +84,10 @@ RSpec.describe Import::GitlabProjectsController do
|
|||
end
|
||||
|
||||
describe 'POST authorize' do
|
||||
subject { post authorize_import_gitlab_project_path, headers: workhorse_headers }
|
||||
it_behaves_like 'handle uploads authorize request' do
|
||||
let(:uploader_class) { ImportExportUploader }
|
||||
|
||||
it 'authorizes importing project with workhorse header' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
|
||||
end
|
||||
|
||||
it 'rejects requests that bypassed gitlab-workhorse' do
|
||||
workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
|
||||
|
||||
expect { subject }.to raise_error(JWT::DecodeError)
|
||||
end
|
||||
|
||||
context 'when using remote storage' do
|
||||
context 'when direct upload is enabled' do
|
||||
before do
|
||||
stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: true)
|
||||
end
|
||||
|
||||
it 'responds with status 200, location of file remote store and object details' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response).not_to have_key('TempPath')
|
||||
expect(json_response['RemoteObject']).to have_key('ID')
|
||||
expect(json_response['RemoteObject']).to have_key('GetURL')
|
||||
expect(json_response['RemoteObject']).to have_key('StoreURL')
|
||||
expect(json_response['RemoteObject']).to have_key('DeleteURL')
|
||||
expect(json_response['RemoteObject']).to have_key('MultipartUpload')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct upload is disabled' do
|
||||
before do
|
||||
stub_uploads_object_storage(ImportExportUploader, enabled: true, direct_upload: false)
|
||||
end
|
||||
|
||||
it 'handles as a local file' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response['TempPath']).to eq(ImportExportUploader.workhorse_local_upload_path)
|
||||
expect(json_response['RemoteObject']).to be_nil
|
||||
end
|
||||
end
|
||||
subject { post authorize_import_gitlab_project_path, headers: workhorse_headers }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,6 +31,7 @@ module GraphqlHelpers
|
|||
end
|
||||
|
||||
# TODO: Remove this method entirely when GraphqlHelpers uses real resolve_field
|
||||
# see: https://gitlab.com/gitlab-org/gitlab/-/issues/287791
|
||||
def aliased_args(resolver, args)
|
||||
definitions = resolver.arguments
|
||||
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples 'handle uploads authorize request' do
|
||||
before do
|
||||
login_as(user)
|
||||
end
|
||||
|
||||
describe 'POST authorize' do
|
||||
it 'authorizes workhorse header' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response['TempPath']).to eq(uploader_class.workhorse_local_upload_path)
|
||||
end
|
||||
|
||||
it 'rejects requests that bypassed gitlab-workhorse' do
|
||||
workhorse_headers.delete(Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER)
|
||||
|
||||
expect { subject }.to raise_error(JWT::DecodeError)
|
||||
end
|
||||
|
||||
context 'when using remote storage' do
|
||||
context 'when direct upload is enabled' do
|
||||
before do
|
||||
stub_uploads_object_storage(uploader_class, enabled: true, direct_upload: true)
|
||||
end
|
||||
|
||||
it 'responds with status 200, location of file remote store and object details' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response).not_to have_key('TempPath')
|
||||
expect(json_response['RemoteObject']).to have_key('ID')
|
||||
expect(json_response['RemoteObject']).to have_key('GetURL')
|
||||
expect(json_response['RemoteObject']).to have_key('StoreURL')
|
||||
expect(json_response['RemoteObject']).to have_key('DeleteURL')
|
||||
expect(json_response['RemoteObject']).to have_key('MultipartUpload')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when direct upload is disabled' do
|
||||
before do
|
||||
stub_uploads_object_storage(uploader_class, enabled: true, direct_upload: false)
|
||||
end
|
||||
|
||||
it 'handles as a local file' do
|
||||
subject
|
||||
|
||||
expect(response).to have_gitlab_http_status(:ok)
|
||||
expect(response.content_type.to_s).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
|
||||
expect(json_response['TempPath']).to eq(uploader_class.workhorse_local_upload_path)
|
||||
expect(json_response['RemoteObject']).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue