From fd51f19c978023160ad759676a0363c12aea3fc8 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Thu, 22 Sep 2016 13:56:43 +0100 Subject: API: disable rails session auth for non-GET/HEAD requests --- lib/api/helpers.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/api') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 714d4ea3dc6..8b8c4eb4d46 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -21,8 +21,11 @@ module API end # Check the Rails session for valid authentication details + # + # Until CSRF protection is added to the API, disallow this method for + # state-changing endpoints def find_user_from_warden - warden ? warden.authenticate : nil + warden.try(:authenticate) if request.get? || request.head? end def find_user_by_private_token -- cgit v1.2.1 From 924a6b7d33a245845071894de8d46d77bd44b52e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 28 Jul 2016 19:38:32 +0200 Subject: New AccessRequestsFinder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/access_requests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/api') diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index 9d1d9058996..7b9de7c9598 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -16,9 +16,9 @@ module API # GET /projects/:id/access_requests get ":id/access_requests" do source = find_source(source_type, params[:id]) - authorize_admin_source!(source_type, source) - access_requesters = paginate(source.requesters.includes(:user)) + access_requesters = AccessRequestsFinder.new(source).execute!(current_user) + access_requesters = paginate(access_requesters.includes(:user)) present access_requesters.map(&:user), with: Entities::AccessRequester, source: source end -- cgit v1.2.1 From ec0061a95cbba02286b2c143048c93d8f26ff5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 16 Sep 2016 17:54:21 +0200 Subject: Allow Member.add_user to handle access requesters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes include: - Ensure Member.add_user is not called directly when not necessary - New GroupMember.add_users_to_group to have the same abstraction level as for Project - Refactor Member.add_user to take a source instead of an array of members - Fix Rubocop offenses - Always use Project#add_user instead of project.team.add_user - Factorize users addition as members in Member.add_users_to_source - Make access_level a keyword argument in GroupMember.add_users_to_group and ProjectMember.add_users_to_projects - Destroy any requester before adding them as a member - Improve the way we handle access requesters in Member.add_user Instead of removing the requester and creating a new member, we now simply accepts their access request. This way, they will receive a "access request granted" email. - Fix error that was previously silently ignored - Stop raising when access level is invalid in Member, let Rails validation do their work Signed-off-by: Rémy Coutable --- lib/api/members.rb | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'lib/api') diff --git a/lib/api/members.rb b/lib/api/members.rb index 37f0a6512f4..a18ce769e29 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -59,13 +59,6 @@ module API authorize_admin_source!(source_type, source) required_attributes! [:user_id, :access_level] - access_requester = source.requesters.find_by(user_id: params[:user_id]) - if access_requester - # We pass current_user = access_requester so that the requester doesn't - # receive a "access denied" email - ::Members::DestroyService.new(access_requester, access_requester.user).execute - end - member = source.members.find_by(user_id: params[:user_id]) # This is to ensure back-compatibility but 409 behavior should be used @@ -73,18 +66,12 @@ module API conflict!('Member already exists') if source_type == 'group' && member unless member - source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at]) - member = source.members.find_by(user_id: params[:user_id]) + member = source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at]) end - if member + if member.persisted? && member.valid? present member.user, with: Entities::Member, member: member else - # Since `source.add_user` doesn't return a member object, we have to - # build a new one and populate its errors in order to render them. - member = source.members.build(attributes_for_keys([:user_id, :access_level, :expires_at])) - member.valid? # populate the errors - # This is to ensure back-compatibility but 400 behavior should be used # for all validation errors in 9.0! render_api_error!('Access level is not known', 422) if member.errors.key?(:access_level) -- cgit v1.2.1 From 93d849beaebb00dc4dcb6cb5ffa4721883e0da51 Mon Sep 17 00:00:00 2001 From: Felipe Artur Date: Thu, 22 Sep 2016 17:31:18 -0300 Subject: Expose project share expiration_date field on API --- lib/api/entities.rb | 2 +- lib/api/projects.rb | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'lib/api') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 92a6f29adb0..29ad9f3565a 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -343,7 +343,7 @@ module API end class ProjectGroupLink < Grape::Entity - expose :id, :project_id, :group_id, :group_access + expose :id, :project_id, :group_id, :group_access, :expires_at end class Todo < Grape::Entity diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 6d99617b56f..680055c95eb 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -393,23 +393,24 @@ module API # Share project with group # # Parameters: - # id (required) - The ID of a project - # group_id (required) - The ID of a group + # id (required) - The ID of a project + # group_id (required) - The ID of a group # group_access (required) - Level of permissions for sharing + # expires_at (optional) - Share expiration date # # Example Request: # POST /projects/:id/share post ":id/share" do authorize! :admin_project, user_project required_attributes! [:group_id, :group_access] + attrs = attributes_for_keys [:group_id, :group_access, :expires_at] unless user_project.allowed_to_share_with_group? return render_api_error!("The project sharing with group is disabled", 400) end - link = user_project.project_group_links.new - link.group_id = params[:group_id] - link.group_access = params[:group_access] + link = user_project.project_group_links.new(attrs) + if link.save present link, with: Entities::ProjectGroupLink else -- cgit v1.2.1 From e80e4cb8b97a3865b7e3c551a856a53d98cccf75 Mon Sep 17 00:00:00 2001 From: Guilherme Salazar Date: Fri, 23 Sep 2016 20:43:57 -0300 Subject: expose pipeline data in builds API add pipeline ref, sha, and status to the build API response add tests of build API (pipeline data) change API documentation for builds API log change to builds API in CHANGELOG CHANGELOG: add reference to pull request and contributor's name --- lib/api/entities.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib/api') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 0adc118ba27..409a4c7cc07 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -545,6 +545,10 @@ module API expose :filename, :size end + class PipelineBasic < Grape::Entity + expose :id, :sha, :ref, :status + end + class Build < Grape::Entity expose :id, :status, :stage, :name, :ref, :tag, :coverage expose :created_at, :started_at, :finished_at @@ -552,6 +556,7 @@ module API expose :artifacts_file, using: BuildArtifactFile, if: -> (build, opts) { build.artifacts? } expose :commit, with: RepoCommit expose :runner, with: Runner + expose :pipeline, with: PipelineBasic end class Trigger < Grape::Entity @@ -562,8 +567,8 @@ module API expose :key, :value end - class Pipeline < Grape::Entity - expose :id, :status, :ref, :sha, :before_sha, :tag, :yaml_errors + class Pipeline < PipelineBasic + expose :before_sha, :tag, :yaml_errors expose :user, with: Entities::UserBasic expose :created_at, :updated_at, :started_at, :finished_at, :committed_at -- cgit v1.2.1 From 59157c0423d34c1f20c446548df540d103bda939 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Tue, 27 Sep 2016 17:56:02 -0700 Subject: Expose the Koding application settings in the API This will allow the Koding app to enable the integration itself once is has authorized an admin user using the application secrets. --- lib/api/entities.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/api') diff --git a/lib/api/entities.rb b/lib/api/entities.rb index c5dc8b22f60..04437322ec1 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -494,6 +494,8 @@ module API expose :after_sign_out_path expose :container_registry_token_expire_delay expose :repository_storage + expose :koding_enabled + expose :koding_url end class Release < Grape::Entity -- cgit v1.2.1 From 56259155d5a0ecfbbafb7319651495582504cfb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 29 Sep 2016 16:55:55 +0200 Subject: Small improvements thanks to Robert's feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/milestones.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/api') diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb index 7a0cb7c99f3..9b73f6826cf 100644 --- a/lib/api/milestones.rb +++ b/lib/api/milestones.rb @@ -108,8 +108,7 @@ module API finder_params = { project_id: user_project.id, - milestone_title: @milestone.title, - state: 'all' + milestone_title: @milestone.title } issues = IssuesFinder.new(current_user, finder_params).execute -- cgit v1.2.1 From dd088d14bb51e9c0baee013b9d3b21a12a768051 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Tue, 13 Sep 2016 20:55:00 +0200 Subject: GrapeDSL for Keys endpoint --- lib/api/keys.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'lib/api') diff --git a/lib/api/keys.rb b/lib/api/keys.rb index 2b723b79504..767f27ef334 100644 --- a/lib/api/keys.rb +++ b/lib/api/keys.rb @@ -4,10 +4,9 @@ module API before { authenticate! } resource :keys do - # Get single ssh key by id. Only available to admin users. - # - # Example Request: - # GET /keys/:id + desc 'Get single ssh key by id. Only available to admin users' do + success Entities::SSHKeyWithUser + end get ":id" do authenticated_as_admin! -- cgit v1.2.1 From d291ea97e0e944610c36469757a10b342ab0e05c Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Fri, 12 Aug 2016 09:27:52 +0200 Subject: GrapeDSL for Award Emoji endpoints --- lib/api/award_emoji.rb | 64 ++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 33 deletions(-) (limited to 'lib/api') diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb index 2461a783ea8..e9ccba3b465 100644 --- a/lib/api/award_emoji.rb +++ b/lib/api/award_emoji.rb @@ -8,16 +8,19 @@ module API awardable_string = awardable_type.pluralize awardable_id_string = "#{awardable_type}_id" + params do + requires :id, type: String, desc: 'The ID of a project' + requires :"#{awardable_id_string}", type: Integer, desc: "The ID of an Issue, Merge Request or Snippet" + end + [ ":id/#{awardable_string}/:#{awardable_id_string}/award_emoji", ":id/#{awardable_string}/:#{awardable_id_string}/notes/:note_id/award_emoji" ].each do |endpoint| - # Get a list of project +awardable+ award emoji - # - # Parameters: - # id (required) - The ID of a project - # awardable_id (required) - The ID of an issue or MR - # Example Request: - # GET /projects/:id/issues/:awardable_id/award_emoji + + desc 'Get a list of project +awardable+ award emoji' do + detail 'This feature was introduced in 8.9' + success Entities::AwardEmoji + end get endpoint do if can_read_awardable? awards = paginate(awardable.award_emoji) @@ -27,14 +30,13 @@ module API end end - # Get a specific award emoji - # - # Parameters: - # id (required) - The ID of a project - # awardable_id (required) - The ID of an issue or MR - # award_id (required) - The ID of the award - # Example Request: - # GET /projects/:id/issues/:awardable_id/award_emoji/:award_id + desc 'Get a specific award emoji' do + detail 'This feature was introduced in 8.9' + success Entities::AwardEmoji + end + params do + requires :award_id, type: Integer, desc: 'The ID of the award' + end get "#{endpoint}/:award_id" do if can_read_awardable? present awardable.award_emoji.find(params[:award_id]), with: Entities::AwardEmoji @@ -43,17 +45,14 @@ module API end end - # Award a new Emoji - # - # Parameters: - # id (required) - The ID of a project - # awardable_id (required) - The ID of an issue or mr - # name (required) - The name of a award_emoji (without colons) - # Example Request: - # POST /projects/:id/issues/:awardable_id/award_emoji + desc 'Award a new Emoji' do + detail 'This feature was introduced in 8.9' + success Entities::AwardEmoji + end + params do + requires :name, type: String, desc: 'The name of a award_emoji (without colons)' + end post endpoint do - required_attributes! [:name] - not_found!('Award Emoji') unless can_read_awardable? && can_award_awardable? award = awardable.create_award_emoji(params[:name], current_user) @@ -65,14 +64,13 @@ module API end end - # Delete a +awardables+ award emoji - # - # Parameters: - # id (required) - The ID of a project - # awardable_id (required) - The ID of an issue or MR - # award_emoji_id (required) - The ID of an award emoji - # Example Request: - # DELETE /projects/:id/issues/:issue_id/notes/:note_id/award_emoji/:award_id + desc 'Delete a +awardables+ award emoji' do + detail 'This feature was introduced in 8.9' + success Entities::AwardEmoji + end + params do + requires :award_id, type: Integer, desc: 'The ID of an award emoji' + end delete "#{endpoint}/:award_id" do award = awardable.award_emoji.find(params[:award_id]) -- cgit v1.2.1 From 3158f57dba6dcef3e586ae8fced7deb6fdbd6dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 28 Jul 2016 19:31:17 +0200 Subject: Improve Members::DestroyService MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/access_requests.rb | 4 +--- lib/api/members.rb | 10 +++++++++- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'lib/api') diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index 7b9de7c9598..75be24efd59 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -75,9 +75,7 @@ module API required_attributes! [:user_id] source = find_source(source_type, params[:id]) - access_requester = source.requesters.find_by!(user_id: params[:user_id]) - - ::Members::DestroyService.new(access_requester, current_user).execute + ::Members::DestroyService.new(source, current_user, declared(params)).execute(:requesters) end end end diff --git a/lib/api/members.rb b/lib/api/members.rb index a18ce769e29..03dbf4eabb8 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -65,6 +65,14 @@ module API # for both project and group members in 9.0! conflict!('Member already exists') if source_type == 'group' && member + access_requester = source.requesters.find_by(user_id: params[:user_id]) + if access_requester + # We delete a potential access requester before creating the new member. + # We pass current_user = access_requester so that the requester doesn't + # receive a "access denied" email. + ::Members::DestroyService.new(source, access_requester.user, params).execute(:requesters) + end + unless member member = source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at]) end @@ -134,7 +142,7 @@ module API if member.nil? { message: "Access revoked", id: params[:user_id].to_i } else - ::Members::DestroyService.new(member, current_user).execute + ::Members::DestroyService.new(source, current_user, params).execute present member.user, with: Entities::Member, member: member end -- cgit v1.2.1 From c8b1311934935c7ac7fd901558e19ac496fbad2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 9 Sep 2016 18:51:31 +0200 Subject: Fix a few things after the initial improvment to Members::DestroyService MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/access_requests.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/api') diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index 75be24efd59..d3db7740830 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -75,7 +75,8 @@ module API required_attributes! [:user_id] source = find_source(source_type, params[:id]) - ::Members::DestroyService.new(source, current_user, declared(params)).execute(:requesters) + ::Members::DestroyService.new(source, current_user, params). + execute(:requesters) end end end -- cgit v1.2.1 From 1592d57c18ba905d7bd1643ab6af56c902d709c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 3 Oct 2016 18:43:33 +0200 Subject: Remove useless code now that Member#add_user handles it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/members.rb | 8 -------- 1 file changed, 8 deletions(-) (limited to 'lib/api') diff --git a/lib/api/members.rb b/lib/api/members.rb index 03dbf4eabb8..34df55fe192 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -65,14 +65,6 @@ module API # for both project and group members in 9.0! conflict!('Member already exists') if source_type == 'group' && member - access_requester = source.requesters.find_by(user_id: params[:user_id]) - if access_requester - # We delete a potential access requester before creating the new member. - # We pass current_user = access_requester so that the requester doesn't - # receive a "access denied" email. - ::Members::DestroyService.new(source, access_requester.user, params).execute(:requesters) - end - unless member member = source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at]) end -- cgit v1.2.1 From d6e42e0ca1ab9962b7d43517c6f6beab5f1ade1c Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Tue, 13 Sep 2016 20:55:26 +0200 Subject: GrapeDSL for Namespace endpoint --- lib/api/namespaces.rb | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'lib/api') diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb index 50d3729449e..fe981d7b9fa 100644 --- a/lib/api/namespaces.rb +++ b/lib/api/namespaces.rb @@ -4,20 +4,18 @@ module API before { authenticate! } resource :namespaces do - # Get a namespaces list - # - # Example Request: - # GET /namespaces + desc 'Get a namespaces list' do + success Entities::Namespace + end + params do + optional :search, type: String, desc: "Search query for namespaces" + end get do - @namespaces = if current_user.admin - Namespace.all - else - current_user.namespaces - end - @namespaces = @namespaces.search(params[:search]) if params[:search].present? - @namespaces = paginate @namespaces + namespaces = current_user.admin ? Namespace.all : current_user.namespaces + + namespaces = namespaces.search(params[:search]) if params[:search].present? - present @namespaces, with: Entities::Namespace + present paginate(namespaces), with: Entities::Namespace end end end -- cgit v1.2.1 From e956a24dfd45baaafe93a520df61015bba40a4da Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Tue, 23 Aug 2016 15:23:56 -0400 Subject: api: add /projects/visible API endpoint FIxes #19361, #3119. --- lib/api/projects.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'lib/api') diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 680055c95eb..52afbb4eef3 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -32,6 +32,21 @@ module API end end + # Get a list of visible projects for authenticated user + # + # Example Request: + # GET /projects/visible + get '/visible' do + @projects = ProjectsFinder.new.execute(current_user) + @projects = filter_projects(@projects) + @projects = paginate @projects + if params[:simple] + present @projects, with: Entities::BasicProjectDetails, user: current_user + else + present @projects, with: Entities::ProjectWithAccess, user: current_user + end + end + # Get an owned projects list for authenticated user # # Example Request: -- cgit v1.2.1 From 35ced4dae480d61ddc4d73eb4695626ecc419e9c Mon Sep 17 00:00:00 2001 From: barthc Date: Thu, 1 Sep 2016 18:23:39 +0100 Subject: fix group links 404 --- lib/api/groups.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/api') diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 953fa474e88..bfb89475025 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -6,6 +6,8 @@ module API resource :groups do # Get a groups list # + # Parameters: + # skip_groups (optional) - Array of group ids to exclude from list # Example Request: # GET /groups get do @@ -16,6 +18,7 @@ module API end @groups = @groups.search(params[:search]) if params[:search].present? + @groups = @groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present? @groups = paginate @groups present @groups, with: Entities::Group end -- cgit v1.2.1 From 84b7dd763bd6a9a55b2a59039c57af588cc2519f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Fri, 29 Jul 2016 16:02:35 +0200 Subject: Use Grape DSL to document methods and their params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/access_requests.rb | 64 +++++++++++++------------------ lib/api/members.rb | 96 ++++++++++++++++++++-------------------------- 2 files changed, 67 insertions(+), 93 deletions(-) (limited to 'lib/api') diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb index d3db7740830..87915b19480 100644 --- a/lib/api/access_requests.rb +++ b/lib/api/access_requests.rb @@ -5,15 +5,14 @@ module API helpers ::API::Helpers::MembersHelpers %w[group project].each do |source_type| + params do + requires :id, type: String, desc: "The #{source_type} ID" + end resource source_type.pluralize do - # Get a list of group/project access requests viewable by the authenticated user. - # - # Parameters: - # id (required) - The group/project ID - # - # Example Request: - # GET /groups/:id/access_requests - # GET /projects/:id/access_requests + desc "Gets a list of access requests for a #{source_type}." do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::AccessRequester + end get ":id/access_requests" do source = find_source(source_type, params[:id]) @@ -23,14 +22,10 @@ module API present access_requesters.map(&:user), with: Entities::AccessRequester, source: source end - # Request access to the group/project - # - # Parameters: - # id (required) - The group/project ID - # - # Example Request: - # POST /groups/:id/access_requests - # POST /projects/:id/access_requests + desc "Requests access for the authenticated user to a #{source_type}." do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::AccessRequester + end post ":id/access_requests" do source = find_source(source_type, params[:id]) access_requester = source.request_access(current_user) @@ -42,37 +37,30 @@ module API end end - # Approve a group/project access request - # - # Parameters: - # id (required) - The group/project ID - # user_id (required) - The user ID of the access requester - # access_level (optional) - Access level - # - # Example Request: - # PUT /groups/:id/access_requests/:user_id/approve - # PUT /projects/:id/access_requests/:user_id/approve + desc 'Approves an access request for the given user.' do + detail 'This feature was introduced in GitLab 8.11.' + success Entities::Member + end + params do + requires :user_id, type: Integer, desc: 'The user ID of the access requester' + optional :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)' + end put ':id/access_requests/:user_id/approve' do - required_attributes! [:user_id] source = find_source(source_type, params[:id]) - member = ::Members::ApproveAccessRequestService.new(source, current_user, params).execute + member = ::Members::ApproveAccessRequestService.new(source, current_user, declared(params)).execute status :created present member.user, with: Entities::Member, member: member end - # Deny a group/project access request - # - # Parameters: - # id (required) - The group/project ID - # user_id (required) - The user ID of the access requester - # - # Example Request: - # DELETE /groups/:id/access_requests/:user_id - # DELETE /projects/:id/access_requests/:user_id + desc 'Denies an access request for the given user.' do + detail 'This feature was introduced in GitLab 8.11.' + end + params do + requires :user_id, type: Integer, desc: 'The user ID of the access requester' + end delete ":id/access_requests/:user_id" do - required_attributes! [:user_id] source = find_source(source_type, params[:id]) ::Members::DestroyService.new(source, current_user, params). diff --git a/lib/api/members.rb b/lib/api/members.rb index 34df55fe192..b80818f0eb6 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -5,16 +5,16 @@ module API helpers ::API::Helpers::MembersHelpers %w[group project].each do |source_type| + params do + requires :id, type: String, desc: "The #{source_type} ID" + end resource source_type.pluralize do - # Get a list of group/project members viewable by the authenticated user. - # - # Parameters: - # id (required) - The group/project ID - # query - Query string - # - # Example Request: - # GET /groups/:id/members - # GET /projects/:id/members + desc 'Gets a list of group or project members viewable by the authenticated user.' do + success Entities::Member + end + params do + optional :query, type: String, desc: 'A query string to search for members' + end get ":id/members" do source = find_source(source_type, params[:id]) @@ -25,15 +25,12 @@ module API present users, with: Entities::Member, source: source end - # Get a group/project member - # - # Parameters: - # id (required) - The group/project ID - # user_id (required) - The user ID of the member - # - # Example Request: - # GET /groups/:id/members/:user_id - # GET /projects/:id/members/:user_id + desc 'Gets a member of a group or project.' do + success Entities::Member + end + params do + requires :user_id, type: Integer, desc: 'The user ID of the member' + end get ":id/members/:user_id" do source = find_source(source_type, params[:id]) @@ -43,26 +40,25 @@ module API present member.user, with: Entities::Member, member: member end - # Add a new group/project member - # - # Parameters: - # id (required) - The group/project ID - # user_id (required) - The user ID of the new member - # access_level (required) - A valid access level - # expires_at (optional) - Date string in the format YEAR-MONTH-DAY - # - # Example Request: - # POST /groups/:id/members - # POST /projects/:id/members + desc 'Adds a member to a group or project.' do + success Entities::Member + end + params do + requires :user_id, type: Integer, desc: 'The user ID of the new member' + requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)' + optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' + end post ":id/members" do source = find_source(source_type, params[:id]) authorize_admin_source!(source_type, source) - required_attributes! [:user_id, :access_level] member = source.members.find_by(user_id: params[:user_id]) - # This is to ensure back-compatibility but 409 behavior should be used - # for both project and group members in 9.0! + # We need this explicit check because `source.add_user` doesn't + # currently return the member created so it would return 201 even if + # the member already existed... + # The `source_type == 'group'` check is to ensure back-compatibility + # but 409 behavior should be used for both project and group members in 9.0! conflict!('Member already exists') if source_type == 'group' && member unless member @@ -79,21 +75,17 @@ module API end end - # Update a group/project member - # - # Parameters: - # id (required) - The group/project ID - # user_id (required) - The user ID of the member - # access_level (required) - A valid access level - # expires_at (optional) - Date string in the format YEAR-MONTH-DAY - # - # Example Request: - # PUT /groups/:id/members/:user_id - # PUT /projects/:id/members/:user_id + desc 'Updates a member of a group or project.' do + success Entities::Member + end + params do + requires :user_id, type: Integer, desc: 'The user ID of the new member' + requires :access_level, type: Integer, desc: 'A valid access level' + optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' + end put ":id/members/:user_id" do source = find_source(source_type, params[:id]) authorize_admin_source!(source_type, source) - required_attributes! [:user_id, :access_level] member = source.members.find_by!(user_id: params[:user_id]) attrs = attributes_for_keys [:access_level, :expires_at] @@ -108,18 +100,12 @@ module API end end - # Remove a group/project member - # - # Parameters: - # id (required) - The group/project ID - # user_id (required) - The user ID of the member - # - # Example Request: - # DELETE /groups/:id/members/:user_id - # DELETE /projects/:id/members/:user_id + desc 'Removes a user from a group or project.' + params do + requires :user_id, type: Integer, desc: 'The user ID of the member' + end delete ":id/members/:user_id" do source = find_source(source_type, params[:id]) - required_attributes! [:user_id] # This is to ensure back-compatibility but find_by! should be used # in that casse in 9.0! @@ -134,7 +120,7 @@ module API if member.nil? { message: "Access revoked", id: params[:user_id].to_i } else - ::Members::DestroyService.new(source, current_user, params).execute + ::Members::DestroyService.new(source, current_user, declared(params)).execute present member.user, with: Entities::Member, member: member end -- cgit v1.2.1 From 0a1baaa80f017ebf068a0a83d7efda45fbe2ef6c Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Wed, 5 Oct 2016 12:13:58 +0100 Subject: Tidy up project list actions --- lib/api/projects.rb | 52 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) (limited to 'lib/api') diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 52afbb4eef3..c24e8e8bd9b 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -22,14 +22,12 @@ module API # Example Request: # GET /projects get do - @projects = current_user.authorized_projects - @projects = filter_projects(@projects) - @projects = paginate @projects - if params[:simple] - present @projects, with: Entities::BasicProjectDetails, user: current_user - else - present @projects, with: Entities::ProjectWithAccess, user: current_user - end + projects = current_user.authorized_projects + projects = filter_projects(projects) + projects = paginate projects + entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess + + present projects, with: entity, user: current_user end # Get a list of visible projects for authenticated user @@ -37,14 +35,12 @@ module API # Example Request: # GET /projects/visible get '/visible' do - @projects = ProjectsFinder.new.execute(current_user) - @projects = filter_projects(@projects) - @projects = paginate @projects - if params[:simple] - present @projects, with: Entities::BasicProjectDetails, user: current_user - else - present @projects, with: Entities::ProjectWithAccess, user: current_user - end + projects = ProjectsFinder.new.execute(current_user) + projects = filter_projects(projects) + projects = paginate projects + entity = params[:simple] ? Entities::BasicProjectDetails : Entities::ProjectWithAccess + + present projects, with: entity, user: current_user end # Get an owned projects list for authenticated user @@ -52,10 +48,10 @@ module API # Example Request: # GET /projects/owned get '/owned' do - @projects = current_user.owned_projects - @projects = filter_projects(@projects) - @projects = paginate @projects - present @projects, with: Entities::ProjectWithAccess, user: current_user + projects = current_user.owned_projects + projects = filter_projects(projects) + projects = paginate projects + present projects, with: Entities::ProjectWithAccess, user: current_user end # Gets starred project for the authenticated user @@ -63,10 +59,10 @@ module API # Example Request: # GET /projects/starred get '/starred' do - @projects = current_user.viewable_starred_projects - @projects = filter_projects(@projects) - @projects = paginate @projects - present @projects, with: Entities::Project, user: current_user + projects = current_user.viewable_starred_projects + projects = filter_projects(projects) + projects = paginate projects + present projects, with: Entities::Project, user: current_user end # Get all projects for admin user @@ -75,10 +71,10 @@ module API # GET /projects/all get '/all' do authenticated_as_admin! - @projects = Project.all - @projects = filter_projects(@projects) - @projects = paginate @projects - present @projects, with: Entities::ProjectWithAccess, user: current_user + projects = Project.all + projects = filter_projects(projects) + projects = paginate projects + present projects, with: Entities::ProjectWithAccess, user: current_user end # Get a single project -- cgit v1.2.1 From 41d70ea300efb73a05f4753ddd3e0c3730e96e0a Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Mon, 3 Oct 2016 00:12:59 -0300 Subject: Added Issue Board API support - Includes documentation and tests --- lib/api/api.rb | 1 + lib/api/boards.rb | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/api/entities.rb | 18 +++++++- 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 lib/api/boards.rb (limited to 'lib/api') diff --git a/lib/api/api.rb b/lib/api/api.rb index cb47ec8f33f..0bbf73a1b63 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -43,6 +43,7 @@ module API mount ::API::Groups mount ::API::Internal mount ::API::Issues + mount ::API::Boards mount ::API::Keys mount ::API::Labels mount ::API::LicenseTemplates diff --git a/lib/api/boards.rb b/lib/api/boards.rb new file mode 100644 index 00000000000..4d5d144a02e --- /dev/null +++ b/lib/api/boards.rb @@ -0,0 +1,115 @@ +module API + # Boards API + class Boards < Grape::API + before { authenticate! } + + resource :projects do + # Get the project board + get ':id/boards' do + authorize!(:read_board, user_project) + present [user_project.board], with: Entities::Board + end + + segment ':id/boards/:board_id' do + helpers do + def project_board + board = user_project.board + if params[:board_id].to_i == board.id + board + else + not_found!('Board') + end + end + + def board_lists + project_board.lists.destroyable + end + end + + # Get the lists of a project board + # Does not include `backlog` and `done` lists + get '/lists' do + authorize!(:read_board, user_project) + present board_lists, with: Entities::List + end + + # Get a list of a project board + get '/lists/:list_id' do + authorize!(:read_board, user_project) + present board_lists.find(params[:list_id]), with: Entities::List + end + + # Create a new board list + # + # Parameters: + # id (required) - The ID of a project + # label_id (required) - The ID of an existing label + # Example Request: + # POST /projects/:id/boards/:board_id/lists + post '/lists' do + required_attributes! [:label_id] + + unless user_project.labels.exists?(params[:label_id]) + render_api_error!({ error: "Label not found!" }, 400) + end + + authorize!(:admin_list, user_project) + + list = ::Boards::Lists::CreateService.new(user_project, current_user, + { label_id: params[:label_id] }).execute + + if list.valid? + present list, with: Entities::List + else + render_validation_error!(list) + end + end + + # Moves a board list to a new position + # + # Parameters: + # id (required) - The ID of a project + # board_id (required) - The ID of a board + # position (required) - The position of the list + # Example Request: + # PUT /projects/:id/boards/:board_id/lists/:list_id + put '/lists/:list_id' do + list = project_board.lists.movable.find(params[:list_id]) + + authorize!(:admin_list, user_project) + + moved = ::Boards::Lists::MoveService.new(user_project, current_user, + { position: params[:position].to_i }).execute(list) + + if moved + present list, with: Entities::List + else + render_api_error!({ error: "List could not be moved!" }, 400) + end + end + + # Delete a board list + # + # Parameters: + # id (required) - The ID of a project + # board_id (required) - The ID of a board + # list_id (required) - The ID of a board list + # Example Request: + # DELETE /projects/:id/boards/:board_id/lists/:list_id + delete "/lists/:list_id" do + list = board_lists.find_by(id: params[:list_id]) + + authorize!(:admin_list, user_project) + + if list + destroyed_list = ::Boards::Lists::DestroyService.new( + user_project, current_user).execute(list) + present destroyed_list, with: Entities::List + else + not_found!('List') + end + end + end + end + end +end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 04437322ec1..feaa0c213bf 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -432,8 +432,11 @@ module API end end - class Label < Grape::Entity + class LabelBasic < Grape::Entity expose :name, :color, :description + end + + class Label < LabelBasic expose :open_issues_count, :closed_issues_count, :open_merge_requests_count expose :subscribed do |label, options| @@ -441,6 +444,19 @@ module API end end + class List < Grape::Entity + expose :id + expose :label, using: Entities::LabelBasic + expose :position + end + + class Board < Grape::Entity + expose :id + expose :lists, using: Entities::List do |board| + board.lists.destroyable + end + end + class Compare < Grape::Entity expose :commit, using: Entities::RepoCommit do |compare, options| Commit.decorate(compare.commits, nil).last -- cgit v1.2.1 From a1ee8cf5ad07256807f15590bdb5f56152d55553 Mon Sep 17 00:00:00 2001 From: Marc Siegfriedt Date: Mon, 29 Aug 2016 23:58:32 +0000 Subject: multi-file commit add docs and tests - add additional validation allow move without content updated response --- lib/api/commits.rb | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'lib/api') diff --git a/lib/api/commits.rb b/lib/api/commits.rb index b4eaf1813d4..14ddc8c9a62 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -29,6 +29,42 @@ module API present commits, with: Entities::RepoCommit end + desc 'Commit multiple file changes as one commit' do + detail 'This feature was introduced in GitLab 8.13' + end + + params do + requires :id, type: Integer, desc: 'The project ID' + requires :branch_name, type: String, desc: 'The name of branch' + requires :commit_message, type: String, desc: 'Commit message' + requires :actions, type: Array, desc: 'Actions to perform in commit' + optional :author_email, type: String, desc: 'Author email for commit' + optional :author_name, type: String, desc: 'Author name for commit' + end + + post ":id/repository/commits" do + authorize! :push_code, user_project + + attrs = declared(params) + attrs[:source_branch] = attrs[:branch_name] + attrs[:target_branch] = attrs[:branch_name] + attrs[:actions].map! do |action| + action[:action] = action[:action].to_sym + action[:file_path].slice!(0) if action[:file_path] && action[:file_path].start_with?('/') + action[:previous_path].slice!(0) if action[:previous_path] && action[:previous_path].start_with?('/') + action + end + + result = ::Files::MultiService.new(user_project, current_user, attrs).execute + + if result[:status] == :success + commit_detail = user_project.repository.commits(result[:result], limit: 1).first + present commit_detail, with: Entities::RepoCommitDetail + else + render_api_error!(result[:message], 400) + end + end + # Get a specific commit of a project # # Parameters: -- cgit v1.2.1 From 86c0c0869dd4b9de301822a7d598bc4528604e5a Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Fri, 30 Sep 2016 12:21:05 +0100 Subject: Switch from request to env in ::API::Helpers Per https://gitlab.com/gitlab-org/gitlab-ce/issues/22820, this helper is mixed in to classes that lack a `request` method. They do include `env`, so use it instead. --- lib/api/helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/api') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 8b8c4eb4d46..281a8f13531 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -25,7 +25,7 @@ module API # Until CSRF protection is added to the API, disallow this method for # state-changing endpoints def find_user_from_warden - warden.try(:authenticate) if request.get? || request.head? + warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD']) end def find_user_by_private_token -- cgit v1.2.1 From fe46e4eb35dd6728a8a5ebcee9cc05a4613effbf Mon Sep 17 00:00:00 2001 From: Justin DiPierro Date: Thu, 29 Sep 2016 12:46:54 -0400 Subject: Load Github::Shell's secret token from file on initialization instead of every request. --- lib/api/helpers.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/api') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 8b8c4eb4d46..e3b947adcc3 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -433,7 +433,7 @@ module API end def secret_token - File.read(Gitlab.config.gitlab_shell.secret_file).chomp + Gitlab::Shell.secret_token end def send_git_blob(repository, blob) -- cgit v1.2.1 From 33ce1976451ee8fc0275e0ae012755053d50ec34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Mon, 10 Oct 2016 13:35:26 +0200 Subject: API: New /users/:id/events endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/users.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'lib/api') diff --git a/lib/api/users.rb b/lib/api/users.rb index 18c4cad09ae..e868f628404 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -321,6 +321,26 @@ module API user.activate end end + + desc 'Get contribution events of a specified user' do + detail 'This feature was introduced in GitLab 8.13.' + success Entities::Event + end + params do + requires :id, type: String, desc: 'The user ID' + end + get ':id/events' do + user = User.find_by(id: declared(params).id) + not_found!('User') unless user + + events = user.recent_events. + merge(ProjectsFinder.new.execute(current_user)). + references(:project). + with_associations. + page(params[:page]) + + present paginate(events), with: Entities::Event + end end resource :user do -- cgit v1.2.1 From d6cfc0042ed2ce9a33f31a6c44661c136e861b98 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 7 Oct 2016 16:39:57 +0300 Subject: Catch any undefined API routing and return 400 Bad Request Signed-off-by: Dmitriy Zaporozhets --- lib/api/api.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/api') diff --git a/lib/api/api.rb b/lib/api/api.rb index 0bbf73a1b63..95a64f14ac7 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -73,5 +73,9 @@ module API mount ::API::Triggers mount ::API::Users mount ::API::Variables + + route :any, '*path' do + error!('400 Bad Request', 400) + end end end -- cgit v1.2.1 From 137ebcfb3cb013174f2885776a47264cffd193a6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 7 Oct 2016 20:18:02 +0300 Subject: Replace undefined Grape routing code from 400 to 404 Signed-off-by: Dmitriy Zaporozhets --- lib/api/api.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/api') diff --git a/lib/api/api.rb b/lib/api/api.rb index 95a64f14ac7..99722a0a65c 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -75,7 +75,7 @@ module API mount ::API::Variables route :any, '*path' do - error!('400 Bad Request', 400) + error!('404 Not Found', 404) end end end -- cgit v1.2.1 From e171c1d80debd88d1aa3a3c74d1937b68a7f0f18 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 6 Oct 2016 17:36:46 -0300 Subject: Update Issue Board API to handle with has_many association --- lib/api/boards.rb | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'lib/api') diff --git a/lib/api/boards.rb b/lib/api/boards.rb index 4d5d144a02e..9b71d335128 100644 --- a/lib/api/boards.rb +++ b/lib/api/boards.rb @@ -7,13 +7,14 @@ module API # Get the project board get ':id/boards' do authorize!(:read_board, user_project) - present [user_project.board], with: Entities::Board + present user_project.boards, with: Entities::Board end segment ':id/boards/:board_id' do helpers do def project_board - board = user_project.board + board = user_project.boards.first + if params[:board_id].to_i == board.id board else @@ -55,8 +56,10 @@ module API authorize!(:admin_list, user_project) - list = ::Boards::Lists::CreateService.new(user_project, current_user, - { label_id: params[:label_id] }).execute + service = ::Boards::Lists::CreateService.new(user_project, current_user, + { label_id: params[:label_id] }) + + list = service.execute(project_board) if list.valid? present list, with: Entities::List @@ -78,10 +81,10 @@ module API authorize!(:admin_list, user_project) - moved = ::Boards::Lists::MoveService.new(user_project, current_user, - { position: params[:position].to_i }).execute(list) + service = ::Boards::Lists::MoveService.new(user_project, current_user, + { position: params[:position].to_i }) - if moved + if service.execute(list) present list, with: Entities::List else render_api_error!({ error: "List could not be moved!" }, 400) @@ -97,16 +100,16 @@ module API # Example Request: # DELETE /projects/:id/boards/:board_id/lists/:list_id delete "/lists/:list_id" do - list = board_lists.find_by(id: params[:list_id]) - authorize!(:admin_list, user_project) - if list - destroyed_list = ::Boards::Lists::DestroyService.new( - user_project, current_user).execute(list) - present destroyed_list, with: Entities::List + list = board_lists.find(params[:list_id]) + + service = ::Boards::Lists::DestroyService.new(user_project, current_user) + + if service.execute(list) + present list, with: Entities::List else - not_found!('List') + render_api_error!({ error: 'List could not be deleted!' }, 400) end end end -- cgit v1.2.1 From 670b2eb5c05a721f810a5b248612cadde0eaf2ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Tue, 11 Oct 2016 10:20:35 +0000 Subject: Merge branch 'api-fix-project-group-sharing' into 'security' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit API: Share projects only with groups current_user can access Aims to address the issues here: https://gitlab.com/gitlab-org/gitlab-ce/issues/23004 * Projects can be shared with non-existent groups * Projects can be shared with groups that the current user does not have access to read Concerns: The new implementation of the API endpoint allows projects to be shared with a larger range of groups than can be done via the web UI. The form for sharing a project with a group uses the following API endpoint to index the available groups: https://gitlab.com/gitlab-org/gitlab-ce/blob/494269fc92f61098ee6bd635a0426129ce2c5456/lib/api/groups.rb#L17. The groups indexed in the web form will only be those groups that the user is currently a member of. The new implementation allows projects to be shared with any group that the authenticated user has access to view. This widens the range of groups to those that are public and internal. See merge request !2005 Signed-off-by: Rémy Coutable --- lib/api/projects.rb | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/api') diff --git a/lib/api/projects.rb b/lib/api/projects.rb index c24e8e8bd9b..da16e24d7ea 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -416,6 +416,12 @@ module API required_attributes! [:group_id, :group_access] attrs = attributes_for_keys [:group_id, :group_access, :expires_at] + group = Group.find_by_id(attrs[:group_id]) + + unless group && can?(current_user, :read_group, group) + not_found!('Group') + end + unless user_project.allowed_to_share_with_group? return render_api_error!("The project sharing with group is disabled", 400) end -- cgit v1.2.1 From b9b13ea8016077e186374e4ee45dfc8a59ea5a78 Mon Sep 17 00:00:00 2001 From: Thomas Balthazar Date: Mon, 8 Aug 2016 13:42:24 +0200 Subject: Create a new /templates API namespace The /licenses, /gitignores and /gitlab_ci_ymls endpoints are now also available under a new /templates namespace. Old endpoints will be deprecated when GitLab 9.0.0 is released. --- lib/api/api.rb | 1 - lib/api/license_templates.rb | 58 -------------------- lib/api/templates.rb | 124 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 100 insertions(+), 83 deletions(-) delete mode 100644 lib/api/license_templates.rb (limited to 'lib/api') diff --git a/lib/api/api.rb b/lib/api/api.rb index 99722a0a65c..c64d444842d 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -46,7 +46,6 @@ module API mount ::API::Boards mount ::API::Keys mount ::API::Labels - mount ::API::LicenseTemplates mount ::API::Lint mount ::API::Members mount ::API::MergeRequests diff --git a/lib/api/license_templates.rb b/lib/api/license_templates.rb deleted file mode 100644 index d0552299ed0..00000000000 --- a/lib/api/license_templates.rb +++ /dev/null @@ -1,58 +0,0 @@ -module API - # License Templates API - class LicenseTemplates < Grape::API - PROJECT_TEMPLATE_REGEX = - /[\<\{\[] - (project|description| - one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here - [\>\}\]]/xi.freeze - YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze - FULLNAME_TEMPLATE_REGEX = - /[\<\{\[] - (fullname|name\sof\s(author|copyright\sowner)) - [\>\}\]]/xi.freeze - - # Get the list of the available license templates - # - # Parameters: - # popular - Filter licenses to only the popular ones - # - # Example Request: - # GET /licenses - # GET /licenses?popular=1 - get 'licenses' do - options = { - featured: params[:popular].present? ? true : nil - } - present Licensee::License.all(options), with: Entities::RepoLicense - end - - # Get text for specific license - # - # Parameters: - # key (required) - The key of a license - # project - Copyrighted project name - # fullname - Full name of copyright holder - # - # Example Request: - # GET /licenses/mit - # - get 'licenses/:key', requirements: { key: /[\w\.-]+/ } do - required_attributes! [:key] - - not_found!('License') unless Licensee::License.find(params[:key]) - - # We create a fresh Licensee::License object since we'll modify its - # content in place below. - license = Licensee::License.new(params[:key]) - - license.content.gsub!(YEAR_TEMPLATE_REGEX, Time.now.year.to_s) - license.content.gsub!(PROJECT_TEMPLATE_REGEX, params[:project]) if params[:project].present? - - fullname = params[:fullname].presence || current_user.try(:name) - license.content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname - - present license, with: Entities::RepoLicense - end - end -end diff --git a/lib/api/templates.rb b/lib/api/templates.rb index b9e718147e1..8a53d9c0095 100644 --- a/lib/api/templates.rb +++ b/lib/api/templates.rb @@ -1,39 +1,115 @@ module API class Templates < Grape::API GLOBAL_TEMPLATE_TYPES = { - gitignores: Gitlab::Template::GitignoreTemplate, - gitlab_ci_ymls: Gitlab::Template::GitlabCiYmlTemplate + gitignores: { + klass: Gitlab::Template::GitignoreTemplate, + gitlab_version: 8.8 + }, + gitlab_ci_ymls: { + klass: Gitlab::Template::GitlabCiYmlTemplate, + gitlab_version: 8.9 + } }.freeze + PROJECT_TEMPLATE_REGEX = + /[\<\{\[] + (project|description| + one\sline\s.+\swhat\sit\sdoes\.) # matching the start and end is enough here + [\>\}\]]/xi.freeze + YEAR_TEMPLATE_REGEX = /[<{\[](year|yyyy)[>}\]]/i.freeze + FULLNAME_TEMPLATE_REGEX = + /[\<\{\[] + (fullname|name\sof\s(author|copyright\sowner)) + [\>\}\]]/xi.freeze + DEPRECATION_MESSAGE = ' This endpoint is deprecated and will be removed in GitLab 9.0.'.freeze helpers do + def parsed_license_template + # We create a fresh Licensee::License object since we'll modify its + # content in place below. + template = Licensee::License.new(params[:name]) + + template.content.gsub!(YEAR_TEMPLATE_REGEX, Time.now.year.to_s) + template.content.gsub!(PROJECT_TEMPLATE_REGEX, params[:project]) if params[:project].present? + + fullname = params[:fullname].presence || current_user.try(:name) + template.content.gsub!(FULLNAME_TEMPLATE_REGEX, fullname) if fullname + template + end + def render_response(template_type, template) not_found!(template_type.to_s.singularize) unless template present template, with: Entities::Template end end - GLOBAL_TEMPLATE_TYPES.each do |template_type, klass| - # Get the list of the available template - # - # Example Request: - # GET /gitignores - # GET /gitlab_ci_ymls - get template_type.to_s do - present klass.all, with: Entities::TemplatesList - end - - # Get the text for a specific template present in local filesystem - # - # Parameters: - # name (required) - The name of a template - # - # Example Request: - # GET /gitignores/Elixir - # GET /gitlab_ci_ymls/Ruby - get "#{template_type}/:name" do - required_attributes! [:name] - new_template = klass.find(params[:name]) - render_response(template_type, new_template) + { "licenses" => :deprecated, "templates/licenses" => :ok }.each do |route, status| + desc 'Get the list of the available license template' do + detailed_desc = 'This feature was introduced in GitLab 8.7.' + detailed_desc << DEPRECATION_MESSAGE unless status == :ok + detail detailed_desc + success Entities::RepoLicense + end + params do + optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses' + end + get route do + options = { + featured: declared(params).popular.present? ? true : nil + } + present Licensee::License.all(options), with: Entities::RepoLicense + end + end + + { "licenses/:name" => :deprecated, "templates/licenses/:name" => :ok }.each do |route, status| + desc 'Get the text for a specific license' do + detailed_desc = 'This feature was introduced in GitLab 8.7.' + detailed_desc << DEPRECATION_MESSAGE unless status == :ok + detail detailed_desc + success Entities::RepoLicense + end + params do + requires :name, type: String, desc: 'The name of the template' + end + get route, requirements: { name: /[\w\.-]+/ } do + not_found!('License') unless Licensee::License.find(declared(params).name) + + template = parsed_license_template + + present template, with: Entities::RepoLicense + end + end + + GLOBAL_TEMPLATE_TYPES.each do |template_type, properties| + klass = properties[:klass] + gitlab_version = properties[:gitlab_version] + + { template_type => :deprecated, "templates/#{template_type}" => :ok }.each do |route, status| + desc 'Get the list of the available template' do + detailed_desc = "This feature was introduced in GitLab #{gitlab_version}." + detailed_desc << DEPRECATION_MESSAGE unless status == :ok + detail detailed_desc + success Entities::TemplatesList + end + get route do + present klass.all, with: Entities::TemplatesList + end + end + + { "#{template_type}/:name" => :deprecated, "templates/#{template_type}/:name" => :ok }.each do |route, status| + desc 'Get the text for a specific template present in local filesystem' do + detailed_desc = "This feature was introduced in GitLab #{gitlab_version}." + detailed_desc << DEPRECATION_MESSAGE unless status == :ok + detail detailed_desc + success Entities::Template + end + params do + requires :name, type: String, desc: 'The name of the template' + end + get route do + new_template = klass.find(declared(params).name) + + render_response(template_type, new_template) + end end end end -- cgit v1.2.1 From b998479c81512b7c9a2cff28e7aabff3c4be0424 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Wed, 12 Oct 2016 13:32:48 +0200 Subject: API: Version information --- lib/api/api.rb | 1 + lib/api/version.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 lib/api/version.rb (limited to 'lib/api') diff --git a/lib/api/api.rb b/lib/api/api.rb index 99722a0a65c..eb4e1fc504f 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -73,6 +73,7 @@ module API mount ::API::Triggers mount ::API::Users mount ::API::Variables + mount ::API::Version route :any, '*path' do error!('404 Not Found', 404) diff --git a/lib/api/version.rb b/lib/api/version.rb new file mode 100644 index 00000000000..9ba576bd828 --- /dev/null +++ b/lib/api/version.rb @@ -0,0 +1,12 @@ +module API + class Version < Grape::API + before { authenticate! } + + desc 'Get the version information of the GitLab instance.' do + detail 'This feature was introduced in GitLab 8.13.' + end + get '/version' do + { version: Gitlab::VERSION, revision: Gitlab::REVISION } + end + end +end -- cgit v1.2.1 From 3d6f18cec57fc6c7776bd05558e0d03dd3b141e1 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Wed, 12 Oct 2016 20:38:33 +0200 Subject: GrapeDSL for variables --- lib/api/variables.rb | 89 +++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 47 deletions(-) (limited to 'lib/api') diff --git a/lib/api/variables.rb b/lib/api/variables.rb index f6495071a11..b9fb3c21dbb 100644 --- a/lib/api/variables.rb +++ b/lib/api/variables.rb @@ -4,27 +4,29 @@ module API before { authenticate! } before { authorize! :admin_build, user_project } + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do - # Get project variables - # - # Parameters: - # id (required) - The ID of a project - # page (optional) - The page number for pagination - # per_page (optional) - The value of items per page to show - # Example Request: - # GET /projects/:id/variables + desc 'Get project variables' do + success Entities::Variable + end + params do + optional :page, type: Integer, desc: 'The page number for pagination' + optional :per_page, type: Integer, desc: 'The value of items per page to show' + end get ':id/variables' do variables = user_project.variables present paginate(variables), with: Entities::Variable end - # Get specific variable of a project - # - # Parameters: - # id (required) - The ID of a project - # key (required) - The `key` of variable - # Example Request: - # GET /projects/:id/variables/:key + desc 'Get a specific variable from a project' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + end get ':id/variables/:key' do key = params[:key] variable = user_project.variables.find_by(key: key.to_s) @@ -34,18 +36,15 @@ module API present variable, with: Entities::Variable end - # Create a new variable in project - # - # Parameters: - # id (required) - The ID of a project - # key (required) - The key of variable - # value (required) - The value of variable - # Example Request: - # POST /projects/:id/variables + desc 'Create a new variable in a project' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + requires :value, type: String, desc: 'The value of the variable' + end post ':id/variables' do - required_attributes! [:key, :value] - - variable = user_project.variables.create(key: params[:key], value: params[:value]) + variable = user_project.variables.create(declared(params, include_parent_namespaces: false).to_h) if variable.valid? present variable, with: Entities::Variable @@ -54,41 +53,37 @@ module API end end - # Update existing variable of a project - # - # Parameters: - # id (required) - The ID of a project - # key (optional) - The `key` of variable - # value (optional) - New value for `value` field of variable - # Example Request: - # PUT /projects/:id/variables/:key + desc 'Update an existing variable from a project' do + success Entities::Variable + end + params do + optional :key, type: String, desc: 'The key of the variable' + optional :value, type: String, desc: 'The value of the variable' + end put ':id/variables/:key' do - variable = user_project.variables.find_by(key: params[:key].to_s) + variable = user_project.variables.find_by(key: params[:key]) return not_found!('Variable') unless variable - attrs = attributes_for_keys [:value] - if variable.update(attrs) + if variable.update(value: params[:value]) present variable, with: Entities::Variable else render_validation_error!(variable) end end - # Delete existing variable of a project - # - # Parameters: - # id (required) - The ID of a project - # key (required) - The ID of a variable - # Example Request: - # DELETE /projects/:id/variables/:key + desc 'Delete an existing variable from a project' do + success Entities::Variable + end + params do + requires :key, type: String, desc: 'The key of the variable' + end delete ':id/variables/:key' do - variable = user_project.variables.find_by(key: params[:key].to_s) + variable = user_project.variables.find_by(key: params[:key]) return not_found!('Variable') unless variable - variable.destroy - present variable, with: Entities::Variable + present variable.destroy, with: Entities::Variable end end end -- cgit v1.2.1 From 2273b5d6d6f64a14d41bc8d25287d615502d2622 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 13 Oct 2016 12:52:51 +0200 Subject: Sort API mounts --- lib/api/api.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib/api') diff --git a/lib/api/api.rb b/lib/api/api.rb index 99722a0a65c..a6f32a9fc04 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -31,11 +31,12 @@ module API # Keep in alphabetical order mount ::API::AccessRequests mount ::API::AwardEmoji + mount ::API::Boards mount ::API::Branches mount ::API::BroadcastMessages mount ::API::Builds - mount ::API::CommitStatuses mount ::API::Commits + mount ::API::CommitStatuses mount ::API::DeployKeys mount ::API::Deployments mount ::API::Environments @@ -43,22 +44,21 @@ module API mount ::API::Groups mount ::API::Internal mount ::API::Issues - mount ::API::Boards mount ::API::Keys mount ::API::Labels mount ::API::LicenseTemplates mount ::API::Lint mount ::API::Members - mount ::API::MergeRequests mount ::API::MergeRequestDiffs + mount ::API::MergeRequests mount ::API::Milestones mount ::API::Namespaces mount ::API::Notes mount ::API::NotificationSettings mount ::API::Pipelines mount ::API::ProjectHooks - mount ::API::ProjectSnippets mount ::API::Projects + mount ::API::ProjectSnippets mount ::API::Repositories mount ::API::Runners mount ::API::Services -- cgit v1.2.1 From e836b904b77e857ad2e1a0bc65129380d5f0e6dc Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 13 Oct 2016 17:52:21 +0200 Subject: Grapify system hooks API --- lib/api/system_hooks.rb | 60 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 31 deletions(-) (limited to 'lib/api') diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb index 22b8f90dc5c..2e76b91051f 100644 --- a/lib/api/system_hooks.rb +++ b/lib/api/system_hooks.rb @@ -7,38 +7,36 @@ module API end resource :hooks do - # Get the list of system hooks - # - # Example Request: - # GET /hooks + desc 'Get the list of system hooks' do + success Entities::Hook + end get do - @hooks = SystemHook.all - present @hooks, with: Entities::Hook + hooks = SystemHook.all + present hooks, with: Entities::Hook end - # Create new system hook - # - # Parameters: - # url (required) - url for system hook - # Example Request - # POST /hooks + desc 'Create a new system hook' do + success Entities::Hook + end + params do + requires :url, type: String, desc: 'The URL for the system hook' + end post do - attrs = attributes_for_keys [:url] - required_attributes! [:url] - @hook = SystemHook.new attrs - if @hook.save - present @hook, with: Entities::Hook + hook = SystemHook.new declared(params).to_h + + if hook.save + present hook, with: Entities::Hook else not_found! end end - # Test a hook - # - # Example Request - # GET /hooks/:id + desc 'Test a hook' + params do + requires :id, type: Integer, desc: 'The ID of the system hook' + end get ":id" do - @hook = SystemHook.find(params[:id]) + hook = SystemHook.find(params[:id]) data = { event_name: "project_create", name: "Ruby", @@ -47,20 +45,20 @@ module API owner_name: "Someone", owner_email: "example@gitlabhq.com" } - @hook.execute(data, 'system_hooks') + hook.execute(data, 'system_hooks') data end - # Delete a hook. This is an idempotent function. - # - # Parameters: - # id (required) - ID of the hook - # Example Request: - # DELETE /hooks/:id + desc 'Delete a hook' do + success Entities::Hook + end + params do + requires :id, type: Integer, desc: 'The ID of the system hook' + end delete ":id" do begin - @hook = SystemHook.find(params[:id]) - @hook.destroy + hook = SystemHook.find(params[:id]) + present hook.destroy, with: Entities::Hook rescue # SystemHook raises an Error if no hook with id found end -- cgit v1.2.1 From b927473c45e42e99adbbe69b71653f1b2981df01 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 14 Oct 2016 09:16:55 +0200 Subject: Grapify todos API --- lib/api/todos.rb | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) (limited to 'lib/api') diff --git a/lib/api/todos.rb b/lib/api/todos.rb index 19df13d8aac..832b04a3bb1 100644 --- a/lib/api/todos.rb +++ b/lib/api/todos.rb @@ -8,18 +8,19 @@ module API 'issues' => ->(id) { find_project_issue(id) } } + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do ISSUABLE_TYPES.each do |type, finder| type_id_str = "#{type.singularize}_id".to_sym - # Create a todo on an issuable - # - # Parameters: - # id (required) - The ID of a project - # issuable_id (required) - The ID of an issuable - # Example Request: - # POST /projects/:id/issues/:issuable_id/todo - # POST /projects/:id/merge_requests/:issuable_id/todo + desc 'Create a todo on an issuable' do + success Entities::Todo + end + params do + requires type_id_str, type: Integer, desc: 'The ID of an issuable' + end post ":id/#{type}/:#{type_id_str}/todo" do issuable = instance_exec(params[type_id_str], &finder) todo = TodoService.new.mark_todo(issuable, current_user).first @@ -40,25 +41,21 @@ module API end end - # Get a todo list - # - # Example Request: - # GET /todos - # + desc 'Get a todo list' do + success Entities::Todo + end get do todos = find_todos present paginate(todos), with: Entities::Todo, current_user: current_user end - # Mark a todo as done - # - # Parameters: - # id: (required) - The ID of the todo being marked as done - # - # Example Request: - # DELETE /todos/:id - # + desc 'Mark a todo as done' do + success Entities::Todo + end + params do + requires :id, type: Integer, desc: 'The ID of the todo being marked as done' + end delete ':id' do todo = current_user.todos.find(params[:id]) TodoService.new.mark_todos_as_done([todo], current_user) @@ -66,11 +63,7 @@ module API present todo.reload, with: Entities::Todo, current_user: current_user end - # Mark all todos as done - # - # Example Request: - # DELETE /todos - # + desc 'Mark all todos as done' delete do todos = find_todos TodoService.new.mark_todos_as_done(todos, current_user) -- cgit v1.2.1 From 4c46c9a9738cfa90dd450e70ccf85e470be1d789 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 14 Oct 2016 09:38:20 +0200 Subject: Grapify boards API --- lib/api/boards.rb | 76 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 31 deletions(-) (limited to 'lib/api') diff --git a/lib/api/boards.rb b/lib/api/boards.rb index 9b71d335128..b14dd4f6e83 100644 --- a/lib/api/boards.rb +++ b/lib/api/boards.rb @@ -3,19 +3,28 @@ module API class Boards < Grape::API before { authenticate! } + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do - # Get the project board + desc 'Get all project boards' do + detail 'This feature was introduced in 8.13' + success Entities::Board + end get ':id/boards' do authorize!(:read_board, user_project) present user_project.boards, with: 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].to_i == board.id + if params[:board_id] == board.id board else not_found!('Board') @@ -27,29 +36,35 @@ module API end end - # Get the lists of a project board - # Does not include `backlog` and `done` lists + desc 'Get the lists of a project board' do + detail 'Does not include `backlog` and `done` lists. This feature was introduced in 8.13' + success Entities::List + end get '/lists' do authorize!(:read_board, user_project) present board_lists, with: Entities::List end - # Get a list of a project board + desc 'Get a list of a project board' do + detail 'This feature was introduced in 8.13' + success Entities::List + end + params do + requires :list_id, type: Integer, desc: 'The ID of a list' + end get '/lists/:list_id' do authorize!(:read_board, user_project) present board_lists.find(params[:list_id]), with: Entities::List end - # Create a new board list - # - # Parameters: - # id (required) - The ID of a project - # label_id (required) - The ID of an existing label - # Example Request: - # POST /projects/:id/boards/:board_id/lists + desc 'Create a new board list' do + detail 'This feature was introduced in 8.13' + success Entities::List + end + params do + requires :label_id, type: Integer, desc: 'The ID of an existing label' + end post '/lists' do - required_attributes! [:label_id] - unless user_project.labels.exists?(params[:label_id]) render_api_error!({ error: "Label not found!" }, 400) end @@ -68,21 +83,21 @@ module API end end - # Moves a board list to a new position - # - # Parameters: - # id (required) - The ID of a project - # board_id (required) - The ID of a board - # position (required) - The position of the list - # Example Request: - # PUT /projects/:id/boards/:board_id/lists/:list_id + desc 'Moves a board list to a new position' do + detail 'This feature was introduced in 8.13' + success Entities::List + end + params do + requires :list_id, type: Integer, desc: 'The ID of a list' + requires :position, type: Integer, desc: 'The position of the list' + end put '/lists/:list_id' do list = project_board.lists.movable.find(params[:list_id]) authorize!(:admin_list, user_project) service = ::Boards::Lists::MoveService.new(user_project, current_user, - { position: params[:position].to_i }) + { position: params[:position] }) if service.execute(list) present list, with: Entities::List @@ -91,14 +106,13 @@ module API end end - # Delete a board list - # - # Parameters: - # id (required) - The ID of a project - # board_id (required) - The ID of a board - # list_id (required) - The ID of a board list - # Example Request: - # DELETE /projects/:id/boards/:board_id/lists/:list_id + desc 'Delete a board list' do + detail 'This feature was introduced in 8.13' + success Entities::List + end + params do + requires :list_id, type: Integer, desc: 'The ID of a board list' + end delete "/lists/:list_id" do authorize!(:admin_list, user_project) -- cgit v1.2.1 From 04593581037bca7a7c3b00c719404e610c158cc1 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Thu, 13 Oct 2016 19:32:10 +0200 Subject: API: Fix Sytem hooks delete behavior --- lib/api/system_hooks.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'lib/api') diff --git a/lib/api/system_hooks.rb b/lib/api/system_hooks.rb index 2e76b91051f..794e34874f4 100644 --- a/lib/api/system_hooks.rb +++ b/lib/api/system_hooks.rb @@ -56,12 +56,10 @@ module API requires :id, type: Integer, desc: 'The ID of the system hook' end delete ":id" do - begin - hook = SystemHook.find(params[:id]) - present hook.destroy, with: Entities::Hook - rescue - # SystemHook raises an Error if no hook with id found - end + hook = SystemHook.find_by(id: params[:id]) + not_found!('System hook') unless hook + + present hook.destroy, with: Entities::Hook end end end -- cgit v1.2.1 From 4de883bc81d605d8cd0d138be5d0e2ff2a263d77 Mon Sep 17 00:00:00 2001 From: "Z.J. van de Weg" Date: Sat, 15 Oct 2016 12:09:02 +0200 Subject: Use GrapeDSL for commits --- lib/api/commits.rb | 129 ++++++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 60 deletions(-) (limited to 'lib/api') diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 14ddc8c9a62..617a240318a 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -6,33 +6,40 @@ module 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 - # Get a project repository commits - # - # Parameters: - # id (required) - The ID of a project - # ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used - # since (optional) - Only commits after or in this date will be returned - # until (optional) - Only commits before or in this date will be returned - # Example Request: - # GET /projects/:id/repository/commits + desc 'Get a project repository commits' do + success Entities::RepoCommit + 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 :since, type: String, desc: 'Only commits after or in this date will be returned' + optional :until, type: String, desc: 'Only commits before or in this date will be returned' + optional :page, type: Integer, default: 0, desc: 'The page for pagination' + optional :per_page, type: Integer, default: 20, desc: 'The number of results per page' + end get ":id/repository/commits" do + # TODO remove the next line for 9.0, use DateTime type in the params block datetime_attributes! :since, :until - page = (params[:page] || 0).to_i - per_page = (params[:per_page] || 20).to_i ref = params[:ref_name] || user_project.try(:default_branch) || 'master' - after = params[:since] - before = params[:until] + offset = params[:page] * params[:per_page] + + commits = user_project.repository.commits(ref, + limit: params[:per_page], + offset: offset, + after: params[:since], + before: params[:until]) - commits = user_project.repository.commits(ref, limit: per_page, offset: page * per_page, after: after, before: before) present commits, with: Entities::RepoCommit end desc 'Commit multiple file changes as one commit' do + success Entities::RepoCommitDetail detail 'This feature was introduced in GitLab 8.13' end - params do requires :id, type: Integer, desc: 'The project ID' requires :branch_name, type: String, desc: 'The name of branch' @@ -41,7 +48,6 @@ module API optional :author_email, type: String, desc: 'Author email for commit' optional :author_name, type: String, desc: 'Author name for commit' end - post ":id/repository/commits" do authorize! :push_code, user_project @@ -65,79 +71,82 @@ module API end end - # Get a specific commit of a project - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The commit hash or name of a repository branch or tag - # Example Request: - # GET /projects/:id/repository/commits/:sha + desc 'Get a specific commit of a project' do + success Entities::RepoCommitDetail + failure [[404, 'Not Found']] + end + params do + requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag' + end get ":id/repository/commits/:sha" do - sha = params[:sha] - commit = user_project.commit(sha) + commit = user_project.commit(params[:sha]) + not_found! "Commit" unless commit + present commit, with: Entities::RepoCommitDetail end - # Get the diff for a specific commit of a project - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The commit or branch name - # Example Request: - # GET /projects/:id/repository/commits/:sha/diff + desc 'Get the diff for a specific commit of a project' do + failure [[404, 'Not Found']] + end + params do + requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag' + end get ":id/repository/commits/:sha/diff" do - sha = params[:sha] - commit = user_project.commit(sha) + commit = user_project.commit(params[:sha]) + not_found! "Commit" unless commit + commit.raw_diffs.to_a end - # Get a commit's comments - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The commit hash - # Examples: - # GET /projects/:id/repository/commits/:sha/comments + desc "Get a commit's comments" do + success Entities::CommitNote + failure [[404, 'Not Found']] + end + params do + requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag' + optional :per_page, type: Integer, desc: 'The amount of items per page for paginaion' + optional :page, type: Integer, desc: 'The page number for pagination' + end get ':id/repository/commits/:sha/comments' do - sha = params[:sha] - commit = user_project.commit(sha) + commit = user_project.commit(params[:sha]) + not_found! 'Commit' unless commit notes = Note.where(commit_id: commit.id).order(:created_at) + present paginate(notes), with: Entities::CommitNote end - # Post comment to commit - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The commit hash - # note (required) - Text of comment - # path (optional) - The file path - # line (optional) - The line number - # line_type (optional) - The type of line (new or old) - # Examples: - # POST /projects/:id/repository/commits/:sha/comments + desc 'Post comment to commit' do + success Entities::CommitNote + end + params do + requires :sha, type: String, regexp: /\A\h{6,40}\z/, desc: "The commit's SHA" + requires :note, type: String, desc: 'The text of the comment' + optional :path, type: String, desc: 'The file path' + given :path do + requires :line, type: Integer, desc: 'The line number' + requires :line_type, type: String, values: ['new', 'old'], default: 'new', desc: 'The type of the line' + end + end post ':id/repository/commits/:sha/comments' do - required_attributes! [:note] - - sha = params[:sha] - commit = user_project.commit(sha) + commit = user_project.commit(params[:sha]) not_found! 'Commit' unless commit + opts = { note: params[:note], noteable_type: 'Commit', commit_id: commit.id } - if params[:path] && params[:line] && params[:line_type] + if params[:path] commit.raw_diffs(all_diffs: true).each do |diff| next unless diff.new_path == params[:path] lines = Gitlab::Diff::Parser.new.parse(diff.diff.each_line) lines.each do |line| - next unless line.new_pos == params[:line].to_i && line.type == params[:line_type] + next unless line.new_pos == params[:line] && line.type == params[:line_type] break opts[:line_code] = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos) end -- cgit v1.2.1 From c7282f89596293423c61d6676db60a9a347d09ef Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Fri, 14 Oct 2016 10:45:23 +0200 Subject: Grapify the commit status API --- lib/api/commit_statuses.rb | 53 +++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'lib/api') diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index dfbdd597d29..f282a3b9cd6 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -6,17 +6,17 @@ module API resource :projects do before { authenticate! } - # Get a commit's statuses - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The commit hash - # ref (optional) - The ref - # stage (optional) - The stage - # name (optional) - The name - # all (optional) - Show all statuses, default: false - # Examples: - # GET /projects/:id/repository/commits/:sha/statuses + desc "Get a commit's statuses" do + success Entities::CommitStatus + end + params do + requires :id, type: String, desc: 'The ID of a project' + requires :sha, type: String, desc: 'The commit hash' + optional :ref, type: String, desc: 'The ref' + optional :stage, type: String, desc: 'The stage' + optional :name, type: String, desc: 'The name' + optional :all, type: String, desc: 'Show all statuses, default: false' + end get ':id/repository/commits/:sha/statuses' do authorize!(:read_commit_status, user_project) @@ -31,22 +31,23 @@ module API present paginate(statuses), with: Entities::CommitStatus end - # Post status to commit - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The commit hash - # ref (optional) - The ref - # state (required) - The state of the status. Can be: pending, running, success, failed or canceled - # target_url (optional) - The target URL to associate with this status - # description (optional) - A short description of the status - # name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default" - # Examples: - # POST /projects/:id/statuses/:sha + desc 'Post status to a commit' do + success Entities::CommitStatus + end + params do + requires :id, type: String, desc: 'The ID of a project' + requires :sha, type: String, desc: 'The commit hash' + requires :state, type: String, desc: 'The state of the status', + values: ['pending', 'running', 'success', 'failed', 'canceled'] + optional :ref, type: String, desc: 'The ref' + optional :target_url, type: String, desc: 'The target URL to associate with this status' + optional :description, type: String, desc: 'A short description of the status' + optional :name, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"' + optional :context, type: String, desc: 'A string label to differentiate this status from the status of other systems. Default: "default"' + end post ':id/statuses/:sha' do authorize! :create_commit_status, user_project - required_attributes! [:state] - attrs = attributes_for_keys [:target_url, :description] + commit = @project.commit(params[:sha]) not_found! 'Commit' unless commit @@ -68,7 +69,7 @@ module API status = GenericCommitStatus.running_or_pending.find_or_initialize_by( project: @project, pipeline: pipeline, user: current_user, name: name, ref: ref) - status.attributes = attrs + status.attributes = declared(params).slice(:target_url, :description) begin case params[:state].to_s -- cgit v1.2.1 From 7e11ca86fdb23c967c25b19735770f99f936b32c Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Mon, 3 Oct 2016 18:41:46 -0300 Subject: Reuse LabelsFinder on Issueable#add_labels_by_names --- lib/api/merge_requests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/api') diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 2b685621da9..67fdd0be927 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -91,7 +91,7 @@ module API if merge_request.valid? # Find or create labels and attach to issue if params[:labels].present? - merge_request.add_labels_by_names(params[:labels].split(",")) + merge_request.add_labels_by_names(params[:labels].split(","), current_user) end present merge_request, with: Entities::MergeRequest, current_user: current_user @@ -201,7 +201,7 @@ module API # Find or create labels and attach to issue unless params[:labels].nil? merge_request.remove_labels - merge_request.add_labels_by_names(params[:labels].split(",")) + merge_request.add_labels_by_names(params[:labels].split(","), current_user) end present merge_request, with: Entities::MergeRequest, current_user: current_user -- cgit v1.2.1 From 033ea9d1e80544df5236ca045c88f649e41afbc7 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 13 Oct 2016 18:04:55 -0300 Subject: Move label management to services on merge requests API --- lib/api/merge_requests.rb | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'lib/api') diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 67fdd0be927..bf8504e1101 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -86,14 +86,11 @@ module API render_api_error!({ labels: errors }, 400) end + attrs[:labels] = params[:labels] if params[:labels] + merge_request = ::MergeRequests::CreateService.new(user_project, current_user, attrs).execute if merge_request.valid? - # Find or create labels and attach to issue - if params[:labels].present? - merge_request.add_labels_by_names(params[:labels].split(","), current_user) - end - present merge_request, with: Entities::MergeRequest, current_user: current_user else handle_merge_request_errors! merge_request.errors @@ -195,15 +192,11 @@ module API render_api_error!({ labels: errors }, 400) end + attrs[:labels] = params[:labels] if params[:labels] + merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, attrs).execute(merge_request) if merge_request.valid? - # Find or create labels and attach to issue - unless params[:labels].nil? - merge_request.remove_labels - merge_request.add_labels_by_names(params[:labels].split(","), current_user) - end - present merge_request, with: Entities::MergeRequest, current_user: current_user else handle_merge_request_errors! merge_request.errors -- cgit v1.2.1 From bf710b5119dce329a1ffd43b01b3a4dbb3e94b09 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 13 Oct 2016 18:34:44 -0300 Subject: Validate label params against all labels available to project on the API --- lib/api/helpers.rb | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'lib/api') diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 67473f300c9..45120898b76 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -71,6 +71,10 @@ module API @project ||= find_project(params[:id]) end + def available_labels + @available_labels ||= LabelsFinder.new(current_user, project_id: user_project.id).execute + end + def find_project(id) project = Project.find_with_namespace(id) || Project.find_by(id: id) @@ -118,7 +122,7 @@ module API end def find_project_label(id) - label = user_project.labels.find_by_id(id) || user_project.labels.find_by_title(id) + label = available_labels.find_by_id(id) || available_labels.find_by_title(id) label || not_found!('Label') end @@ -197,16 +201,11 @@ module API def validate_label_params(params) errors = {} - if params[:labels].present? - params[:labels].split(',').each do |label_name| - label = user_project.labels.create_with( - color: Label::DEFAULT_COLOR).find_or_initialize_by( - title: label_name.strip) + params[:labels].to_s.split(',').each do |label_name| + label = available_labels.find_or_initialize_by(title: label_name.strip) + next if label.valid? - if label.invalid? - errors[label.title] = label.errors - end - end + errors[label.title] = label.errors end errors -- cgit v1.2.1 From 68f30b2fff362805568588f416709e7000d75ce3 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 13 Oct 2016 18:48:59 -0300 Subject: Add support to group labels on issues board API --- lib/api/boards.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/api') diff --git a/lib/api/boards.rb b/lib/api/boards.rb index b14dd4f6e83..4ac491edc1b 100644 --- a/lib/api/boards.rb +++ b/lib/api/boards.rb @@ -65,8 +65,8 @@ module API requires :label_id, type: Integer, desc: 'The ID of an existing label' end post '/lists' do - unless user_project.labels.exists?(params[:label_id]) - render_api_error!({ error: "Label not found!" }, 400) + unless available_labels.exists?(params[:label_id]) + render_api_error!({ error: 'Label not found!' }, 400) end authorize!(:admin_list, user_project) -- cgit v1.2.1 From 9b288238549dac5b59fd467f6ee1fdc53b6c783e Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 13 Oct 2016 18:53:06 -0300 Subject: List all available labels to the project on the labels API --- lib/api/labels.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/api') diff --git a/lib/api/labels.rb b/lib/api/labels.rb index c806829d69e..642e6345b9e 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -11,7 +11,7 @@ module API # Example Request: # GET /projects/:id/labels get ':id/labels' do - present user_project.labels, with: Entities::Label, current_user: current_user + present available_labels, with: Entities::Label, current_user: current_user end # Creates a new label -- cgit v1.2.1 From b4359fb24e599c278edeae843bd6a25c980b1243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 20 Oct 2016 18:51:03 +0200 Subject: Don't use Hash#slice since it's not supported in Ruby 2.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/commit_statuses.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'lib/api') diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index f282a3b9cd6..f54d4f06627 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -67,9 +67,14 @@ module API pipeline = @project.ensure_pipeline(ref, commit.sha, current_user) status = GenericCommitStatus.running_or_pending.find_or_initialize_by( - project: @project, pipeline: pipeline, - user: current_user, name: name, ref: ref) - status.attributes = declared(params).slice(:target_url, :description) + project: @project, + pipeline: pipeline, + user: current_user, + name: name, + ref: ref, + target_url: params[:target_url], + description: params[:description] + ) begin case params[:state].to_s -- cgit v1.2.1 From 3a29ea9da0abd6cfd0788f6d717a08862ed6b062 Mon Sep 17 00:00:00 2001 From: Lemures Lemniscati Date: Sat, 22 Oct 2016 03:07:26 +0900 Subject: Fix documents and comments on Build API `scope`. #23146 #19131 --- lib/api/builds.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/api') diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 52bdbcae5a8..7b00c5037f1 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -8,7 +8,7 @@ module API # # Parameters: # id (required) - The ID of a project - # scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled; + # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; # if none provided showing all builds) # Example Request: # GET /projects/:id/builds @@ -25,7 +25,7 @@ module API # Parameters: # id (required) - The ID of a project # sha (required) - The SHA id of a commit - # scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled; + # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; # if none provided showing all builds) # Example Request: # GET /projects/:id/repository/commits/:sha/builds -- cgit v1.2.1 From f79f3a1dd621362b0894eff0a54f220f8415a2e0 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Tue, 6 Sep 2016 10:35:34 +0530 Subject: Fix branch protection API. 1. Previously, we were not removing existing access levels before creating new ones. This is not a problem for EE, but _is_ for CE, since we restrict the number of access levels in CE to 1. 2. The correct approach is: CE -> delete all access levels before updating a protected branch EE -> delete developer access levels if "developers_can_{merge,push}" is switched off 3. The dispatch is performed by checking if a "length: 1" validation is present on the access levels or not. 4. Another source of problems was that we didn't put multiple queries in a transaction. If the `destroy_all` passes, but the `update` fails, we should have a rollback. 5. Modifying the API to provide users direct access to CRUD access levels will make things a lot simpler. 6. Create `create/update` services separately for this API, which perform the necessary data translation, before calling the regular `create/update` services. The translation code was getting too large for the API endpoint itself, so this move makes sense. --- lib/api/branches.rb | 47 ++++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 31 deletions(-) (limited to 'lib/api') diff --git a/lib/api/branches.rb b/lib/api/branches.rb index b615703df93..6941cc4aca6 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -57,40 +57,25 @@ module API developers_can_merge = to_boolean(params[:developers_can_merge]) developers_can_push = to_boolean(params[:developers_can_push]) - protected_branch_params = { - name: @branch.name + params = { + name: @branch.name, } - # If `developers_can_merge` is switched off, _all_ `DEVELOPER` - # merge_access_levels need to be deleted. - if developers_can_merge == false - protected_branch.merge_access_levels.where(access_level: Gitlab::Access::DEVELOPER).destroy_all - end + service_args = [user_project, current_user, params, + developers_can_push: developers_can_push, + developers_can_merge: developers_can_merge] - # If `developers_can_push` is switched off, _all_ `DEVELOPER` - # push_access_levels need to be deleted. - if developers_can_push == false - protected_branch.push_access_levels.where(access_level: Gitlab::Access::DEVELOPER).destroy_all - end + protected_branch = if protected_branch + ProtectedBranches::ApiUpdateService.new(*service_args).execute(protected_branch) + else + ProtectedBranches::ApiCreateService.new(*service_args).execute + end - protected_branch_params.merge!( - merge_access_levels_attributes: [{ - access_level: developers_can_merge ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER - }], - push_access_levels_attributes: [{ - access_level: developers_can_push ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER - }] - ) - - if protected_branch - service = ProtectedBranches::UpdateService.new(user_project, current_user, protected_branch_params) - service.execute(protected_branch) + if protected_branch.valid? + present @branch, with: Entities::RepoBranch, project: user_project else - service = ProtectedBranches::CreateService.new(user_project, current_user, protected_branch_params) - service.execute + render_api_error!(protected_branch.errors.full_messages, 422) end - - present @branch, with: Entities::RepoBranch, project: user_project end # Unprotect a single branch @@ -123,7 +108,7 @@ module API post ":id/repository/branches" do authorize_push_project result = CreateBranchService.new(user_project, current_user). - execute(params[:branch_name], params[:ref]) + execute(params[:branch_name], params[:ref]) if result[:status] == :success present result[:branch], @@ -142,10 +127,10 @@ module API # Example Request: # DELETE /projects/:id/repository/branches/:branch delete ":id/repository/branches/:branch", - requirements: { branch: /.+/ } do + requirements: { branch: /.+/ } do authorize_push_project result = DeleteBranchService.new(user_project, current_user). - execute(params[:branch]) + execute(params[:branch]) if result[:status] == :success { -- cgit v1.2.1 From b803bc7bb8ad481790d01848902e80602e77da67 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Thu, 22 Sep 2016 20:38:05 +0530 Subject: Implement review comments from @DouweM. --- lib/api/branches.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/api') diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 6941cc4aca6..7feb47784b7 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -57,11 +57,11 @@ module API developers_can_merge = to_boolean(params[:developers_can_merge]) developers_can_push = to_boolean(params[:developers_can_push]) - params = { + protected_branch_params = { name: @branch.name, } - service_args = [user_project, current_user, params, + service_args = [user_project, current_user, protected_branch_params, developers_can_push: developers_can_push, developers_can_merge: developers_can_merge] -- cgit v1.2.1 From 1051087ac4efc3dbf45bd075e36af647d2b66d62 Mon Sep 17 00:00:00 2001 From: Timothy Andrew Date: Fri, 30 Sep 2016 13:14:46 +0530 Subject: Implement second round of review comments from @DouweM. - Pass `developers_and_merge` and `developers_can_push` in `params` instead of using keyword arguments. - Refactor a slightly complex boolean check to a simple `nil?` check. --- lib/api/branches.rb | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'lib/api') diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 7feb47784b7..6d827448994 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -54,16 +54,13 @@ module API not_found!('Branch') unless @branch protected_branch = user_project.protected_branches.find_by(name: @branch.name) - developers_can_merge = to_boolean(params[:developers_can_merge]) - developers_can_push = to_boolean(params[:developers_can_push]) - protected_branch_params = { name: @branch.name, + developers_can_push: to_boolean(params[:developers_can_push]), + developers_can_merge: to_boolean(params[:developers_can_merge]) } - service_args = [user_project, current_user, protected_branch_params, - developers_can_push: developers_can_push, - developers_can_merge: developers_can_merge] + service_args = [user_project, current_user, protected_branch_params] protected_branch = if protected_branch ProtectedBranches::ApiUpdateService.new(*service_args).execute(protected_branch) -- cgit v1.2.1 From 8f527cbf06d31c084bdf71c7f882d73ff6591e29 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 24 Oct 2016 13:06:17 +0200 Subject: Grapify builds API --- lib/api/builds.rb | 162 ++++++++++++++++++++++++++---------------------------- 1 file changed, 79 insertions(+), 83 deletions(-) (limited to 'lib/api') diff --git a/lib/api/builds.rb b/lib/api/builds.rb index 7b00c5037f1..67adca6605f 100644 --- a/lib/api/builds.rb +++ b/lib/api/builds.rb @@ -3,15 +3,32 @@ module API class Builds < Grape::API before { authenticate! } + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do - # Get a project builds - # - # Parameters: - # id (required) - The ID of a project - # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; - # if none provided showing all builds) - # Example Request: - # GET /projects/:id/builds + helpers do + params :optional_scope do + optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', + values: ['pending', 'running', 'failed', 'success', 'canceled'], + coerce_with: ->(scope) { + if scope.is_a?(String) + [scope] + elsif scope.is_a?(Hashie::Mash) + scope.values + else + ['unknown'] + end + } + end + end + + desc 'Get a project builds' do + success Entities::Build + end + params do + use :optional_scope + end get ':id/builds' do builds = user_project.builds.order('id DESC') builds = filter_builds(builds, params[:scope]) @@ -20,15 +37,13 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Get builds for a specific commit of a project - # - # Parameters: - # id (required) - The ID of a project - # sha (required) - The SHA id of a commit - # scope (optional) - The scope of builds to show (one or array of: created, pending, running, failed, success, canceled, skipped; - # if none provided showing all builds) - # Example Request: - # GET /projects/:id/repository/commits/:sha/builds + desc 'Get builds for a specific commit of a project' do + success Entities::Build + end + params do + requires :sha, type: String, desc: 'The SHA id of a commit' + use :optional_scope + end get ':id/repository/commits/:sha/builds' do authorize_read_builds! @@ -42,13 +57,12 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Get a specific build of a project - # - # Parameters: - # id (required) - The ID of a project - # build_id (required) - The ID of a build - # Example Request: - # GET /projects/:id/builds/:build_id + desc 'Get a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end get ':id/builds/:build_id' do authorize_read_builds! @@ -58,13 +72,12 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Download the artifacts file from build - # - # Parameters: - # id (required) - The ID of a build - # token (required) - The build authorization token - # Example Request: - # GET /projects/:id/builds/:build_id/artifacts + desc 'Download the artifacts file from build' do + detail 'This feature was introduced in GitLab 8.5' + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end get ':id/builds/:build_id/artifacts' do authorize_read_builds! @@ -73,14 +86,13 @@ module API present_artifacts!(build.artifacts_file) end - # Download the artifacts file from ref_name and job - # - # Parameters: - # id (required) - The ID of a project - # ref_name (required) - The ref from repository - # job (required) - The name for the build - # Example Request: - # GET /projects/:id/builds/artifacts/:ref_name/download?job=name + desc 'Download the artifacts file from build' do + detail 'This feature was introduced in GitLab 8.10' + end + params do + requires :ref_name, type: String, desc: 'The ref from repository' + requires :job, type: String, desc: 'The name for the build' + end get ':id/builds/artifacts/:ref_name/download', requirements: { ref_name: /.+/ } do authorize_read_builds! @@ -91,17 +103,13 @@ module API present_artifacts!(latest_build.artifacts_file) end - # Get a trace of a specific build of a project - # - # Parameters: - # id (required) - The ID of a project - # build_id (required) - The ID of a build - # Example Request: - # GET /projects/:id/build/:build_id/trace - # # TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace # is saved in the DB instead of file). But before that, we need to consider how to replace the value of # `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse. + desc 'Get a trace of a specific build of a project' + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end get ':id/builds/:build_id/trace' do authorize_read_builds! @@ -115,13 +123,12 @@ module API body trace end - # Cancel a specific build of a project - # - # parameters: - # id (required) - the id of a project - # build_id (required) - the id of a build - # example request: - # post /projects/:id/build/:build_id/cancel + desc 'Cancel a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end post ':id/builds/:build_id/cancel' do authorize_update_builds! @@ -133,13 +140,12 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Retry a specific build of a project - # - # parameters: - # id (required) - the id of a project - # build_id (required) - the id of a build - # example request: - # post /projects/:id/build/:build_id/retry + desc 'Retry a specific build of a project' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end post ':id/builds/:build_id/retry' do authorize_update_builds! @@ -152,13 +158,12 @@ module API user_can_download_artifacts: can?(current_user, :read_build, user_project) end - # Erase build (remove artifacts and build trace) - # - # Parameters: - # id (required) - the id of a project - # build_id (required) - the id of a build - # example Request: - # post /projects/:id/build/:build_id/erase + desc 'Erase build (remove artifacts and build trace)' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end post ':id/builds/:build_id/erase' do authorize_update_builds! @@ -170,13 +175,12 @@ module API user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project) end - # Keep the artifacts to prevent them from being deleted - # - # Parameters: - # id (required) - the id of a project - # build_id (required) - The ID of a build - # Example Request: - # POST /projects/:id/builds/:build_id/artifacts/keep + desc 'Keep the artifacts to prevent them from being deleted' do + success Entities::Build + end + params do + requires :build_id, type: Integer, desc: 'The ID of a build' + end post ':id/builds/:build_id/artifacts/keep' do authorize_update_builds! @@ -235,14 +239,6 @@ module API return builds if scope.nil? || scope.empty? available_statuses = ::CommitStatus::AVAILABLE_STATUSES - scope = - if scope.is_a?(String) - [scope] - elsif scope.is_a?(Hashie::Mash) - scope.values - else - ['unknown'] - end unknown = scope - available_statuses render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty? -- cgit v1.2.1 From f73d83db76ae8386ad4db604efb40156593b7a7a Mon Sep 17 00:00:00 2001 From: Luis HGO Date: Mon, 20 Jun 2016 22:37:40 -0300 Subject: Added path parameter to Commits API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rémy Coutable --- lib/api/commits.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/api') diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 617a240318a..2f2cf769481 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -19,6 +19,7 @@ module API optional :until, type: String, desc: 'Only commits before or in this date will be returned' optional :page, type: Integer, default: 0, desc: 'The page for pagination' optional :per_page, type: Integer, default: 20, desc: 'The number of results per page' + optional :path, type: String, desc: 'The file path' end get ":id/repository/commits" do # TODO remove the next line for 9.0, use DateTime type in the params block @@ -28,6 +29,7 @@ module API offset = params[:page] * params[:per_page] commits = user_project.repository.commits(ref, + path: params[:path], limit: params[:per_page], offset: offset, after: params[:since], -- cgit v1.2.1 From 21666dbe119df7f2d86f0ac13c4addc814af4485 Mon Sep 17 00:00:00 2001 From: Robert Schilling Date: Mon, 24 Oct 2016 14:05:33 +0200 Subject: Grapify the labels API --- lib/api/labels.rb | 91 +++++++++++++++++++++++-------------------------------- 1 file changed, 38 insertions(+), 53 deletions(-) (limited to 'lib/api') diff --git a/lib/api/labels.rb b/lib/api/labels.rb index 642e6345b9e..326e1e7ae00 100644 --- a/lib/api/labels.rb +++ b/lib/api/labels.rb @@ -3,37 +3,32 @@ module API class Labels < Grape::API before { authenticate! } + params do + requires :id, type: String, desc: 'The ID of a project' + end resource :projects do - # Get all labels of the project - # - # Parameters: - # id (required) - The ID of a project - # Example Request: - # GET /projects/:id/labels + desc 'Get all labels of the project' do + success Entities::Label + end get ':id/labels' do present available_labels, with: Entities::Label, current_user: current_user end - # Creates a new label - # - # Parameters: - # id (required) - The ID of a project - # name (required) - The name of the label to be created - # color (required) - Color of the label given in 6-digit hex - # notation with leading '#' sign (e.g. #FFAABB) - # description (optional) - The description of label to be created - # Example Request: - # POST /projects/:id/labels + desc 'Create a new label' do + success Entities::Label + end + params do + requires :name, type: String, desc: 'The name of the label to be created' + requires :color, type: String, desc: "The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)" + optional :description, type: String, desc: 'The description of label to be created' + end post ':id/labels' do authorize! :admin_label, user_project - required_attributes! [:name, :color] - - attrs = attributes_for_keys [:name, :color, :description] - label = user_project.find_label(attrs[:name]) + label = user_project.find_label(params[:name]) conflict!('Label already exists') if label - label = user_project.labels.create(attrs) + label = user_project.labels.create(declared(params, include_parent_namespaces: false).to_h) if label.valid? present label, with: Entities::Label, current_user: current_user @@ -42,54 +37,44 @@ module API end end - # Deletes an existing label - # - # Parameters: - # id (required) - The ID of a project - # name (required) - The name of the label to be deleted - # - # Example Request: - # DELETE /projects/:id/labels + desc 'Delete an existing label' do + success Entities::Label + end + params do + requires :name, type: String, desc: 'The name of the label to be deleted' + end delete ':id/labels' do authorize! :admin_label, user_project - required_attributes! [:name] label = user_project.find_label(params[:name]) not_found!('Label') unless label - label.destroy + present label.destroy, with: Entities::Label, current_user: current_user end - # Updates an existing label. At least one optional parameter is required. - # - # Parameters: - # id (required) - The ID of a project - # name (required) - The name of the label to be deleted - # new_name (optional) - The new name of the label - # color (optional) - Color of the label given in 6-digit hex - # notation with leading '#' sign (e.g. #FFAABB) - # description (optional) - The description of label to be created - # Example Request: - # PUT /projects/:id/labels + desc 'Update an existing label. At least one optional parameter is required.' do + success Entities::Label + end + params do + requires :name, type: String, desc: 'The name of the label to be updated' + optional :new_name, type: String, desc: 'The new name of the label' + optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB)" + optional :description, type: String, desc: 'The new description of label' + at_least_one_of :new_name, :color, :description + end put ':id/labels' do authorize! :admin_label, user_project - required_attributes! [:name] label = user_project.find_label(params[:name]) not_found!('Label not found') unless label - attrs = attributes_for_keys [:new_name, :color, :description] - - if attrs.empty? - render_api_error!('Required parameters "new_name" or "color" ' \ - 'missing', - 400) - end - + update_params = declared(params, + include_parent_namespaces: false, + include_missing: false).to_h # Rename new name to the actual label attribute name - attrs[:name] = attrs.delete(:new_name) if attrs.key?(:new_name) + update_params['name'] = update_params.delete('new_name') if update_params.key?('new_name') - if label.update(attrs) + if label.update(update_params) present label, with: Entities::Label, current_user: current_user else render_validation_error!(label) -- cgit v1.2.1