diff --git a/lib/api/api.rb b/lib/api/api.rb index f69dfa9d4443e2ec1b4a6e6f658f5876041c1b6e..a09ab3ff9e9ec35cfe1b8b9e861b6234752d6e5f 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -226,6 +226,7 @@ class API < ::API::Base mount ::API::Lint mount ::API::Markdown mount ::API::MergeRequestApprovals + mount ::API::MergeRequests mount ::API::MergeRequestDiffs mount ::API::Metadata mount ::API::Metrics::Dashboard::Annotations @@ -302,7 +303,6 @@ class API < ::API::Base mount ::API::Labels mount ::API::MavenPackages mount ::API::Members - mount ::API::MergeRequests mount ::API::Notes mount ::API::NotificationSettings mount ::API::NpmInstancePackages diff --git a/lib/api/entities/issuable_time_stats.rb b/lib/api/entities/issuable_time_stats.rb index f93b4651b1fc69d7dfe15a981d7bb32a9c079769..717d2282441c385f11aca0e10c35a6a4b3a0378b 100644 --- a/lib/api/entities/issuable_time_stats.rb +++ b/lib/api/entities/issuable_time_stats.rb @@ -7,12 +7,12 @@ class IssuableTimeStats < Grape::Entity Gitlab::TimeTrackingFormatter.output(time_spent) end - expose :time_estimate - expose :total_time_spent - expose :human_time_estimate + expose :time_estimate, documentation: { type: 'integer', example: 12600 } + expose :total_time_spent, documentation: { type: 'integer', example: 3600 } + expose :human_time_estimate, documentation: { type: 'string', example: '3h 30m' } with_options(format_with: :time_tracking_formatter) do - expose :total_time_spent, as: :human_total_time_spent + expose :total_time_spent, as: :human_total_time_spent, documentation: { type: 'string', example: '1h' } end # rubocop: disable CodeReuse/ActiveRecord diff --git a/lib/api/helpers/merge_requests_helpers.rb b/lib/api/helpers/merge_requests_helpers.rb index eed9fa30d3c9ae8ef36233c5c6474cc92ad5bd76..ee3bb49c97f2166a00d922cd1009fe02fc2b7664 100644 --- a/lib/api/helpers/merge_requests_helpers.rb +++ b/lib/api/helpers/merge_requests_helpers.rb @@ -11,100 +11,107 @@ module MergeRequestsHelpers params :ee_approval_params do end - params :merge_requests_negatable_params do - optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID' - optional :author_username, type: String, desc: 'Return merge requests which are authored by the user with the given username' + params :merge_requests_negatable_params do |options| + optional :author_id, type: Integer, + desc: "#{options[:prefix]}Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. Combine with `scope=all` or `scope=assigned_to_me`." + optional :author_username, type: String, + desc: "#{options[:prefix]}Returns merge requests created by the given `username`. Mutually exclusive with `author_id`." mutually_exclusive :author_id, :author_username - - optional :assignee_id, - types: [Integer, String], - integer_none_any: true, - desc: 'Return merge requests which are assigned to the user with the given ID' - optional :assignee_username, - type: Array[String], - check_assignees_count: true, - coerce_with: Validations::Validators::CheckAssigneesCount.coerce, - desc: 'Return merge requests which are assigned to the user with the given username' + optional :assignee_id, types: [Integer, String], + integer_none_any: true, + desc: "#{options[:prefix]}Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee." + optional :assignee_username, type: Array[String], + check_assignees_count: true, + coerce_with: Validations::Validators::CheckAssigneesCount.coerce, + desc: "#{options[:prefix]}Returns merge requests created by the given `username`. Mutually exclusive with `author_id`.", + documentation: { is_array: true } mutually_exclusive :assignee_id, :assignee_username - optional :reviewer_username, - type: String, - desc: 'Return merge requests which have the user as a reviewer with the given username' - - optional :labels, - type: Array[String], - coerce_with: Validations::Types::CommaSeparatedToArray.coerce, - desc: 'Comma-separated list of label names' - optional :milestone, type: String, desc: 'Return merge requests for a specific milestone' - optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji' + optional :reviewer_username, type: String, + desc: "#{options[:prefix]}Returns merge requests which have the user as a reviewer with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. Introduced in GitLab 13.8." + optional :labels, type: Array[String], + coerce_with: Validations::Types::CommaSeparatedToArray.coerce, + desc: "#{options[:prefix]}Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive.", + documentation: { is_array: true } + optional :milestone, type: String, + desc: "#{options[:prefix]}Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone." + optional :my_reaction_emoji, type: String, + desc: "#{options[:prefix]}Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction." end params :merge_requests_base_params do - use :merge_requests_negatable_params - optional :reviewer_id, - types: [Integer, String], - integer_none_any: true, - desc: 'Return merge requests which have the user as a reviewer with the given ID' + use :merge_requests_negatable_params, prefix: '' + + optional :reviewer_id, types: [Integer, String], + integer_none_any: true, + desc: 'Returns merge requests which have the user as a reviewer with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`.' mutually_exclusive :reviewer_id, :reviewer_username - optional :state, - type: String, - values: %w[opened closed locked merged all], - default: 'all', - desc: 'Return opened, closed, locked, merged, or all merge requests' - optional :order_by, - type: String, - values: Helpers::MergeRequestsHelpers.sort_options, - default: 'created_at', - desc: "Return merge requests ordered by #{Helpers::MergeRequestsHelpers.sort_options_help} fields." - optional :sort, - type: String, - values: %w[asc desc], - default: 'desc', - desc: 'Return merge requests sorted in `asc` or `desc` order.' - optional :with_labels_details, type: Boolean, desc: 'Return titles of labels and other details', default: false - optional :with_merge_status_recheck, type: Boolean, desc: 'Request that stale merge statuses be rechecked asynchronously', default: false - optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time' - optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time' - optional :updated_after, type: DateTime, desc: 'Return merge requests updated after the specified time' - optional :updated_before, type: DateTime, desc: 'Return merge requests updated before the specified time' - optional :view, - type: String, - values: %w[simple], - desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request' - - optional :scope, - type: String, - values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], - desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`' - optional :source_branch, type: String, desc: 'Return merge requests with the given source branch' - optional :source_project_id, type: Integer, desc: 'Return merge requests with the given source project id' - optional :target_branch, type: String, desc: 'Return merge requests with the given target branch' - optional :search, - type: String, - desc: 'Search merge requests for text present in the title, description, or any combination of these' - optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma' - optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title' - optional :not, type: Hash, desc: 'Parameters to negate' do - use :merge_requests_negatable_params - optional :reviewer_id, - types: Integer, - desc: 'Return merge requests which have the user as a reviewer with the given ID' + optional :state, type: String, + values: %w[opened closed locked merged all], + default: 'all', + desc: 'Returns `all` merge requests or just those that are `opened`, `closed`, `locked`, or `merged`.' + optional :order_by, type: String, + values: Helpers::MergeRequestsHelpers.sort_options, + default: 'created_at', + desc: "Returns merge requests ordered by #{Helpers::MergeRequestsHelpers.sort_options_help} fields. Introduced in GitLab 14.8." + optional :sort, type: String, + values: %w[asc desc], + default: 'desc', + desc: 'Returns merge requests sorted in `asc` or `desc` order.' + optional :with_labels_details, type: Boolean, + default: false, + desc: 'If `true`, response returns more details for each label in labels field: `:name`,`:color`, `:description`, `:description_html`, `:text_color`' + optional :with_merge_status_recheck, type: Boolean, + default: false, + desc: 'If `true`, this projection requests (but does not guarantee) that the `merge_status` field be recalculated asynchronously. Introduced in GitLab 13.0.' + optional :created_after, type: DateTime, + desc: 'Returns merge requests created on or after the given time. Expected in ISO 8601 format.', + documentation: { example: '2019-03-15T08:00:00Z' } + optional :created_before, type: DateTime, + desc: 'Returns merge requests created on or before the given time. Expected in ISO 8601 format.', + documentation: { example: '2019-03-15T08:00:00Z' } + optional :updated_after, type: DateTime, + desc: 'Returns merge requests updated on or after the given time. Expected in ISO 8601 format.', + documentation: { example: '2019-03-15T08:00:00Z' } + optional :updated_before, type: DateTime, + desc: 'Returns merge requests updated on or before the given time. Expected in ISO 8601 format.', + documentation: { example: '2019-03-15T08:00:00Z' } + optional :view, type: String, + values: %w[simple], + desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request' + optional :scope, type: String, + values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], + desc: 'Returns merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`' + optional :source_branch, type: String, desc: 'Returns merge requests with the given source branch' + optional :source_project_id, type: Integer, desc: 'Returns merge requests with the given source project id' + optional :target_branch, type: String, desc: 'Returns merge requests with the given target branch' + optional :search, type: String, + desc: 'Search merge requests against their `title` and `description`.' + optional :in, type: String, + desc: 'Modify the scope of the search attribute. `title`, `description`, or a string joining them with comma.', + documentation: { example: 'title,description' } + optional :wip, type: String, + values: %w[yes no], + desc: 'Filter merge requests against their `wip` status. `yes` to return only draft merge requests, `no` to return non-draft merge requests.' + optional :not, type: Hash, desc: 'Returns merge requests that do not match the parameters supplied' do + use :merge_requests_negatable_params, prefix: '`` ' + + optional :reviewer_id, types: Integer, + desc: '`` Returns merge requests which have the user as a reviewer with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`.' mutually_exclusive :reviewer_id, :reviewer_username end - - optional :deployed_before, - 'Return merge requests deployed before the given date/time' - optional :deployed_after, - 'Return merge requests deployed after the given date/time' - optional :environment, - 'Returns merge requests deployed to the given environment' + optional :deployed_before, desc: 'Returns merge requests deployed before the given date/time. Expected in ISO 8601 format.', + documentation: { example: '2019-03-15T08:00:00Z' } + optional :deployed_after, desc: 'Returns merge requests deployed after the given date/time. Expected in ISO 8601 format', + documentation: { example: '2019-03-15T08:00:00Z' } + optional :environment, desc: 'Returns merge requests deployed to the given environment', + documentation: { example: '2019-03-15T08:00:00Z' } end params :optional_scope_param do - optional :scope, - type: String, - values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], - default: 'created_by_me', - desc: 'Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`' + optional :scope, type: String, + values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all], + default: 'created_by_me', + desc: 'Returns merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`' end def handle_merge_request_errors!(merge_request) diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index bb2861aa221cdcf500f457ec583f61f66f84b019..de52f3405e309c036e4c270c2d529b06c45b27b9 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -134,7 +134,13 @@ def recheck_mergeability_of(merge_requests:) resource :merge_requests do desc 'List merge requests' do + detail 'Get all merge requests the authenticated user has access to. By default it returns only merge requests created by the current user. To get all merge requests, use parameter `scope=all`.' success Entities::MergeRequestBasic + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 422, message: 'Unprocessable entity' } + ] + tags %w[merge_requests] end params do use :merge_requests_params @@ -150,16 +156,24 @@ def recheck_mergeability_of(merge_requests:) end params do - requires :id, type: String, desc: 'The ID of a group' + requires :id, type: String, desc: 'The ID or URL-encoded path of the group owned by the authenticated user.' end resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do - desc 'Get a list of group merge requests' do + desc 'List group merge requests' do + detail 'Get all merge requests for this group and its subgroups.' success Entities::MergeRequestBasic + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 422, message: 'Unprocessable entity' } + ] + tags %w[merge_requests] end params do use :merge_requests_params - optional :non_archived, type: Boolean, desc: 'Return merge requests from non archived projects', - default: true + optional :non_archived, type: Boolean, + default: true, + desc: 'Returns merge requests from non archived projects only.' end get ":id/merge_requests", feature_category: :code_review, urgency: :low do validate_anonymous_search_access! if declared_params[:search].present? @@ -170,36 +184,62 @@ def recheck_mergeability_of(merge_requests:) end params do - requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project.' end resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do include TimeTrackingEndpoints helpers do params :optional_params do - optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request' - optional :assignee_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Comma-separated list of assignee ids' - optional :reviewer_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Comma-separated list of reviewer ids' - optional :description, type: String, desc: 'The description of the merge request' - optional :labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names' - optional :add_labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names' - optional :remove_labels, type: Array[String], coerce_with: Validations::Types::CommaSeparatedToArray.coerce, desc: 'Comma-separated list of label names' - optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request' - optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging' - optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch' + optional :assignee_id, type: Integer, desc: 'Assignee user ID.' + optional :assignee_ids, type: Array[Integer], + coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, + desc: 'The IDs of the users to assign the merge request to, as a comma-separated list. Set to 0 or provide an empty value to unassign all assignees.', + documentation: { is_array: true } + optional :reviewer_ids, type: Array[Integer], + coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, + desc: 'The IDs of the users to review the merge request, as a comma-separated list. Set to 0 or provide an empty value to unassign all reviewers.', + documentation: { is_array: true } + optional :description, type: String, desc: 'Description of the merge request. Limited to 1,048,576 characters.' + optional :labels, type: Array[String], + coerce_with: Validations::Types::CommaSeparatedToArray.coerce, + desc: 'Comma-separated label names for a merge request. Set to an empty string to unassign all labels.', + documentation: { is_array: true } + optional :add_labels, type: Array[String], + coerce_with: Validations::Types::CommaSeparatedToArray.coerce, + desc: 'Comma-separated label names to add to a merge request.', + documentation: { is_array: true } + optional :remove_labels, type: Array[String], + coerce_with: Validations::Types::CommaSeparatedToArray.coerce, + desc: 'Comma-separated label names to remove from a merge request.', + documentation: { is_array: true } + optional :milestone_id, type: Integer, desc: 'The global ID of a milestone to assign the merge reques to.' + optional :remove_source_branch, type: Boolean, desc: 'Flag indicating if a merge request should remove the source branch when merging.' + optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch.' optional :allow_maintainer_to_push, type: Boolean, as: :allow_collaboration, desc: '[deprecated] See allow_collaboration' - optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge' + optional :squash, type: Grape::API::Boolean, desc: 'Squash commits into a single commit when merging.' use :optional_params_ee end end - desc 'List merge requests' do + desc 'List project merge requests' do + detail 'Get all merge requests for this project.' success Entities::MergeRequestBasic + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 422, message: 'Unprocessable entity' } + ] + tags %w[merge_requests] end params do use :merge_requests_params - optional :iids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'The IID array of merge requests' + + optional :iids, type: Array[Integer], + coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, + desc: 'Returns the request having the given `iid`.', + documentation: { is_array: true } end get ":id/merge_requests", feature_category: :code_review, urgency: :low do authorize! :read_merge_request, user_project @@ -226,15 +266,24 @@ def recheck_mergeability_of(merge_requests:) **options end - desc 'Create a merge request' do + desc 'Create merge request' do + detail 'Create a new merge request.' + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 409, message: 'Conflict' }, + { code: 422, message: 'Unprocessable entity' } + ] success Entities::MergeRequest + tags %w[merge_requests] end params do - requires :title, type: String, desc: 'The title of the merge request' - requires :source_branch, type: String, desc: 'The source branch' - requires :target_branch, type: String, desc: 'The target branch' + requires :title, type: String, desc: 'The title of the merge request.' + requires :source_branch, type: String, desc: 'The source branch.' + requires :target_branch, type: String, desc: 'The target branch.' optional :target_project_id, type: Integer, - desc: 'The target project of the merge request defaults to the :id of the project' + desc: 'The target project of the merge request defaults to the :id of the project.' use :optional_params end post ":id/merge_requests", feature_category: :code_review, urgency: :low do @@ -253,9 +302,17 @@ def recheck_mergeability_of(merge_requests:) present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project end - desc 'Delete a merge request' + desc 'Delete a merge request' do + detail 'Only for administrators and project owners. Deletes the merge request in question. ' + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 412, message: 'Precondition failed' } + ] + tags %w[merge_requests] + end params do - requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request' + requires :merge_request_iid, type: Integer, desc: 'The internal ID of the merge request.' end delete ":id/merge_requests/:merge_request_iid", feature_category: :code_review, urgency: :low do merge_request = find_project_merge_request(params[:merge_request_iid]) @@ -268,13 +325,19 @@ def recheck_mergeability_of(merge_requests:) end params do - requires :merge_request_iid, type: Integer, desc: 'The IID of a merge request' - optional :render_html, type: Boolean, desc: 'Returns the description and title rendered HTML' - optional :include_diverged_commits_count, type: Boolean, desc: 'Returns the commits count behind the target branch' - optional :include_rebase_in_progress, type: Boolean, desc: 'Returns whether a rebase operation is ongoing ' + requires :merge_request_iid, type: Integer, desc: 'The internal ID of the merge request.' + optional :render_html, type: Boolean, desc: 'If `true`, response includes rendered HTML for title and description.' + optional :include_diverged_commits_count, type: Boolean, desc: 'If `true`, response includes the commits behind the target branch.' + optional :include_rebase_in_progress, type: Boolean, desc: 'If `true`, response includes whether a rebase operation is in progress.' end - desc 'Get a single merge request' do + desc 'Get single merge request' do + detail 'Shows information about a single merge request. Note: the `changes_count` value in the response is a string, not an integer. This is because when an merge request has too many changes to display and store, it is capped at 1,000. In that case, the API returns the string `"1000+"` for the changes count.' + success Entities::MergeRequest + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end get ':id/merge_requests/:merge_request_iid', feature_category: :code_review, urgency: :low do merge_request = find_merge_request_with_access(params[:merge_request_iid]) @@ -289,8 +352,13 @@ def recheck_mergeability_of(merge_requests:) include_rebase_in_progress: params[:include_rebase_in_progress] end - desc 'Get the participants of a merge request' do + desc 'Get single merge request participants' do + detail 'Get a list of merge request participants.' success Entities::UserBasic + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end get ':id/merge_requests/:merge_request_iid/participants', feature_category: :code_review, urgency: :low do merge_request = find_merge_request_with_access(params[:merge_request_iid]) @@ -300,8 +368,13 @@ def recheck_mergeability_of(merge_requests:) present paginate(participants), with: Entities::UserBasic end - desc 'Get the reviewers of a merge request' do + desc 'Get single merge request reviewers' do + detail 'Get a list of merge request reviewers.' success Entities::MergeRequestReviewer + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end get ':id/merge_requests/:merge_request_iid/reviewers', feature_category: :code_review, urgency: :low do merge_request = find_merge_request_with_access(params[:merge_request_iid]) @@ -311,8 +384,13 @@ def recheck_mergeability_of(merge_requests:) present paginate(reviewers), with: Entities::MergeRequestReviewer end - desc 'Get the commits of a merge request' do + desc 'Get single merge request commits' do + detail 'Get a list of merge request commits.' success Entities::Commit + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end get ':id/merge_requests/:merge_request_iid/commits', feature_category: :code_review, urgency: :low do merge_request = find_merge_request_with_access(params[:merge_request_iid]) @@ -324,8 +402,13 @@ def recheck_mergeability_of(merge_requests:) present commits, with: Entities::Commit end - desc 'Get the context commits of a merge request' do + desc 'List merge request context commits' do + detail 'Get a list of merge request context commits.' success Entities::Commit + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end get ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review, urgency: :high do merge_request = find_merge_request_with_access(params[:merge_request_iid]) @@ -336,10 +419,20 @@ def recheck_mergeability_of(merge_requests:) end params do - requires :commits, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, allow_blank: false, desc: 'List of context commits sha' - end - desc 'create context commits of merge request' do + requires :commits, type: Array[String], + coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, + allow_blank: false, + desc: 'The context commits’ SHA.', + documentation: { is_array: true } + end + desc 'Create merge request context commits' do + detail 'Create a list of merge request context commits.' success Entities::Commit + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end post ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do commit_ids = params[:commits] @@ -363,9 +456,21 @@ def recheck_mergeability_of(merge_requests:) end params do - requires :commits, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, allow_blank: false, desc: 'List of context commits sha' + requires :commits, type: Array[String], + coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, + allow_blank: false, + desc: 'The context commits’ SHA.', + documentation: { is_array: true } + end + desc 'Delete merge request context commits' do + detail 'Delete a list of merge request context commits.' + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end - desc 'remove context commits of merge request' delete ':id/merge_requests/:merge_request_iid/context_commits', feature_category: :code_review do commit_ids = params[:commits] merge_request = find_merge_request_with_access(params[:merge_request_iid]) @@ -382,8 +487,13 @@ def recheck_mergeability_of(merge_requests:) status 204 end - desc 'Show the merge request changes' do + desc 'Get single merge request changes' do + detail 'Shows information about the merge request including its files and changes.' success Entities::MergeRequestChanges + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end get ':id/merge_requests/:merge_request_iid/changes', feature_category: :code_review, urgency: :low do merge_request = find_merge_request_with_access(params[:merge_request_iid]) @@ -395,17 +505,28 @@ def recheck_mergeability_of(merge_requests:) access_raw_diffs: to_boolean(params.fetch(:access_raw_diffs, false)) end - desc 'Get the merge request pipelines' do + desc 'Get single merge request pipelines' do + detail 'Get a list of merge request pipelines.' success Entities::Ci::PipelineBasic + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end get ':id/merge_requests/:merge_request_iid/pipelines', urgency: :low, feature_category: :continuous_integration do pipelines = merge_request_pipelines_with_access - present paginate(pipelines), with: Entities::Ci::PipelineBasic end - desc 'Create a pipeline for merge request' do + desc 'Create merge request pipeline' do + detail 'Create a new pipeline for a merge request. A pipeline created via this endpoint doesn’t run a regular branch/tag pipeline. It requires `.gitlab-ci.yml` to be configured with `only: [merge_requests]` to create jobs.' success ::API::Entities::Ci::Pipeline + failure [ + { code: 400, message: 'Bad request' }, + { code: 404, message: 'Not found' }, + { code: 405, message: 'Method not allowed' } + ] + tags %w[merge_requests] end post ':id/merge_requests/:merge_request_iid/pipelines', urgency: :low, feature_category: :continuous_integration do pipeline = ::MergeRequests::CreatePipelineService @@ -423,15 +544,25 @@ def recheck_mergeability_of(merge_requests:) end end - desc 'Update a merge request' do + desc 'Update merge request' do + detail 'Updates an existing merge request. You can change the target branch, title, or even close the merge request.' success Entities::MergeRequest + failure [ + { code: 400, message: 'Bad request' }, + { code: 404, message: 'Not found' }, + { code: 409, message: 'Conflict' }, + { code: 422, message: 'Unprocessable entity' } + ] + tags %w[merge_requests] end params do - optional :title, type: String, allow_blank: false, desc: 'The title of the merge request' - optional :target_branch, type: String, allow_blank: false, desc: 'The target branch' - optional :state_event, type: String, values: %w[close reopen], - desc: 'Status of the merge request' - optional :discussion_locked, type: Boolean, desc: 'Whether the MR discussion is locked' + optional :title, type: String, allow_blank: false, desc: 'The title of the merge request.' + optional :target_branch, type: String, allow_blank: false, desc: 'The target branch.' + optional :state_event, type: String, + values: %w[close reopen], + desc: 'New state (close/reopen).' + optional :discussion_locked, type: Boolean, + desc: 'Flag indicating if the merge request’s discussion is locked. If the discussion is locked only project members can add, edit or resolve comments.' use :optional_params at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of) @@ -456,17 +587,27 @@ def recheck_mergeability_of(merge_requests:) end desc 'Merge a merge request' do + detail 'Accept and merge changes submitted with the merge request using this API.' success Entities::MergeRequest + failure [ + { code: 400, message: 'Bad request' }, + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 405, message: 'Method not allowed' }, + { code: 409, message: 'Conflict' }, + { code: 422, message: 'Unprocessable entity' } + ] + tags %w[merge_requests] end params do - optional :merge_commit_message, type: String, desc: 'Custom merge commit message' - optional :squash_commit_message, type: String, desc: 'Custom squash commit message' + optional :merge_commit_message, type: String, desc: 'Custom merge commit message.' + optional :squash_commit_message, type: String, desc: 'Custom squash commit message.' optional :should_remove_source_branch, type: Boolean, - desc: 'When true, the source branch will be deleted if possible' + desc: 'If `true`, removes the source branch.' optional :merge_when_pipeline_succeeds, type: Boolean, - desc: 'When true, this merge request will be merged when the pipeline succeeds' - optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch' - optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge' + desc: 'If `true`, the merge request is merged when the pipeline succeeds.' + optional :sha, type: String, desc: 'If present, then this SHA must match the HEAD of the source branch, otherwise the merge fails.' + optional :squash, type: Grape::API::Boolean, desc: 'If `true`, the commits are squashed into a single commit on merge.' end put ':id/merge_requests/:merge_request_iid/merge', feature_category: :code_review, urgency: :low do Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/4796') @@ -512,7 +653,13 @@ def recheck_mergeability_of(merge_requests:) end end - desc 'Returns the up to date merge-ref HEAD commit' + desc 'Returns the up to date merge-ref HEAD commit' do + detail 'Returns the up to date merge-ref HEAD commit' + failure [ + { code: 400, message: 'Bad request' } + ] + tags %w[merge_requests] + end get ':id/merge_requests/:merge_request_iid/merge_ref', feature_category: :code_review do merge_request = find_project_merge_request(params[:merge_request_iid]) @@ -525,8 +672,16 @@ def recheck_mergeability_of(merge_requests:) end end - desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do + desc 'Cancel Merge When Pipeline Succeeds' do + detail 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' success Entities::MergeRequest + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' }, + { code: 405, message: 'Method not allowed' }, + { code: 406, message: 'Not acceptable' } + ] + tags %w[merge_requests] end post ':id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds', feature_category: :code_review do merge_request = find_project_merge_request(params[:merge_request_iid]) @@ -536,11 +691,17 @@ def recheck_mergeability_of(merge_requests:) AutoMergeService.new(merge_request.target_project, current_user).cancel(merge_request) end - desc 'Rebase the merge request against its target branch' do - detail 'This feature was added in GitLab 11.6' + desc 'Rebase a merge request' do + detail 'Automatically rebase the `source_branch` of the merge request against its `target_branch`. This feature was added in GitLab 11.6' + failure [ + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' }, + { code: 409, message: 'Conflict' } + ] + tags %w[merge_requests] end params do - optional :skip_ci, type: Boolean, desc: 'Do not create CI pipeline' + optional :skip_ci, type: Boolean, desc: 'Set to true to skip creating a CI pipeline.' end put ':id/merge_requests/:merge_request_iid/rebase', feature_category: :code_review, urgency: :low do merge_request = find_project_merge_request(params[:merge_request_iid]) @@ -555,8 +716,13 @@ def recheck_mergeability_of(merge_requests:) render_api_error!(e.message, 409) end - desc 'Remove merge request approvals' do - detail 'This feature was added in GitLab 15.4' + desc 'Reset approvals of a merge request' do + detail 'Clear all approvals of merge request. This feature was added in GitLab 15.4' + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end put ':id/merge_requests/:merge_request_iid/reset_approvals', feature_category: :code_review, urgency: :low do merge_request = find_project_merge_request(params[:merge_request_iid]) @@ -568,8 +734,13 @@ def recheck_mergeability_of(merge_requests:) status :accepted end - desc 'List issues that will be closed on merge' do + desc 'List issues that close on merge' do + detail 'Get all the issues that would be closed by merging the provided merge request.' success Entities::MRNote + failure [ + { code: 404, message: 'Not found' } + ] + tags %w[merge_requests] end params do use :pagination diff --git a/lib/api/time_tracking_endpoints.rb b/lib/api/time_tracking_endpoints.rb index b8323304957928b424bc44f9b9b82981d0e52bd0..20f4b2988eb9741efeb60f9cdf59b52ed99a8142 100644 --- a/lib/api/time_tracking_endpoints.rb +++ b/lib/api/time_tracking_endpoints.rb @@ -54,10 +54,18 @@ def update_service issuable_collection_name = issuable_name.pluralize issuable_key = "#{issuable_name}_iid".to_sym - desc "Set a time estimate for a project #{issuable_name}" + desc "Set a time estimate for a #{issuable_name}" do + detail " Sets an estimated time of work for this #{issuable_name}." + success Entities::IssuableTimeStats + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags [issuable_collection_name] + end params do - requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" - requires :duration, type: String, desc: 'The duration to be parsed' + requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}." + requires :duration, type: String, desc: 'The duration in human format.', documentation: { example: '3h30m' } end post ":id/#{issuable_collection_name}/:#{issuable_key}/time_estimate" do authorize! admin_issuable_key, load_issuable @@ -66,9 +74,17 @@ def update_service update_issuable(time_estimate: Gitlab::TimeTrackingFormatter.parse(params.delete(:duration))) end - desc "Reset the time estimate for a project #{issuable_name}" + desc "Reset the time estimate for a project #{issuable_name}" do + detail "Resets the estimated time for this #{issuable_name} to 0 seconds." + success Entities::IssuableTimeStats + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags [issuable_collection_name] + end params do - requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" + requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}." end post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_time_estimate" do authorize! admin_issuable_key, load_issuable @@ -77,10 +93,18 @@ def update_service update_issuable(time_estimate: 0) end - desc "Add spent time for a project #{issuable_name}" + desc "Add spent time for a #{issuable_name}" do + detail "Adds spent time for this #{issuable_name}." + success Entities::IssuableTimeStats + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags [issuable_collection_name] + end params do - requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" - requires :duration, type: String, desc: 'The duration to be parsed' + requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}." + requires :duration, type: String, desc: 'The duration in human format.' end post ":id/#{issuable_collection_name}/:#{issuable_key}/add_spent_time" do authorize! admin_issuable_key, load_issuable @@ -97,9 +121,17 @@ def update_service update_issuable(update_params) end - desc "Reset spent time for a project #{issuable_name}" + desc "Reset spent time for a #{issuable_name}" do + detail "Resets the total spent time for this #{issuable_name} to 0 seconds." + success Entities::IssuableTimeStats + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags [issuable_collection_name] + end params do - requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" + requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}" end post ":id/#{issuable_collection_name}/:#{issuable_key}/reset_spent_time" do authorize! admin_issuable_key, load_issuable @@ -108,9 +140,17 @@ def update_service update_issuable(spend_time: { duration: :reset, user_id: current_user.id }) end - desc "Show time stats for a project #{issuable_name}" + desc "Get time tracking stats" do + detail "Get time tracking stats" + success Entities::IssuableTimeStats + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 404, message: 'Not found' } + ] + tags [issuable_collection_name] + end params do - requires issuable_key, type: Integer, desc: "The ID of a project #{issuable_name}" + requires issuable_key, type: Integer, desc: "The internal ID of the #{issuable_name}" end get ":id/#{issuable_collection_name}/:#{issuable_key}/time_stats" do authorize! read_issuable_key, load_issuable