diff options
Diffstat (limited to 'lib/api')
66 files changed, 697 insertions, 468 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb index 54e5cc5c8d0..2a3033753f7 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -52,8 +52,6 @@ module API api_endpoint = env['api.endpoint'] feature_category = api_endpoint.options[:for].try(:feature_category_for_app, api_endpoint).to_s - header[Gitlab::Metrics::RequestsRackMiddleware::FEATURE_CATEGORY_HEADER] = feature_category - Gitlab::ApplicationContext.push( user: -> { @current_user }, project: -> { @project }, @@ -170,11 +168,11 @@ module API mount ::API::ErrorTracking mount ::API::Events mount ::API::FeatureFlags - mount ::API::FeatureFlagScopes mount ::API::FeatureFlagsUserLists mount ::API::Features mount ::API::Files mount ::API::FreezePeriods + mount ::API::GroupAvatar mount ::API::GroupBoards mount ::API::GroupClusters mount ::API::GroupExport @@ -224,10 +222,12 @@ module API mount ::API::NpmInstancePackages mount ::API::GenericPackages mount ::API::GoProxy + mount ::API::HelmPackages mount ::API::Pages mount ::API::PagesDomains mount ::API::ProjectClusters mount ::API::ProjectContainerRepositories + mount ::API::ProjectDebianDistributions mount ::API::ProjectEvents mount ::API::ProjectExport mount ::API::ProjectImport diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 1ee120f982a..0db5bb82296 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -17,6 +17,10 @@ module API authorize! :download_code, user_project end + rescue_from Gitlab::Git::Repository::NoRepository do + not_found! + end + helpers do params :filter_params do optional :search, type: String, desc: 'Return list of branches matching the search criteria' diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb index 33980b38e2b..c4e0b699524 100644 --- a/lib/api/ci/runner.rb +++ b/lib/api/ci/runner.rb @@ -98,6 +98,9 @@ module API optional :architecture, type: String, desc: %q(Runner's architecture) optional :executor, type: String, desc: %q(Runner's executor) optional :features, type: Hash, desc: %q(Runner's features) + optional :config, type: Hash, desc: %q(Runner's config) do + optional :gpus, type: String, desc: %q(GPUs enabled) + end end optional :session, type: Hash, desc: %q(Runner's session data) do optional :url, type: String, desc: %q(Session's url) @@ -165,7 +168,6 @@ module API params do requires :token, type: String, desc: %q(Runners's authentication token) requires :id, type: Integer, desc: %q(Job's ID) - optional :trace, type: String, desc: %q(Job's full trace) optional :state, type: String, desc: %q(Job's status: success, failed) optional :checksum, type: String, desc: %q(Job's trace CRC32 checksum) optional :failure_reason, type: String, desc: %q(Job's failure_reason) diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index e199111c975..27fee7fdea2 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -96,10 +96,8 @@ module API protected: user_project.protected_for?(ref) ) - optional_attributes = - attributes_for_keys(%w[target_url description coverage]) - - status.update(optional_attributes) if optional_attributes.any? + updatable_optional_attributes = %w[target_url description coverage] + status.assign_attributes(attributes_for_keys(updatable_optional_attributes)) if status.valid? status.update_older_statuses_retried! if Feature.enabled?(:ci_fix_commit_status_retried, user_project, default_enabled: :yaml) diff --git a/lib/api/commits.rb b/lib/api/commits.rb index bd9f83ac24c..541a37b0abe 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true - require 'mime/types' module API @@ -41,6 +40,7 @@ module API optional :with_stats, type: Boolean, desc: 'Stats about each commit will be added to the response' optional :first_parent, type: Boolean, desc: 'Only include the first parent of merges' optional :order, type: String, desc: 'List commits in order', default: 'default', values: %w[default topo] + optional :trailers, type: Boolean, desc: 'Parse and include Git trailers for every commit', default: false use :pagination end get ':id/repository/commits' do @@ -62,7 +62,8 @@ module API after: after, all: all, first_parent: first_parent, - order: order) + order: order, + trailers: params[:trailers]) serializer = with_stats ? Entities::CommitWithStats : Entities::Commit @@ -203,6 +204,7 @@ module API requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag to be cherry picked' requires :branch, type: String, desc: 'The name of the branch', allow_blank: false optional :dry_run, type: Boolean, default: false, desc: "Does not commit any changes" + optional :message, type: String, desc: 'A custom commit message to use for the picked commit' end post ':id/repository/commits/:sha/cherry_pick', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do authorize_push_to_branch!(params[:branch]) @@ -216,7 +218,8 @@ module API commit: commit, start_branch: params[:branch], branch_name: params[:branch], - dry_run: params[:dry_run] + dry_run: params[:dry_run], + message: params[:message] } result = ::Commits::CherryPickService diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb index 115a6b8ac4f..7b3750b37ee 100644 --- a/lib/api/composer_packages.rb +++ b/lib/api/composer_packages.rb @@ -137,7 +137,7 @@ module API bad_request! end - track_package_event('push_package', :composer) + track_package_event('push_package', :composer, project: authorized_user_project, user: current_user, namespace: authorized_user_project.namespace) ::Packages::Composer::CreatePackageService .new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job)) @@ -161,7 +161,7 @@ module API not_found! unless metadata - track_package_event('pull_package', :composer) + track_package_event('pull_package', :composer, project: unauthorized_user_project, namespace: unauthorized_user_project.namespace) send_git_archive unauthorized_user_project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true end diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb index eb762be8285..3194cdebde8 100644 --- a/lib/api/concerns/packages/conan_endpoints.rb +++ b/lib/api/concerns/packages/conan_endpoints.rb @@ -255,7 +255,7 @@ module API delete do authorize!(:destroy_package, project) - track_package_event('delete_package', :conan, category: 'API::ConanPackages') + track_package_event('delete_package', :conan, category: 'API::ConanPackages', user: current_user, project: project, namespace: project.namespace) package.destroy end diff --git a/lib/api/concerns/packages/debian_distribution_endpoints.rb b/lib/api/concerns/packages/debian_distribution_endpoints.rb new file mode 100644 index 00000000000..4670c3e3521 --- /dev/null +++ b/lib/api/concerns/packages/debian_distribution_endpoints.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +module API + module Concerns + module Packages + module DebianDistributionEndpoints + extend ActiveSupport::Concern + + included do + include PaginationParams + + feature_category :package_registry + + helpers ::API::Helpers::PackagesHelpers + helpers ::API::Helpers::Packages::BasicAuthHelpers + include ::API::Helpers::Authentication + + namespace 'debian_distributions' do + helpers do + params :optional_distribution_params do + optional :suite, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Suite' + optional :origin, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Origin' + optional :label, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Label' + optional :version, type: String, regexp: Gitlab::Regex.debian_version_regex, desc: 'The Debian Version' + optional :description, type: String, desc: 'The Debian Description' + optional :valid_time_duration_seconds, type: Integer, desc: 'The duration before the Release file should be considered expired by the client' + + optional :components, type: Array[String], + coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, + regexp: Gitlab::Regex.debian_component_regex, + desc: 'The list of Components' + optional :architectures, type: Array[String], + coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, + regexp: Gitlab::Regex.debian_architecture_regex, + desc: 'The list of Architectures' + end + end + + authenticate_with do |accept| + accept.token_types(:personal_access_token, :deploy_token, :job_token) + .sent_through(:http_basic_auth) + end + + content_type :json, 'application/json' + format :json + + # POST {projects|groups}/:id/debian_distributions + desc 'Create a Debian Distribution' do + detail 'This feature was introduced in 14.0' + success ::API::Entities::Packages::Debian::Distribution + end + + params do + requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename' + use :optional_distribution_params + end + post '/' do + authorize_create_package!(project_or_group) + + distribution_params = declared_params(include_missing: false) + result = ::Packages::Debian::CreateDistributionService.new(project_or_group, current_user, distribution_params).execute + distribution = result.payload[:distribution] + + if result.success? + present distribution, with: ::API::Entities::Packages::Debian::Distribution + else + render_validation_error!(distribution) + end + end + + # GET {projects|groups}/:id/debian_distributions + desc 'Get a list of Debian Distributions' do + detail 'This feature was introduced in 14.0' + success ::API::Entities::Packages::Debian::Distribution + end + + params do + use :pagination + optional :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename' + use :optional_distribution_params + end + get '/' do + distribution_params = declared_params(include_missing: false) + distributions = ::Packages::Debian::DistributionsFinder.new(project_or_group, distribution_params).execute + + present paginate(distributions), with: ::API::Entities::Packages::Debian::Distribution + end + + # GET {projects|groups}/:id/debian_distributions/:codename + desc 'Get a Debian Distribution' do + detail 'This feature was introduced in 14.0' + success ::API::Entities::Packages::Debian::Distribution + end + + params do + requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename' + end + get '/:codename' do + distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last! + + present distribution, with: ::API::Entities::Packages::Debian::Distribution + end + + # PUT {projects|groups}/:id/debian_distributions/:codename + desc 'Update a Debian Distribution' do + detail 'This feature was introduced in 14.0' + success ::API::Entities::Packages::Debian::Distribution + end + + params do + requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename' + use :optional_distribution_params + end + put '/:codename' do + authorize_create_package!(project_or_group) + + distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last! + distribution_params = declared_params(include_missing: false).except(:codename) + result = ::Packages::Debian::UpdateDistributionService.new(distribution, distribution_params).execute + distribution = result.payload[:distribution] + + if result.success? + present distribution, with: ::API::Entities::Packages::Debian::Distribution + else + render_validation_error!(distribution) + end + end + + # DELETE {projects|groups}/:id/debian_distributions/:codename + desc 'Delete a Debian Distribution' do + detail 'This feature was introduced in 14.0' + end + + params do + requires :codename, type: String, regexp: Gitlab::Regex.debian_distribution_regex, desc: 'The Debian Codename' + use :optional_distribution_params + end + delete '/:codename' do + authorize_destroy_package!(project_or_group) + + distribution = ::Packages::Debian::DistributionsFinder.new(project_or_group, codename: params[:codename]).execute.last! + + accepted! if distribution.destroy + + render_api_error!('Failed to delete distribution', 400) + end + end + end + end + end + end +end diff --git a/lib/api/concerns/packages/debian_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb index 6fc7c439464..c79ae3068b4 100644 --- a/lib/api/concerns/packages/debian_endpoints.rb +++ b/lib/api/concerns/packages/debian_package_endpoints.rb @@ -3,7 +3,7 @@ module API module Concerns module Packages - module DebianEndpoints + module DebianPackageEndpoints extend ActiveSupport::Concern DISTRIBUTION_REGEX = %r{[a-zA-Z0-9][a-zA-Z0-9.-]*}.freeze @@ -32,23 +32,17 @@ module API helpers ::API::Helpers::PackagesHelpers helpers ::API::Helpers::Packages::BasicAuthHelpers + include ::API::Helpers::Authentication - format :txt - content_type :txt, 'text/plain' - - rescue_from ArgumentError do |e| - render_api_error!(e.message, 400) - end - - rescue_from ActiveRecord::RecordInvalid do |e| - render_api_error!(e.message, 400) - end + namespace 'packages/debian' do + authenticate_with do |accept| + accept.token_types(:personal_access_token, :deploy_token, :job_token) + .sent_through(:http_basic_auth) + end - before do - require_packages_enabled! - end + format :txt + content_type :txt, 'text/plain' - namespace 'packages/debian' do params do requires :distribution, type: String, desc: 'The Debian Codename', regexp: Gitlab::Regex.debian_distribution_regex end @@ -59,7 +53,7 @@ module API detail 'This feature was introduced in GitLab 13.5' end - route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true + route_setting :authentication, authenticate_non_public: true get 'Release.gpg' do not_found! end @@ -69,7 +63,7 @@ module API detail 'This feature was introduced in GitLab 13.5' end - route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true + route_setting :authentication, authenticate_non_public: true get 'Release' do # https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286 'TODO Release' @@ -80,7 +74,7 @@ module API detail 'This feature was introduced in GitLab 13.5' end - route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true + route_setting :authentication, authenticate_non_public: true get 'InRelease' do not_found! end @@ -96,7 +90,7 @@ module API detail 'This feature was introduced in GitLab 13.5' end - route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true + route_setting :authentication, authenticate_non_public: true get 'Packages' do # https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286 'TODO Packages' @@ -119,7 +113,7 @@ module API detail 'This feature was introduced in GitLab 13.5' end - route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth, authenticate_non_public: true + route_setting :authentication, authenticate_non_public: true get ':file_name', requirements: FILE_NAME_REQUIREMENTS do # https://gitlab.com/gitlab-org/gitlab/-/issues/5835#note_414103286 'TODO File' diff --git a/lib/api/concerns/packages/nuget_endpoints.rb b/lib/api/concerns/packages/nuget_endpoints.rb index 5364eeb1880..208daeb3037 100644 --- a/lib/api/concerns/packages/nuget_endpoints.rb +++ b/lib/api/concerns/packages/nuget_endpoints.rb @@ -58,7 +58,8 @@ module API end get 'index', format: :json do authorize_read_package!(project_or_group) - track_package_event('cli_metadata', :nuget, category: 'API::NugetPackages') + + track_package_event('cli_metadata', :nuget, **snowplow_gitlab_standard_context.merge(category: 'API::NugetPackages')) present ::Packages::Nuget::ServiceIndexPresenter.new(project_or_group), with: ::API::Entities::Nuget::ServiceIndex @@ -117,7 +118,7 @@ module API results = search_packages(params[:q], search_options) - track_package_event('search_package', :nuget, category: 'API::NugetPackages') + track_package_event('search_package', :nuget, **snowplow_gitlab_standard_context.merge(category: 'API::NugetPackages')) present ::Packages::Nuget::SearchResultsPresenter.new(results), with: ::API::Entities::Nuget::SearchResults diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb index 06edab662bf..c6116a8b28f 100644 --- a/lib/api/debian_group_packages.rb +++ b/lib/api/debian_group_packages.rb @@ -7,6 +7,14 @@ module API end resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + rescue_from ArgumentError do |e| + render_api_error!(e.message, 400) + end + + rescue_from ActiveRecord::RecordInvalid do |e| + render_api_error!(e.message, 400) + end + before do require_packages_enabled! @@ -16,7 +24,7 @@ module API end namespace ':id/-' do - include ::API::Concerns::Packages::DebianEndpoints + include ::API::Concerns::Packages::DebianPackageEndpoints end end end diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb index 0ed828fd639..70ddf9dea37 100644 --- a/lib/api/debian_project_packages.rb +++ b/lib/api/debian_project_packages.rb @@ -7,7 +7,15 @@ module API end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - before do + rescue_from ArgumentError do |e| + render_api_error!(e.message, 400) + end + + rescue_from ActiveRecord::RecordInvalid do |e| + render_api_error!(e.message, 400) + end + + after_validation do require_packages_enabled! not_found! unless ::Feature.enabled?(:debian_packages, user_project) @@ -16,13 +24,20 @@ module API end namespace ':id' do - include ::API::Concerns::Packages::DebianEndpoints + helpers do + def project_or_group + user_project + end + end + + include ::API::Concerns::Packages::DebianPackageEndpoints params do requires :file_name, type: String, desc: 'The file name' end namespace 'packages/debian/:file_name', requirements: FILE_NAME_REQUIREMENTS do + format :txt content_type :json, Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE # PUT {projects|groups}/:id/packages/debian/:file_name @@ -35,8 +50,22 @@ module API authorize_upload!(authorized_user_project) bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:debian_max_file_size, params[:file].size) - track_package_event('push_package', :debian) + file_params = { + file: params['file'], + file_name: params['file_name'], + file_sha1: params['file.sha1'], + file_md5: params['file.md5'] + } + + package = ::Packages::Debian::FindOrCreateIncomingService.new(authorized_user_project, current_user).execute + + package_file = ::Packages::Debian::CreatePackageFileService.new(package, file_params).execute + + if params['file_name'].end_with? '.changes' + ::Packages::Debian::ProcessChangesWorker.perform_async(package_file.id, current_user.id) # rubocop:disable CodeReuse/Worker + end + track_package_event('push_package', :debian, user: current_user, project: authorized_user_project, namespace: authorized_user_project.namespace) created! rescue ObjectStorage::RemoteStoreError => e Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: authorized_user_project.id }) diff --git a/lib/api/entities/basic_project_details.rb b/lib/api/entities/basic_project_details.rb index 2de49d6ed40..c75b74b4368 100644 --- a/lib/api/entities/basic_project_details.rb +++ b/lib/api/entities/basic_project_details.rb @@ -4,15 +4,13 @@ module API module Entities class BasicProjectDetails < Entities::ProjectIdentity include ::API::ProjectsRelationBuilder + include Gitlab::Utils::StrongMemoize expose :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) } # Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770 - expose :tag_list do |project| - # Tags is a preloaded association. If we perform then sorting - # through the database, it will trigger a new query, ending up - # in an N+1 if we have several projects - project.tags.pluck(:name).sort # rubocop:disable CodeReuse/ActiveRecord - end + + expose :topic_names, as: :tag_list + expose :topic_names, as: :topics expose :ssh_url_to_repo, :http_url_to_repo, :web_url, :readme_url @@ -40,16 +38,29 @@ module API # rubocop: disable CodeReuse/ActiveRecord def self.preload_relation(projects_relation, options = {}) - # Preloading tags, should be done with using only `:tags`, - # as `:tags` are defined as: `has_many :tags, through: :taggings` - # N+1 is solved then by using `subject.tags.map(&:name)` + # Preloading topics, should be done with using only `:topics`, + # as `:topics` are defined as: `has_many :topics, through: :taggings` + # N+1 is solved then by using `subject.topics.map(&:name)` # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20555 projects_relation.preload(:project_feature, :route) - .preload(:import_state, :tags) + .preload(:import_state, :topics) .preload(:auto_devops) .preload(namespace: [:route, :owner]) end # rubocop: enable CodeReuse/ActiveRecord + + private + + alias_method :project, :object + + def topic_names + # Topics is a preloaded association. If we perform then sorting + # through the database, it will trigger a new query, ending up + # in an N+1 if we have several projects + strong_memoize(:topic_names) do + project.topics.pluck(:name).sort # rubocop:disable CodeReuse/ActiveRecord + end + end end end end diff --git a/lib/api/entities/commit.rb b/lib/api/entities/commit.rb index 3eaf896f1ac..fd23c23b980 100644 --- a/lib/api/entities/commit.rb +++ b/lib/api/entities/commit.rb @@ -9,6 +9,7 @@ module API expose :safe_message, as: :message expose :author_name, :author_email, :authored_date expose :committer_name, :committer_email, :committed_date + expose :trailers expose :web_url do |commit, _options| Gitlab::UrlBuilder.build(commit) diff --git a/lib/api/entities/group_detail.rb b/lib/api/entities/group_detail.rb index e63a3fc1334..408254a89be 100644 --- a/lib/api/entities/group_detail.rb +++ b/lib/api/entities/group_detail.rb @@ -29,11 +29,7 @@ module API end def projects_limit - if ::Feature.enabled?(:limit_projects_in_groups_api, default_enabled: true) - GroupProjectsFinder::DEFAULT_PROJECTS_LIMIT - else - nil - end + GroupProjectsFinder::DEFAULT_PROJECTS_LIMIT end end end diff --git a/lib/api/entities/issue_basic.rb b/lib/api/entities/issue_basic.rb index d27cc5498bd..6c332870228 100644 --- a/lib/api/entities/issue_basic.rb +++ b/lib/api/entities/issue_basic.rb @@ -23,7 +23,7 @@ module API expose :issue_type, as: :type, format_with: :upcase, - documentation: { type: "String", desc: "One of #{Issue.issue_types.keys.map(&:upcase)}" } + documentation: { type: "String", desc: "One of #{::Issue.issue_types.keys.map(&:upcase)}" } expose :assignee, using: ::API::Entities::UserBasic do |issue| issue.assignees.first diff --git a/lib/api/entities/label_basic.rb b/lib/api/entities/label_basic.rb index 00ecea26ec3..ed52688638e 100644 --- a/lib/api/entities/label_basic.rb +++ b/lib/api/entities/label_basic.rb @@ -3,7 +3,7 @@ module API module Entities class LabelBasic < Grape::Entity - expose :id, :name, :color, :description, :description_html, :text_color, :remove_on_close + expose :id, :name, :color, :description, :description_html, :text_color end end end diff --git a/lib/api/entities/merge_request_basic.rb b/lib/api/entities/merge_request_basic.rb index cf8d03bf176..d5cf2f653db 100644 --- a/lib/api/entities/merge_request_basic.rb +++ b/lib/api/entities/merge_request_basic.rb @@ -36,7 +36,11 @@ module API merge_request.labels.map(&:title).sort end end - expose :work_in_progress?, as: :work_in_progress + expose :draft?, as: :draft + + # [Deprecated] see draft + # + expose :draft?, as: :work_in_progress expose :milestone, using: Entities::Milestone expose :merge_when_pipeline_succeeds diff --git a/lib/api/entities/package.rb b/lib/api/entities/package.rb index 2f60a0bf6bd..1efd457aa5f 100644 --- a/lib/api/entities/package.rb +++ b/lib/api/entities/package.rb @@ -25,8 +25,12 @@ module API expose :status expose :_links do - expose :web_path do |package| - ::Gitlab::Routing.url_helpers.project_package_path(package.project, package) + expose :web_path do |package, opts| + if package.infrastructure_package? + ::Gitlab::Routing.url_helpers.namespace_project_infrastructure_registry_path(opts[:namespace], package.project, package) + else + ::Gitlab::Routing.url_helpers.project_package_path(package.project, package) + end end expose :delete_api_path, if: can_destroy(:package, &:project) do |package| diff --git a/lib/api/entities/packages/debian/distribution.rb b/lib/api/entities/packages/debian/distribution.rb new file mode 100644 index 00000000000..97a3c479f40 --- /dev/null +++ b/lib/api/entities/packages/debian/distribution.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module API + module Entities + module Packages + module Debian + class Distribution < Grape::Entity + expose :id + expose :codename + expose :suite + expose :origin + expose :label + expose :version + expose :description + expose :valid_time_duration_seconds + + expose :component_names, as: :components + expose :architecture_names, as: :architectures + end + end + end + end +end diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index 442013c07dd..68d91fc6970 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -43,7 +43,6 @@ module API expose :visibility expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group } expose :resolve_outdated_diff_discussions - expose :container_registry_enabled expose :container_expiration_policy, using: Entities::ContainerExpirationPolicy, if: -> (project, _) { project.container_expiration_policy } @@ -54,6 +53,13 @@ module API expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) } expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } + expose(:container_registry_enabled) do |project, options| + if ::Feature.enabled?(:read_container_registry_access_level, project.namespace, default_enabled: :yaml) + project.feature_available?(:container_registry, options[:current_user]) + else + project.read_attribute(:container_registry_enabled) + end + end expose :service_desk_enabled expose :service_desk_address @@ -89,6 +95,7 @@ module API expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :ci_default_git_depth expose :ci_forward_deployment_enabled + expose :ci_job_token_scope_enabled expose :public_builds, as: :public_jobs expose :build_git_strategy, if: lambda { |project, options| options[:user_can_admin_project] } do |project, options| project.build_allow_git_fetch ? 'fetch' : 'clone' @@ -108,6 +115,7 @@ module API expose :remove_source_branch_after_merge expose :printing_merge_request_link_enabled expose :merge_method + expose :squash_option expose :suggestion_commit_message expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) { options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project) @@ -120,12 +128,13 @@ module API expose :repository_storage, if: ->(project, options) { Ability.allowed?(options[:current_user], :change_repository_storage, project) } + expose :keep_latest_artifacts_available?, as: :keep_latest_artifact # rubocop: disable CodeReuse/ActiveRecord def self.preload_relation(projects_relation, options = {}) - # Preloading tags, should be done with using only `:tags`, - # as `:tags` are defined as: `has_many :tags, through: :taggings` - # N+1 is solved then by using `subject.tags.map(&:name)` + # Preloading topics, should be done with using only `:topics`, + # as `:topics` are defined as: `has_many :topics, through: :taggings` + # N+1 is solved then by using `subject.topics.map(&:name)` # MR describing the solution: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/20555 super(projects_relation).preload(group: :namespace_settings) .preload(:ci_cd_settings) @@ -136,7 +145,7 @@ module API .preload(project_group_links: { group: :route }, fork_network: :root_project, fork_network_member: :forked_from_project, - forked_from_project: [:route, :forks, :tags, :group, :project_feature, namespace: [:route, :owner]]) + forked_from_project: [:route, :topics, :group, :project_feature, namespace: [:route, :owner]]) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/lib/api/entities/project_repository_storage.rb b/lib/api/entities/project_repository_storage.rb new file mode 100644 index 00000000000..0816bebde2c --- /dev/null +++ b/lib/api/entities/project_repository_storage.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module API + module Entities + class ProjectRepositoryStorage < Grape::Entity + include Gitlab::Routing + + expose :disk_path do |project| + project.repository.disk_path + end + + expose :id, as: :project_id + expose :repository_storage, :created_at + end + end +end diff --git a/lib/api/entities/runner.rb b/lib/api/entities/runner.rb index 6165b54cddb..e78f14cf920 100644 --- a/lib/api/entities/runner.rb +++ b/lib/api/entities/runner.rb @@ -8,6 +8,7 @@ module API expose :ip_address expose :active expose :instance_type?, as: :is_shared + expose :runner_type expose :name expose :online?, as: :online expose :status diff --git a/lib/api/entities/snippet.rb b/lib/api/entities/snippet.rb index f05e593a302..af885aaf0eb 100644 --- a/lib/api/entities/snippet.rb +++ b/lib/api/entities/snippet.rb @@ -5,16 +5,22 @@ module API class Snippet < BasicSnippet expose :author, using: Entities::UserBasic expose :file_name do |snippet| - snippet.file_name_on_repo || snippet.file_name + snippet_files.first || snippet.file_name end expose :files do |snippet, options| - snippet.list_files.map do |file| + snippet_files.map do |file| { path: file, raw_url: Gitlab::UrlBuilder.build(snippet, file: file, ref: snippet.repository.root_ref) } end end + + private + + def snippet_files + @snippet_files ||= object.list_files + end end end end diff --git a/lib/api/entities/user_preferences.rb b/lib/api/entities/user_preferences.rb index 7a6df9b6c59..ceee6c610d3 100644 --- a/lib/api/entities/user_preferences.rb +++ b/lib/api/entities/user_preferences.rb @@ -3,7 +3,7 @@ module API module Entities class UserPreferences < Grape::Entity - expose :id, :user_id, :view_diffs_file_by_file + expose :id, :user_id, :view_diffs_file_by_file, :show_whitespace_in_diffs end end end diff --git a/lib/api/feature_flag_scopes.rb b/lib/api/feature_flag_scopes.rb deleted file mode 100644 index 3f3bf4d9f42..00000000000 --- a/lib/api/feature_flag_scopes.rb +++ /dev/null @@ -1,160 +0,0 @@ -# frozen_string_literal: true - -module API - class FeatureFlagScopes < ::API::Base - include PaginationParams - - ENVIRONMENT_SCOPE_ENDPOINT_REQUIREMENTS = FeatureFlags::FEATURE_FLAG_ENDPOINT_REQUIREMENTS - .merge(environment_scope: API::NO_SLASH_URL_PART_REGEX) - - feature_category :feature_flags - - before do - authorize_read_feature_flags! - end - - params do - requires :id, type: String, desc: 'The ID of a project' - end - resource 'projects/:id', requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - resource :feature_flag_scopes do - desc 'Get all effective feature flags under the environment' do - detail 'This feature was introduced in GitLab 12.5' - success ::API::Entities::FeatureFlag::DetailedLegacyScope - end - params do - requires :environment, type: String, desc: 'The environment name' - end - get do - present scopes_for_environment, with: ::API::Entities::FeatureFlag::DetailedLegacyScope - end - end - - params do - requires :name, type: String, desc: 'The name of the feature flag' - end - resource 'feature_flags/:name', requirements: FeatureFlags::FEATURE_FLAG_ENDPOINT_REQUIREMENTS do - resource :scopes do - desc 'Get all scopes of a feature flag' do - detail 'This feature was introduced in GitLab 12.5' - success ::API::Entities::FeatureFlag::LegacyScope - end - params do - use :pagination - end - get do - present paginate(feature_flag.scopes), with: ::API::Entities::FeatureFlag::LegacyScope - end - - desc 'Create a scope of a feature flag' do - detail 'This feature was introduced in GitLab 12.5' - success ::API::Entities::FeatureFlag::LegacyScope - end - params do - requires :environment_scope, type: String, desc: 'The environment scope of the scope' - requires :active, type: Boolean, desc: 'Whether the scope is active' - requires :strategies, type: JSON, desc: 'The strategies of the scope' - end - post do - authorize_update_feature_flag! - - result = ::FeatureFlags::UpdateService - .new(user_project, current_user, scopes_attributes: [declared_params]) - .execute(feature_flag) - - if result[:status] == :success - present scope, with: ::API::Entities::FeatureFlag::LegacyScope - else - render_api_error!(result[:message], result[:http_status]) - end - end - - params do - requires :environment_scope, type: String, desc: 'URL-encoded environment scope' - end - resource ':environment_scope', requirements: ENVIRONMENT_SCOPE_ENDPOINT_REQUIREMENTS do - desc 'Get a scope of a feature flag' do - detail 'This feature was introduced in GitLab 12.5' - success ::API::Entities::FeatureFlag::LegacyScope - end - get do - present scope, with: ::API::Entities::FeatureFlag::LegacyScope - end - - desc 'Update a scope of a feature flag' do - detail 'This feature was introduced in GitLab 12.5' - success ::API::Entities::FeatureFlag::LegacyScope - end - params do - optional :active, type: Boolean, desc: 'Whether the scope is active' - optional :strategies, type: JSON, desc: 'The strategies of the scope' - end - put do - authorize_update_feature_flag! - - scope_attributes = declared_params.merge(id: scope.id) - - result = ::FeatureFlags::UpdateService - .new(user_project, current_user, scopes_attributes: [scope_attributes]) - .execute(feature_flag) - - if result[:status] == :success - updated_scope = result[:feature_flag].scopes - .find { |scope| scope.environment_scope == params[:environment_scope] } - - present updated_scope, with: ::API::Entities::FeatureFlag::LegacyScope - else - render_api_error!(result[:message], result[:http_status]) - end - end - - desc 'Delete a scope from a feature flag' do - detail 'This feature was introduced in GitLab 12.5' - success ::API::Entities::FeatureFlag::LegacyScope - end - delete do - authorize_update_feature_flag! - - param = { scopes_attributes: [{ id: scope.id, _destroy: true }] } - - result = ::FeatureFlags::UpdateService - .new(user_project, current_user, param) - .execute(feature_flag) - - if result[:status] == :success - status :no_content - else - render_api_error!(result[:message], result[:http_status]) - end - end - end - end - end - end - - helpers do - def authorize_read_feature_flags! - authorize! :read_feature_flag, user_project - end - - def authorize_update_feature_flag! - authorize! :update_feature_flag, feature_flag - end - - def feature_flag - @feature_flag ||= user_project.operations_feature_flags - .find_by_name!(params[:name]) - end - - def scope - @scope ||= feature_flag.scopes - .find_by_environment_scope!(CGI.unescape(params[:environment_scope])) - end - - def scopes_for_environment - Operations::FeatureFlagScope - .for_unleash_client(user_project, params[:environment]) - end - end - end -end diff --git a/lib/api/feature_flags.rb b/lib/api/feature_flags.rb index 6fdc4535be3..fb5858bc10b 100644 --- a/lib/api/feature_flags.rb +++ b/lib/api/feature_flags.rb @@ -90,56 +90,11 @@ module API end get do authorize_read_feature_flag! + exclude_legacy_flags_check! present_entity(feature_flag) end - desc 'Enable a strategy for a feature flag on an environment' do - detail 'This feature was introduced in GitLab 12.5' - success ::API::Entities::FeatureFlag - end - params do - requires :environment_scope, type: String, desc: 'The environment scope of the feature flag' - requires :strategy, type: JSON, desc: 'The strategy to be enabled on the scope' - end - post :enable do - not_found! unless Feature.enabled?(:feature_flag_api, user_project) - render_api_error!('Version 2 flags not supported', :unprocessable_entity) if new_version_flag_present? - - result = ::FeatureFlags::EnableService - .new(user_project, current_user, params).execute - - if result[:status] == :success - status :ok - present_entity(result[:feature_flag]) - else - render_api_error!(result[:message], result[:http_status]) - end - end - - desc 'Disable a strategy for a feature flag on an environment' do - detail 'This feature is going to be introduced in GitLab 12.5 if `feature_flag_api` feature flag is removed' - success ::API::Entities::FeatureFlag - end - params do - requires :environment_scope, type: String, desc: 'The environment scope of the feature flag' - requires :strategy, type: JSON, desc: 'The strategy to be disabled on the scope' - end - post :disable do - not_found! unless Feature.enabled?(:feature_flag_api, user_project) - render_api_error!('Version 2 flags not supported', :unprocessable_entity) if feature_flag.new_version_flag? - - result = ::FeatureFlags::DisableService - .new(user_project, current_user, params).execute - - if result[:status] == :success - status :ok - present_entity(result[:feature_flag]) - else - render_api_error!(result[:message], result[:http_status]) - end - end - desc 'Update a feature flag' do detail 'This feature was introduced in GitLab 13.2' success ::API::Entities::FeatureFlag @@ -162,6 +117,7 @@ module API end put do authorize_update_feature_flag! + exclude_legacy_flags_check! render_api_error!('PUT operations are not supported for legacy feature flags', :unprocessable_entity) if feature_flag.legacy_flag? attrs = declared_params(include_missing: false) @@ -232,6 +188,10 @@ module API @feature_flag ||= user_project.operations_feature_flags.find_by_name!(params[:feature_flag_name]) end + def project + @project ||= feature_flag.project + end + def new_version_flag_present? user_project.operations_feature_flags.new_version_flag.find_by_name(params[:name]).present? end @@ -245,6 +205,12 @@ module API hash[key] = yield(hash[key]) if hash.key?(key) hash end + + def exclude_legacy_flags_check! + if feature_flag.legacy_flag? + not_found! + end + end end end end diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb index d0680ad7bc5..a57d6bbcd2a 100644 --- a/lib/api/generic_packages.rb +++ b/lib/api/generic_packages.rb @@ -62,7 +62,7 @@ module API authorize_upload!(project) bad_request!('File is too large') if max_file_size_exceeded? - ::Gitlab::Tracking.event(self.options[:for].name, 'push_package') + ::Gitlab::Tracking.event(self.options[:for].name, 'push_package', user: current_user, project: project, namespace: project.namespace) create_package_file_params = declared_params.merge(build: current_authenticated_job) ::Packages::Generic::CreatePackageFileService @@ -96,7 +96,7 @@ module API package = ::Packages::Generic::PackageFinder.new(project).execute!(params[:package_name], params[:package_version]) package_file = ::Packages::PackageFileFinder.new(package, params[:file_name]).execute! - ::Gitlab::Tracking.event(self.options[:for].name, 'pull_package') + ::Gitlab::Tracking.event(self.options[:for].name, 'pull_package', user: current_user, project: project, namespace: project.namespace) present_carrierwave_file!(package_file.file) end diff --git a/lib/api/group_avatar.rb b/lib/api/group_avatar.rb new file mode 100644 index 00000000000..ddf6787f913 --- /dev/null +++ b/lib/api/group_avatar.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module API + class GroupAvatar < ::API::Base + helpers Helpers::GroupsHelpers + + feature_category :subgroups + + resource :groups do + desc 'Download the group avatar' do + detail 'This feature was introduced in GitLab 14.0' + end + params do + requires :id, type: String, desc: 'The group id' + end + get ':id/avatar' do + present_carrierwave_file!(user_group.avatar) + end + end + end +end diff --git a/lib/api/group_container_repositories.rb b/lib/api/group_container_repositories.rb index 4fede0ad583..96175f31696 100644 --- a/lib/api/group_container_repositories.rb +++ b/lib/api/group_container_repositories.rb @@ -31,7 +31,7 @@ module API user: current_user, subject: user_group ).execute - track_package_event('list_repositories', :container) + track_package_event('list_repositories', :container, user: current_user, namespace: user_group) present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count] end diff --git a/lib/api/group_export.rb b/lib/api/group_export.rb index 6134515032f..7e4fdba6033 100644 --- a/lib/api/group_export.rb +++ b/lib/api/group_export.rb @@ -23,7 +23,11 @@ module API check_rate_limit! :group_download_export, [current_user, user_group] if user_group.export_file_exists? - present_carrierwave_file!(user_group.export_file) + if user_group.export_archive_exists? + present_carrierwave_file!(user_group.export_file) + else + render_api_error!('The group export file is not available yet', 404) + end else render_api_error!('404 Not found or has expired', 404) end diff --git a/lib/api/group_packages.rb b/lib/api/group_packages.rb index ab4e91ff925..d9010dfd329 100644 --- a/lib/api/group_packages.rb +++ b/lib/api/group_packages.rb @@ -43,7 +43,7 @@ module API declared(params).slice(:exclude_subgroups, :order_by, :sort, :package_type, :package_name, :include_versionless, :status) ).execute - present paginate(packages), with: ::API::Entities::Package, user: current_user, group: true + present paginate(packages), with: ::API::Entities::Package, user: current_user, group: true, namespace: user_group.root_ancestor end end end diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 1a604e70bf1..0efb8b57885 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -372,7 +372,7 @@ module API expires_at: params[:expires_at] } - result = ::Groups::GroupLinks::CreateService.new(shared_with_group, current_user, group_link_create_params).execute(shared_group) + result = ::Groups::GroupLinks::CreateService.new(shared_group, shared_with_group, current_user, group_link_create_params).execute shared_group.preload_shared_group_links if result[:status] == :success diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb new file mode 100644 index 00000000000..dc5630a1395 --- /dev/null +++ b/lib/api/helm_packages.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +### +# API endpoints for the Helm package registry +module API + class HelmPackages < ::API::Base + helpers ::API::Helpers::PackagesHelpers + helpers ::API::Helpers::Packages::BasicAuthHelpers + include ::API::Helpers::Authentication + + feature_category :package_registry + + FILE_NAME_REQUIREMENTS = { + file_name: API::NO_SLASH_URL_PART_REGEX + }.freeze + + content_type :binary, 'application/octet-stream' + + authenticate_with do |accept| + accept.token_types(:personal_access_token, :deploy_token, :job_token) + .sent_through(:http_basic_auth) + end + + before do + require_packages_enabled! + end + + after_validation do + not_found! unless Feature.enabled?(:helm_packages, authorized_user_project) + end + + params do + requires :id, type: String, desc: 'The ID or full path of a project' + end + resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + namespace ':id/packages/helm' do + desc 'Download a chart' do + detail 'This feature was introduced in GitLab 14.0' + end + params do + requires :channel, type: String, desc: 'Helm channel', regexp: Gitlab::Regex.helm_channel_regex + requires :file_name, type: String, desc: 'Helm package file name' + end + get ":channel/charts/:file_name.tgz", requirements: FILE_NAME_REQUIREMENTS do + authorize_read_package!(authorized_user_project) + + package_file = Packages::Helm::PackageFilesFinder.new(authorized_user_project, params[:channel], file_name: "#{params[:file_name]}.tgz").execute.last! + + track_package_event('pull_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace) + + present_carrierwave_file!(package_file.file) + end + end + end + end +end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 632717e1b73..6ce04be373f 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -74,6 +74,11 @@ module API save_current_user_in_env(@current_user) if @current_user + if @current_user + ::Gitlab::Database::LoadBalancing::RackMiddleware + .stick_or_unstick(env, :user, @current_user.id) + end + @current_user end # rubocop:enable Gitlab/ModuleWithInstanceVariables @@ -482,9 +487,8 @@ module API def handle_api_exception(exception) if report_exception?(exception) define_params_for_grape_middleware - Gitlab::ApplicationContext.with_context(user: current_user) do - Gitlab::ErrorTracking.track_exception(exception) - end + Gitlab::ApplicationContext.push(user: current_user) + Gitlab::ErrorTracking.track_exception(exception) end # This is used with GrapeLogging::Loggers::ExceptionLogger @@ -599,6 +603,7 @@ module API :custom_attributes, :last_activity_after, :last_activity_before, + :topic, :repository_storage) .symbolize_keys .compact @@ -611,7 +616,6 @@ module API finder_params[:user] = params.delete(:user) if params[:user] finder_params[:id_after] = sanitize_id_param(params[:id_after]) if params[:id_after] finder_params[:id_before] = sanitize_id_param(params[:id_before]) if params[:id_before] - finder_params[:tag] = params[:topic] if params[:topic].present? finder_params end diff --git a/lib/api/helpers/label_helpers.rb b/lib/api/helpers/label_helpers.rb index 796b8928243..da0ee8f207e 100644 --- a/lib/api/helpers/label_helpers.rb +++ b/lib/api/helpers/label_helpers.rb @@ -5,34 +5,27 @@ module API module LabelHelpers extend Grape::API::Helpers - params :optional_label_params do - optional :description, type: String, desc: 'The description of the label' - optional :remove_on_close, type: Boolean, desc: 'Whether the label should be removed from an issue when the issue is closed' - end - params :label_create_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) or one of the allowed CSS color names" - - use :optional_label_params + optional :description, type: String, desc: 'The description of label to be created' end params :label_update_params do 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) or one of the allowed CSS color names" - - use :optional_label_params + optional :description, type: String, desc: 'The new description of label' end params :project_label_update_params do use :label_update_params optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true - at_least_one_of :new_name, :color, :description, :priority, :remove_on_close + at_least_one_of :new_name, :color, :description, :priority end params :group_label_update_params do use :label_update_params - at_least_one_of :new_name, :color, :description, :remove_on_close + at_least_one_of :new_name, :color, :description end def find_label(parent, id_or_title, params = { include_ancestor_groups: true }) diff --git a/lib/api/helpers/packages/basic_auth_helpers.rb b/lib/api/helpers/packages/basic_auth_helpers.rb index c32ce199dd6..6c381d85cd8 100644 --- a/lib/api/helpers/packages/basic_auth_helpers.rb +++ b/lib/api/helpers/packages/basic_auth_helpers.rb @@ -22,6 +22,14 @@ module API unauthorized_user_project || not_found! end + def unauthorized_user_group + @unauthorized_user_group ||= find_group(params[:id]) + end + + def unauthorized_user_group! + unauthorized_user_group || not_found! + end + def authorized_user_project @authorized_user_project ||= authorized_project_find! end diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb index b18f52b5be6..4b6dac39348 100644 --- a/lib/api/helpers/packages/conan/api_helpers.rb +++ b/lib/api/helpers/packages/conan/api_helpers.rb @@ -155,7 +155,7 @@ module API conan_package_reference: params[:conan_package_reference] ).execute! - track_package_event('pull_package', :conan, category: 'API::ConanPackages') if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY + track_package_event('pull_package', :conan, category: 'API::ConanPackages', user: current_user, project: project, namespace: project.namespace) if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY present_carrierwave_file!(package_file.file) end @@ -170,7 +170,7 @@ module API def track_push_package_event if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY && params[:file].size > 0 # rubocop: disable Style/ZeroLengthPredicate - track_package_event('push_package', :conan, category: 'API::ConanPackages') + track_package_event('push_package', :conan, category: 'API::ConanPackages', user: current_user, project: project, namespace: project.namespace) end end diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb index d9c0b4f67c8..69a83043617 100644 --- a/lib/api/helpers/projects_helpers.rb +++ b/lib/api/helpers/projects_helpers.rb @@ -16,6 +16,7 @@ module API optional :build_coverage_regex, type: String, desc: 'Test coverage parsing' optional :ci_config_path, type: String, desc: 'The path to CI config file. Defaults to `.gitlab-ci.yml`' optional :service_desk_enabled, type: Boolean, desc: 'Disable or enable the service desk' + optional :keep_latest_artifact, type: Boolean, desc: 'Indicates if the latest artifact should be kept for this project.' # TODO: remove in API v5, replaced by *_access_level optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled' @@ -51,7 +52,8 @@ module API optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed' optional :allow_merge_on_skipped_pipeline, type: Boolean, desc: 'Allow to merge if pipeline is skipped' optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved' - optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of tags for a project' + optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Deprecated: Use :topics instead' + optional :topics, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of topics for a project' # TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960 optional :avatar, type: File, desc: 'Avatar image for project' # rubocop:disable Scalability/FileUploads optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line' @@ -146,6 +148,7 @@ module API :shared_runners_enabled, :snippets_access_level, :tag_list, + :topics, :visibility, :wiki_access_level, :avatar, @@ -154,6 +157,7 @@ module API :compliance_framework_setting, :packages_enabled, :service_desk_enabled, + :keep_latest_artifact, # TODO: remove in API v5, replaced by *_access_level :issues_enabled, diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb index 6f25cf507bc..9ec9b5e1e35 100644 --- a/lib/api/helpers/runner.rb +++ b/lib/api/helpers/runner.rb @@ -25,6 +25,7 @@ module API return get_runner_ip unless params['info'].present? attributes_for_keys(%w(name version revision platform architecture), params['info']) + .merge(get_runner_config_from_request) .merge(get_runner_ip) end @@ -33,8 +34,15 @@ module API end def current_runner + token = params[:token] + + if token + ::Gitlab::Database::LoadBalancing::RackMiddleware + .stick_or_unstick(env, :runner, token) + end + strong_memoize(:current_runner) do - ::Ci::Runner.find_by_token(params[:token].to_s) + ::Ci::Runner.find_by_token(token.to_s) end end @@ -64,8 +72,15 @@ module API end def current_job + id = params[:id] + + if id + ::Gitlab::Database::LoadBalancing::RackMiddleware + .stick_or_unstick(env, :build, id) + end + strong_memoize(:current_job) do - ::Ci::Build.find_by_id(params[:id]) + ::Ci::Build.find_by_id(id) end end @@ -91,6 +106,12 @@ module API def track_ci_minutes_usage!(_build, _runner) # noop: overridden in EE end + + private + + def get_runner_config_from_request + { config: attributes_for_keys(%w(gpus), params.dig('info', 'config')) } + end end end end diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb index d123db8e3df..ca13ea0789a 100644 --- a/lib/api/helpers/services_helpers.rb +++ b/lib/api/helpers/services_helpers.rb @@ -777,41 +777,41 @@ module API ::Integrations::Asana, ::Integrations::Assembla, ::Integrations::Bamboo, + ::Integrations::Bugzilla, + ::Integrations::Buildkite, ::Integrations::Campfire, ::Integrations::Confluence, + ::Integrations::CustomIssueTracker, ::Integrations::Datadog, + ::Integrations::Discord, + ::Integrations::DroneCi, ::Integrations::EmailsOnPush, - ::BugzillaService, - ::BuildkiteService, - ::CustomIssueTrackerService, - ::DiscordService, - ::DroneCiService, - ::EwmService, - ::ExternalWikiService, - ::FlowdockService, - ::HangoutsChatService, - ::IrkerService, - ::JenkinsService, - ::JiraService, - ::MattermostSlashCommandsService, - ::SlackSlashCommandsService, - ::PackagistService, - ::PipelinesEmailService, - ::PivotaltrackerService, - ::PrometheusService, - ::PushoverService, - ::RedmineService, - ::YoutrackService, - ::SlackService, - ::MattermostService, - ::MicrosoftTeamsService, - ::TeamcityService + ::Integrations::Ewm, + ::Integrations::ExternalWiki, + ::Integrations::Flowdock, + ::Integrations::HangoutsChat, + ::Integrations::Irker, + ::Integrations::Jenkins, + ::Integrations::Jira, + ::Integrations::Mattermost, + ::Integrations::MattermostSlashCommands, + ::Integrations::MicrosoftTeams, + ::Integrations::Packagist, + ::Integrations::PipelinesEmail, + ::Integrations::Pivotaltracker, + ::Integrations::Pushover, + ::Integrations::Redmine, + ::Integrations::Slack, + ::Integrations::SlackSlashCommands, + ::Integrations::Teamcity, + ::Integrations::Youtrack, + ::PrometheusService ] end def self.development_service_classes [ - ::MockCiService, + ::Integrations::MockCi, ::MockMonitoringService ] end diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb index e16149185c9..ee0ddccc8d4 100644 --- a/lib/api/internal/base.rb +++ b/lib/api/internal/base.rb @@ -10,8 +10,6 @@ module API api_endpoint = env['api.endpoint'] feature_category = api_endpoint.options[:for].try(:feature_category_for_app, api_endpoint).to_s - header[Gitlab::Metrics::RequestsRackMiddleware::FEATURE_CATEGORY_HEADER] = feature_category - Gitlab::ApplicationContext.push( user: -> { actor&.user }, project: -> { project }, @@ -169,18 +167,15 @@ module API end # - # Get a ssh key using the fingerprint + # Check whether an SSH key is known to GitLab # - # rubocop: disable CodeReuse/ActiveRecord get '/authorized_keys', feature_category: :source_code_management do - fingerprint = params.fetch(:fingerprint) do - Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint - end - key = Key.find_by(fingerprint: fingerprint) + fingerprint = Gitlab::InsecureKeyFingerprint.new(params.fetch(:key)).fingerprint + + key = Key.find_by_fingerprint(fingerprint) not_found!('Key') if key.nil? present key, with: Entities::SSHKey end - # rubocop: enable CodeReuse/ActiveRecord # # Discover user by ssh key, user id or username diff --git a/lib/api/invitations.rb b/lib/api/invitations.rb index 0d562cc18f8..46d8c0c958d 100644 --- a/lib/api/invitations.rb +++ b/lib/api/invitations.rb @@ -23,6 +23,7 @@ module API requires :email, types: [String, Array[String]], email_or_email_list: true, desc: 'The email address to invite, or multiple emails separated by comma' requires :access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'A valid access level (defaults: `30`, developer access level)' optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' + optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'invitations-api' end post ":id/invitations" do params[:source] = find_source(source_type, params[:id]) diff --git a/lib/api/jobs.rb b/lib/api/jobs.rb index cf65bfdfd0e..723a5b0fa3a 100644 --- a/lib/api/jobs.rb +++ b/lib/api/jobs.rb @@ -3,7 +3,6 @@ module API class Jobs < ::API::Base include PaginationParams - before { authenticate! } resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do diff --git a/lib/api/lint.rb b/lib/api/lint.rb index 945cdf3edb2..3580a7b5e24 100644 --- a/lib/api/lint.rb +++ b/lib/api/lint.rb @@ -11,7 +11,11 @@ module API optional :include_merged_yaml, type: Boolean, desc: 'Whether or not to include merged CI config yaml in the response' end post '/lint' do - unauthorized! if (Gitlab::CurrentSettings.signup_disabled? || Gitlab::CurrentSettings.signup_limited?) && current_user.nil? + if Feature.enabled?(:security_ci_lint_authorization) + unauthorized! if (Gitlab::CurrentSettings.signup_disabled? || Gitlab::CurrentSettings.signup_limited?) && current_user.nil? + else + unauthorized! if Gitlab::CurrentSettings.signup_disabled? && current_user.nil? + end result = Gitlab::Ci::YamlProcessor.new(params[:content], user: current_user).execute diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb index 22f7b07809b..9e5705abe88 100644 --- a/lib/api/maven_packages.rb +++ b/lib/api/maven_packages.rb @@ -24,8 +24,6 @@ module API helpers do def path_exists?(path) - # return true when FF disabled so that processing the request is not stopped - return true unless Feature.enabled?(:check_maven_path_first, default_enabled: :yaml) return false if path.blank? Packages::Maven::Metadatum.with_path(path) @@ -132,7 +130,7 @@ module API when 'sha1' package_file.file_sha1 else - track_package_event('pull_package', :maven) if jar_file?(format) + track_package_event('pull_package', :maven, project: project, namespace: project.namespace) if jar_file?(format) present_carrierwave_file_with_head_support!(package_file.file) end end @@ -172,7 +170,7 @@ module API when 'sha1' package_file.file_sha1 else - track_package_event('pull_package', :maven) if jar_file?(format) + track_package_event('pull_package', :maven, project: package.project, namespace: package.project.namespace) if jar_file?(format) present_carrierwave_file_with_head_support!(package_file.file) end @@ -210,7 +208,7 @@ module API when 'sha1' package_file.file_sha1 else - track_package_event('pull_package', :maven) if jar_file?(format) + track_package_event('pull_package', :maven, project: user_project, namespace: user_project.namespace) if jar_file?(format) present_carrierwave_file_with_head_support!(package_file.file) end @@ -266,7 +264,7 @@ module API when 'md5' '' else - track_package_event('push_package', :maven) if jar_file?(format) + track_package_event('push_package', :maven, user: current_user, project: user_project, namespace: user_project.namespace) if jar_file?(format) file_params = { file: params[:file], diff --git a/lib/api/members.rb b/lib/api/members.rb index a1a733ea7ae..0956806da5b 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -93,6 +93,7 @@ module API requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)' requires :user_id, types: [Integer, String], desc: 'The user ID of the new member or multiple IDs separated by commas.' optional :expires_at, type: DateTime, desc: 'Date string in the format YEAR-MONTH-DAY' + optional :invite_source, type: String, desc: 'Source that triggered the member creation process', default: 'members-api' end # rubocop: disable CodeReuse/ActiveRecord post ":id/members" do @@ -116,6 +117,7 @@ module API not_allowed! # This currently can only be reached in EE elsif member.valid? && member.persisted? present_members(member) + Gitlab::Tracking.event(::Members::CreateService.name, 'create_member', label: params[:invite_source], property: 'existing_user', user: current_user) else render_validation_error!(member) end diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index 931d2322c98..a9617482557 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -436,14 +436,11 @@ module API mr_params = declared_params(include_missing: false) mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params.has_key?(:remove_source_branch) mr_params = convert_parameters_from_legacy_format(mr_params) + mr_params[:use_specialized_service] = true - service = if mr_params.one? && (mr_params.keys & %i[assignee_id assignee_ids]).one? - ::MergeRequests::UpdateAssigneesService - else - ::MergeRequests::UpdateService - end - - merge_request = service.new(project: user_project, current_user: current_user, params: mr_params).execute(merge_request) + merge_request = ::MergeRequests::UpdateService + .new(project: user_project, current_user: current_user, params: mr_params) + .execute(merge_request) handle_merge_request_errors!(merge_request) diff --git a/lib/api/npm_project_packages.rb b/lib/api/npm_project_packages.rb index 887084dc9ae..7ff4439ce04 100644 --- a/lib/api/npm_project_packages.rb +++ b/lib/api/npm_project_packages.rb @@ -32,7 +32,7 @@ module API package_file = ::Packages::PackageFileFinder .new(package, params[:file_name]).execute! - track_package_event('pull_package', package, category: 'API::NpmPackages') + track_package_event('pull_package', package, category: 'API::NpmPackages', project: project, namespace: project.namespace) present_carrierwave_file!(package_file.file) end @@ -48,7 +48,7 @@ module API put ':package_name', requirements: ::API::Helpers::Packages::Npm::NPM_ENDPOINT_REQUIREMENTS do authorize_create_package!(project) - track_package_event('push_package', :npm, category: 'API::NpmPackages') + track_package_event('push_package', :npm, category: 'API::NpmPackages', project: project, user: current_user, namespace: project.namespace) created_package = ::Packages::Npm::CreatePackageService .new(project, current_user, params.merge(build: current_authenticated_job)).execute diff --git a/lib/api/nuget_group_packages.rb b/lib/api/nuget_group_packages.rb index a80de06d6b0..eb55e4cbf70 100644 --- a/lib/api/nuget_group_packages.rb +++ b/lib/api/nuget_group_packages.rb @@ -38,6 +38,10 @@ module API def require_authenticated! unauthorized! unless current_user end + + def snowplow_gitlab_standard_context + { namespace: find_authorized_group! } + end end params do diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb index 73ecc140959..5bae08d4dae 100644 --- a/lib/api/nuget_project_packages.rb +++ b/lib/api/nuget_project_packages.rb @@ -36,6 +36,10 @@ module API def project_or_group authorized_user_project end + + def snowplow_gitlab_standard_context + { project: authorized_user_project, namespace: authorized_user_project.namespace } + end end params do @@ -69,7 +73,7 @@ module API package_file = ::Packages::CreatePackageFileService.new(package, file_params.merge(build: current_authenticated_job)) .execute - track_package_event('push_package', :nuget, category: 'API::NugetPackages') + track_package_event('push_package', :nuget, category: 'API::NugetPackages', user: current_user, project: package.project, namespace: package.project.namespace) ::Packages::Nuget::ExtractionWorker.perform_async(package_file.id) # rubocop:disable CodeReuse/Worker @@ -118,7 +122,7 @@ module API not_found!('Package') unless package_file - track_package_event('pull_package', :nuget, category: 'API::NugetPackages') + track_package_event('pull_package', :nuget, category: 'API::NugetPackages', project: package_file.project, namespace: package_file.project.namespace) # nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false present_carrierwave_file!(package_file.file, supports_direct_download: false) diff --git a/lib/api/project_container_repositories.rb b/lib/api/project_container_repositories.rb index 2580f7adbc9..28cfa9e3ae0 100644 --- a/lib/api/project_container_repositories.rb +++ b/lib/api/project_container_repositories.rb @@ -31,7 +31,7 @@ module API user: current_user, subject: user_project ).execute - track_package_event('list_repositories', :container) + track_package_event('list_repositories', :container, user: current_user, project: user_project, namespace: user_project.namespace) present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags], tags_count: params[:tags_count] end @@ -46,7 +46,7 @@ module API authorize_admin_container_image! DeleteContainerRepositoryWorker.perform_async(current_user.id, repository.id) # rubocop:disable CodeReuse/Worker - track_package_event('delete_repository', :container) + track_package_event('delete_repository', :container, user: current_user, project: user_project, namespace: user_project.namespace) status :accepted end @@ -63,7 +63,7 @@ module API authorize_read_container_image! tags = Kaminari.paginate_array(repository.tags) - track_package_event('list_tags', :container) + track_package_event('list_tags', :container, user: current_user, project: user_project, namespace: user_project.namespace) present paginate(tags), with: Entities::ContainerRegistry::Tag end @@ -92,7 +92,7 @@ module API declared_params.except(:repository_id).merge(container_expiration_policy: false)) # rubocop:enable CodeReuse/Worker - track_package_event('delete_tag_bulk', :container) + track_package_event('delete_tag_bulk', :container, user: current_user, project: user_project, namespace: user_project.namespace) status :accepted end @@ -128,7 +128,7 @@ module API .execute(repository) if result[:status] == :success - track_package_event('delete_tag', :container) + track_package_event('delete_tag', :container, user: current_user, project: user_project, namespace: user_project.namespace) status :ok else diff --git a/lib/api/project_debian_distributions.rb b/lib/api/project_debian_distributions.rb new file mode 100644 index 00000000000..58edf51f4f7 --- /dev/null +++ b/lib/api/project_debian_distributions.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module API + class ProjectDebianDistributions < ::API::Base + params do + requires :id, type: String, desc: 'The ID of a project' + end + + resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + rescue_from ArgumentError do |e| + render_api_error!(e.message, 400) + end + + rescue_from ActiveRecord::RecordInvalid do |e| + render_api_error!(e.message, 400) + end + + after_validation do + require_packages_enabled! + + not_found! unless ::Feature.enabled?(:debian_packages, user_project) + + authorize_read_package! + end + + namespace ':id' do + helpers do + def project_or_group + user_project + end + end + + include ::API::Concerns::Packages::DebianDistributionEndpoints + end + end + end +end diff --git a/lib/api/project_export.rb b/lib/api/project_export.rb index 76b3dea723a..4041e130f9e 100644 --- a/lib/api/project_export.rb +++ b/lib/api/project_export.rb @@ -30,7 +30,11 @@ module API check_rate_limit! :project_download_export, [current_user, user_project] if user_project.export_file_exists? - present_carrierwave_file!(user_project.export_file) + if user_project.export_archive_exists? + present_carrierwave_file!(user_project.export_file) + else + render_api_error!('The project export file is not available yet', 404) + end else render_api_error!('404 Not found or has expired', 404) end diff --git a/lib/api/project_packages.rb b/lib/api/project_packages.rb index babc7b9dd58..276cbe50e42 100644 --- a/lib/api/project_packages.rb +++ b/lib/api/project_packages.rb @@ -41,7 +41,7 @@ module API declared_params.slice(:order_by, :sort, :package_type, :package_name, :include_versionless, :status) ).execute - present paginate(packages), with: ::API::Entities::Package, user: current_user + present paginate(packages), with: ::API::Entities::Package, user: current_user, namespace: user_project.root_ancestor end desc 'Get a single project package' do @@ -55,7 +55,7 @@ module API package = ::Packages::PackageFinder .new(user_project, params[:package_id]).execute - present package, with: ::API::Entities::Package, user: current_user + present package, with: ::API::Entities::Package, user: current_user, namespace: user_project.root_ancestor end desc 'Remove a package' do diff --git a/lib/api/project_snippets.rb b/lib/api/project_snippets.rb index 899984fe0ba..084492fd503 100644 --- a/lib/api/project_snippets.rb +++ b/lib/api/project_snippets.rb @@ -75,7 +75,7 @@ module API snippet_params = process_create_params(declared_params(include_missing: false)) - service_response = ::Snippets::CreateService.new(user_project, current_user, snippet_params).execute + service_response = ::Snippets::CreateService.new(project: user_project, current_user: current_user, params: snippet_params).execute snippet = service_response.payload[:snippet] if service_response.success? @@ -116,7 +116,7 @@ module API snippet_params = process_update_params(declared_params(include_missing: false)) - service_response = ::Snippets::UpdateService.new(user_project, current_user, snippet_params).execute(snippet) + service_response = ::Snippets::UpdateService.new(project: user_project, current_user: current_user, params: snippet_params).execute(snippet) snippet = service_response.payload[:snippet] if service_response.success? diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb index 5d6f67ccbae..acf9bfece65 100644 --- a/lib/api/project_templates.rb +++ b/lib/api/project_templates.rb @@ -26,7 +26,7 @@ module API use :pagination end get ':id/templates/:type' do - templates = TemplateFinder.all_template_names_array(user_project, params[:type]) + templates = TemplateFinder.all_template_names(user_project, params[:type]).values.flatten present paginate(::Kaminari.paginate_array(templates)), with: Entities::TemplatesList end diff --git a/lib/api/projects.rb b/lib/api/projects.rb index 4e8786fbe1f..83c335a3248 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -61,7 +61,7 @@ module API # Temporarily introduced for upload API: https://gitlab.com/gitlab-org/gitlab/-/issues/325788 def project_attachment_size(user_project) return PROJECT_ATTACHMENT_SIZE_EXEMPT if exempt_from_global_attachment_size?(user_project) - return user_project.max_attachment_size if Feature.enabled?(:enforce_max_attachment_size_upload_api, user_project) + return user_project.max_attachment_size if Feature.enabled?(:enforce_max_attachment_size_upload_api, user_project, default_enabled: :yaml) PROJECT_ATTACHMENT_SIZE_EXEMPT end @@ -234,6 +234,7 @@ module API params do optional :name, type: String, desc: 'The name of the project' optional :path, type: String, desc: 'The path of the repository' + optional :default_branch, type: String, desc: 'The default branch of the project' at_least_one_of :name, :path use :optional_create_project_params use :create_params @@ -660,6 +661,18 @@ module API render_api_error!("Failed to transfer project #{user_project.errors.messages}", 400) end end + + desc 'Show the storage information' do + success Entities::ProjectRepositoryStorage + end + params do + requires :id, type: String, desc: 'ID of a project' + end + get ':id/storage', feature_category: :projects do + authenticated_as_admin! + + present user_project, with: Entities::ProjectRepositoryStorage, current_user: current_user + end end end end diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb index 73b2f658825..7c5f8bb4d99 100644 --- a/lib/api/pypi_packages.rb +++ b/lib/api/pypi_packages.rb @@ -28,6 +28,73 @@ module API require_packages_enabled! end + helpers do + params :package_download do + requires :file_identifier, type: String, desc: 'The PyPi package file identifier', file_path: true + requires :sha256, type: String, desc: 'The PyPi package sha256 check sum' + end + + params :package_name do + requires :package_name, type: String, file_path: true, desc: 'The PyPi package name' + end + end + + params do + requires :id, type: Integer, desc: 'The ID of a group' + end + resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + after_validation do + unauthorized_user_group! + end + + namespace ':id/-/packages/pypi' do + params do + use :package_download + end + + route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth + get 'files/:sha256/*file_identifier' do + group = unauthorized_user_group! + + filename = "#{params[:file_identifier]}.#{params[:format]}" + package = Packages::Pypi::PackageFinder.new(current_user, group, { filename: filename, sha256: params[:sha256] }).execute + package_file = ::Packages::PackageFileFinder.new(package, filename, with_file_name_like: false).execute + + track_package_event('pull_package', :pypi) + + present_carrierwave_file!(package_file.file, supports_direct_download: true) + end + + desc 'The PyPi Simple Endpoint' do + detail 'This feature was introduced in GitLab 12.10' + end + + params do + use :package_name + end + + # An Api entry point but returns an HTML file instead of JSON. + # PyPi simple API returns the package descriptor as a simple HTML file. + route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth + get 'simple/*package_name', format: :txt do + group = find_authorized_group! + authorize_read_package!(group) + + track_package_event('list_package', :pypi) + + packages = Packages::Pypi::PackagesFinder.new(current_user, group, { package_name: params[:package_name] }).execute! + presenter = ::Packages::Pypi::PackagePresenter.new(packages, group) + + # Adjusts grape output format + # to be HTML + content_type "text/html; charset=utf-8" + env['api.format'] = :binary + + body presenter.body + end + end + end + params do requires :id, type: Integer, desc: 'The ID of a project' end @@ -43,8 +110,7 @@ module API end params do - requires :file_identifier, type: String, desc: 'The PyPi package file identifier', file_path: true - requires :sha256, type: String, desc: 'The PyPi package sha256 check sum' + use :package_download end route_setting :authentication, deploy_token_allowed: true, basic_auth_personal_access_token: true, job_token_allowed: :basic_auth @@ -55,7 +121,7 @@ module API package = Packages::Pypi::PackageFinder.new(current_user, project, { filename: filename, sha256: params[:sha256] }).execute package_file = ::Packages::PackageFileFinder.new(package, filename, with_file_name_like: false).execute - track_package_event('pull_package', :pypi) + track_package_event('pull_package', :pypi, project: project, namespace: project.namespace) present_carrierwave_file!(package_file.file, supports_direct_download: true) end @@ -65,7 +131,7 @@ module API end params do - requires :package_name, type: String, file_path: true, desc: 'The PyPi package name' + use :package_name end # An Api entry point but returns an HTML file instead of JSON. @@ -74,7 +140,7 @@ module API get 'simple/*package_name', format: :txt do authorize_read_package!(authorized_user_project) - track_package_event('list_package', :pypi) + track_package_event('list_package', :pypi, project: authorized_user_project, namespace: authorized_user_project.namespace) packages = Packages::Pypi::PackagesFinder.new(current_user, authorized_user_project, { package_name: params[:package_name] }).execute! presenter = ::Packages::Pypi::PackagePresenter.new(packages, authorized_user_project) @@ -105,7 +171,7 @@ module API authorize_upload!(authorized_user_project) bad_request!('File is too large') if authorized_user_project.actual_limits.exceeded?(:pypi_max_file_size, params[:content].size) - track_package_event('push_package', :pypi) + track_package_event('push_package', :pypi, project: authorized_user_project, user: current_user, namespace: authorized_user_project.namespace) ::Packages::Pypi::CreatePackageService .new(authorized_user_project, current_user, declared_params.merge(build: current_authenticated_job)) diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb index 1d17148e0df..d7f9c584c67 100644 --- a/lib/api/rubygem_packages.rb +++ b/lib/api/rubygem_packages.rb @@ -70,7 +70,7 @@ module API user_project, params[:file_name] ).last! - track_package_event('pull_package', :rubygems) + track_package_event('pull_package', :rubygems, project: user_project, namespace: user_project.namespace) present_carrierwave_file!(package_file.file) end @@ -97,7 +97,7 @@ module API authorize_upload!(user_project) bad_request!('File is too large') if user_project.actual_limits.exceeded?(:rubygems_max_file_size, params[:file].size) - track_package_event('push_package', :rubygems) + track_package_event('push_package', :rubygems, user: current_user, project: user_project, namespace: user_project.namespace) package_file = nil diff --git a/lib/api/settings.rb b/lib/api/settings.rb index 372bc7b3d8f..b4f8320cb74 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -46,7 +46,7 @@ module API optional :asset_proxy_allowlist, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Assets that match these domain(s) will NOT be proxied. Wildcards allowed. Your GitLab installation URL is automatically allowed.' optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)' optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts" - optional :default_ci_config_path, type: String, desc: 'The instance default CI configuration path for new projects' + optional :default_ci_config_path, type: String, desc: 'The instance default CI/CD configuration file and path for new projects' optional :default_project_creation, type: Integer, values: ::Gitlab::Access.project_creation_values, desc: 'Determine if developers can create projects in the group' optional :default_branch_protection, type: Integer, values: ::Gitlab::Access.protection_values, desc: 'Determine if developers can push to master' optional :default_group_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default group visibility' diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb index 52b597fb788..b506192fe1c 100644 --- a/lib/api/snippets.rb +++ b/lib/api/snippets.rb @@ -84,7 +84,7 @@ module API attrs = process_create_params(declared_params(include_missing: false)) - service_response = ::Snippets::CreateService.new(nil, current_user, attrs).execute + service_response = ::Snippets::CreateService.new(project: nil, current_user: current_user, params: attrs).execute snippet = service_response.payload[:snippet] if service_response.success? @@ -126,7 +126,7 @@ module API attrs = process_update_params(declared_params(include_missing: false)) - service_response = ::Snippets::UpdateService.new(nil, current_user, attrs).execute(snippet) + service_response = ::Snippets::UpdateService.new(project: nil, current_user: current_user, params: attrs).execute(snippet) snippet = service_response.payload[:snippet] diff --git a/lib/api/tags.rb b/lib/api/tags.rb index e77d7e34de3..6c8e2c69a6d 100644 --- a/lib/api/tags.rb +++ b/lib/api/tags.rb @@ -51,35 +51,22 @@ module API end desc 'Create a new repository tag' do - detail 'This optional release_description parameter was deprecated in GitLab 11.7.' success Entities::Tag end params do requires :tag_name, type: String, desc: 'The name of the tag' requires :ref, type: String, desc: 'The commit sha or branch name' optional :message, type: String, desc: 'Specifying a message creates an annotated tag' - optional :release_description, type: String, desc: 'Specifying release notes stored in the GitLab database (deprecated in GitLab 11.7)' end post ':id/repository/tags', :release_orchestration do + deprecate_release_notes unless params[:release_description].blank? + authorize_admin_tag result = ::Tags::CreateService.new(user_project, current_user) .execute(params[:tag_name], params[:ref], params[:message]) if result[:status] == :success - # Release creation with Tags API was deprecated in GitLab 11.7 - if params[:release_description].present? - release_create_params = { - tag: params[:tag_name], - name: params[:tag_name], # Name can be specified in new API - description: params[:release_description] - } - - ::Releases::CreateService - .new(user_project, current_user, release_create_params) - .execute - end - present result[:tag], with: Entities::Tag, project: user_project @@ -109,74 +96,6 @@ module API end end end - - desc 'Add a release note to a tag' do - detail 'This feature was deprecated in GitLab 11.7.' - success Entities::TagRelease - end - params do - requires :tag_name, type: String, desc: 'The name of the tag', as: :tag - requires :description, type: String, desc: 'Release notes with markdown support' - end - post ':id/repository/tags/:tag_name/release', requirements: TAG_ENDPOINT_REQUIREMENTS, feature_category: :release_orchestration do - authorize_create_release! - - ## - # Legacy API does not support tag auto creation. - not_found!('Tag') unless user_project.repository.find_tag(params[:tag]) - - release_create_params = { - tag: params[:tag], - name: params[:tag], # Name can be specified in new API - description: params[:description] - } - - result = ::Releases::CreateService - .new(user_project, current_user, release_create_params) - .execute - - if result[:status] == :success - present result[:release], with: Entities::TagRelease - else - render_api_error!(result[:message], result[:http_status]) - end - end - - desc "Update a tag's release note" do - detail 'This feature was deprecated in GitLab 11.7.' - success Entities::TagRelease - end - params do - requires :tag_name, type: String, desc: 'The name of the tag', as: :tag - requires :description, type: String, desc: 'Release notes with markdown support' - end - put ':id/repository/tags/:tag_name/release', requirements: TAG_ENDPOINT_REQUIREMENTS, feature_category: :release_orchestration do - authorize_update_release! - - result = ::Releases::UpdateService - .new(user_project, current_user, declared_params(include_missing: false)) - .execute - - if result[:status] == :success - present result[:release], with: Entities::TagRelease - else - render_api_error!(result[:message], result[:http_status]) - end - end - end - - helpers do - def authorize_create_release! - authorize! :create_release, user_project - end - - def authorize_update_release! - authorize! :update_release, release - end - - def release - @release ||= user_project.releases.find_by_tag(params[:tag]) - end end end end diff --git a/lib/api/terraform/modules/v1/packages.rb b/lib/api/terraform/modules/v1/packages.rb index 34e77e09800..aa59b6a4fee 100644 --- a/lib/api/terraform/modules/v1/packages.rb +++ b/lib/api/terraform/modules/v1/packages.rb @@ -124,7 +124,7 @@ module API end get do - track_package_event('pull_package', :terraform_module) + track_package_event('pull_package', :terraform_module, project: package.project, namespace: module_namespace, user: current_user) present_carrierwave_file!(package_file.file) end @@ -183,7 +183,7 @@ module API render_api_error!(result[:message], result[:http_status]) if result[:status] == :error - track_package_event('push_package', :terraform_module) + track_package_event('push_package', :terraform_module, project: authorized_user_project, user: current_user, namespace: authorized_user_project.namespace) created! rescue ObjectStorage::RemoteStoreError => e diff --git a/lib/api/unleash.rb b/lib/api/unleash.rb index 3148c56339a..37fe540cde1 100644 --- a/lib/api/unleash.rb +++ b/lib/api/unleash.rb @@ -69,10 +69,7 @@ module API def feature_flags return [] unless unleash_app_name.present? - legacy_flags = Operations::FeatureFlagScope.for_unleash_client(project, unleash_app_name) - new_version_flags = Operations::FeatureFlag.for_unleash_client(project, unleash_app_name) - - legacy_flags + new_version_flags + Operations::FeatureFlag.for_unleash_client(project, unleash_app_name) end end end diff --git a/lib/api/users.rb b/lib/api/users.rb index 565a3544da2..2608fb87e22 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -241,7 +241,7 @@ module API authenticated_as_admin! params = declared_params(include_missing: false) - user = ::Users::CreateService.new(current_user, params).execute(skip_authorization: true) + user = ::Users::AuthorizedCreateService.new(current_user, params).execute if user.persisted? present user, with: Entities::UserWithAdmin, current_user: current_user @@ -1025,7 +1025,9 @@ module API detail 'This feature was introduced in GitLab 13.10.' end params do - requires :view_diffs_file_by_file, type: Boolean, desc: 'Flag indicating the user sees only one file diff per page' + optional :view_diffs_file_by_file, type: Boolean, desc: 'Flag indicating the user sees only one file diff per page' + optional :show_whitespace_in_diffs, type: Boolean, desc: 'Flag indicating the user sees whitespace changes in diffs' + at_least_one_of :view_diffs_file_by_file, :show_whitespace_in_diffs end put "preferences", feature_category: :users do authenticate! @@ -1043,6 +1045,14 @@ module API end end + desc "Get the current user's preferences" do + success Entities::UserPreferences + detail 'This feature was introduced in GitLab 14.0.' + end + get "preferences", feature_category: :users do + present current_user.user_preference, with: Entities::UserPreferences + end + desc 'Get a single email address owned by the currently authenticated user' do success Entities::Email end |