diff options
author | Lin Jen-Shin <godfat@godfat.org> | 2017-02-15 11:13:14 +0300 |
---|---|---|
committer | Lin Jen-Shin <godfat@godfat.org> | 2017-02-15 11:13:14 +0300 |
commit | a065ee341c599f9500cd0b52a873cdb71a0bce55 (patch) | |
tree | 28277b71118c7f7385add7c8ae4493f1d964305b /lib/api | |
parent | 188c231304845ff29506dc152aaea6ec42373015 (diff) | |
parent | 1452729304393978ec93b712130dff6687db01b9 (diff) |
Merge remote-tracking branch 'upstream/master' into use-update-runner-service
* upstream/master: (488 commits)
Remove duplicate CHANGELOG.md entries for 8.16.5
Update CHANGELOG.md for 8.14.9
Update CHANGELOG.md for 8.15.6
#27631: Add missing top-area div to activity header page
Update CHANGELOG.md for 8.16.5
Update CHANGELOG.md for 8.16.5
Update CHANGELOG.md for 8.16.5
Fix yarn lock and package.json mismatch caused by MR 9133
sync yarn.lock with recent changes to package.json
Add changelog
Fix z index bugs
Add Links to Branches in Calendar Activity
SidekiqStatus need to be qualified in some cases
Replace static fixture for behaviors/requires_input_spec.js (!9162)
API: Consolidate /projects endpoint
Add MySQL info in install requirements
Fix timezone on issue boards due date
Use Gitlab::Database.with_connection_pool from !9192
Disconnect the pool after done
Use threads directly, introduce pool later:
...
Diffstat (limited to 'lib/api')
-rw-r--r-- | lib/api/api.rb | 4 | ||||
-rw-r--r-- | lib/api/branches.rb | 2 | ||||
-rw-r--r-- | lib/api/deploy_keys.rb | 172 | ||||
-rw-r--r-- | lib/api/entities.rb | 8 | ||||
-rw-r--r-- | lib/api/files.rb | 2 | ||||
-rw-r--r-- | lib/api/groups.rb | 6 | ||||
-rw-r--r-- | lib/api/helpers.rb | 8 | ||||
-rw-r--r-- | lib/api/members.rb | 13 | ||||
-rw-r--r-- | lib/api/merge_requests.rb | 2 | ||||
-rw-r--r-- | lib/api/notes.rb | 2 | ||||
-rw-r--r-- | lib/api/projects.rb | 80 | ||||
-rw-r--r-- | lib/api/templates.rb | 95 | ||||
-rw-r--r-- | lib/api/users.rb | 18 | ||||
-rw-r--r-- | lib/api/v3/deploy_keys.rb | 122 | ||||
-rw-r--r-- | lib/api/v3/entities.rb | 16 | ||||
-rw-r--r-- | lib/api/v3/issues.rb | 28 | ||||
-rw-r--r-- | lib/api/v3/members.rb | 134 | ||||
-rw-r--r-- | lib/api/v3/merge_requests.rb | 40 | ||||
-rw-r--r-- | lib/api/v3/project_snippets.rb | 135 | ||||
-rw-r--r-- | lib/api/v3/projects.rb | 74 | ||||
-rw-r--r-- | lib/api/v3/templates.rb | 122 |
21 files changed, 772 insertions, 311 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb index 1950d2791ab..06346ae822a 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -5,9 +5,13 @@ module API version %w(v3 v4), using: :path version 'v3', using: :path do + mount ::API::V3::DeployKeys mount ::API::V3::Issues + mount ::API::V3::Members mount ::API::V3::MergeRequests mount ::API::V3::Projects + mount ::API::V3::ProjectSnippets + mount ::API::V3::Templates end before { allow_access_with_scope :api } diff --git a/lib/api/branches.rb b/lib/api/branches.rb index be659fa4a6a..9331be1f7de 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -84,7 +84,7 @@ module API branch = user_project.repository.find_branch(params[:branch]) not_found!("Branch") unless branch protected_branch = user_project.protected_branches.find_by(name: branch.name) - protected_branch.destroy if protected_branch + protected_branch&.destroy present branch, with: Entities::RepoBranch, project: user_project end diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb index 64da7d6b86f..3f5183d46a2 100644 --- a/lib/api/deploy_keys.rb +++ b/lib/api/deploy_keys.rb @@ -1,5 +1,4 @@ module API - # Projects API class DeployKeys < Grape::API before { authenticate! } @@ -16,107 +15,102 @@ module API resource :projects do before { authorize_admin_project } - # Routing "projects/:id/keys/..." is DEPRECATED and WILL BE REMOVED in version 9.0 - # Use "projects/:id/deploy_keys/..." instead. - # - %w(keys deploy_keys).each do |path| - desc "Get a specific project's deploy keys" do - success Entities::SSHKey - end - get ":id/#{path}" do - present user_project.deploy_keys, with: Entities::SSHKey - end + desc "Get a specific project's deploy keys" do + success Entities::SSHKey + end + get ":id/deploy_keys" do + present user_project.deploy_keys, with: Entities::SSHKey + end - desc 'Get single deploy key' do - success Entities::SSHKey - end - params do - requires :key_id, type: Integer, desc: 'The ID of the deploy key' - end - get ":id/#{path}/:key_id" do - key = user_project.deploy_keys.find params[:key_id] + desc 'Get single deploy key' do + success Entities::SSHKey + end + params do + requires :key_id, type: Integer, desc: 'The ID of the deploy key' + end + get ":id/deploy_keys/:key_id" do + key = user_project.deploy_keys.find params[:key_id] + present key, with: Entities::SSHKey + end + + desc 'Add new deploy key to currently authenticated user' do + success Entities::SSHKey + end + params do + requires :key, type: String, desc: 'The new deploy key' + requires :title, type: String, desc: 'The name of the deploy key' + end + post ":id/deploy_keys" do + params[:key].strip! + + # Check for an existing key joined to this project + key = user_project.deploy_keys.find_by(key: params[:key]) + if key present key, with: Entities::SSHKey + break end - desc 'Add new deploy key to currently authenticated user' do - success Entities::SSHKey - end - params do - requires :key, type: String, desc: 'The new deploy key' - requires :title, type: String, desc: 'The name of the deploy key' + # Check for available deploy keys in other projects + key = current_user.accessible_deploy_keys.find_by(key: params[:key]) + if key + user_project.deploy_keys << key + present key, with: Entities::SSHKey + break end - post ":id/#{path}" do - params[:key].strip! - # Check for an existing key joined to this project - key = user_project.deploy_keys.find_by(key: params[:key]) - if key - present key, with: Entities::SSHKey - break - end - - # Check for available deploy keys in other projects - key = current_user.accessible_deploy_keys.find_by(key: params[:key]) - if key - user_project.deploy_keys << key - present key, with: Entities::SSHKey - break - end - - # Create a new deploy key - key = DeployKey.new(declared_params(include_missing: false)) - if key.valid? && user_project.deploy_keys << key - present key, with: Entities::SSHKey - else - render_validation_error!(key) - end + # Create a new deploy key + key = DeployKey.new(declared_params(include_missing: false)) + if key.valid? && user_project.deploy_keys << key + present key, with: Entities::SSHKey + else + render_validation_error!(key) end + end - desc 'Enable a deploy key for a project' do - detail 'This feature was added in GitLab 8.11' - success Entities::SSHKey - end - params do - requires :key_id, type: Integer, desc: 'The ID of the deploy key' - end - post ":id/#{path}/:key_id/enable" do - key = ::Projects::EnableDeployKeyService.new(user_project, - current_user, declared_params).execute + desc 'Enable a deploy key for a project' do + detail 'This feature was added in GitLab 8.11' + success Entities::SSHKey + end + params do + requires :key_id, type: Integer, desc: 'The ID of the deploy key' + end + post ":id/deploy_keys/:key_id/enable" do + key = ::Projects::EnableDeployKeyService.new(user_project, + current_user, declared_params).execute - if key - present key, with: Entities::SSHKey - else - not_found!('Deploy Key') - end + if key + present key, with: Entities::SSHKey + else + not_found!('Deploy Key') end + end - desc 'Disable a deploy key for a project' do - detail 'This feature was added in GitLab 8.11' - success Entities::SSHKey - end - params do - requires :key_id, type: Integer, desc: 'The ID of the deploy key' - end - delete ":id/#{path}/:key_id/disable" do - key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) - key.destroy + desc 'Disable a deploy key for a project' do + detail 'This feature was added in GitLab 8.11' + success Entities::SSHKey + end + params do + requires :key_id, type: Integer, desc: 'The ID of the deploy key' + end + delete ":id/deploy_keys/:key_id/disable" do + key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) + key.destroy - present key.deploy_key, with: Entities::SSHKey - end + present key.deploy_key, with: Entities::SSHKey + end - desc 'Delete deploy key for a project' do - success Key - end - params do - requires :key_id, type: Integer, desc: 'The ID of the deploy key' - end - delete ":id/#{path}/:key_id" do - key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) - if key - key.destroy - else - not_found!('Deploy Key') - end + desc 'Delete deploy key for a project' do + success Key + end + params do + requires :key_id, type: Integer, desc: 'The ID of the deploy key' + end + delete ":id/deploy_keys/:key_id" do + key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) + if key + key.destroy + else + not_found!('Deploy Key') end end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index b1ead48caf7..2a071e649fa 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -138,6 +138,7 @@ module API expose :web_url expose :request_access_enabled expose :full_name, :full_path + expose :parent_id expose :statistics, if: :statistics do with_options format_with: -> (value) { value.to_i } do @@ -213,9 +214,6 @@ module API expose :author, using: Entities::UserBasic expose :updated_at, :created_at - # TODO (rspeicher): Deprecated; remove in 9.0 - expose(:expires_at) { |snippet| nil } - expose :web_url do |snippet, options| Gitlab::UrlBuilder.build(snippet) end @@ -382,9 +380,7 @@ module API expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author } expose :author_username do |event, options| - if event.author - event.author.username - end + event.author&.username end end diff --git a/lib/api/files.rb b/lib/api/files.rb index c58472de578..2ecdd747c8e 100644 --- a/lib/api/files.rb +++ b/lib/api/files.rb @@ -117,7 +117,7 @@ module API authorize! :push_code, user_project file_params = declared_params(include_missing: false) - result = ::Files::DeleteService.new(user_project, current_user, commit_params(file_params)).execute + result = ::Files::DestroyService.new(user_project, current_user, commit_params(file_params)).execute if result[:status] == :success status(200) diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 7682d286866..9f29c4466ab 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -73,6 +73,7 @@ module API params do requires :name, type: String, desc: 'The name of the group' requires :path, type: String, desc: 'The path of the group' + optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group' use :optional_params end post do @@ -125,7 +126,7 @@ module API delete ":id" do group = find_group!(params[:id]) authorize! :admin_group, group - DestroyGroupService.new(group, current_user).execute + ::Groups::DestroyService.new(group, current_user).execute end desc 'Get a list of projects in this group.' do @@ -142,6 +143,9 @@ module API desc: 'Return projects sorted in ascending and descending order' optional :simple, type: Boolean, default: false, desc: 'Return only the ID, URL, name, and path of each project' + optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' + optional :starred, type: Boolean, default: false, desc: 'Limit by starred status' + use :pagination end get ":id/projects" do diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index dfab60f7fa5..13896dd91b9 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -256,6 +256,14 @@ module API # project helpers def filter_projects(projects) + if params[:owned] + projects = projects.merge(current_user.owned_projects) + end + + if params[:starred] + projects = projects.merge(current_user.starred_projects) + end + if params[:search].present? projects = projects.search(params[:search]) end diff --git a/lib/api/members.rb b/lib/api/members.rb index d85f1f78cd6..d1d78775c6d 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -56,16 +56,9 @@ module API member = source.members.find_by(user_id: params[:user_id]) - # 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 - member = source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at]) - end + conflict!('Member already exists') if member + + member = source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at]) if member.persisted? && member.valid? present member.user, with: Entities::Member, member: member diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 782147883c8..8e09a6f7354 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -138,7 +138,7 @@ module API params do optional :title, type: String, allow_blank: false, desc: 'The title of the merge request' optional :target_branch, type: String, allow_blank: false, desc: 'The target branch' - optional :state_event, type: String, values: %w[close reopen merge], + optional :state_event, type: String, values: %w[close reopen], desc: 'Status of the merge request' use :optional_params at_least_one_of :title, :target_branch, :description, :assignee_id, diff --git a/lib/api/notes.rb b/lib/api/notes.rb index 4d2a8f48267..8beccaaabd1 100644 --- a/lib/api/notes.rb +++ b/lib/api/notes.rb @@ -131,7 +131,7 @@ module API note = user_project.notes.find(params[:note_id]) authorize! :admin_note, note - ::Notes::DeleteService.new(user_project, current_user).execute(note) + ::Notes::DestroyService.new(user_project, current_user).execute(note) present note, with: Entities::Note end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 92a70faf1c2..68c2732ec80 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -16,7 +16,6 @@ module API optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project' optional :container_registry_enabled, type: Boolean, desc: 'Flag indication if the container registry is enabled for that project' optional :lfs_enabled, type: Boolean, desc: 'Flag indication if Git LFS is enabled for that project' - optional :public, type: Boolean, desc: 'Create a public project. The same as visibility_level = 20.' optional :visibility_level, type: Integer, values: [ Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::INTERNAL, @@ -26,16 +25,6 @@ module API optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed' optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved' end - - def map_public_to_visibility_level(attrs) - publik = attrs.delete(:public) - if !publik.nil? && !attrs[:visibility_level].present? - # Since setting the public attribute to private could mean either - # private or internal, use the more conservative option, private. - attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE - end - attrs - end end resource :projects do @@ -61,6 +50,8 @@ module API optional :visibility, type: String, values: %w[public internal private], desc: 'Limit by visibility' optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria' + optional :owned, type: Boolean, default: false, desc: 'Limit by owned by authenticated user' + optional :starred, type: Boolean, default: false, desc: 'Limit by starred status' end params :statistics_params do @@ -93,62 +84,9 @@ module API params do use :collection_params end - get '/visible' do - entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails - present_projects ProjectsFinder.new.execute(current_user), with: entity - end - - desc 'Get a projects list for authenticated user' do - success Entities::BasicProjectDetails - end - params do - use :collection_params - end get do - authenticate! - - present_projects current_user.authorized_projects, - with: Entities::ProjectWithAccess - end - - desc 'Get an owned projects list for authenticated user' do - success Entities::BasicProjectDetails - end - params do - use :collection_params - use :statistics_params - end - get '/owned' do - authenticate! - - present_projects current_user.owned_projects, - with: Entities::ProjectWithAccess, - statistics: params[:statistics] - end - - desc 'Gets starred project for the authenticated user' do - success Entities::BasicProjectDetails - end - params do - use :collection_params - end - get '/starred' do - authenticate! - - present_projects current_user.viewable_starred_projects - end - - desc 'Get all projects for admin user' do - success Entities::BasicProjectDetails - end - params do - use :collection_params - use :statistics_params - end - get '/all' do - authenticated_as_admin! - - present_projects Project.all, with: Entities::ProjectWithAccess, statistics: params[:statistics] + entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails + present_projects ProjectsFinder.new.execute(current_user), with: entity, statistics: params[:statistics] end desc 'Create new project' do @@ -161,7 +99,7 @@ module API use :create_params end post do - attrs = map_public_to_visibility_level(declared_params(include_missing: false)) + attrs = declared_params(include_missing: false) project = ::Projects::CreateService.new(current_user, attrs).execute if project.saved? @@ -190,7 +128,7 @@ module API user = User.find_by(id: params.delete(:user_id)) not_found!('User') unless user - attrs = map_public_to_visibility_level(declared_params(include_missing: false)) + attrs = declared_params(include_missing: false) project = ::Projects::CreateService.new(user, attrs).execute if project.saved? @@ -231,7 +169,7 @@ module API params do optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into' end - post 'fork/:id' do + post ':id/fork' do fork_params = declared_params(include_missing: false) namespace_id = fork_params[:namespace] @@ -268,14 +206,14 @@ module API at_least_one_of :name, :description, :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :shared_runners_enabled, :container_registry_enabled, - :lfs_enabled, :public, :visibility_level, :public_builds, + :lfs_enabled, :visibility_level, :public_builds, :request_access_enabled, :only_allow_merge_if_build_succeeds, :only_allow_merge_if_all_discussions_are_resolved, :path, :default_branch end put ':id' do authorize_admin_project - attrs = map_public_to_visibility_level(declared_params(include_missing: false)) + attrs = declared_params(include_missing: false) authorize! :rename_project, user_project if attrs[:name].present? authorize! :change_visibility_level, user_project if attrs[:visibility_level].present? diff --git a/lib/api/templates.rb b/lib/api/templates.rb index e23f99256a5..8a2d66efd89 100644 --- a/lib/api/templates.rb +++ b/lib/api/templates.rb @@ -24,7 +24,6 @@ module API /[\<\{\[] (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 @@ -46,74 +45,58 @@ module API end end - { "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 + desc 'Get the list of the available license template' do + detail 'This feature was introduced in GitLab 8.7.' + success ::API::Entities::RepoLicense + end + params do + optional :popular, type: Boolean, desc: 'If passed, returns only popular licenses' + end + get "templates/licenses" do + options = { + featured: declared(params).popular.present? ? true : nil + } + present Licensee::License.all(options), with: ::API::Entities::RepoLicense 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) + desc 'Get the text for a specific license' do + detail 'This feature was introduced in GitLab 8.7.' + success ::API::Entities::RepoLicense + end + params do + requires :name, type: String, desc: 'The name of the template' + end + get "templates/licenses/:name", requirements: { name: /[\w\.-]+/ } do + not_found!('License') unless Licensee::License.find(declared(params).name) - template = parsed_license_template + template = parsed_license_template - present template, with: Entities::RepoLicense - end + present template, with: ::API::Entities::RepoLicense 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 + desc 'Get the list of the available template' do + detail "This feature was introduced in GitLab #{gitlab_version}." + success Entities::TemplatesList + end + get "templates/#{template_type}" do + present klass.all, with: Entities::TemplatesList 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) + desc 'Get the text for a specific template present in local filesystem' do + detail "This feature was introduced in GitLab #{gitlab_version}." + success Entities::Template + end + params do + requires :name, type: String, desc: 'The name of the template' + end + get "templates/#{template_type}/:name" do + new_template = klass.find(declared(params).name) - render_response(template_type, new_template) - end + render_response(template_type, new_template) end end end diff --git a/lib/api/users.rb b/lib/api/users.rb index 0ed468626b7..82ac3886ac3 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -82,7 +82,9 @@ module API end params do requires :email, type: String, desc: 'The email of the user' - requires :password, type: String, desc: 'The password of the new user' + optional :password, type: String, desc: 'The password of the new user' + optional :reset_password, type: Boolean, desc: 'Flag indicating the user will be sent a password reset token' + at_least_one_of :password, :reset_password requires :name, type: String, desc: 'The name of the user' requires :username, type: String, desc: 'The username of the user' use :optional_attributes @@ -94,8 +96,18 @@ module API user_params = declared_params(include_missing: false) identity_attrs = user_params.slice(:provider, :extern_uid) confirm = user_params.delete(:confirm) + user = User.new(user_params.except(:extern_uid, :provider, :reset_password)) + + if user_params.delete(:reset_password) + user.attributes = { + force_random_password: true, + password_expires_at: nil, + created_by_id: current_user.id + } + user.generate_password + user.generate_reset_token + end - user = User.new(user_params.except(:extern_uid, :provider)) user.skip_confirmation! unless confirm if identity_attrs.any? @@ -293,7 +305,7 @@ module API user = User.find_by(id: params[:id]) not_found!('User') unless user - DeleteUserService.new(current_user).execute(user) + ::Users::DestroyService.new(current_user).execute(user) end desc 'Block a user. Available only for admins.' diff --git a/lib/api/v3/deploy_keys.rb b/lib/api/v3/deploy_keys.rb new file mode 100644 index 00000000000..5bbb167755c --- /dev/null +++ b/lib/api/v3/deploy_keys.rb @@ -0,0 +1,122 @@ +module API + module V3 + class DeployKeys < Grape::API + before { authenticate! } + + get "deploy_keys" do + authenticated_as_admin! + + keys = DeployKey.all + present keys, with: ::API::Entities::SSHKey + end + + params do + requires :id, type: String, desc: 'The ID of the project' + end + resource :projects do + before { authorize_admin_project } + + %w(keys deploy_keys).each do |path| + desc "Get a specific project's deploy keys" do + success ::API::Entities::SSHKey + end + get ":id/#{path}" do + present user_project.deploy_keys, with: ::API::Entities::SSHKey + end + + desc 'Get single deploy key' do + success ::API::Entities::SSHKey + end + params do + requires :key_id, type: Integer, desc: 'The ID of the deploy key' + end + get ":id/#{path}/:key_id" do + key = user_project.deploy_keys.find params[:key_id] + present key, with: ::API::Entities::SSHKey + end + + desc 'Add new deploy key to currently authenticated user' do + success ::API::Entities::SSHKey + end + params do + requires :key, type: String, desc: 'The new deploy key' + requires :title, type: String, desc: 'The name of the deploy key' + end + post ":id/#{path}" do + params[:key].strip! + + # Check for an existing key joined to this project + key = user_project.deploy_keys.find_by(key: params[:key]) + if key + present key, with: ::API::Entities::SSHKey + break + end + + # Check for available deploy keys in other projects + key = current_user.accessible_deploy_keys.find_by(key: params[:key]) + if key + user_project.deploy_keys << key + present key, with: ::API::Entities::SSHKey + break + end + + # Create a new deploy key + key = DeployKey.new(declared_params(include_missing: false)) + if key.valid? && user_project.deploy_keys << key + present key, with: ::API::Entities::SSHKey + else + render_validation_error!(key) + end + end + + desc 'Enable a deploy key for a project' do + detail 'This feature was added in GitLab 8.11' + success ::API::Entities::SSHKey + end + params do + requires :key_id, type: Integer, desc: 'The ID of the deploy key' + end + post ":id/#{path}/:key_id/enable" do + key = ::Projects::EnableDeployKeyService.new(user_project, + current_user, declared_params).execute + + if key + present key, with: ::API::Entities::SSHKey + else + not_found!('Deploy Key') + end + end + + desc 'Disable a deploy key for a project' do + detail 'This feature was added in GitLab 8.11' + success ::API::Entities::SSHKey + end + params do + requires :key_id, type: Integer, desc: 'The ID of the deploy key' + end + delete ":id/#{path}/:key_id/disable" do + key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) + key.destroy + + present key.deploy_key, with: ::API::Entities::SSHKey + end + + desc 'Delete deploy key for a project' do + success Key + end + params do + requires :key_id, type: Integer, desc: 'The ID of the deploy key' + end + delete ":id/#{path}/:key_id" do + key = user_project.deploy_keys_projects.find_by(deploy_key_id: params[:key_id]) + if key + key.destroy + else + not_found!('Deploy Key') + end + end + end + end + end + end +end diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb new file mode 100644 index 00000000000..3cc0dc968a8 --- /dev/null +++ b/lib/api/v3/entities.rb @@ -0,0 +1,16 @@ +module API + module V3 + module Entities + class ProjectSnippet < Grape::Entity + expose :id, :title, :file_name + expose :author, using: ::API::Entities::UserBasic + expose :updated_at, :created_at + expose(:expires_at) { |snippet| nil } + + expose :web_url do |snippet, options| + Gitlab::UrlBuilder.build(snippet) + end + end + end + end +end diff --git a/lib/api/v3/issues.rb b/lib/api/v3/issues.rb index be3ecc29449..081d45165e8 100644 --- a/lib/api/v3/issues.rb +++ b/lib/api/v3/issues.rb @@ -50,7 +50,7 @@ module API resource :issues do desc "Get currently authenticated user's issues" do - success Entities::Issue + success ::API::Entities::Issue end params do optional :state, type: String, values: %w[opened closed all], default: 'all', @@ -60,7 +60,7 @@ module API get do issues = find_issues(scope: 'authored') - present paginate(issues), with: Entities::Issue, current_user: current_user + present paginate(issues), with: ::API::Entities::Issue, current_user: current_user end end @@ -69,7 +69,7 @@ module API end resource :groups do desc 'Get a list of group issues' do - success Entities::Issue + success ::API::Entities::Issue end params do optional :state, type: String, values: %w[opened closed all], default: 'opened', @@ -81,7 +81,7 @@ module API issues = find_issues(group_id: group.id, state: params[:state] || 'opened', match_all_labels: true) - present paginate(issues), with: Entities::Issue, current_user: current_user + present paginate(issues), with: ::API::Entities::Issue, current_user: current_user end end @@ -93,7 +93,7 @@ module API desc 'Get a list of project issues' do detail 'iid filter is deprecated have been removed on V4' - success Entities::Issue + success ::API::Entities::Issue end params do optional :state, type: String, values: %w[opened closed all], default: 'all', @@ -106,22 +106,22 @@ module API issues = find_issues(project_id: project.id) - present paginate(issues), with: Entities::Issue, current_user: current_user, project: user_project + present paginate(issues), with: ::API::Entities::Issue, current_user: current_user, project: user_project end desc 'Get a single project issue' do - success Entities::Issue + success ::API::Entities::Issue end params do requires :issue_id, type: Integer, desc: 'The ID of a project issue' end get ":id/issues/:issue_id" do issue = find_project_issue(params[:issue_id]) - present issue, with: Entities::Issue, current_user: current_user, project: user_project + present issue, with: ::API::Entities::Issue, current_user: current_user, project: user_project end desc 'Create a new project issue' do - success Entities::Issue + success ::API::Entities::Issue end params do requires :title, type: String, desc: 'The title of an issue' @@ -153,14 +153,14 @@ module API end if issue.valid? - present issue, with: Entities::Issue, current_user: current_user, project: user_project + present issue, with: ::API::Entities::Issue, current_user: current_user, project: user_project else render_validation_error!(issue) end end desc 'Update an existing issue' do - success Entities::Issue + success ::API::Entities::Issue end params do requires :issue_id, type: Integer, desc: 'The ID of a project issue' @@ -186,14 +186,14 @@ module API declared_params(include_missing: false)).execute(issue) if issue.valid? - present issue, with: Entities::Issue, current_user: current_user, project: user_project + present issue, with: ::API::Entities::Issue, current_user: current_user, project: user_project else render_validation_error!(issue) end end desc 'Move an existing issue' do - success Entities::Issue + success ::API::Entities::Issue end params do requires :issue_id, type: Integer, desc: 'The ID of a project issue' @@ -208,7 +208,7 @@ module API begin issue = ::Issues::MoveService.new(user_project, current_user).execute(issue, new_project) - present issue, with: Entities::Issue, current_user: current_user, project: user_project + present issue, with: ::API::Entities::Issue, current_user: current_user, project: user_project rescue ::Issues::MoveService::MoveError => error render_api_error!(error.message, 400) end diff --git a/lib/api/v3/members.rb b/lib/api/v3/members.rb new file mode 100644 index 00000000000..4e6cb2e3c52 --- /dev/null +++ b/lib/api/v3/members.rb @@ -0,0 +1,134 @@ +module API + module V3 + class Members < Grape::API + include PaginationParams + + before { authenticate! } + + 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 + desc 'Gets a list of group or project members viewable by the authenticated user.' do + success ::API::Entities::Member + end + params do + optional :query, type: String, desc: 'A query string to search for members' + use :pagination + end + get ":id/members" do + source = find_source(source_type, params[:id]) + + users = source.users + users = users.merge(User.search(params[:query])) if params[:query] + + present paginate(users), with: ::API::Entities::Member, source: source + end + + desc 'Gets a member of a group or project.' do + success ::API::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]) + + members = source.members + member = members.find_by!(user_id: params[:user_id]) + + present member.user, with: ::API::Entities::Member, member: member + end + + desc 'Adds a member to a group or project.' do + success ::API::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) + + member = source.members.find_by(user_id: params[:user_id]) + + # 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 + member = source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at]) + end + if member.persisted? && member.valid? + present member.user, with: ::API::Entities::Member, member: member + else + # 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) + render_validation_error!(member) + end + end + + desc 'Updates a member of a group or project.' do + success ::API::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) + + member = source.members.find_by!(user_id: params[:user_id]) + attrs = attributes_for_keys [:access_level, :expires_at] + + if member.update_attributes(attrs) + present member.user, with: ::API::Entities::Member, member: member + else + # 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) + render_validation_error!(member) + end + end + + 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]) + + # This is to ensure back-compatibility but find_by! should be used + # in that casse in 9.0! + member = source.members.find_by(user_id: params[:user_id]) + + # This is to ensure back-compatibility but this should be removed in + # favor of find_by! in 9.0! + not_found!("Member: user_id:#{params[:user_id]}") if source_type == 'group' && member.nil? + + # This is to ensure back-compatibility but 204 behavior should be used + # for all DELETE endpoints in 9.0! + if member.nil? + { message: "Access revoked", id: params[:user_id].to_i } + else + ::Members::DestroyService.new(source, current_user, declared_params).execute + + present member.user, with: ::API::Entities::Member, member: member + end + end + end + end + end + end +end diff --git a/lib/api/v3/merge_requests.rb b/lib/api/v3/merge_requests.rb index 1af70cf58cc..129f9d850e9 100644 --- a/lib/api/v3/merge_requests.rb +++ b/lib/api/v3/merge_requests.rb @@ -39,7 +39,7 @@ module API desc 'List merge requests' do detail 'iid filter is deprecated have been removed on V4' - success Entities::MergeRequest + success ::API::Entities::MergeRequest end params do optional :state, type: String, values: %w[opened closed merged all], default: 'all', @@ -66,11 +66,11 @@ module API end merge_requests = merge_requests.reorder(params[:order_by] => params[:sort]) - present paginate(merge_requests), with: Entities::MergeRequest, current_user: current_user, project: user_project + present paginate(merge_requests), with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project end desc 'Create a merge request' do - success Entities::MergeRequest + success ::API::Entities::MergeRequest end params do requires :title, type: String, desc: 'The title of the merge request' @@ -89,7 +89,7 @@ module API merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute if merge_request.valid? - present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project + present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project else handle_merge_request_errors! merge_request.errors end @@ -114,34 +114,34 @@ module API if status == :deprecated detail DEPRECATION_MESSAGE end - success Entities::MergeRequest + success ::API::Entities::MergeRequest end get path do merge_request = find_merge_request_with_access(params[:merge_request_id]) - present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project + present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project end desc 'Get the commits of a merge request' do - success Entities::RepoCommit + success ::API::Entities::RepoCommit end get "#{path}/commits" do merge_request = find_merge_request_with_access(params[:merge_request_id]) - present merge_request.commits, with: Entities::RepoCommit + present merge_request.commits, with: ::API::Entities::RepoCommit end desc 'Show the merge request changes' do - success Entities::MergeRequestChanges + success ::API::Entities::MergeRequestChanges end get "#{path}/changes" do merge_request = find_merge_request_with_access(params[:merge_request_id]) - present merge_request, with: Entities::MergeRequestChanges, current_user: current_user + present merge_request, with: ::API::Entities::MergeRequestChanges, current_user: current_user end desc 'Update a merge request' do - success Entities::MergeRequest + success ::API::Entities::MergeRequest end params do optional :title, type: String, allow_blank: false, desc: 'The title of the merge request' @@ -162,14 +162,14 @@ module API merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request) if merge_request.valid? - present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project + present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project else handle_merge_request_errors! merge_request.errors end end desc 'Merge a merge request' do - success Entities::MergeRequest + success ::API::Entities::MergeRequest end params do optional :merge_commit_message, type: String, desc: 'Custom merge commit message' @@ -209,11 +209,11 @@ module API .execute(merge_request) end - present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project + present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project end desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do - success Entities::MergeRequest + success ::API::Entities::MergeRequest end post "#{path}/cancel_merge_when_build_succeeds" do merge_request = find_project_merge_request(params[:merge_request_id]) @@ -227,19 +227,19 @@ module API desc 'Get the comments of a merge request' do detail 'Duplicate. DEPRECATED and HAS BEEN REMOVED in V4' - success Entities::MRNote + success ::API::Entities::MRNote end params do use :pagination end get "#{path}/comments" do merge_request = find_merge_request_with_access(params[:merge_request_id]) - present paginate(merge_request.notes.fresh), with: Entities::MRNote + present paginate(merge_request.notes.fresh), with: ::API::Entities::MRNote end desc 'Post a comment to a merge request' do detail 'Duplicate. DEPRECATED and HAS BEEN REMOVED in V4' - success Entities::MRNote + success ::API::Entities::MRNote end params do requires :note, type: String, desc: 'The text of the comment' @@ -256,14 +256,14 @@ module API note = ::Notes::CreateService.new(user_project, current_user, opts).execute if note.save - present note, with: Entities::MRNote + present note, with: ::API::Entities::MRNote else render_api_error!("Failed to save note #{note.errors.messages}", 400) end end desc 'List issues that will be closed on merge' do - success Entities::MRNote + success ::API::Entities::MRNote end params do use :pagination diff --git a/lib/api/v3/project_snippets.rb b/lib/api/v3/project_snippets.rb new file mode 100644 index 00000000000..9f95d4395fa --- /dev/null +++ b/lib/api/v3/project_snippets.rb @@ -0,0 +1,135 @@ +module API + module V3 + class ProjectSnippets < Grape::API + include PaginationParams + + before { authenticate! } + + params do + requires :id, type: String, desc: 'The ID of a project' + end + resource :projects do + helpers do + def handle_project_member_errors(errors) + if errors[:project_access].any? + error!(errors[:project_access], 422) + end + not_found! + end + + def snippets_for_current_user + finder_params = { filter: :by_project, project: user_project } + SnippetsFinder.new.execute(current_user, finder_params) + end + end + + desc 'Get all project snippets' do + success ::API::V3::Entities::ProjectSnippet + end + params do + use :pagination + end + get ":id/snippets" do + present paginate(snippets_for_current_user), with: ::API::V3::Entities::ProjectSnippet + end + + desc 'Get a single project snippet' do + success ::API::V3::Entities::ProjectSnippet + end + params do + requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' + end + get ":id/snippets/:snippet_id" do + snippet = snippets_for_current_user.find(params[:snippet_id]) + present snippet, with: ::API::V3::Entities::ProjectSnippet + end + + desc 'Create a new project snippet' do + success ::API::V3::Entities::ProjectSnippet + end + params do + requires :title, type: String, desc: 'The title of the snippet' + requires :file_name, type: String, desc: 'The file name of the snippet' + requires :code, type: String, desc: 'The content of the snippet' + requires :visibility_level, type: Integer, + values: [Gitlab::VisibilityLevel::PRIVATE, + Gitlab::VisibilityLevel::INTERNAL, + Gitlab::VisibilityLevel::PUBLIC], + desc: 'The visibility level of the snippet' + end + post ":id/snippets" do + authorize! :create_project_snippet, user_project + snippet_params = declared_params.merge(request: request, api: true) + snippet_params[:content] = snippet_params.delete(:code) + + snippet = CreateSnippetService.new(user_project, current_user, snippet_params).execute + + if snippet.persisted? + present snippet, with: ::API::V3::Entities::ProjectSnippet + else + render_validation_error!(snippet) + end + end + + desc 'Update an existing project snippet' do + success ::API::V3::Entities::ProjectSnippet + end + params do + requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' + optional :title, type: String, desc: 'The title of the snippet' + optional :file_name, type: String, desc: 'The file name of the snippet' + optional :code, type: String, desc: 'The content of the snippet' + optional :visibility_level, type: Integer, + values: [Gitlab::VisibilityLevel::PRIVATE, + Gitlab::VisibilityLevel::INTERNAL, + Gitlab::VisibilityLevel::PUBLIC], + desc: 'The visibility level of the snippet' + at_least_one_of :title, :file_name, :code, :visibility_level + end + put ":id/snippets/:snippet_id" do + snippet = snippets_for_current_user.find_by(id: params.delete(:snippet_id)) + not_found!('Snippet') unless snippet + + authorize! :update_project_snippet, snippet + + snippet_params = declared_params(include_missing: false) + snippet_params[:content] = snippet_params.delete(:code) if snippet_params[:code].present? + + UpdateSnippetService.new(user_project, current_user, snippet, + snippet_params).execute + + if snippet.persisted? + present snippet, with: ::API::V3::Entities::ProjectSnippet + else + render_validation_error!(snippet) + end + end + + desc 'Delete a project snippet' + params do + requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' + end + delete ":id/snippets/:snippet_id" do + snippet = snippets_for_current_user.find_by(id: params[:snippet_id]) + not_found!('Snippet') unless snippet + + authorize! :admin_project_snippet, snippet + snippet.destroy + end + + desc 'Get a raw project snippet' + params do + requires :snippet_id, type: Integer, desc: 'The ID of a project snippet' + end + get ":id/snippets/:snippet_id/raw" do + snippet = snippets_for_current_user.find_by(id: params[:snippet_id]) + not_found!('Snippet') unless snippet + + env['api.format'] = :txt + content_type 'text/plain' + present snippet.content + end + end + end + end +end diff --git a/lib/api/v3/projects.rb b/lib/api/v3/projects.rb index bac7d485a22..6796da83f07 100644 --- a/lib/api/v3/projects.rb +++ b/lib/api/v3/projects.rb @@ -74,32 +74,32 @@ module API def present_projects(projects, options = {}) options = options.reverse_merge( - with: Entities::Project, + with: ::API::Entities::Project, current_user: current_user, simple: params[:simple], ) projects = filter_projects(projects) projects = projects.with_statistics if options[:statistics] - options[:with] = Entities::BasicProjectDetails if options[:simple] + options[:with] = ::API::Entities::BasicProjectDetails if options[:simple] present paginate(projects), options end end desc 'Get a list of visible projects for authenticated user' do - success Entities::BasicProjectDetails + success ::API::Entities::BasicProjectDetails end params do use :collection_params end get '/visible' do - entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails + entity = current_user ? ::API::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails present_projects ProjectsFinder.new.execute(current_user), with: entity end desc 'Get a projects list for authenticated user' do - success Entities::BasicProjectDetails + success ::API::Entities::BasicProjectDetails end params do use :collection_params @@ -108,11 +108,11 @@ module API authenticate! present_projects current_user.authorized_projects, - with: Entities::ProjectWithAccess + with: ::API::Entities::ProjectWithAccess end desc 'Get an owned projects list for authenticated user' do - success Entities::BasicProjectDetails + success ::API::Entities::BasicProjectDetails end params do use :collection_params @@ -122,12 +122,12 @@ module API authenticate! present_projects current_user.owned_projects, - with: Entities::ProjectWithAccess, + with: ::API::Entities::ProjectWithAccess, statistics: params[:statistics] end desc 'Gets starred project for the authenticated user' do - success Entities::BasicProjectDetails + success ::API::Entities::BasicProjectDetails end params do use :collection_params @@ -139,7 +139,7 @@ module API end desc 'Get all projects for admin user' do - success Entities::BasicProjectDetails + success ::API::Entities::BasicProjectDetails end params do use :collection_params @@ -148,11 +148,11 @@ module API get '/all' do authenticated_as_admin! - present_projects Project.all, with: Entities::ProjectWithAccess, statistics: params[:statistics] + present_projects Project.all, with: ::API::Entities::ProjectWithAccess, statistics: params[:statistics] end desc 'Search for projects the current user has access to' do - success Entities::Project + success ::API::Entities::Project end params do requires :query, type: String, desc: 'The project name to be searched' @@ -164,11 +164,11 @@ module API projects = search_service.objects('projects', params[:page]) projects = projects.reorder(params[:order_by] => params[:sort]) - present paginate(projects), with: Entities::Project + present paginate(projects), with: ::API::Entities::Project end desc 'Create new project' do - success Entities::Project + success ::API::Entities::Project end params do requires :name, type: String, desc: 'The name of the project' @@ -181,7 +181,7 @@ module API project = ::Projects::CreateService.new(current_user, attrs).execute if project.saved? - present project, with: Entities::Project, + present project, with: ::API::Entities::Project, user_can_admin_project: can?(current_user, :admin_project, project) else if project.errors[:limit_reached].present? @@ -192,7 +192,7 @@ module API end desc 'Create new project for a specified user. Only available to admin users.' do - success Entities::Project + success ::API::Entities::Project end params do requires :name, type: String, desc: 'The name of the project' @@ -210,7 +210,7 @@ module API project = ::Projects::CreateService.new(user, attrs).execute if project.saved? - present project, with: Entities::Project, + present project, with: ::API::Entities::Project, user_can_admin_project: can?(current_user, :admin_project, project) else render_validation_error!(project) @@ -223,26 +223,26 @@ module API end resource :projects, requirements: { id: /[^\/]+/ } do desc 'Get a single project' do - success Entities::ProjectWithAccess + success ::API::Entities::ProjectWithAccess end get ":id" do - entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails + entity = current_user ? ::API::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails present user_project, with: entity, current_user: current_user, user_can_admin_project: can?(current_user, :admin_project, user_project) end desc 'Get events for a single project' do - success Entities::Event + success ::API::Entities::Event end params do use :pagination end get ":id/events" do - present paginate(user_project.events.recent), with: Entities::Event + present paginate(user_project.events.recent), with: ::API::Entities::Event end desc 'Fork new project for the current user or provided namespace.' do - success Entities::Project + success ::API::Entities::Project end params do optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into' @@ -268,13 +268,13 @@ module API if forked_project.errors.any? conflict!(forked_project.errors.messages) else - present forked_project, with: Entities::Project, + present forked_project, with: ::API::Entities::Project, user_can_admin_project: can?(current_user, :admin_project, forked_project) end end desc 'Update an existing project' do - success Entities::Project + success ::API::Entities::Project end params do optional :name, type: String, desc: 'The name of the project' @@ -298,7 +298,7 @@ module API result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute if result[:status] == :success - present user_project, with: Entities::Project, + present user_project, with: ::API::Entities::Project, user_can_admin_project: can?(current_user, :admin_project, user_project) else render_validation_error!(user_project) @@ -306,29 +306,29 @@ module API end desc 'Archive a project' do - success Entities::Project + success ::API::Entities::Project end post ':id/archive' do authorize!(:archive_project, user_project) user_project.archive! - present user_project, with: Entities::Project + present user_project, with: ::API::Entities::Project end desc 'Unarchive a project' do - success Entities::Project + success ::API::Entities::Project end post ':id/unarchive' do authorize!(:archive_project, user_project) user_project.unarchive! - present user_project, with: Entities::Project + present user_project, with: ::API::Entities::Project end desc 'Star a project' do - success Entities::Project + success ::API::Entities::Project end post ':id/star' do if current_user.starred?(user_project) @@ -337,19 +337,19 @@ module API current_user.toggle_star(user_project) user_project.reload - present user_project, with: Entities::Project + present user_project, with: ::API::Entities::Project end end desc 'Unstar a project' do - success Entities::Project + success ::API::Entities::Project end delete ':id/star' do if current_user.starred?(user_project) current_user.toggle_star(user_project) user_project.reload - present user_project, with: Entities::Project + present user_project, with: ::API::Entities::Project else not_modified! end @@ -390,7 +390,7 @@ module API end desc 'Share the project with a group' do - success Entities::ProjectGroupLink + success ::API::Entities::ProjectGroupLink end params do requires :group_id, type: Integer, desc: 'The ID of a group' @@ -412,7 +412,7 @@ module API link = user_project.project_group_links.new(declared_params(include_missing: false)) if link.save - present link, with: Entities::ProjectGroupLink + present link, with: ::API::Entities::ProjectGroupLink else render_api_error!(link.errors.full_messages.first, 409) end @@ -440,7 +440,7 @@ module API end desc 'Get the users list of a project' do - success Entities::UserBasic + success ::API::Entities::UserBasic end params do optional :search, type: String, desc: 'Return list of users matching the search criteria' @@ -450,7 +450,7 @@ module API users = user_project.team.users users = users.search(params[:search]) if params[:search].present? - present paginate(users), with: Entities::UserBasic + present paginate(users), with: ::API::Entities::UserBasic end end end diff --git a/lib/api/v3/templates.rb b/lib/api/v3/templates.rb new file mode 100644 index 00000000000..4c577a8d2b7 --- /dev/null +++ b/lib/api/v3/templates.rb @@ -0,0 +1,122 @@ +module API + module V3 + class Templates < Grape::API + GLOBAL_TEMPLATE_TYPES = { + gitignores: { + klass: Gitlab::Template::GitignoreTemplate, + gitlab_version: 8.8 + }, + gitlab_ci_ymls: { + klass: Gitlab::Template::GitlabCiYmlTemplate, + gitlab_version: 8.9 + }, + dockerfiles: { + klass: Gitlab::Template::DockerfileTemplate, + gitlab_version: 8.15 + } + }.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 has been removed in V4.'.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: ::API::Entities::Template + end + end + + { "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 ::API::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: ::API::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 ::API::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: ::API::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 ::API::Entities::TemplatesList + end + get route do + present klass.all, with: ::API::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 ::API::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 + end +end |