Commit ce54a801 authored by Robert Schilling's avatar Robert Schilling

Backport API to v3

parent c70dfbc6
...@@ -24,3 +24,4 @@ changes are in V4: ...@@ -24,3 +24,4 @@ changes are in V4:
- `/dockerfiles/:key` - `/dockerfiles/:key`
- Moved `/projects/fork/:id` to `/projects/:id/fork` - Moved `/projects/fork/:id` to `/projects/:id/fork`
- Endpoints `/projects/owned`, `/projects/visible`, `/projects/starred` & `/projects/all` are consolidated into `/projects` using query parameters - Endpoints `/projects/owned`, `/projects/visible`, `/projects/starred` & `/projects/all` are consolidated into `/projects` using query parameters
- Return pagination headers for all endpoints that return an array
...@@ -5,13 +5,22 @@ module API ...@@ -5,13 +5,22 @@ module API
version %w(v3 v4), using: :path version %w(v3 v4), using: :path
version 'v3', using: :path do version 'v3', using: :path do
mount ::API::V3::Boards
mount ::API::V3::Branches
mount ::API::V3::DeployKeys mount ::API::V3::DeployKeys
mount ::API::V3::Issues mount ::API::V3::Issues
mount ::API::V3::Labels
mount ::API::V3::Members mount ::API::V3::Members
mount ::API::V3::MergeRequestDiffs
mount ::API::V3::MergeRequests mount ::API::V3::MergeRequests
mount ::API::V3::ProjectHooks
mount ::API::V3::Projects mount ::API::V3::Projects
mount ::API::V3::ProjectSnippets mount ::API::V3::ProjectSnippets
mount ::API::V3::Repositories
mount ::API::V3::SystemHooks
mount ::API::V3::Tags
mount ::API::V3::Templates mount ::API::V3::Templates
mount ::API::V3::Users
end end
before { allow_access_with_scope :api } before { allow_access_with_scope :api }
......
...@@ -91,7 +91,7 @@ module API ...@@ -91,7 +91,7 @@ module API
end end
get "templates/#{template_type}" do get "templates/#{template_type}" do
templates = ::Kaminari.paginate_array(klass.all) templates = ::Kaminari.paginate_array(klass.all)
present paginate(templates), with: Entities::TemplatesLis present paginate(templates), with: Entities::TemplatesList
end end
desc 'Get the text for a specific template present in local filesystem' do desc 'Get the text for a specific template present in local filesystem' do
......
module API
module V3
class Boards < Grape::API
before { authenticate! }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
desc 'Get all project boards' do
detail 'This feature was introduced in 8.13'
success ::API::Entities::Board
end
get ':id/boards' do
authorize!(:read_board, user_project)
present user_project.boards, with: ::API::Entities::Board
end
params do
requires :board_id, type: Integer, desc: 'The ID of a board'
end
segment ':id/boards/:board_id' do
helpers do
def project_board
board = user_project.boards.first
if params[:board_id] == board.id
board
else
not_found!('Board')
end
end
def board_lists
project_board.lists.destroyable
end
end
desc 'Get the lists of a project board' do
detail 'Does not include `done` list. This feature was introduced in 8.13'
success ::API::Entities::List
end
get '/lists' do
authorize!(:read_board, user_project)
present board_lists, with: ::API::Entities::List
end
end
end
end
end
end
require 'mime/types'
module API
module V3
class Branches < Grape::API
before { authenticate! }
before { authorize! :download_code, user_project }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
desc 'Get a project repository branches' do
success ::API::Entities::RepoBranch
end
get ":id/repository/branches" do
branches = user_project.repository.branches.sort_by(&:name)
present branches, with: ::API::Entities::RepoBranch, project: user_project
end
end
end
end
end
module API
module V3
class Labels < Grape::API
before { authenticate! }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
desc 'Get all labels of the project' do
success ::API::Entities::Label
end
get ':id/labels' do
present available_labels, with: ::API::Entities::Label, current_user: current_user, project: user_project
end
end
end
end
end
require 'mime/types'
module API
module V3
class Repositories < Grape::API
before { authorize! :download_code, user_project }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
helpers do
def handle_project_member_errors(errors)
if errors[:project_access].any?
error!(errors[:project_access], 422)
end
not_found!
end
end
desc 'Get a project repository tree' do
success ::API::Entities::RepoTreeObject
end
params do
optional :ref_name, type: String, desc: 'The name of a repository branch or tag, if not given the default branch is used'
optional :path, type: String, desc: 'The path of the tree'
optional :recursive, type: Boolean, default: false, desc: 'Used to get a recursive tree'
end
get ':id/repository/tree' do
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
path = params[:path] || nil
commit = user_project.commit(ref)
not_found!('Tree') unless commit
tree = user_project.repository.tree(commit.id, path, recursive: params[:recursive])
present tree.sorted_entries, with: ::API::Entities::RepoTreeObject
end
desc 'Get repository contributors' do
success ::API::Entities::Contributor
end
get ':id/repository/contributors' do
begin
present user_project.repository.contributors,
with: ::API::Entities::Contributor
rescue
not_found!
end
end
end
end
end
end
module API
module V3
class SystemHooks < Grape::API
before do
authenticate!
authenticated_as_admin!
end
resource :hooks do
desc 'Get the list of system hooks' do
success ::API::Entities::Hook
end
get do
present SystemHook.all, with: ::API::Entities::Hook
end
end
end
end
end
module API
module V3
class Tags < Grape::API
before { authorize! :download_code, user_project }
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
desc 'Get a project repository tags' do
success ::API::Entities::RepoTag
end
get ":id/repository/tags" do
tags = user_project.repository.tags.sort_by(&:name).reverse
present tags, with: ::API::Entities::RepoTag, project: user_project
end
end
end
end
end
module API
module V3
class Users < Grape::API
include PaginationParams
before do
allow_access_with_scope :read_user if request.get?
authenticate!
end
resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do
desc 'Get the SSH keys of a specified user. Available only for admins.' do
success ::API::Entities::SSHKey
end
params do
requires :id, type: Integer, desc: 'The ID of the user'
use :pagination
end
get ':id/keys' do
authenticated_as_admin!
user = User.find_by(id: params[:id])
not_found!('User') unless user
present paginate(user.keys), with: ::API::Entities::SSHKey
end
desc 'Get the emails addresses of a specified user. Available only for admins.' do
success ::API::Entities::Email
end
params do
requires :id, type: Integer, desc: 'The ID of the user'
use :pagination
end
get ':id/emails' do
authenticated_as_admin!
user = User.find_by(id: params[:id])
not_found!('User') unless user
present user.emails, with: ::API::Entities::Email
end
end
resource :user do
desc "Get the currently authenticated user's SSH keys" do
success ::API::Entities::SSHKey
end
params do
use :pagination
end
get "keys" do
present current_user.keys, with: ::API::Entities::SSHKey
end
desc "Get the currently authenticated user's email addresses" do
success ::API::Entities::Email
end
get "emails" do
present current_user.emails, with: ::API::Entities::Email
end
end
end
end
end
require 'spec_helper'
describe API::V3::Boards, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:guest) { create(:user) }
let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:dev_label) do
create(:label, title: 'Development', color: '#FFAABB', project: project)
end
let!(:test_label) do
create(:label, title: 'Testing', color: '#FFAACC', project: project)
end
let!(:dev_list) do
create(:list, label: dev_label, position: 1)
end
let!(:test_list) do
create(:list, label: test_label, position: 2)
end
let!(:board) do
create(:board, project: project, lists: [dev_list, test_list])
end
before do
project.team << [user, :reporter]
project.team << [guest, :guest]
end
describe "GET /projects/:id/boards" do
let(:base_url) { "/projects/#{project.id}/boards" }
context "when unauthenticated" do
it "returns authentication error" do
get v3_api(base_url)
expect(response).to have_http_status(401)
end
end
context "when authenticated" do
it "returns the project issue board" do
get v3_api(base_url, user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(board.id)
expect(json_response.first['lists']).to be_an Array
expect(json_response.first['lists'].length).to eq(2)
expect(json_response.first['lists'].last).to have_key('position')
end
end
end
describe "GET /projects/:id/boards/:board_id/lists" do
let(:base_url) { "/projects/#{project.id}/boards/#{board.id}/lists" }
it 'returns issue board lists' do
get v3_api(base_url, user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
expect(json_response.first['label']['name']).to eq(dev_label.title)
end
it 'returns 404 if board not found' do
get v3_api("/projects/#{project.id}/boards/22343/lists", user)
expect(response).to have_http_status(404)
end
end
end
require 'spec_helper'
require 'mime/types'
describe API::V3::Branches, api: true do
include ApiHelpers
let(:user) { create(:user) }
let!(:project) { create(:project, :repository, creator: user) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
describe "GET /projects/:id/repository/branches" do
it "returns an array of project branches" do
project.repository.expire_all_method_caches
get v3_api("/projects/#{project.id}/repository/branches", user), per_page: 100
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
branch_names = json_response.map { |x| x['name'] }
expect(branch_names).to match_array(project.repository.branch_names)
end
end
end
require 'spec_helper'
describe API::V3::Labels, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
let!(:label1) { create(:label, title: 'label1', project: project) }
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
before do
project.team << [user, :master]
end
describe 'GET /projects/:id/labels' do
it 'returns all available labels to the project' do
group = create(:group)
group_label = create(:group_label, title: 'feature', group: group)
project.update(group: group)
create(:labeled_issue, project: project, labels: [group_label], author: user)
create(:labeled_issue, project: project, labels: [label1], author: user, state: :closed)
create(:labeled_merge_request, labels: [priority_label], author: user, source_project: project )
expected_keys = [
'id', 'name', 'color', 'description',
'open_issues_count', 'closed_issues_count', 'open_merge_requests_count',
'subscribed', 'priority'
]
get v3_api("/projects/#{project.id}/labels", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(3)
expect(json_response.first.keys).to match_array expected_keys
expect(json_response.map { |l| l['name'] }).to match_array([group_label.name, priority_label.name, label1.name])
label1_response = json_response.find { |l| l['name'] == label1.title }
group_label_response = json_response.find { |l| l['name'] == group_label.title }
priority_label_response = json_response.find { |l| l['name'] == priority_label.title }
expect(label1_response['open_issues_count']).to eq(0)
expect(label1_response['closed_issues_count']).to eq(1)
expect(label1_response['open_merge_requests_count']).to eq(0)
expect(label1_response['name']).to eq(label1.name)
expect(label1_response['color']).to be_present
expect(label1_response['description']).to be_nil
expect(label1_response['priority']).to be_nil
expect(label1_response['subscribed']).to be_falsey
expect(group_label_response['open_issues_count']).to eq(1)
expect(group_label_response['closed_issues_count']).to eq(0)
expect(group_label_response['open_merge_requests_count']).to eq(0)
expect(group_label_response['name']).to eq(group_label.name)
expect(group_label_response['color']).to be_present
expect(group_label_response['description']).to be_nil
expect(group_label_response['priority']).to be_nil
expect(group_label_response['subscribed']).to be_falsey
expect(priority_label_response['open_issues_count']).to eq(0)
expect(priority_label_response['closed_issues_count']).to eq(0)
expect(priority_label_response['open_merge_requests_count']).to eq(1)
expect(priority_label_response['name']).to eq(priority_label.name)
expect(priority_label_response['color']).to be_present
expect(priority_label_response['description']).to be_nil
expect(priority_label_response['priority']).to eq(3)
expect(priority_label_response['subscribed']).to be_falsey
end
end
end
require 'spec_helper'
require 'mime/types'
describe API::V3::Repositories, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:guest) { create(:user).tap { |u| create(:project_member, :guest, user: u, project: project) } }
let!(:project) { create(:project, :repository, creator: user) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
describe "GET /projects/:id/repository/tree" do
let(:route) { "/projects/#{project.id}/repository/tree" }
shared_examples_for 'repository tree' do
it 'returns the repository tree' do
get v3_api(route, current_user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
first_commit = json_response.first
expect(first_commit['name']).to eq('bar')
expect(first_commit['type']).to eq('tree')
expect(first_commit['mode']).to eq('040000')
end
context 'when ref does not exist' do
it_behaves_like '404 response' do
let(:request) { get v3_api("#{route}?ref_name=foo", current_user) }
let(:message) { '404 Tree Not Found' }
end
end
context 'when repository is disabled' do
include_context 'disabled repository'
it_behaves_like '403 response' do
let(:request) { get v3_api(route, current_user) }
end
end
context 'with recursive=1' do
it 'returns recursive project paths tree' do
get v3_api("#{route}?recursive=1", current_user)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response[4]['name']).to eq('html')
expect(json_response[4]['path']).to eq('files/html')
expect(json_response[4]['type']).to eq('tree')
expect(json_response[4]['mode']).to eq('040000')
end
context 'when repository is disabled' do
include_context 'disabled repository'
it_behaves_like '403 response' do
let(:request) { get v3_api(route, current_user) }
end
end
context 'when ref does not exist' do
it_behaves_like '404 response' do
let(:request) { get v3_api("#{route}?recursive=1&ref_name=foo", current_user) }
let(:message) { '404 Tree Not Found' }
end
end
end
end
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository tree' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get v3_api(route) }
let(:message) { '404 Project Not Found' }
end
end
context 'when authenticated', 'as a developer' do
it_behaves_like 'repository tree' do
let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { get v3_api(route, guest) }
end
end
end
describe 'GET /projects/:id/repository/contributors' do
let(:route) { "/projects/#{project.id}/repository/contributors" }
shared_examples_for 'repository contributors' do
it 'returns valid data' do
get v3_api(route, current_user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
first_contributor = json_response.first
expect(first_contributor['email']).to eq('tiagonbotelho@hotmail.com')
expect(first_contributor['name']).to eq('tiagonbotelho')
expect(first_contributor['commits']).to eq(1)
expect(first_contributor['additions']).to eq(0)
expect(first_contributor['deletions']).to eq(0)
end
end
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository contributors' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get v3_api(route) }
let(:message) { '404 Project Not Found' }
end
end
context 'when authenticated', 'as a developer' do
it_behaves_like 'repository contributors' do
let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { get v3_api(route, guest) }
end
end
end
end
require 'spec_helper'
describe API::V3::SystemHooks, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let!(:hook) { create(:system_hook, url: "http://example.com") }
before { stub_request(:post, hook.url) }
describe "GET /hooks" do
context "when no user" do
it "returns authentication error" do
get v3_api("/hooks")
expect(response).to have_http_status(401)
end
end
context "when not an admin" do
it "returns forbidden error" do
get v3_api("/hooks", user)
expect(response).to have_http_status(403)
end
end
context "when authenticated as admin" do
it "returns an array of hooks" do
get v3_api("/hooks", admin)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['url']).to eq(hook.url)
expect(json_response.first['push_events']).to be true
expect(json_response.first['tag_push_events']).to be false
end
end
end
end
require 'spec_helper'
require 'mime/types'
describe API::V3::Tags, api: true do
include ApiHelpers
include RepoHelpers
let(:user) { create(:user) }
let(:user2) { create(:user) }
let!(:project) { create(:project, :repository, creator: user) }
let!(:master) { create(:project_member, :master, user: user, project: project) }
describe "GET /projects/:id/repository/tags" do
let(:tag_name) { project.repository.tag_names.sort.reverse.first }
let(:description) { 'Awesome release!' }
shared_examples_for 'repository tags' do
it 'returns the repository tags' do
get v3_api("/projects/#{project.id}/repository/tags", current_user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(tag_name)
end
end
context 'when unauthenticated' do
it_behaves_like 'repository tags' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
context 'when authenticated' do
it_behaves_like 'repository tags' do
let(:current_user) { user }
end
end
context 'without releases' do
it "returns an array of project tags" do
get v3_api("/projects/#{project.id}/repository/tags", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(tag_name)
end
end
context 'with releases' do
before do
release = project.releases.find_or_initialize_by(tag: tag_name)
release.update_attributes(description: description)
end
it "returns an array of project tags with release info" do
get v3_api("/projects/#{project.id}/repository/tags", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['name']).to eq(tag_name)
expect(json_response.first['message']).to eq('Version 1.1.0')
expect(json_response.first['release']['description']).to eq(description)
end
end
end
end
require 'spec_helper'
describe API::V3::Users, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:key) { create(:key, user: user) }
let(:email) { create(:email, user: user) }
describe 'GET /user/:id/keys' do
before { admin }
context 'when unauthenticated' do
it 'returns authentication error' do
get v3_api("/users/#{user.id}/keys")
expect(response).to have_http_status(401)
end
end
context 'when authenticated' do
it 'returns 404 for non-existing user' do
get v3_api('/users/999999/keys', admin)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
it 'returns array of ssh keys' do
user.keys << key
user.save
get v3_api("/users/#{user.id}/keys", admin)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['title']).to eq(key.title)
end
end
end
describe 'GET /user/:id/emails' do
before { admin }
context 'when unauthenticated' do
it 'returns authentication error' do
get v3_api("/users/#{user.id}/emails")
expect(response).to have_http_status(401)
end
end
context 'when authenticated' do
it 'returns 404 for non-existing user' do
get v3_api('/users/999999/emails', admin)
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
it 'returns array of emails' do
user.emails << email
user.save
get v3_api("/users/#{user.id}/emails", admin)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['email']).to eq(email.email)
end
it "returns a 404 for invalid ID" do
put v3_api("/users/ASDF/emails", admin)
expect(response).to have_http_status(404)
end
end
end
describe "GET /user/keys" do
context "when unauthenticated" do
it "returns authentication error" do
get v3_api("/user/keys")
expect(response).to have_http_status(401)
end
end
context "when authenticated" do
it "returns array of ssh keys" do
user.keys << key
user.save
get v3_api("/user/keys", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first["title"]).to eq(key.title)
end
end
end
describe "GET /user/emails" do
context "when unauthenticated" do
it "returns authentication error" do
get v3_api("/user/emails")
expect(response).to have_http_status(401)
end
end
context "when authenticated" do
it "returns array of emails" do
user.emails << email
user.save
get v3_api("/user/emails", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first["email"]).to eq(email.email)
end
end
end
end
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