API for importing external repos
This commit is contained in:
parent
f598daf284
commit
1e2bd85333
|
@ -18,6 +18,7 @@ class Import::BaseController < ApplicationController
|
|||
end
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
# deprecated: being replaced by app/services/import/base_service.rb
|
||||
def find_or_create_namespace(names, owner)
|
||||
names = params[:target_namespace].presence || names
|
||||
|
||||
|
@ -32,6 +33,7 @@ class Import::BaseController < ApplicationController
|
|||
current_user.namespace
|
||||
end
|
||||
|
||||
# deprecated: being replaced by app/services/import/base_service.rb
|
||||
def project_save_error(project)
|
||||
project.errors.full_messages.join(', ')
|
||||
end
|
||||
|
|
|
@ -39,28 +39,21 @@ class Import::GithubController < Import::BaseController
|
|||
end
|
||||
|
||||
def create
|
||||
repo = client.repo(params[:repo_id].to_i)
|
||||
project_name = params[:new_name].presence || repo.name
|
||||
namespace_path = params[:target_namespace].presence || current_user.namespace_path
|
||||
target_namespace = find_or_create_namespace(namespace_path, current_user.namespace_path)
|
||||
result = Import::GithubService.new(client, current_user, import_params).execute(access_params, provider)
|
||||
|
||||
if can?(current_user, :create_projects, target_namespace)
|
||||
project = Gitlab::LegacyGithubImport::ProjectCreator
|
||||
.new(repo, project_name, target_namespace, current_user, access_params, type: provider)
|
||||
.execute(extra_project_attrs)
|
||||
|
||||
if project.persisted?
|
||||
render json: ProjectSerializer.new.represent(project)
|
||||
else
|
||||
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
|
||||
end
|
||||
if result[:status] == :success
|
||||
render json: ProjectSerializer.new.represent(result[:project])
|
||||
else
|
||||
render json: { errors: 'This namespace has already been taken! Please choose another one.' }, status: :unprocessable_entity
|
||||
render json: { errors: result[:message] }, status: result[:http_status]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def import_params
|
||||
params.permit(:repo_id, :new_name, :target_namespace)
|
||||
end
|
||||
|
||||
def client
|
||||
@client ||= Gitlab::LegacyGithubImport::Client.new(session[access_token_key], client_options)
|
||||
end
|
||||
|
@ -124,10 +117,6 @@ class Import::GithubController < Import::BaseController
|
|||
{}
|
||||
end
|
||||
|
||||
def extra_project_attrs
|
||||
{}
|
||||
end
|
||||
|
||||
def extra_import_params
|
||||
{}
|
||||
end
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Import
|
||||
class BaseService < ::BaseService
|
||||
def initialize(client, user, params)
|
||||
@client = client
|
||||
@current_user = user
|
||||
@params = params
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_or_create_namespace(namespace, owner)
|
||||
namespace = params[:target_namespace].presence || namespace
|
||||
|
||||
return current_user.namespace if namespace == owner
|
||||
|
||||
group = Groups::NestedCreateService.new(current_user, group_path: namespace).execute
|
||||
|
||||
group.errors.any? ? current_user.namespace : group
|
||||
rescue => e
|
||||
Gitlab::AppLogger.error(e)
|
||||
|
||||
current_user.namespace
|
||||
end
|
||||
|
||||
def project_save_error(project)
|
||||
project.errors.full_messages.join(', ')
|
||||
end
|
||||
|
||||
def success(project)
|
||||
super().merge(project: project, status: :success)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Import
|
||||
class GithubService < Import::BaseService
|
||||
attr_accessor :client
|
||||
attr_reader :params, :current_user
|
||||
|
||||
def execute(access_params, provider)
|
||||
unless authorized?
|
||||
return error('This namespace has already been taken! Please choose another one.', :unprocessable_entity)
|
||||
end
|
||||
|
||||
project = Gitlab::LegacyGithubImport::ProjectCreator
|
||||
.new(repo, project_name, target_namespace, current_user, access_params, type: provider)
|
||||
.execute(extra_project_attrs)
|
||||
|
||||
if project.persisted?
|
||||
success(project)
|
||||
else
|
||||
error(project_save_error(project), :unprocessable_entity)
|
||||
end
|
||||
end
|
||||
|
||||
def repo
|
||||
@repo ||= client.repo(params[:repo_id].to_i)
|
||||
end
|
||||
|
||||
def project_name
|
||||
@project_name ||= params[:new_name].presence || repo.name
|
||||
end
|
||||
|
||||
def namespace_path
|
||||
@namespace_path ||= params[:target_namespace].presence || current_user.namespace_path
|
||||
end
|
||||
|
||||
def target_namespace
|
||||
@target_namespace ||= find_or_create_namespace(namespace_path, current_user.namespace_path)
|
||||
end
|
||||
|
||||
def extra_project_attrs
|
||||
{}
|
||||
end
|
||||
|
||||
def authorized?
|
||||
can?(current_user, :create_projects, target_namespace)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,33 @@
|
|||
# Import API
|
||||
|
||||
## Import repository from GitHub
|
||||
|
||||
Import your projects from GitHub to GitLab via the API.
|
||||
|
||||
```
|
||||
POST /import/github
|
||||
```
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
|------------|---------|----------|---------------------|
|
||||
| `personal_access_token` | string | yes | GitHub personal access token |
|
||||
| `repo_id` | integer | yes | GitHub repository ID |
|
||||
| `new_name` | string | no | New repo name |
|
||||
| `target_namespace` | string | yes | Namespace to import repo into |
|
||||
|
||||
|
||||
```bash
|
||||
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "personal_access_token=abc123&repo_id=12345&target_namespace=root" https://gitlab.example.com/api/v4/import/github
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 27,
|
||||
"name": "my-repo",
|
||||
"full_path": "/root/my-repo",
|
||||
"full_name": "Administrator / my-repo"
|
||||
}
|
||||
```
|
||||
|
|
@ -110,6 +110,7 @@ module API
|
|||
mount ::API::GroupMilestones
|
||||
mount ::API::Groups
|
||||
mount ::API::GroupVariables
|
||||
mount ::API::ImportGithub
|
||||
mount ::API::Internal
|
||||
mount ::API::Issues
|
||||
mount ::API::JobArtifacts
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
class ImportGithub < Grape::API
|
||||
rescue_from Octokit::Unauthorized, with: :provider_unauthorized
|
||||
|
||||
helpers do
|
||||
def client
|
||||
@client ||= Gitlab::LegacyGithubImport::Client.new(params[:personal_access_token], client_options)
|
||||
end
|
||||
|
||||
def access_params
|
||||
{ github_access_token: params[:personal_access_token] }
|
||||
end
|
||||
|
||||
def client_options
|
||||
{}
|
||||
end
|
||||
|
||||
def provider
|
||||
:github
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Import a GitHub project' do
|
||||
detail 'This feature was introduced in GitLab 11.3.4.'
|
||||
success Entities::ProjectEntity
|
||||
end
|
||||
params do
|
||||
requires :personal_access_token, type: String, desc: 'GitHub personal access token'
|
||||
requires :repo_id, type: Integer, desc: 'GitHub repository ID'
|
||||
optional :new_name, type: String, desc: 'New repo name'
|
||||
requires :target_namespace, type: String, desc: 'Namespace to import repo into'
|
||||
end
|
||||
post 'import/github' do
|
||||
result = Import::GithubService.new(client, current_user, params).execute(access_params, provider)
|
||||
|
||||
if result[:status] == :success
|
||||
present ProjectSerializer.new.represent(result[:project])
|
||||
else
|
||||
status result[:http_status]
|
||||
{ errors: result[:message] }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,56 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe API::ImportGithub do
|
||||
include ApiHelpers
|
||||
|
||||
let(:token) { "asdasd12345" }
|
||||
let(:provider) { :github }
|
||||
let(:access_params) { { github_access_token: token } }
|
||||
|
||||
describe "POST /import/github" do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project) }
|
||||
let(:provider_username) { user.username }
|
||||
let(:provider_user) { OpenStruct.new(login: provider_username) }
|
||||
let(:provider_repo) do
|
||||
OpenStruct.new(
|
||||
name: 'vim',
|
||||
full_name: "#{provider_username}/vim",
|
||||
owner: OpenStruct.new(login: provider_username)
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
Grape::Endpoint.before_each do |endpoint|
|
||||
allow(endpoint).to receive(:client).and_return(double('client', user: provider_user, repo: provider_repo).as_null_object)
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns 201 response when the project is imported successfully' do
|
||||
allow(Gitlab::LegacyGithubImport::ProjectCreator)
|
||||
.to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
|
||||
.and_return(double(execute: project))
|
||||
|
||||
post api("/import/github", user), params: {
|
||||
target_namespace: user.namespace_path,
|
||||
personal_access_token: token,
|
||||
repo_id: 1234
|
||||
}
|
||||
expect(response).to have_gitlab_http_status(201)
|
||||
expect(json_response).to be_a Hash
|
||||
expect(json_response['name']).to eq(project.name)
|
||||
end
|
||||
|
||||
it 'returns 422 response when user can not create projects in the chosen namespace' do
|
||||
other_namespace = create(:group, name: 'other_namespace')
|
||||
|
||||
post api("/import/github", user), params: {
|
||||
target_namespace: other_namespace.name,
|
||||
personal_access_token: token,
|
||||
repo_id: 1234
|
||||
}
|
||||
|
||||
expect(response).to have_gitlab_http_status(422)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue