a0101ebf84
Rename column in the database Rename fields related to import/export feature Rename API endpoints Rename documentation links Rename the rest of occurrences in the code Replace the images that contain the words "build succeeds" and docs referencing to them Make sure pipeline is green and nothing is missing. updated doc images renamed only_allow_merge_if_build_succeeds in projects and fixed references more updates fix some spec failures fix rubocop offences fix v3 api spec fix MR specs fixed issues with partials fix MR spec fix alignment add missing v3 to v4 doc wip - refactor v3 endpoints fix specs fix a few typos fix project specs copy entities fully to V3 fix entity error more fixes fix failing specs fixed missing entities in V3 API remove comment updated code based on feedback typo fix spec
395 lines
14 KiB
Ruby
395 lines
14 KiB
Ruby
module API
|
|
# Projects API
|
|
class Projects < Grape::API
|
|
include PaginationParams
|
|
|
|
before { authenticate_non_get! }
|
|
|
|
helpers do
|
|
params :optional_params do
|
|
optional :description, type: String, desc: 'The description of the project'
|
|
optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
|
|
optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled'
|
|
optional :wiki_enabled, type: Boolean, desc: 'Flag indication if the wiki is enabled'
|
|
optional :builds_enabled, type: Boolean, desc: 'Flag indication if builds are enabled'
|
|
optional :snippets_enabled, type: Boolean, desc: 'Flag indication if snippets are enabled'
|
|
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 :visibility_level, type: Integer, values: [
|
|
Gitlab::VisibilityLevel::PRIVATE,
|
|
Gitlab::VisibilityLevel::INTERNAL,
|
|
Gitlab::VisibilityLevel::PUBLIC
|
|
], desc: 'Create a public project. The same as visibility_level = 20.'
|
|
optional :public_builds, type: Boolean, desc: 'Perform public builds'
|
|
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
|
|
optional :only_allow_merge_if_pipeline_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
|
|
end
|
|
|
|
resource :projects do
|
|
helpers do
|
|
params :collection_params do
|
|
use :sort_params
|
|
use :filter_params
|
|
use :pagination
|
|
|
|
optional :simple, type: Boolean, default: false,
|
|
desc: 'Return only the ID, URL, name, and path of each project'
|
|
end
|
|
|
|
params :sort_params do
|
|
optional :order_by, type: String, values: %w[id name path created_at updated_at last_activity_at],
|
|
default: 'created_at', desc: 'Return projects ordered by field'
|
|
optional :sort, type: String, values: %w[asc desc], default: 'desc',
|
|
desc: 'Return projects sorted in ascending and descending order'
|
|
end
|
|
|
|
params :filter_params do
|
|
optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
|
|
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
|
|
optional :statistics, type: Boolean, default: false, desc: 'Include project statistics'
|
|
end
|
|
|
|
params :create_params do
|
|
optional :namespace_id, type: Integer, desc: 'Namespace ID for the new project. Default to the user namespace.'
|
|
optional :import_url, type: String, desc: 'URL from which the project is imported'
|
|
end
|
|
|
|
def present_projects(projects, options = {})
|
|
options = options.reverse_merge(
|
|
with: 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]
|
|
|
|
present paginate(projects), options
|
|
end
|
|
end
|
|
|
|
desc 'Get a list of visible projects for authenticated user' do
|
|
success Entities::BasicProjectDetails
|
|
end
|
|
params do
|
|
use :collection_params
|
|
end
|
|
get do
|
|
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
|
|
success Entities::Project
|
|
end
|
|
params do
|
|
optional :name, type: String, desc: 'The name of the project'
|
|
optional :path, type: String, desc: 'The path of the repository'
|
|
at_least_one_of :name, :path
|
|
use :optional_params
|
|
use :create_params
|
|
end
|
|
post do
|
|
attrs = declared_params(include_missing: false)
|
|
project = ::Projects::CreateService.new(current_user, attrs).execute
|
|
|
|
if project.saved?
|
|
present project, with: Entities::Project,
|
|
user_can_admin_project: can?(current_user, :admin_project, project)
|
|
else
|
|
if project.errors[:limit_reached].present?
|
|
error!(project.errors[:limit_reached], 403)
|
|
end
|
|
render_validation_error!(project)
|
|
end
|
|
end
|
|
|
|
desc 'Create new project for a specified user. Only available to admin users.' do
|
|
success Entities::Project
|
|
end
|
|
params do
|
|
requires :name, type: String, desc: 'The name of the project'
|
|
requires :user_id, type: Integer, desc: 'The ID of a user'
|
|
optional :default_branch, type: String, desc: 'The default branch of the project'
|
|
use :optional_params
|
|
use :create_params
|
|
end
|
|
post "user/:user_id" do
|
|
authenticated_as_admin!
|
|
user = User.find_by(id: params.delete(:user_id))
|
|
not_found!('User') unless user
|
|
|
|
attrs = declared_params(include_missing: false)
|
|
project = ::Projects::CreateService.new(user, attrs).execute
|
|
|
|
if project.saved?
|
|
present project, with: Entities::Project,
|
|
user_can_admin_project: can?(current_user, :admin_project, project)
|
|
else
|
|
render_validation_error!(project)
|
|
end
|
|
end
|
|
end
|
|
|
|
params do
|
|
requires :id, type: String, desc: 'The ID of a project'
|
|
end
|
|
resource :projects, requirements: { id: /[^\/]+/ } do
|
|
desc 'Get a single project' do
|
|
success Entities::ProjectWithAccess
|
|
end
|
|
get ":id" do
|
|
entity = current_user ? Entities::ProjectWithAccess : 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
|
|
end
|
|
params do
|
|
use :pagination
|
|
end
|
|
get ":id/events" do
|
|
present paginate(user_project.events.recent), with: Entities::Event
|
|
end
|
|
|
|
desc 'Fork new project for the current user or provided namespace.' do
|
|
success Entities::Project
|
|
end
|
|
params do
|
|
optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
|
|
end
|
|
post ':id/fork' do
|
|
fork_params = declared_params(include_missing: false)
|
|
namespace_id = fork_params[:namespace]
|
|
|
|
if namespace_id.present?
|
|
fork_params[:namespace] = if namespace_id =~ /^\d+$/
|
|
Namespace.find_by(id: namespace_id)
|
|
else
|
|
Namespace.find_by_path_or_name(namespace_id)
|
|
end
|
|
|
|
unless fork_params[:namespace] && can?(current_user, :create_projects, fork_params[:namespace])
|
|
not_found!('Target Namespace')
|
|
end
|
|
end
|
|
|
|
forked_project = ::Projects::ForkService.new(user_project, current_user, fork_params).execute
|
|
|
|
if forked_project.errors.any?
|
|
conflict!(forked_project.errors.messages)
|
|
else
|
|
present forked_project, with: Entities::Project,
|
|
user_can_admin_project: can?(current_user, :admin_project, forked_project)
|
|
end
|
|
end
|
|
|
|
desc 'Update an existing project' do
|
|
success Entities::Project
|
|
end
|
|
params do
|
|
optional :name, type: String, desc: 'The name of the project'
|
|
optional :default_branch, type: String, desc: 'The default branch of the project'
|
|
optional :path, type: String, desc: 'The path of the repository'
|
|
use :optional_params
|
|
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, :visibility_level, :public_builds,
|
|
:request_access_enabled, :only_allow_merge_if_pipeline_succeeds,
|
|
:only_allow_merge_if_all_discussions_are_resolved, :path,
|
|
:default_branch
|
|
end
|
|
put ':id' do
|
|
authorize_admin_project
|
|
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?
|
|
|
|
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
|
|
|
|
if result[:status] == :success
|
|
present user_project, with: Entities::Project,
|
|
user_can_admin_project: can?(current_user, :admin_project, user_project)
|
|
else
|
|
render_validation_error!(user_project)
|
|
end
|
|
end
|
|
|
|
desc 'Archive a project' do
|
|
success Entities::Project
|
|
end
|
|
post ':id/archive' do
|
|
authorize!(:archive_project, user_project)
|
|
|
|
user_project.archive!
|
|
|
|
present user_project, with: Entities::Project
|
|
end
|
|
|
|
desc 'Unarchive a project' do
|
|
success Entities::Project
|
|
end
|
|
post ':id/unarchive' do
|
|
authorize!(:archive_project, user_project)
|
|
|
|
user_project.unarchive!
|
|
|
|
present user_project, with: Entities::Project
|
|
end
|
|
|
|
desc 'Star a project' do
|
|
success Entities::Project
|
|
end
|
|
post ':id/star' do
|
|
if current_user.starred?(user_project)
|
|
not_modified!
|
|
else
|
|
current_user.toggle_star(user_project)
|
|
user_project.reload
|
|
|
|
present user_project, with: Entities::Project
|
|
end
|
|
end
|
|
|
|
desc 'Unstar a project' do
|
|
success Entities::Project
|
|
end
|
|
post ':id/unstar' do
|
|
if current_user.starred?(user_project)
|
|
current_user.toggle_star(user_project)
|
|
user_project.reload
|
|
|
|
present user_project, with: Entities::Project
|
|
else
|
|
not_modified!
|
|
end
|
|
end
|
|
|
|
desc 'Remove a project'
|
|
delete ":id" do
|
|
authorize! :remove_project, user_project
|
|
::Projects::DestroyService.new(user_project, current_user, {}).async_execute
|
|
|
|
accepted!
|
|
end
|
|
|
|
desc 'Mark this project as forked from another'
|
|
params do
|
|
requires :forked_from_id, type: String, desc: 'The ID of the project it was forked from'
|
|
end
|
|
post ":id/fork/:forked_from_id" do
|
|
authenticated_as_admin!
|
|
|
|
forked_from_project = find_project!(params[:forked_from_id])
|
|
not_found!("Source Project") unless forked_from_project
|
|
|
|
if user_project.forked_from_project.nil?
|
|
user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
|
|
else
|
|
render_api_error!("Project already forked", 409)
|
|
end
|
|
end
|
|
|
|
desc 'Remove a forked_from relationship'
|
|
delete ":id/fork" do
|
|
authorize! :remove_fork_project, user_project
|
|
|
|
if user_project.forked?
|
|
user_project.forked_project_link.destroy
|
|
else
|
|
not_modified!
|
|
end
|
|
end
|
|
|
|
desc 'Share the project with a group' do
|
|
success Entities::ProjectGroupLink
|
|
end
|
|
params do
|
|
requires :group_id, type: Integer, desc: 'The ID of a group'
|
|
requires :group_access, type: Integer, values: Gitlab::Access.values, desc: 'The group access level'
|
|
optional :expires_at, type: Date, desc: 'Share expiration date'
|
|
end
|
|
post ":id/share" do
|
|
authorize! :admin_project, user_project
|
|
group = Group.find_by_id(params[: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
|
|
|
|
link = user_project.project_group_links.new(declared_params(include_missing: false))
|
|
|
|
if link.save
|
|
present link, with: Entities::ProjectGroupLink
|
|
else
|
|
render_api_error!(link.errors.full_messages.first, 409)
|
|
end
|
|
end
|
|
|
|
params do
|
|
requires :group_id, type: Integer, desc: 'The ID of the group'
|
|
end
|
|
delete ":id/share/:group_id" do
|
|
authorize! :admin_project, user_project
|
|
|
|
link = user_project.project_group_links.find_by(group_id: params[:group_id])
|
|
not_found!('Group Link') unless link
|
|
|
|
link.destroy
|
|
end
|
|
|
|
desc 'Upload a file'
|
|
params do
|
|
requires :file, type: File, desc: 'The file to be uploaded'
|
|
end
|
|
post ":id/uploads" do
|
|
::Projects::UploadService.new(user_project, params[:file]).execute
|
|
end
|
|
|
|
desc 'Get the users list of a project' do
|
|
success Entities::UserBasic
|
|
end
|
|
params do
|
|
optional :search, type: String, desc: 'Return list of users matching the search criteria'
|
|
use :pagination
|
|
end
|
|
get ':id/users' do
|
|
users = user_project.team.users
|
|
users = users.search(params[:search]) if params[:search].present?
|
|
|
|
present paginate(users), with: Entities::UserBasic
|
|
end
|
|
|
|
desc 'Start the housekeeping task for a project' do
|
|
detail 'This feature was introduced in GitLab 9.0.'
|
|
end
|
|
post ':id/housekeeping' do
|
|
authorize_admin_project
|
|
|
|
begin
|
|
::Projects::HousekeepingService.new(user_project).execute
|
|
rescue ::Projects::HousekeepingService::LeaseTaken => error
|
|
conflict!(error.message)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|