diff --git a/.rubocop.yml b/.rubocop.yml index 6cde10a..c5aa833 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -27,12 +27,3 @@ Lint/NonDeterministicRequireOrder: Enabled: false - -Style/HashEachMethods: - Enabled: true - -Style/HashTransformKeys: - Enabled: true - -Style/HashTransformValues: - Enabled: true diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d9e1a9a..5212878 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2020-06-28 04:00:53 UTC using RuboCop version 0.86.0. +# on 2020-11-28 09:16:25 UTC using RuboCop version 1.4.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -9,20 +9,20 @@ # Offense count: 4 # Configuration parameters: IgnoredMethods. Metrics/AbcSize: - Max: 34 + Max: 35 # Offense count: 3 # Configuration parameters: IgnoredMethods. Metrics/CyclomaticComplexity: Max: 13 -# Offense count: 8 -# Configuration parameters: CountComments, ExcludedMethods. +# Offense count: 10 +# Configuration parameters: CountComments, CountAsOne, ExcludedMethods. Metrics/MethodLength: Max: 34 # Offense count: 2 -# Configuration parameters: CountComments. +# Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: Max: 156 @@ -31,10 +31,10 @@ Metrics/ParameterLists: Max: 6 -# Offense count: 1 +# Offense count: 2 # Configuration parameters: IgnoredMethods. Metrics/PerceivedComplexity: - Max: 10 + Max: 11 # Offense count: 1 # Cop supports --auto-correct. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 99e6542..ba9086c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -169,16 +169,22 @@ rake ``` -6. Push your topic branch up to your fork: +6. Make sure you comply with rubocop style guide. You can run the linter using + + ```sh + rake rubocop + ``` + +7. Push your topic branch up to your fork: ```sh git push origin ``` -7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) +8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) with a clear title and description. -8. If you haven't updated your pull request for a while, you should consider +9. If you haven't updated your pull request for a while, you should consider rebasing on master and resolving any conflicts. **IMPORTANT**: _Never ever_ merge upstream `master` into your branches. You diff --git a/README.md b/README.md index a625f72..c25a20d 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ # gem 'gitlab', github: 'NARKOZ/gitlab' ``` -Mac OS users can install using Homebrew: +Mac OS users can install using Homebrew (may not be the latest version): ```sh brew install gitlab-gem diff --git a/gitlab.gemspec b/gitlab.gemspec index 6af658a..13ef749 100644 --- a/gitlab.gemspec +++ b/gitlab.gemspec @@ -22,7 +22,7 @@ gem.required_ruby_version = '>= 2.5' - gem.add_runtime_dependency 'httparty', '~> 0.14', '>= 0.14.0' + gem.add_runtime_dependency 'httparty', '~> 0.18' gem.add_runtime_dependency 'terminal-table', '~> 1.5', '>= 1.5.1' gem.add_development_dependency 'rake' diff --git a/lib/gitlab/api.rb b/lib/gitlab/api.rb index cc8c2b7..509f1dd 100644 --- a/lib/gitlab/api.rb +++ b/lib/gitlab/api.rb @@ -10,6 +10,7 @@ # Creates a new API. # @raise [Error:MissingCredentials] + # rubocop:disable Lint/MissingSuper def initialize(options = {}) options = Gitlab.options.merge(options) (Configuration::VALID_OPTIONS_KEYS + [:auth_token]).each do |key| @@ -18,5 +19,6 @@ request_defaults(sudo) self.class.headers 'User-Agent' => user_agent end + # rubocop:enable Lint/MissingSuper end end diff --git a/lib/gitlab/cli.rb b/lib/gitlab/cli.rb index 7f58e0a..5819bff 100644 --- a/lib/gitlab/cli.rb +++ b/lib/gitlab/cli.rb @@ -17,10 +17,10 @@ # @param [Array] args The command and it's optional arguments. def self.start(args) command = begin - args.shift.strip - rescue StandardError - 'help' - end + args.shift.strip + rescue StandardError + 'help' + end run(command, args) end diff --git a/lib/gitlab/cli_helpers.rb b/lib/gitlab/cli_helpers.rb index 97b0723..08a3b46 100644 --- a/lib/gitlab/cli_helpers.rb +++ b/lib/gitlab/cli_helpers.rb @@ -111,7 +111,7 @@ else hash_result = case data when Gitlab::ObjectifiedHash, Gitlab::FileResponse - record_hash([data], cmd, args, true) + record_hash([data], cmd, args, single_value: true) when Gitlab::PaginatedResponse record_hash(data, cmd, args) else @@ -162,7 +162,7 @@ # @param [Array] args Options passed to the API call # @param [bool] single_value If set to true, a single result should be returned # @return [Hash] Result hash - def record_hash(data, cmd, args, single_value = false) + def record_hash(data, cmd, args, single_value: false) if data.empty? result = nil else diff --git a/lib/gitlab/client/commits.rb b/lib/gitlab/client/commits.rb index dcb2716..77a44dd 100644 --- a/lib/gitlab/client/commits.rb +++ b/lib/gitlab/client/commits.rb @@ -59,9 +59,30 @@ # @param [Integer, String] project The ID or name of a project. # @param [String] sha The commit hash or name of a repository branch or tag # @param [String] branch The name of the branch - # @return [Gitlab::ObjectifiedHash] - def cherry_pick_commit(project, sha, branch) - post("/projects/#{url_encode project}/repository/commits/#{sha}/cherry_pick", body: { branch: branch }) + # @param [Hash] options A customizable set of options. + # @option options [Boolean] :dry_run Don't commit any changes + # @return [Gitlab::ObjectifiedHash] + def cherry_pick_commit(project, sha, branch, options = {}) + options[:branch] = branch + + post("/projects/#{url_encode project}/repository/commits/#{sha}/cherry_pick", body: options) + end + + # Reverts a commit in a given branch. + # + # @example + # Gitlab.revert_commit(42, '6104942438c14ec7bd21c6cd5bd995272b3faff6', 'master') + # + # @param [Integer, String] project The ID or name of a project. + # @param [String] sha The commit hash or name of a repository branch or tag + # @param [String] branch The name of the branch + # @param [Hash] options A customizable set of options. + # @option options [Boolean] :dry_run Don't commit any changes + # @return [Gitlab::ObjectifiedHash] + def revert_commit(project, sha, branch, options = {}) + options[:branch] = branch + + post("/projects/#{url_encode project}/repository/commits/#{sha}/revert", body: options) end # Get the diff of a commit in a project. @@ -145,7 +166,7 @@ # @option options [String] :name Filter by status name, eg. jenkins # @option options [String] :target_url The target URL to associate with this status def update_commit_status(project, sha, state, options = {}) - post("/projects/#{url_encode project}/statuses/#{sha}", query: options.merge(state: state)) + post("/projects/#{url_encode project}/statuses/#{sha}", body: options.merge(state: state)) end alias repo_update_commit_status update_commit_status diff --git a/lib/gitlab/client/container_registry.rb b/lib/gitlab/client/container_registry.rb index ff8caf7..dc9a5ea 100644 --- a/lib/gitlab/client/container_registry.rb +++ b/lib/gitlab/client/container_registry.rb @@ -79,7 +79,7 @@ # @option options [String] :older_than(required) Tags to delete that are older than the given time, written in human readable form 1h, 1d, 1month. # @return [void] This API call returns an empty response body. def bulk_delete_registry_repository_tags(project, repository_id, options = {}) - delete("/projects/#{url_encode project}/registry/repositories/#{repository_id}/tags", query: options) + delete("/projects/#{url_encode project}/registry/repositories/#{repository_id}/tags", body: options) end end end diff --git a/lib/gitlab/client/group_badges.rb b/lib/gitlab/client/group_badges.rb new file mode 100644 index 0000000..c5eceda --- /dev/null +++ b/lib/gitlab/client/group_badges.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +class Gitlab::Client + # Defines methods related to group badges. + # @see https://docs.gitlab.com/ee/api/group_badges.html + module GroupBadges + # Gets a list of a groups badges. + # + # @example + # Gitlab.group_badges(5) + # Gitlab.group_badges(5, 'Coverage') + # + # @param [Integer, String] group(required) The ID or URL-encoded path of the group owned by the authenticated user. + # @param [String] name(optional) Name of the badges to return (case-sensitive). + # @return [Array] List of all badges of a group + def group_badges(group, name = nil) + query = { name: name } if name + get("/groups/#{url_encode group}/badges", query: query) + end + + # Gets a badge of a group. + # + # @example + # Gitlab.group_badge(5, 42) + # + # @param [Integer, String] group(required) The ID or URL-encoded path of the group owned by the authenticated user. + # @param [Integer] badge_id(required) The badge ID. + # @return [Gitlab::ObjectifiedHash] Information about the requested badge + def group_badge(group, badge_id) + get("/groups/#{url_encode group}/badges/#{badge_id}") + end + + # Adds a badge to a group. + # + # @example + # Gitlab.add_group_badge(5, { link_url: 'https://abc.com/gitlab/gitlab-ce/commits/master', image_url: 'https://shields.io/my/badge1' }) + # + # @param [Integer, String] group(required) The ID or URL-encoded path of the group owned by the authenticated user. + # @param [Hash] options A customizable set of options. + # @option options [String] :link_url(required) URL of the badge link + # @option options [String] :image_url(required) URL of the badge image + # @return [Gitlab::ObjectifiedHash] Information about the added group badge. + def add_group_badge(group, options = {}) + post("/groups/#{url_encode group}/badges", body: options) + end + + # Updates a badge of a group. + # + # @example + # Gitlab.edit_group_badge(5, 1, { link_url: 'https://abc.com/gitlab/gitlab-ce/commits/master', image_url: 'https://shields.io/my/badge1' }) + # + # @param [Integer, String] group(required) The ID or URL-encoded path of the group owned by the authenticated user. + # @param [Integer] badge_id(required) The badge ID. + # @param [Hash] options A customizable set of options. + # @option options [String] :link_url(optional) URL of the badge link + # @option options [String] :image_url(optional) URL of the badge image + # @return [Gitlab::ObjectifiedHash] Information about the updated group badge. + def edit_group_badge(group, badge_id, options = {}) + put("/groups/#{url_encode group}/badges/#{badge_id}", body: options) + end + + # Removes a badge from a group. + # + # @example + # Gitlab.remove_group_badge(5, 42) + # + # @param [Integer, String] group(required) The ID or URL-encoded path of the group owned by the authenticated user. + # @param [Integer] badge_id(required) The badge ID. + # @return [nil] This API call returns an empty response body. + def remove_group_badge(group, badge_id) + delete("/groups/#{url_encode group}/badges/#{badge_id}") + end + + # Preview a badge from a group. + # + # @example + # Gitlab.preview_group_badge(3, 'https://abc.com/gitlab/gitlab-ce/commits/master', 'https://shields.io/my/badge1') + # + # @param [Integer, String] group(required) The ID or URL-encoded path of the group owned by the authenticated user. + # @param [String] :link_url(required) URL of the badge link + # @param [String] :image_url(required) URL of the badge image + # @return [Gitlab::ObjectifiedHash] Returns how the link_url and image_url final URLs would be after resolving the placeholder interpolation. + def preview_group_badge(group, link_url, image_url) + query = { link_url: link_url, image_url: image_url } + get("/groups/#{url_encode group}/badges/render", query: query) + end + end +end diff --git a/lib/gitlab/client/group_labels.rb b/lib/gitlab/client/group_labels.rb index 9cca4bb..eecd2a3 100644 --- a/lib/gitlab/client/group_labels.rb +++ b/lib/gitlab/client/group_labels.rb @@ -58,7 +58,7 @@ # @param [String] name The name of a label. # @return [Gitlab::ObjectifiedHash] Information about deleted label. def delete_group_label(group, name) - delete("/groups/#{url_encode group}/labels", body: { name: name }) + delete("/groups/#{url_encode group}/labels/#{name}") end # Subscribes the user to a group label to receive notifications diff --git a/lib/gitlab/client/groups.rb b/lib/gitlab/client/groups.rb index 141bf33..4c08b30 100644 --- a/lib/gitlab/client/groups.rb +++ b/lib/gitlab/client/groups.rb @@ -69,6 +69,21 @@ # @return [Array] def group_members(id, options = {}) get("/groups/#{url_encode id}/members", query: options) + end + + # Get a list of group members that are billable. + # + # @example + # Gitlab.group_billable_members(1) + # Gitlab.group_billable_members(1, { per_page: 40 }) + # + # @param [Integer] id The ID of a group. + # @param [Hash] options A customizable set of options. + # @option options [Integer] :page The page number. + # @option options [Integer] :per_page The number of results per page. + # @return [Array] + def group_billable_members(id, options = {}) + get("/groups/#{url_encode id}/billable_members", query: options) end # Get details of a single group member. diff --git a/lib/gitlab/client/jobs.rb b/lib/gitlab/client/jobs.rb index 39cfad4..a961cc8 100644 --- a/lib/gitlab/client/jobs.rb +++ b/lib/gitlab/client/jobs.rb @@ -36,6 +36,21 @@ get("/projects/#{url_encode project_id}/pipelines/#{pipeline_id}/jobs", query: options) end + # Gets a list of Bridge Jobs from a pipeline + # + # @example + # Gitlab.pipeline_bridges(1, 2) + # Gitlab.pipeline_bridges("project", 2) + # + # @param [Integer, String] The ID or name of a project. + # @param [Integer] the id of the pipeline + # @param [Hash] options A customizable set of options. + # @option options [Array] :scope The scope of bridge jobs to show, one or array of: created, pending, running, failed, success, canceled, skipped, manual; showing all bridge jobs if none provided. + # @return [Array] + def pipeline_bridges(project_id, pipeline_id, options = {}) + get("/projects/#{url_encode project_id}/pipelines/#{pipeline_id}/bridges", query: options) + end + # Gets a single job # # @example @@ -70,16 +85,70 @@ # Gitlab.job_artifacts_download(1, "master", "release") # Gitlab.job_artifacts_download("project", "master", "release") # - # @param [Integer, String] id, The ID or name of a project. - # @param [String] ref, Ref Name - # @param [String] job, jobname - # @return [Array] + # @param [Integer, String] project_id The ID or name of a project. + # @param [String] ref Ref Name + # @param [String] job jobname + # @return [Gitlab::FileResponse] def job_artifacts_download(project_id, ref_name, job_name) - get("/projects/#{url_encode project_id}/jobs/artifacts/#{ref_name}/download", query: { job: job_name }, - format: nil, - headers: { Accept: 'text/plain' }, - parser: ::Gitlab::Request::Parser) - end + get("/projects/#{url_encode project_id}/jobs/artifacts/#{ref_name}/download", + query: { job: job_name }, + format: nil, + headers: { Accept: 'application/octet-stream' }, + parser: proc { |body, _| + if body.encoding == Encoding::ASCII_8BIT # binary response + ::Gitlab::FileResponse.new StringIO.new(body, 'rb+') + else # error with json response + ::Gitlab::Request.parse(body) + end + }) + end + + # Download a single artifact file by job ID + # + # @example + # Gitlab.download_job_artifact_file(1, 5, "some/release/file.pdf") + # + # @param [Integer, String] project_id(required) The ID or name of a project. + # @param [String] job_id(required) The unique job identifier. + # @param [String] artifact_path(required) Path to a file inside the artifacts archive. + # @return [Gitlab::FileResponse] + def download_job_artifact_file(project_id, job_id, artifact_path) + get("/projects/#{url_encode project_id}/jobs/#{job_id}/artifacts/#{artifact_path}", + format: nil, + headers: { Accept: 'application/octet-stream' }, + parser: proc { |body, _| + if body.encoding == Encoding::ASCII_8BIT # binary response + ::Gitlab::FileResponse.new StringIO.new(body, 'rb+') + else # error with json response + ::Gitlab::Request.parse(body) + end + }) + end + + # Download a single artifact file from specific tag or branch + # + # @example + # Gitlab.download_branch_artifact_file(1, "master", "some/release/file.pdf", 'pdf') + # + # @param [Integer, String] project_id(required) The ID or name of a project. + # @param [String] ref_name(required) Branch or tag name in repository. HEAD or SHA references are not supported. + # @param [String] artifact_path(required) Path to a file inside the artifacts archive. + # @param [String] job(required) The name of the job. + # @return [Gitlab::FileResponse] + def download_branch_artifact_file(project_id, ref_name, artifact_path, job) + get("/projects/#{url_encode project_id}/jobs/artifacts/#{ref_name}/raw/#{artifact_path}", + query: { job: job }, + format: nil, + headers: { Accept: 'application/octet-stream' }, + parser: proc { |body, _| + if body.encoding == Encoding::ASCII_8BIT # binary response + ::Gitlab::FileResponse.new StringIO.new(body, 'rb+') + else # error with json response + ::Gitlab::Request.parse(body) + end + }) + end + alias download_tag_artifact_file download_branch_artifact_file # Get Job Trace # diff --git a/lib/gitlab/client/labels.rb b/lib/gitlab/client/labels.rb index 7373121..39dba86 100644 --- a/lib/gitlab/client/labels.rb +++ b/lib/gitlab/client/labels.rb @@ -58,7 +58,7 @@ # @param [String] name The name of a label. # @return [Gitlab::ObjectifiedHash] Information about deleted label. def delete_label(project, name) - delete("/projects/#{url_encode project}/labels", body: { name: name }) + delete("/projects/#{url_encode project}/labels/#{name}") end # Subscribes the user to a label to receive notifications diff --git a/lib/gitlab/client/pipeline_schedules.rb b/lib/gitlab/client/pipeline_schedules.rb index 9e9fe1e..2b6d2f6 100644 --- a/lib/gitlab/client/pipeline_schedules.rb +++ b/lib/gitlab/client/pipeline_schedules.rb @@ -44,7 +44,7 @@ # @option options [Boolean] :active The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially (default: true). # @return [Array] def create_pipeline_schedule(project, options = {}) - post("/projects/#{url_encode project}/pipeline_schedules", query: options) + post("/projects/#{url_encode project}/pipeline_schedules", body: options) end # Updates the pipeline schedule of a project. @@ -62,7 +62,7 @@ # @option options [Boolean] :active The activation of pipeline schedule. If false is set, the pipeline schedule will deactivated initially (default: true). # @return [Array] The updated pipeline schedule. def edit_pipeline_schedule(project, pipeline_schedule_id, options = {}) - put("/projects/#{url_encode project}/pipeline_schedules/#{pipeline_schedule_id}", query: options) + put("/projects/#{url_encode project}/pipeline_schedules/#{pipeline_schedule_id}", body: options) end # Take ownership of a pipeline schedule. @@ -101,7 +101,7 @@ # @option options [String] :value The value of a variable # @return [Array] The created pipeline schedule variable. def create_pipeline_schedule_variable(project, pipeline_schedule_id, options = {}) - post("/projects/#{url_encode project}/pipeline_schedules/#{pipeline_schedule_id}/variables", query: options) + post("/projects/#{url_encode project}/pipeline_schedules/#{pipeline_schedule_id}/variables", body: options) end # Updates the variable of a pipeline schedule. @@ -116,7 +116,7 @@ # @option options [String] :value The value of a variable. # @return [Array] The updated pipeline schedule variable. def edit_pipeline_schedule_variable(project, pipeline_schedule_id, key, options = {}) - put("/projects/#{url_encode project}/pipeline_schedules/#{pipeline_schedule_id}/variables/#{url_encode key}", query: options) + put("/projects/#{url_encode project}/pipeline_schedules/#{pipeline_schedule_id}/variables/#{url_encode key}", body: options) end # Delete the variable of a pipeline schedule diff --git a/lib/gitlab/client/pipelines.rb b/lib/gitlab/client/pipelines.rb index b8e71e6..b497054 100644 --- a/lib/gitlab/client/pipelines.rb +++ b/lib/gitlab/client/pipelines.rb @@ -29,6 +29,18 @@ # @return [Gitlab::ObjectifiedHash] def pipeline(project, id) get("/projects/#{url_encode project}/pipelines/#{id}") + end + + # Gets a single pipeline's test report. + # + # @example + # Gitlab.pipeline_test_report(5, 36) + # + # @param [Integer, String] project The ID or name of a project. + # @param [Integer] id The ID of a pipeline. + # @return [Gitlab::ObjectifiedHash] + def pipeline_test_report(project, id) + get("/projects/#{url_encode project}/pipelines/#{id}/test_report") end # Create a pipeline. diff --git a/lib/gitlab/client/projects.rb b/lib/gitlab/client/projects.rb index b0455df..400f75e 100644 --- a/lib/gitlab/client/projects.rb +++ b/lib/gitlab/client/projects.rb @@ -102,6 +102,22 @@ get("/projects/#{url_encode project}/members", query: options) end + # Gets a list of all project team members including inherited members. + # + # @example + # Gitlab.all_members(42) + # Gitlab.all_members('gitlab') + # + # @param [Integer, String] project The ID or path of a project. + # @param [Hash] options A customizable set of options. + # @option options [String] :query The search query. + # @option options [Integer] :page The page number. + # @option options [Integer] :per_page The number of results per page. + # @return [Array] + def all_members(project, options = {}) + get("/projects/#{url_encode project}/members/all", query: options) + end + # Gets a project team member. # # @example @@ -519,6 +535,25 @@ delete("/projects/#{url_encode id}/star") end + # Get a list of visible projects that the given user has starred. + # @see https://docs.gitlab.com/ee/api/projects.html#list-projects-starred-by-a-user + # + # @example + # Gitlab.user_starred_projects(1) + # Gitlab.user_starred_projects(1, { order_by: 'last_activity_at' }) + # Gitlab.user_starred_projects('username', { order_by: 'name', sort: 'asc' }) + # + # @param [Integer, String] user_id The ID or username of the user. + # @param [Hash] options A customizable set of options. + # @option options [String] :per_page Number of projects to return per page + # @option options [String] :page The page to retrieve + # @option options [String] :order_by Return projects ordered by id, name, path, created_at, updated_at, or last_activity_at fields. + # @option options [String] :sort Return projects sorted in asc or desc order. + # @return [Array] + def user_starred_projects(user_id, options = {}) + get("/users/#{url_encode user_id}/starred_projects", query: options) + end + # Get a list of visible projects for the given user. # @see https://docs.gitlab.com/ee/api/projects.html#list-user-projects # diff --git a/lib/gitlab/client/resource_state_events.rb b/lib/gitlab/client/resource_state_events.rb new file mode 100644 index 0000000..f2b2ebd --- /dev/null +++ b/lib/gitlab/client/resource_state_events.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +class Gitlab::Client + # Defines methods related to resource state events. + # @see https://docs.gitlab.com/ee/api/resource_state_events.html + module ResourceStateEvents + # Gets a list of all state events for a single issue. + # + # @example + # Gitlab.issue_state_events(5, 42) + # + # @param [Integer, String] project The ID or name of a project. + # @param [Integer] issue_iid The IID of an issue. + # @return [Array] + def issue_state_events(project, issue_iid) + get("/projects/#{url_encode project}/issues/#{issue_iid}/resource_state_events") + end + + # Returns a single state event for a specific project issue + # + # @example + # Gitlab.issue_state_event(5, 42, 1) + # + # @param [Integer, String] project The ID or name of a project. + # @param [Integer] issue_iid The IID of an issue. + # @param [Integer] id The ID of a resource event. + # @return Gitlab::ObjectifiedHash + def issue_state_event(project, issue_iid, id) + get("/projects/#{url_encode project}/issues/#{issue_iid}/resource_state_events/#{id}") + end + + # Gets a list of all state events for a single merge request. + # + # @example + # Gitlab.merge_request_state_events(5, 42) + # + # @param [Integer, String] project The ID or name of a project. + # @param [Integer] merge_request_iid The IID of a merge request. + # @return [Array] + def merge_request_state_events(project, merge_request_iid) + get("/projects/#{url_encode project}/merge_requests/#{merge_request_iid}/resource_state_events") + end + + # Returns a single state event for a specific project merge request + # + # @example + # Gitlab.merge_request_state_event(5, 42, 1) + # + # @param [Integer, String] project The ID or name of a project. + # @param [Integer] merge_request_iid The IID of an merge request. + # @param [Integer] id The ID of a state event. + # @return Gitlab::ObjectifiedHash + def merge_request_state_event(project, merge_request_iid, id) + get("/projects/#{url_encode project}/merge_requests/#{merge_request_iid}/resource_state_events/#{id}") + end + end +end diff --git a/lib/gitlab/client/runners.rb b/lib/gitlab/client/runners.rb index a8976af..653d694 100644 --- a/lib/gitlab/client/runners.rb +++ b/lib/gitlab/client/runners.rb @@ -9,11 +9,13 @@ # # @example # Gitlab.runners - # Gitlab.runners(:active) - # Gitlab.runners(:paused) - # - # @param [Hash] options A customizable set of options. - # @option options [String] :scope The scope of specific runners to show, one of: active, paused, online; showing all runners if none provided + # Gitlab.runners(type: 'instance_type', status: 'active') + # Gitlab.runners(tag_list: 'tag1,tag2') + # + # @param [Hash] options A customizable set of options. + # @option options [String] :type(optional) The type of runners to show, one of: instance_type, group_type, project_type + # @option options [String] :status(optional) The status of runners to show, one of: active, paused, online, offline + # @option options [String] :tag_list(optional) List of the runners tags (separated by comma) # @return [Array] def runners(options = {}) get('/runners', query: options) @@ -24,9 +26,13 @@ # # @example # Gitlab.all_runners - # - # @param [Hash] options A customizable set of options. - # @option options [String] :scope The scope of runners to show, one of: specific, shared, active, paused, online; showing all runners if none provided + # Gitlab.all_runners(type: 'instance_type', status: 'active') + # Gitlab.all_runners(tag_list: 'tag1,tag2') + # + # @param [Hash] options A customizable set of options. + # @option options [String] :type(optional) The type of runners to show, one of: instance_type, group_type, project_type + # @option options [String] :status(optional) The status of runners to show, one of: active, paused, online, offline + # @option options [String] :tag_list(optional) List of the runners tags (separated by comma) # @return [Array] def all_runners(options = {}) get('/runners/all', query: options) @@ -50,15 +56,19 @@ # @example # Gitlab.update_runner(42, { description: 'Awesome runner' }) # Gitlab.update_runner(42, { active: false }) - # Gitlab.update_runner(42, { tag_list: [ 'awesome', 'runner' ] }) # # @param [Integer, String] id The ID of a runner # @param [Hash] options A customizable set of options. - # @option options [String] :active The state of a runner; can be set to true or false. - # @option options [String] :tag_list The list of tags for a runner; put array of tags, that should be finally assigned to a runner + # @option options [String] :description(optional) The description of a runner + # @option options [Boolean] :active(optional) The state of a runner; can be set to true or false + # @option options [String] :tag_list(optional) The list of tags for a runner; put array of tags, that should be finally assigned to a runner(separated by comma) + # @option options [Boolean] :run_untagged(optional) Flag indicating the runner can execute untagged jobs + # @option options [Boolean] :locked(optional) Flag indicating the runner is locked + # @option options [String] :access_level(optional) The access_level of the runner; not_protected or ref_protected + # @option options [Integer] :maximum_timeout(optional) Maximum timeout set when this runner will handle the job # @return def update_runner(id, options = {}) - put("/runners/#{id}", query: options) + put("/runners/#{id}", body: options) end # Remove a runner. @@ -68,19 +78,23 @@ # Gitlab.delete_runner(42) # # @param [Integer, String] id The ID of a runner - # @return + # @return [nil] This API call returns an empty response body. def delete_runner(id) delete("/runners/#{id}") end - # Gets a list of Jobs for a Runner + # List jobs that are being processed or were processed by specified runner. # # @example # Gitlab.runner_jobs(1) + # Gitlab.runner_jobs(1, status: 'success') + # Gitlab.runner_jobs(1, sort: 'desc') # # @param [Integer] id The ID of a runner. # @param [Hash] options A customizable set of options. - # @option options [String] :status Status of the job; one of: running, success, failed, canceled + # @option options [String] :status(optional) Status of the job; one of: running, success, failed, canceled + # @option options [String] :order_by(optional) Order jobs by id. + # @option options [String] :sort(optional) Sort jobs in asc or desc order (default: desc) # @return [Array] def runner_jobs(runner_id, options = {}) get("/runners/#{url_encode runner_id}/jobs", query: options) @@ -91,11 +105,17 @@ # # @example # Gitlab.project_runners(42) - # - # @param [Integer, String] id The ID or name of a project. - # @return [Array] - def project_runners(project_id) - get("/projects/#{url_encode project_id}/runners") + # Gitlab.project_runners(42, type: 'instance_type', status: 'active') + # Gitlab.project_runners(42, tag_list: 'tag1,tag2') + # + # @param [Integer, String] id The ID or name of a project. + # @param [Hash] options A customizable set of options. + # @option options [String] :type(optional) The type of runners to show, one of: instance_type, group_type, project_type + # @option options [String] :status(optional) The status of runners to show, one of: active, paused, online, offline + # @option options [String] :tag_list(optional) List of the runners tags (separated by comma) + # @return [Array] + def project_runners(project_id, options = {}) + get("/projects/#{url_encode project_id}/runners", query: options) end # Enable an available specific runner in the project. @@ -125,21 +145,39 @@ delete("/projects/#{url_encode id}/runners/#{runner_id}") end + # List all runners (specific and shared) available in the group as well its ancestor groups. Shared runners are listed if at least one shared runner is defined. + # @see https://docs.gitlab.com/ee/api/runners.html#list-groups-runners + # + # @example + # Gitlab.group_runners(9) + # Gitlab.group_runners(9, type: 'instance_type', status: 'active') + # Gitlab.group_runners(9, tag_list: 'tag1,tag2') + # + # @param [Integer, String] id The ID or name of a project. + # @param [Hash] options A customizable set of options. + # @option options [String] :type(optional) The type of runners to show, one of: instance_type, group_type, project_type + # @option options [String] :status(optional) The status of runners to show, one of: active, paused, online, offline + # @option options [String] :tag_list(optional) List of the runners tags (separated by comma) + # @return [Array] + def group_runners(group, options = {}) + get("/groups/#{url_encode group}/runners", query: options) + end + # Register a new Runner for the instance. # # @example # Gitlab.register_runner('9142c16ea169eaaea3d752313a434a6e') # Gitlab.register_runner('9142c16ea169eaaea3d752313a434a6e', description: 'Some Description', active: true, locked: false) # - # @param [String] token Registration token. - # @param [Hash] options A customizable set of options. - # @option options [String] :description Runner description. - # @option options [Hash] :info Runner metadata. - # @option options [Boolean] :active Whether the Runner is active. - # @option options [Boolean] :locked Whether the Runner should be locked for current project. - # @option options [Boolean] :run_untagged Whether the Runner should handle untagged jobs. - # @option options [Array] :tag_list List of Runner tags. - # @option options [Integer] :maximum_timeout Maximum timeout set when this Runner will handle the job. + # @param [String] token(required) Registration token. + # @param [Hash] options A customizable set of options. + # @option options [String] :description(optional) Runner description. + # @option options [Hash] :info(optional) Runner metadata. + # @option options [Boolean] :active(optional) Whether the Runner is active. + # @option options [Boolean] :locked(optional) Whether the Runner should be locked for current project. + # @option options [Boolean] :run_untagged(optional) Whether the Runner should handle untagged jobs. + # @option options [Array] :tag_list(optional) List of Runner tags. + # @option options [Integer] :maximum_timeout(optional) Maximum timeout set when this Runner will handle the job. # @return Response against runner registration def register_runner(token, options = {}) body = { token: token }.merge(options) diff --git a/lib/gitlab/client.rb b/lib/gitlab/client.rb index 18023ca..a4848a5 100644 --- a/lib/gitlab/client.rb +++ b/lib/gitlab/client.rb @@ -23,6 +23,7 @@ include Epics include Events include Features + include GroupBadges include GroupBoards include GroupLabels include GroupMilestones @@ -52,6 +53,7 @@ include RepositoryFiles include RepositorySubmodules include ResourceLabelEvents + include ResourceStateEvents include Runners include Search include Services @@ -80,7 +82,7 @@ # # @return [String] def url_encode(url) - url.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/n) { |m| sprintf('%%%02X', m.unpack1('C')) } # rubocop:disable Style/FormatString, Style/FormatStringToken + url.to_s.b.gsub(/[^a-zA-Z0-9_\-.~]/n) { |m| sprintf('%%%02X', m.unpack1('C')) } # rubocop:disable Style/FormatString end private diff --git a/lib/gitlab/error.rb b/lib/gitlab/error.rb index a37ccbe..25f5594 100644 --- a/lib/gitlab/error.rb +++ b/lib/gitlab/error.rb @@ -32,6 +32,17 @@ # @return [String] def response_message @response.parsed_response.message + end + + # Additional error context returned by some API endpoints + # + # @return [String] + def error_code + if @response.respond_to?(:error_code) + @response.error_code + else + '' + end end private diff --git a/lib/gitlab/help.rb b/lib/gitlab/help.rb index 23edef7..f7ed37b 100644 --- a/lib/gitlab/help.rb +++ b/lib/gitlab/help.rb @@ -74,14 +74,14 @@ # Returns full namespace of a command (e.g. Gitlab::Client::Branches.cmd) def namespace(cmd) method_owners.select { |method| method[:name] == cmd } - .map { |method| method[:owner] + '.' + method[:name] } + .map { |method| "#{method[:owner]}.#{method[:name]}" } .shift end # Massage output from 'ri'. def change_help_output!(cmd, output_str) output_str = +output_str - output_str.gsub!(/#{cmd}\((.*?)\)/m, cmd + ' \1') + output_str.gsub!(/#{cmd}\((.*?)\)/m, "#{cmd} \1") output_str.gsub!(/,\s*/, ' ') # Ensure @option descriptions are on a single line diff --git a/lib/gitlab/paginated_response.rb b/lib/gitlab/paginated_response.rb index 07b7e91..88794ff 100644 --- a/lib/gitlab/paginated_response.rb +++ b/lib/gitlab/paginated_response.rb @@ -43,17 +43,17 @@ end def lazy_paginate - to_enum(:each_page).lazy.flat_map(&:to_ary) + to_enum(:each_page).lazy.flat_map(&:to_ary) # rubocop:disable Lint/ToEnumArguments end def auto_paginate(&block) - return lazy_paginate.to_a unless block_given? + return lazy_paginate.to_a unless block lazy_paginate.each(&block) end def paginate_with_limit(limit, &block) - return lazy_paginate.take(limit).to_a unless block_given? + return lazy_paginate.take(limit).to_a unless block lazy_paginate.take(limit).each(&block) end diff --git a/lib/gitlab/request.rb b/lib/gitlab/request.rb index f4941ab..732c17f 100644 --- a/lib/gitlab/request.rb +++ b/lib/gitlab/request.rb @@ -24,8 +24,6 @@ elsif body true elsif !body - false - elsif body.nil? false else raise Error::Parsing, "Couldn't parse a response body" diff --git a/lib/gitlab/shell_history.rb b/lib/gitlab/shell_history.rb index 55a404f..dc41980 100644 --- a/lib/gitlab/shell_history.rb +++ b/lib/gitlab/shell_history.rb @@ -42,10 +42,10 @@ File.expand_path(@file_path) end - def read_from_file + def read_from_file(&block) path = history_file_path - File.foreach(path) { |line| yield(line) } if File.exist?(path) + File.foreach(path, &block) if File.exist?(path) rescue StandardError => e warn "History file not loaded: #{e.message}" end diff --git a/lib/gitlab/version.rb b/lib/gitlab/version.rb index 9891e3d..71707dc 100644 --- a/lib/gitlab/version.rb +++ b/lib/gitlab/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Gitlab - VERSION = '4.16.1' + VERSION = '4.17.0' end diff --git a/spec/fixtures/cherry_pick_commit_failure.json b/spec/fixtures/cherry_pick_commit_failure.json new file mode 100644 index 0000000..a1f265d --- /dev/null +++ b/spec/fixtures/cherry_pick_commit_failure.json @@ -0,0 +1 @@ +{"message":"Sorry, we cannot cherry-pick this commit automatically. This commit may already have been cherry-picked, or a more recent commit may have updated some of its content.","error_code":"conflict"} diff --git a/spec/fixtures/group_badge.json b/spec/fixtures/group_badge.json new file mode 100644 index 0000000..96ba178 --- /dev/null +++ b/spec/fixtures/group_badge.json @@ -0,0 +1 @@ +{"id":1,"link_url":"http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}","image_url":"https://shields.io/my/badge","rendered_link_url":"http://example.com/ci_status.svg?project=example-org/example-project&ref=master","rendered_image_url":"https://shields.io/my/badge","kind":"project"} \ No newline at end of file diff --git a/spec/fixtures/group_badges.json b/spec/fixtures/group_badges.json new file mode 100644 index 0000000..a62260d --- /dev/null +++ b/spec/fixtures/group_badges.json @@ -0,0 +1 @@ +[{"id":1,"link_url":"http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}","image_url":"https://shields.io/my/badge","rendered_link_url":"http://example.com/ci_status.svg?project=example-org/example-project&ref=master","rendered_image_url":"https://shields.io/my/badge","kind":"project"},{"id":2,"link_url":"http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}","image_url":"https://shields.io/my/badge","rendered_link_url":"http://example.com/ci_status.svg?project=example-org/example-project&ref=master","rendered_image_url":"https://shields.io/my/badge","kind":"group"}] \ No newline at end of file diff --git a/spec/fixtures/group_billable_members.json b/spec/fixtures/group_billable_members.json new file mode 100644 index 0000000..2e491c6 --- /dev/null +++ b/spec/fixtures/group_billable_members.json @@ -0,0 +1 @@ +[{"id":1,"username":"eraymond","email":"eraymond@local.host","name":"Edward Raymond","state":"active","created_at":"2013-08-30T16:16:22Z","access_level":50},{"id":1,"username":"jsmith","email":"jsmith@local.host","name":"John Smith","state":"active","created_at":"2013-08-30T16:16:22Z","access_level":50}] diff --git a/spec/fixtures/group_runners.json b/spec/fixtures/group_runners.json new file mode 100644 index 0000000..fbb68fe --- /dev/null +++ b/spec/fixtures/group_runners.json @@ -0,0 +1,32 @@ +[ + { + "id": 3, + "description": "Shared", + "ip_address": "127.0.0.1", + "active": true, + "is_shared": true, + "name": "gitlab-runner", + "online": null, + "status": "not_connected" + }, + { + "id": 6, + "description": "Test", + "ip_address": "127.0.0.1", + "active": true, + "is_shared": true, + "name": "gitlab-runner", + "online": false, + "status": "offline" + }, + { + "id": 8, + "description": "Test 2", + "ip_address": "127.0.0.1", + "active": true, + "is_shared": false, + "name": "gitlab-runner", + "online": null, + "status": "not_connected" + } +] diff --git a/spec/fixtures/issue_resource_state_event.json b/spec/fixtures/issue_resource_state_event.json new file mode 100644 index 0000000..2f837d9 --- /dev/null +++ b/spec/fixtures/issue_resource_state_event.json @@ -0,0 +1,15 @@ +{ + "id": 143, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "created_at": "2018-08-21T14:38:20.077Z", + "resource_type": "Issue", + "resource_id": 11, + "state": "closed" + } \ No newline at end of file diff --git a/spec/fixtures/issue_resource_state_events.json b/spec/fixtures/issue_resource_state_events.json new file mode 100644 index 0000000..31ae123 --- /dev/null +++ b/spec/fixtures/issue_resource_state_events.json @@ -0,0 +1,32 @@ +[ + { + "id": 142, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "created_at": "2018-08-20T13:38:20.077Z", + "resource_type": "Issue", + "resource_id": 11, + "state": "opened" + }, + { + "id": 143, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "created_at": "2018-08-21T14:38:20.077Z", + "resource_type": "Issue", + "resource_id": 11, + "state": "closed" + } + ] \ No newline at end of file diff --git a/spec/fixtures/job_artifacts.zip b/spec/fixtures/job_artifacts.zip new file mode 100644 index 0000000..6851e34 Binary files /dev/null and b/spec/fixtures/job_artifacts.zip differ diff --git a/spec/fixtures/mr_resource_state_event.json b/spec/fixtures/mr_resource_state_event.json new file mode 100644 index 0000000..c25edd1 --- /dev/null +++ b/spec/fixtures/mr_resource_state_event.json @@ -0,0 +1,15 @@ +{ + "id": 120, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "created_at": "2018-08-21T14:38:20.077Z", + "resource_type": "MergeRequest", + "resource_id": 11, + "state": "closed" + } \ No newline at end of file diff --git a/spec/fixtures/mr_resource_state_events.json b/spec/fixtures/mr_resource_state_events.json new file mode 100644 index 0000000..1586f13 --- /dev/null +++ b/spec/fixtures/mr_resource_state_events.json @@ -0,0 +1,32 @@ +[ + { + "id": 142, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "created_at": "2018-08-20T13:38:20.077Z", + "resource_type": "MergeRequest", + "resource_id": 11, + "state": "opened" + }, + { + "id": 143, + "user": { + "id": 1, + "name": "Administrator", + "username": "root", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", + "web_url": "http://gitlab.example.com/root" + }, + "created_at": "2018-08-21T14:38:20.077Z", + "resource_type": "MergeRequest", + "resource_id": 11, + "state": "closed" + } + ] \ No newline at end of file diff --git a/spec/fixtures/pipeline_bridges.json b/spec/fixtures/pipeline_bridges.json new file mode 100644 index 0000000..7696413 --- /dev/null +++ b/spec/fixtures/pipeline_bridges.json @@ -0,0 +1 @@ +[{"commit":{"author_email":"admin@example.com","author_name":"Administrator","created_at":"2015-12-24T16:51:14.000+01:00","id":"0ff3ae198f8601a285adcf5c0fff204ee6fba5fd","message":"Test the CI integration.","short_id":"0ff3ae19","title":"Test the CI integration."},"coverage":null,"allow_failure":false,"created_at":"2015-12-24T15:51:21.802Z","started_at":"2015-12-24T17:54:27.722Z","finished_at":"2015-12-24T17:58:27.895Z","duration":240,"id":7,"name":"teaspoon","pipeline":{"id":6,"ref":"master","sha":"0ff3ae198f8601a285adcf5c0fff204ee6fba5fd","status":"pending","created_at":"2015-12-24T15:50:16.123Z","updated_at":"2015-12-24T18:00:44.432Z","web_url":"https://example.com/foo/bar/pipelines/6"},"ref":"master","stage":"test","status":"pending","tag":false,"web_url":"https://example.com/foo/bar/-/jobs/7","user":{"id":1,"name":"Administrator","username":"root","state":"active","avatar_url":"http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon","web_url":"http://gitlab.dev/root","created_at":"2015-12-21T13:14:24.077Z","bio":null,"location":null,"public_email":"","skype":"","linkedin":"","twitter":"","website_url":"","organization":""},"downstream_pipeline":{"id":5,"sha":"f62a4b2fb89754372a346f24659212eb8da13601","ref":"master","status":"pending","created_at":"2015-12-24T17:54:27.722Z","updated_at":"2015-12-24T17:58:27.896Z","web_url":"https://example.com/diaspora/diaspora-client/pipelines/5"}}] diff --git a/spec/fixtures/pipeline_test_report.json b/spec/fixtures/pipeline_test_report.json new file mode 100644 index 0000000..f218ff4 --- /dev/null +++ b/spec/fixtures/pipeline_test_report.json @@ -0,0 +1,29 @@ +{ + "total_time": 5, + "total_count": 1, + "success_count": 1, + "failed_count": 0, + "skipped_count": 0, + "error_count": 0, + "test_suites": [ + { + "name": "Secure", + "total_time": 5, + "total_count": 1, + "success_count": 1, + "failed_count": 0, + "skipped_count": 0, + "error_count": 0, + "test_cases": [ + { + "status": "success", + "name": "Security Reports can create an auto-remediation MR", + "classname": "vulnerability_management_spec", + "execution_time": 5, + "system_output": null, + "stack_trace": null + } + ] + } + ] +} diff --git a/spec/fixtures/preview_group_badge.json b/spec/fixtures/preview_group_badge.json new file mode 100644 index 0000000..358e6d2 --- /dev/null +++ b/spec/fixtures/preview_group_badge.json @@ -0,0 +1 @@ +{"link_url":"http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}","image_url":"https://shields.io/my/badge","rendered_link_url":"http://example.com/ci_status.svg?project=example-org/example-project&ref=master","rendered_image_url":"https://shields.io/my/badge"} \ No newline at end of file diff --git a/spec/fixtures/revert_commit_failure.json b/spec/fixtures/revert_commit_failure.json new file mode 100644 index 0000000..1605bfd --- /dev/null +++ b/spec/fixtures/revert_commit_failure.json @@ -0,0 +1 @@ +{"message":"Sorry, we cannot revert this commit automatically. This commit may already have been reverted, or a more recent commit may have updated some of its content.","error_code":"empty"} diff --git a/spec/gitlab/cli_spec.rb b/spec/gitlab/cli_spec.rb index 3b1b768..092d128 100644 --- a/spec/gitlab/cli_spec.rb +++ b/spec/gitlab/cli_spec.rb @@ -99,7 +99,7 @@ end it 'renders output as json' do - expect(JSON.parse(@output)['result']).to eq(JSON.parse(File.read(File.dirname(__FILE__) + '/../fixtures/user.json'))) + expect(JSON.parse(@output)['result']).to eq(JSON.parse(File.read("#{File.dirname(__FILE__)}/../fixtures/user.json"))) expect(JSON.parse(@output)['cmd']).to eq('Gitlab.user') end end diff --git a/spec/gitlab/client/commits_spec.rb b/spec/gitlab/client/commits_spec.rb index 3512513..f261c74 100644 --- a/spec/gitlab/client/commits_spec.rb +++ b/spec/gitlab/client/commits_spec.rb @@ -65,20 +65,88 @@ end describe '.cherry_pick_commit' do - before do - stub_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/cherry_pick', 'project_commit').with(body: { branch: 'master' }) - @cherry_pick_commit = Gitlab.cherry_pick_commit(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6', 'master') - end - - it 'gets the correct resource' do - expect(a_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/cherry_pick') - .with(body: { branch: 'master' })) - .to have_been_made - end - - it 'returns the correct response' do - expect(@cherry_pick_commit).to be_a Gitlab::ObjectifiedHash - expect(@cherry_pick_commit.id).to eq('6104942438c14ec7bd21c6cd5bd995272b3faff6') + context 'on success' do + before do + stub_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/cherry_pick', 'project_commit').with(body: { branch: 'master' }) + @cherry_pick_commit = Gitlab.cherry_pick_commit(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6', 'master') + end + + it 'gets the correct resource' do + expect(a_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/cherry_pick') + .with(body: { branch: 'master' })) + .to have_been_made + end + + it 'returns the correct response' do + expect(@cherry_pick_commit).to be_a Gitlab::ObjectifiedHash + expect(@cherry_pick_commit.id).to eq('6104942438c14ec7bd21c6cd5bd995272b3faff6') + end + end + + context 'on failure' do + it 'includes the error_code' do + stub_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/cherry_pick', 'cherry_pick_commit_failure', 400) + + expect { Gitlab.cherry_pick_commit(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6', 'master') }.to raise_error(Gitlab::Error::BadRequest) do |ex| + expect(ex.error_code).to eq('conflict') + end + end + end + + context 'with additional options' do + it 'passes additional options' do + stub_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/cherry_pick', 'project_commit') + .with(body: { branch: 'master', dry_run: true }) + + Gitlab.cherry_pick_commit(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6', 'master', dry_run: true) + + expect(a_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/cherry_pick') + .with(body: { branch: 'master', dry_run: true })) + .to have_been_made + end + end + end + + describe '.revert_commit' do + context 'on success' do + before do + stub_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/revert', 'project_commit').with(body: { branch: 'master' }) + @revert_commit = Gitlab.revert_commit(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6', 'master') + end + + it 'gets the correct resource' do + expect(a_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/revert') + .with(body: { branch: 'master' })) + .to have_been_made + end + + it 'returns the correct response' do + expect(@revert_commit).to be_a Gitlab::ObjectifiedHash + expect(@revert_commit.id).to eq('6104942438c14ec7bd21c6cd5bd995272b3faff6') + end + end + + context 'on failure' do + it 'includes the error_code' do + stub_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/revert', 'revert_commit_failure', 400) + + expect { Gitlab.revert_commit(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6', 'master') }.to raise_error(Gitlab::Error::BadRequest) do |ex| + expect(ex.error_code).to eq('empty') + end + end + end + + context 'with additional options' do + it 'passes additional options' do + stub_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/revert', 'project_commit') + .with(body: { branch: 'master', dry_run: true }) + + Gitlab.revert_commit(3, '6104942438c14ec7bd21c6cd5bd995272b3faff6', 'master', dry_run: true) + + expect(a_post('/projects/3/repository/commits/6104942438c14ec7bd21c6cd5bd995272b3faff6/revert') + .with(body: { branch: 'master', dry_run: true })) + .to have_been_made + end end end @@ -156,13 +224,13 @@ describe '.update_commit_status' do before do stub_post('/projects/6/statuses/7d938cb8ac15788d71f4b67c035515a160ea76d8', 'project_update_commit_status') - .with(query: { name: 'test', ref: 'decreased-spec', state: 'failed' }) + .with(body: { name: 'test', ref: 'decreased-spec', state: 'failed' }) @status = Gitlab.update_commit_status(6, '7d938cb8ac15788d71f4b67c035515a160ea76d8', 'failed', name: 'test', ref: 'decreased-spec') end it 'gets the correct resource' do expect(a_post('/projects/6/statuses/7d938cb8ac15788d71f4b67c035515a160ea76d8') - .with(query: { name: 'test', ref: 'decreased-spec', state: 'failed' })) + .with(body: { name: 'test', ref: 'decreased-spec', state: 'failed' })) end it 'returns information about the newly created status' do diff --git a/spec/gitlab/client/container_registry_spec.rb b/spec/gitlab/client/container_registry_spec.rb index 8ece648..eb72803 100644 --- a/spec/gitlab/client/container_registry_spec.rb +++ b/spec/gitlab/client/container_registry_spec.rb @@ -73,25 +73,25 @@ describe '.bulk_delete_registry_repository_tags' do context 'when just name_regex provided for deletion' do before do - stub_delete('/projects/3/registry/repositories/1/tags', 'empty').with(query: { name_regex: '.*' }) + stub_delete('/projects/3/registry/repositories/1/tags', 'empty').with(body: { name_regex: '.*' }) Gitlab.bulk_delete_registry_repository_tags(3, 1, name_regex: '.*') end it 'gets the correct resource' do expect(a_delete('/projects/3/registry/repositories/1/tags') - .with(query: { name_regex: '.*' })).to have_been_made + .with(body: { name_regex: '.*' })).to have_been_made end end context 'when all options provided for deletion' do before do - stub_delete('/projects/3/registry/repositories/1/tags', 'empty').with(query: { name_regex: '[0-9a-z]{40}', keep_n: 5, older_than: '1d' }) + stub_delete('/projects/3/registry/repositories/1/tags', 'empty').with(body: { name_regex: '[0-9a-z]{40}', keep_n: 5, older_than: '1d' }) Gitlab.bulk_delete_registry_repository_tags(3, 1, name_regex: '[0-9a-z]{40}', keep_n: 5, older_than: '1d') end it 'gets the correct resource' do expect(a_delete('/projects/3/registry/repositories/1/tags') - .with(query: { name_regex: '[0-9a-z]{40}', keep_n: 5, older_than: '1d' })).to have_been_made + .with(body: { name_regex: '[0-9a-z]{40}', keep_n: 5, older_than: '1d' })).to have_been_made end end end diff --git a/spec/gitlab/client/group_badges_spec.rb b/spec/gitlab/client/group_badges_spec.rb new file mode 100644 index 0000000..e271b6a --- /dev/null +++ b/spec/gitlab/client/group_badges_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +# rubocop:disable Style/FormatStringToken + +require 'spec_helper' + +describe Gitlab::Client do + describe '.group_badges' do + before do + stub_get('/groups/3/badges', 'group_badges') + @group_badges = Gitlab.group_badges(3) + end + + it 'gets the correct resource' do + expect(a_get('/groups/3/badges')).to have_been_made + end + + it "returns a paginated response of group's badges" do + expect(@group_badges).to be_a Gitlab::PaginatedResponse + end + end + + describe '.group_badge' do + before do + stub_get('/groups/3/badges/1', 'group_badge') + @group_badge = Gitlab.group_badge(3, 1) + end + + it 'gets the correct resource' do + expect(a_get('/groups/3/badges/1')).to have_been_made + end + + it 'returns information about a badge' do + expect(@group_badge.id).to eq(1) + end + end + + describe '.add_group_badge' do + before do + stub_post('/groups/3/badges', 'group_badge') + @group_badge = Gitlab.add_group_badge(3, link_url: 'http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}', image_url: 'https://shields.io/my/badge') + end + + it 'gets the correct resource' do + expect(a_post('/groups/3/badges') + .with(body: { link_url: 'http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}', image_url: 'https://shields.io/my/badge' })).to have_been_made + end + + it 'returns information about an added group badge' do + expect(@group_badge.link_url).to eq('http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}') + expect(@group_badge.image_url).to eq('https://shields.io/my/badge') + end + end + + describe '.edit_group_badge' do + before do + stub_put('/groups/3/badges/1', 'group_badge') + @group_badge = Gitlab.edit_group_badge(3, 1, link_url: 'http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}', image_url: 'https://shields.io/my/badge') + end + + it 'gets the correct resource' do + expect(a_put('/groups/3/badges/1') + .with(body: { link_url: 'http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}', image_url: 'https://shields.io/my/badge' })).to have_been_made + end + + it 'returns information about an edited group badge' do + expect(@group_badge.link_url).to eq('http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}') + expect(@group_badge.image_url).to eq('https://shields.io/my/badge') + end + end + + describe '.remove_group_badge' do + before do + stub_delete('/groups/3/badges/3', 'empty') + @group_badge = Gitlab.remove_group_badge(3, 3) + end + + it 'gets the correct resource' do + expect(a_delete('/groups/3/badges/3')).to have_been_made + end + end + + describe '.preview_group_badge' do + before do + stub_get('/groups/3/badges/render?image_url=https://shields.io/my/badge&link_url=http://example.com/ci_status.svg?project=%25%7Bproject_path%7D%26ref=%25%7Bdefault_branch%7D', 'preview_group_badge') + @preview_group_badge = Gitlab.preview_group_badge(3, 'http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}', 'https://shields.io/my/badge') + end + + it 'gets the correct resource' do + expect(a_get('/groups/3/badges/render?image_url=https://shields.io/my/badge&link_url=http://example.com/ci_status.svg?project=%25%7Bproject_path%7D%26ref=%25%7Bdefault_branch%7D')).to have_been_made + end + + it 'returns information about the rendered values of a badge' do + expect(@preview_group_badge.link_url).to eq('http://example.com/ci_status.svg?project=%{project_path}&ref=%{default_branch}') + expect(@preview_group_badge.image_url).to eq('https://shields.io/my/badge') + end + end +end +# rubocop:enable Style/FormatStringToken diff --git a/spec/gitlab/client/group_labels_spec.rb b/spec/gitlab/client/group_labels_spec.rb index e8d4436..36350d6 100644 --- a/spec/gitlab/client/group_labels_spec.rb +++ b/spec/gitlab/client/group_labels_spec.rb @@ -54,13 +54,12 @@ describe '.delete_group_label' do before do - stub_delete('/groups/3/labels', 'label') + stub_delete('/groups/3/labels/Backlog', 'label') @label = Gitlab.delete_group_label(3, 'Backlog') end it 'gets the correct resource' do - expect(a_delete('/groups/3/labels') - .with(body: { name: 'Backlog' })).to have_been_made + expect(a_delete('/groups/3/labels/Backlog')).to have_been_made end it 'returns information about a deleted snippet' do diff --git a/spec/gitlab/client/groups_spec.rb b/spec/gitlab/client/groups_spec.rb index 8289014..69a7e9b 100644 --- a/spec/gitlab/client/groups_spec.rb +++ b/spec/gitlab/client/groups_spec.rb @@ -126,6 +126,23 @@ end end + describe '.group_billable_members' do + before do + stub_get('/groups/3/billable_members', 'group_billable_members') + @members = Gitlab.group_billable_members(3) + end + + it 'gets the correct resource' do + expect(a_get('/groups/3/billable_members')).to have_been_made + end + + it "returns information about a group's billable members" do + expect(@members).to be_a Gitlab::PaginatedResponse + expect(@members.size).to eq(2) + expect(@members[1].name).to eq('John Smith') + end + end + describe '.group_member' do before do stub_get('/groups/3/members/2', 'group_member') diff --git a/spec/gitlab/client/jobs_spec.rb b/spec/gitlab/client/jobs_spec.rb index 0f9e085..29a9e1c 100644 --- a/spec/gitlab/client/jobs_spec.rb +++ b/spec/gitlab/client/jobs_spec.rb @@ -47,6 +47,28 @@ end end + describe '.pipeline_bridges' do + before do + stub_get('/projects/1/pipelines/1/bridges', 'pipeline_bridges') + @jobs = Gitlab.pipeline_bridges(1, 1) + end + + it 'gets the correct resource' do + expect(a_get('/projects/1/pipelines/1/bridges')).to have_been_made + end + end + + describe '.pipeline_bridges - with scope' do + before do + stub_get('/projects/1/pipelines/1/bridges?scope[]=running&scope[]=created', 'pipeline_bridges') + @jobs = Gitlab.pipeline_bridges(1, 1, scope: %w[running created]) + end + + it 'gets the correct resource' do + expect(a_get('/projects/1/pipelines/1/bridges?scope[]=running&scope[]=created')).to have_been_made + end + end + describe '.job' do before do stub_get('/projects/1/jobs/1', 'job') @@ -70,13 +92,136 @@ end describe '.job_artifacts_download' do - before do - stub_get('/projects/1/jobs/artifacts/master/download?job=Release%20Build', 'job') - @projects = Gitlab.job_artifacts_download(1, 'master', 'Release Build') - end - - it 'gets the correct resource' do - expect(a_get('/projects/1/jobs/artifacts/master/download?job=Release%20Build')).to have_been_made + context 'when successful request' do + before do + fixture = load_fixture('job_artifacts.zip') + fixture.set_encoding(Encoding::ASCII_8BIT) + stub_request(:get, "#{Gitlab.endpoint}/projects/3/jobs/artifacts/master/download") + .with(query: { job: 'test' }, headers: { 'PRIVATE-TOKEN' => Gitlab.private_token }) + .to_return(body: fixture.read, headers: { 'Content-Disposition' => 'attachment; filename=job_artifacts.zip' }) + @job_artifacts = Gitlab.job_artifacts_download(3, 'master', 'test') + end + + it 'gets the correct resource' do + expect(a_get('/projects/3/jobs/artifacts/master/download') + .with(query: { job: 'test' })).to have_been_made + end + + it 'returns a FileResponse' do + expect(@job_artifacts).to be_a Gitlab::FileResponse + end + + it 'returns a file with filename' do + expect(@job_artifacts.filename).to eq 'job_artifacts.zip' + end + end + + context 'when bad request' do + it 'throws an exception' do + stub_get('/projects/3/jobs/artifacts/master/download', 'error_project_not_found', 404) + .with(query: { job: 'test' }) + expect { Gitlab.job_artifacts_download(3, 'master', 'test') }.to raise_error(Gitlab::Error::NotFound, "Server responded with code 404, message: 404 Project Not Found. Request URI: #{Gitlab.endpoint}/projects/3/jobs/artifacts/master/download") + end + end + end + + describe '.download_job_artifact_file' do + context 'when successful request' do + before do + fixture = load_fixture('raw_file.txt') + fixture.set_encoding(Encoding::ASCII_8BIT) + stub_request(:get, "#{Gitlab.endpoint}/projects/3/jobs/5/artifacts/raw_file.txt") + .with(headers: { 'PRIVATE-TOKEN' => Gitlab.private_token }) + .to_return(body: fixture.read, headers: { 'Content-Disposition' => 'attachment; filename=raw_file.txt' }) + @job_artifact_file = Gitlab.download_job_artifact_file(3, 5, 'raw_file.txt') + end + + it 'gets the correct resource' do + expect(a_get('/projects/3/jobs/5/artifacts/raw_file.txt')).to have_been_made + end + + it 'returns a FileResponse' do + expect(@job_artifact_file).to be_a Gitlab::FileResponse + end + + it 'returns a file with filename' do + expect(@job_artifact_file.filename).to eq 'raw_file.txt' + end + end + + context 'when bad request' do + it 'throws an exception' do + stub_get('/projects/3/jobs/5/artifacts/raw_file.txt', 'error_project_not_found', 404) + expect { Gitlab.download_job_artifact_file(3, 5, 'raw_file.txt') }.to raise_error(Gitlab::Error::NotFound, "Server responded with code 404, message: 404 Project Not Found. Request URI: #{Gitlab.endpoint}/projects/3/jobs/5/artifacts/raw_file.txt") + end + end + end + + describe '.download_branch_artifact_file' do + context 'when successful request' do + before do + fixture = load_fixture('raw_file.txt') + fixture.set_encoding(Encoding::ASCII_8BIT) + stub_request(:get, "#{Gitlab.endpoint}/projects/1/jobs/artifacts/master/raw/raw_file.txt") + .with(query: { job: 'txt' }, headers: { 'PRIVATE-TOKEN' => Gitlab.private_token }) + .to_return(body: fixture.read, headers: { 'Content-Disposition' => 'attachment; filename=raw_file.txt' }) + @branch_artifact_file = Gitlab.download_branch_artifact_file(1, 'master', 'raw_file.txt', 'txt') + end + + it 'gets the correct resource' do + expect(a_get('/projects/1/jobs/artifacts/master/raw/raw_file.txt') + .with(query: { job: 'txt' })).to have_been_made + end + + it 'returns a FileResponse' do + expect(@branch_artifact_file).to be_a Gitlab::FileResponse + end + + it 'returns a file with filename' do + expect(@branch_artifact_file.filename).to eq 'raw_file.txt' + end + end + + context 'when bad request' do + it 'throws an exception' do + stub_get('/projects/1/jobs/artifacts/master/raw/raw_file.txt', 'error_project_not_found', 404) + .with(query: { job: 'txt' }) + expect { Gitlab.download_branch_artifact_file(1, 'master', 'raw_file.txt', 'txt') }.to raise_error(Gitlab::Error::NotFound, "Server responded with code 404, message: 404 Project Not Found. Request URI: #{Gitlab.endpoint}/projects/1/jobs/artifacts/master/raw/raw_file.txt") + end + end + end + + describe '.download_tag_artifact_file' do + context 'when successful request' do + before do + fixture = load_fixture('raw_file.txt') + fixture.set_encoding(Encoding::ASCII_8BIT) + stub_request(:get, "#{Gitlab.endpoint}/projects/1/jobs/artifacts/release/raw/raw_file.txt") + .with(query: { job: 'txt' }, headers: { 'PRIVATE-TOKEN' => Gitlab.private_token }) + .to_return(body: fixture.read, headers: { 'Content-Disposition' => 'attachment; filename=raw_file.txt' }) + @branch_artifact_file = Gitlab.download_tag_artifact_file(1, 'release', 'raw_file.txt', 'txt') + end + + it 'gets the correct resource' do + expect(a_get('/projects/1/jobs/artifacts/release/raw/raw_file.txt') + .with(query: { job: 'txt' })).to have_been_made + end + + it 'returns a FileResponse' do + expect(@branch_artifact_file).to be_a Gitlab::FileResponse + end + + it 'returns a file with filename' do + expect(@branch_artifact_file.filename).to eq 'raw_file.txt' + end + end + + context 'when bad request' do + it 'throws an exception' do + stub_get('/projects/1/jobs/artifacts/release/raw/raw_file.txt', 'error_project_not_found', 404) + .with(query: { job: 'txt' }) + expect { Gitlab.download_tag_artifact_file(1, 'release', 'raw_file.txt', 'txt') }.to raise_error(Gitlab::Error::NotFound, "Server responded with code 404, message: 404 Project Not Found. Request URI: #{Gitlab.endpoint}/projects/1/jobs/artifacts/release/raw/raw_file.txt") + end end end diff --git a/spec/gitlab/client/labels_spec.rb b/spec/gitlab/client/labels_spec.rb index 52651a7..e093ee5 100644 --- a/spec/gitlab/client/labels_spec.rb +++ b/spec/gitlab/client/labels_spec.rb @@ -21,13 +21,12 @@ describe '.delete' do before do - stub_delete('/projects/3/labels', 'label') + stub_delete('/projects/3/labels/Backlog', 'label') @label = Gitlab.delete_label(3, 'Backlog') end it 'gets the correct resource' do - expect(a_delete('/projects/3/labels') - .with(body: { name: 'Backlog' })).to have_been_made + expect(a_delete('/projects/3/labels/Backlog')).to have_been_made end it 'returns information about a deleted snippet' do diff --git a/spec/gitlab/client/pipeline_schedules_spec.rb b/spec/gitlab/client/pipeline_schedules_spec.rb index 9117b3d..bb04f97 100644 --- a/spec/gitlab/client/pipeline_schedules_spec.rb +++ b/spec/gitlab/client/pipeline_schedules_spec.rb @@ -108,14 +108,16 @@ describe '.create_pipeline_schedule_variable' do before do - stub_post('/projects/3/pipeline_schedules/13/variables?key=NEW%20VARIABLE&value=new%20value', 'pipeline_schedule_variable') + stub_post('/projects/3/pipeline_schedules/13/variables', 'pipeline_schedule_variable') + .with(body: { key: 'NEW VARIABLE', value: 'new value' }) @pipeline_schedule_variable = Gitlab.create_pipeline_schedule_variable(3, 13, key: 'NEW VARIABLE', value: 'new value') end it 'gets the correct resource' do - expect(a_post('/projects/3/pipeline_schedules/13/variables?key=NEW%20VARIABLE&value=new%20value')).to have_been_made + expect(a_post('/projects/3/pipeline_schedules/13/variables') + .with(body: { key: 'NEW VARIABLE', value: 'new value' })).to have_been_made end it 'returns a single variable' do @@ -129,7 +131,8 @@ describe '.edit_pipeline_schedule_variable' do before do - stub_put('/projects/3/pipeline_schedules/13/variables/NEW%20VARIABLE?value=update%20value', 'pipeline_schedule_variable_update') + stub_put('/projects/3/pipeline_schedules/13/variables/NEW%20VARIABLE', 'pipeline_schedule_variable_update') + .with(body: { value: 'update value' }) @pipeline_schedule_variable = Gitlab.edit_pipeline_schedule_variable(3, 13, 'NEW VARIABLE', value: 'update value') end diff --git a/spec/gitlab/client/pipelines_spec.rb b/spec/gitlab/client/pipelines_spec.rb index b50272a..3748a99 100644 --- a/spec/gitlab/client/pipelines_spec.rb +++ b/spec/gitlab/client/pipelines_spec.rb @@ -35,6 +35,26 @@ it 'returns information about a pipeline' do expect(@pipeline.id).to eq(46) expect(@pipeline.user.name).to eq('Administrator') + end + end + + describe '.pipeline_test_report' do + before do + stub_get('/projects/3/pipelines/46/test_report', 'pipeline_test_report') + @report = Gitlab.pipeline_test_report(3, 46) + end + + it 'gets the correct resource' do + expect(a_get('/projects/3/pipelines/46/test_report')).to have_been_made + end + + it 'returns a single pipeline' do + expect(@report).to be_a Gitlab::ObjectifiedHash + end + + it 'returns information about a pipeline' do + expect(@report.total_time).to eq(5) + expect(@report.test_suites[0].name).to eq('Secure') end end diff --git a/spec/gitlab/client/projects_spec.rb b/spec/gitlab/client/projects_spec.rb index 1adb9e8..3f8bd07 100644 --- a/spec/gitlab/client/projects_spec.rb +++ b/spec/gitlab/client/projects_spec.rb @@ -167,6 +167,22 @@ it 'returns a paginated response of team members' do expect(@team_members).to be_a Gitlab::PaginatedResponse expect(@team_members.first.name).to eq('John Smith') + end + end + + describe '.all_members' do + before do + stub_get('/projects/3/members/all', 'team_members') + @all_members = Gitlab.all_members(3) + end + + it 'gets the correct resource' do + expect(a_get('/projects/3/members/all')).to have_been_made + end + + it 'returns a paginated response of all team members including inherited' do + expect(@all_members).to be_a Gitlab::PaginatedResponse + expect(@all_members.first.name).to eq('John Smith') end end diff --git a/spec/gitlab/client/resource_state_events_spec.rb b/spec/gitlab/client/resource_state_events_spec.rb new file mode 100644 index 0000000..6949551 --- /dev/null +++ b/spec/gitlab/client/resource_state_events_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Client do + describe '.issue_state_events' do + before do + stub_get('/projects/5/issues/42/resource_state_events', 'issue_resource_state_events') + @events = Gitlab.issue_state_events(5, 42) + end + + it 'gets the correct resource' do + expect(a_get('/projects/5/issues/42/resource_state_events')).to have_been_made + end + + it "returns a paginated response of project's issue's state events" do + expect(@events).to be_a Gitlab::PaginatedResponse + expect(@events.first.id).to eq(142) + end + end + + describe '.issue_state_event' do + before do + stub_get('/projects/5/issues/42/resource_state_events/142', 'issue_resource_state_event') + @event = Gitlab.issue_state_event(5, 42, 142) + end + + it 'gets the correct resource' do + expect(a_get('/projects/5/issues/42/resource_state_events/142')).to have_been_made + end + + it "returns a paginated response of project's issue's state event" do + expect(@event).to be_a Gitlab::ObjectifiedHash + expect(@event.user.name).to eq('Administrator') + end + end + + describe '.merge_request_state_events' do + before do + stub_get('/projects/5/merge_requests/42/resource_state_events', 'mr_resource_state_events') + @events = Gitlab.merge_request_state_events(5, 42) + end + + it 'gets the correct resource' do + expect(a_get('/projects/5/merge_requests/42/resource_state_events')).to have_been_made + end + + it "returns a paginated response of project's merge request's state events" do + expect(@events).to be_a Gitlab::PaginatedResponse + expect(@events.first.id).to eq(142) + end + end + + describe '.merge_request_state_event' do + before do + stub_get('/projects/5/merge_requests/42/resource_state_events/142', 'mr_resource_state_event') + @event = Gitlab.merge_request_state_event(5, 42, 142) + end + + it 'gets the correct resource' do + expect(a_get('/projects/5/merge_requests/42/resource_state_events/142')).to have_been_made + end + + it "returns a paginated response of project's merge request's state event" do + expect(@event).to be_a Gitlab::ObjectifiedHash + expect(@event.user.name).to eq('Administrator') + end + end +end diff --git a/spec/gitlab/client/runners_spec.rb b/spec/gitlab/client/runners_spec.rb index 14392e6..400a471 100644 --- a/spec/gitlab/client/runners_spec.rb +++ b/spec/gitlab/client/runners_spec.rb @@ -8,7 +8,7 @@ stub_get('/runners', 'runners') end - context 'without scope' do + context 'without extra queries' do before do @runner = Gitlab.runners end @@ -24,14 +24,14 @@ end end - context 'with scope' do - before do - stub_get('/runners?scope=online', 'runners') - @runner = Gitlab.runners(scope: :online) - end - - it 'gets the correct resource' do - expect(a_get('/runners').with(query: { scope: :online })).to have_been_made + context 'with queries' do + before do + stub_get('/runners?type=instance_type', 'runners') + @runner = Gitlab.runners(type: :instance_type) + end + + it 'gets the correct resource' do + expect(a_get('/runners').with(query: { type: :instance_type })).to have_been_made end it 'returns a paginated response of runners' do @@ -47,7 +47,7 @@ stub_get('/runners/all', 'runners') end - context 'without scope' do + context 'without extra queries' do before do @runner = Gitlab.all_runners end @@ -63,14 +63,14 @@ end end - context 'with scope' do - before do - stub_get('/runners/all?scope=online', 'runners') - @runner = Gitlab.all_runners(scope: :online) - end - - it 'gets the correct resource' do - expect(a_get('/runners/all').with(query: { scope: :online })).to have_been_made + context 'with queries' do + before do + stub_get('/runners/all?type=instance_type', 'runners') + @runner = Gitlab.all_runners(type: :instance_type) + end + + it 'gets the correct resource' do + expect(a_get('/runners/all').with(query: { type: :instance_type })).to have_been_made end it 'returns a paginated response of runners' do @@ -100,12 +100,12 @@ describe '.update_runner' do before do - stub_put('/runners/6', 'runner_edit').with(query: { description: 'abcefg' }) + stub_put('/runners/6', 'runner_edit').with(body: { description: 'abcefg' }) @runner = Gitlab.update_runner(6, description: 'abcefg') end it 'gets the correct resource' do - expect(a_put('/runners/6').with(query: { description: 'abcefg' })).to have_been_made + expect(a_put('/runners/6').with(body: { description: 'abcefg' })).to have_been_made end it 'returns an updated response of a runner' do @@ -192,6 +192,41 @@ end end + describe '.group_runners' do + before do + stub_get('/groups/9/runners', 'group_runners') + end + + context 'without extra queries' do + before do + @runners = Gitlab.group_runners(9) + end + + it 'gets the correct resource' do + expect(a_get('/groups/9/runners')).to have_been_made + end + + it 'returns a paginated response of runners' do + expect(@runners).to be_a Gitlab::PaginatedResponse + end + end + + context 'with queries' do + before do + stub_get('/groups/9/runners?type=instance_type', 'group_runners') + @runner = Gitlab.group_runners(9, type: :instance_type) + end + + it 'gets the correct resource' do + expect(a_get('/groups/9/runners').with(query: { type: :instance_type })).to have_been_made + end + + it 'returns a paginated response of runners' do + expect(@runner).to be_a Gitlab::PaginatedResponse + end + end + end + describe '.register_runner' do before do stub_post('/runners', 'register_runner_response').with(body: { token: '6337ff461c94fd3fa32ba3b1ff4125', description: 'Some Description', active: true, locked: false }) diff --git a/spec/gitlab/error_spec.rb b/spec/gitlab/error_spec.rb index 7fdc12e..cf16f90 100644 --- a/spec/gitlab/error_spec.rb +++ b/spec/gitlab/error_spec.rb @@ -68,4 +68,39 @@ expect(described_class.new(response_double).send(:build_error_message)).to match(/Retry text/) end end + + describe '#error_code' do + it 'returns the value when available' do + headers = { 'content-type' => 'application/json' } + response_double = double( + 'response', + body: 'Retry later', + to_s: 'Retry text', + parsed_response: { message: 'Retry hash' }, + code: 400, + error_code: 'conflict', + options: {}, + headers: headers, + request: @request_double + ) + + expect(described_class.new(response_double).error_code).to eq 'conflict' + end + + it 'returns nothing when unavailable' do + headers = { 'content-type' => 'application/json' } + response_double = double( + 'response', + body: 'Retry later', + to_s: 'Retry text', + parsed_response: { message: 'Retry hash' }, + code: 400, + options: {}, + headers: headers, + request: @request_double + ) + + expect(described_class.new(response_double).error_code).to eq '' + end + end end diff --git a/spec/gitlab/objectified_hash_spec.rb b/spec/gitlab/objectified_hash_spec.rb index 9b96cfc..d05bf5d 100644 --- a/spec/gitlab/objectified_hash_spec.rb +++ b/spec/gitlab/objectified_hash_spec.rb @@ -13,8 +13,8 @@ let(:oh) { described_class.new(hash) } it 'allows to call Hash methods' do - expect(oh.dig('foo')).to eq('bar') - expect(oh.merge(key: :value)).to eq({ 'foo' => 'bar', key: :value }) + expect(oh['foo']).to eq('bar') + expect(oh.merge(key: :value)).to eq('foo' => 'bar', key: :value) end it 'warns about calling Hash methods' do diff --git a/spec/gitlab/paginated_response_spec.rb b/spec/gitlab/paginated_response_spec.rb index 97119da..3f65084 100644 --- a/spec/gitlab/paginated_response_spec.rb +++ b/spec/gitlab/paginated_response_spec.rb @@ -73,10 +73,9 @@ allow(@paginated_response).to receive(:has_next_page?).and_return(true) allow(@paginated_response).to receive(:next_page).and_return(next_page) allow(next_page).to receive(:has_next_page?).and_return(true) - # NOTE: - # Do not define :next_page on the next_page double to prove that it is NOT - # called even though :has_next_page? has been defined to claim another - # page is available. + # NOTE: Do not define :next_page on the next_page double + # to prove that it is NOT called even though :has_next_page? + # has been defined to claim another page is available. allow(next_page).to receive(:to_ary).and_return([5, 6, 7, 8]) expect(@paginated_response.lazy_paginate.take(8)).to contain_exactly(1, 2, 3, 4, 5, 6, 7, 8) end diff --git a/spec/gitlab/shell_spec.rb b/spec/gitlab/shell_spec.rb index a6ebf71..a1055b4 100644 --- a/spec/gitlab/shell_spec.rb +++ b/spec/gitlab/shell_spec.rb @@ -63,7 +63,7 @@ it 'returns an Array of matching commands' do completed_cmds = @comp.call 'issue' expect(completed_cmds).to be_a Array - expect(completed_cmds.sort).to eq(%w[issue issue_label_event issue_label_events issue_links issue_note issue_notes issues]) + expect(completed_cmds.sort).to eq(%w[issue issue_label_event issue_label_events issue_links issue_note issue_notes issue_state_event issue_state_events issues]) end end end