API for importing external repos
This commit is contained in:
parent
f598daf284
commit
1e2bd85333
|
@ -18,6 +18,7 @@ class Import::BaseController < ApplicationController
|
||||||
end
|
end
|
||||||
# rubocop: enable CodeReuse/ActiveRecord
|
# rubocop: enable CodeReuse/ActiveRecord
|
||||||
|
|
||||||
|
# deprecated: being replaced by app/services/import/base_service.rb
|
||||||
def find_or_create_namespace(names, owner)
|
def find_or_create_namespace(names, owner)
|
||||||
names = params[:target_namespace].presence || names
|
names = params[:target_namespace].presence || names
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ class Import::BaseController < ApplicationController
|
||||||
current_user.namespace
|
current_user.namespace
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# deprecated: being replaced by app/services/import/base_service.rb
|
||||||
def project_save_error(project)
|
def project_save_error(project)
|
||||||
project.errors.full_messages.join(', ')
|
project.errors.full_messages.join(', ')
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,28 +39,21 @@ class Import::GithubController < Import::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
repo = client.repo(params[:repo_id].to_i)
|
result = Import::GithubService.new(client, current_user, import_params).execute(access_params, provider)
|
||||||
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)
|
|
||||||
|
|
||||||
if can?(current_user, :create_projects, target_namespace)
|
if result[:status] == :success
|
||||||
project = Gitlab::LegacyGithubImport::ProjectCreator
|
render json: ProjectSerializer.new.represent(result[:project])
|
||||||
.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
|
|
||||||
else
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def import_params
|
||||||
|
params.permit(:repo_id, :new_name, :target_namespace)
|
||||||
|
end
|
||||||
|
|
||||||
def client
|
def client
|
||||||
@client ||= Gitlab::LegacyGithubImport::Client.new(session[access_token_key], client_options)
|
@client ||= Gitlab::LegacyGithubImport::Client.new(session[access_token_key], client_options)
|
||||||
end
|
end
|
||||||
|
@ -124,10 +117,6 @@ class Import::GithubController < Import::BaseController
|
||||||
{}
|
{}
|
||||||
end
|
end
|
||||||
|
|
||||||
def extra_project_attrs
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
|
|
||||||
def extra_import_params
|
def extra_import_params
|
||||||
{}
|
{}
|
||||||
end
|
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::GroupMilestones
|
||||||
mount ::API::Groups
|
mount ::API::Groups
|
||||||
mount ::API::GroupVariables
|
mount ::API::GroupVariables
|
||||||
|
mount ::API::ImportGithub
|
||||||
mount ::API::Internal
|
mount ::API::Internal
|
||||||
mount ::API::Issues
|
mount ::API::Issues
|
||||||
mount ::API::JobArtifacts
|
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