├── .rspec ├── spec ├── fixtures │ ├── repositories │ │ ├── test_owner │ │ │ └── test_repo │ │ │ │ ├── pullrequests │ │ │ │ └── 1 │ │ │ │ │ ├── approve │ │ │ │ │ ├── delete.json │ │ │ │ │ └── post.json │ │ │ │ │ ├── diff │ │ │ │ │ └── get.txt │ │ │ │ │ └── comments │ │ │ │ │ ├── 1 │ │ │ │ │ └── get.json │ │ │ │ │ └── get.json │ │ │ │ ├── branch-restrictions │ │ │ │ └── 1 │ │ │ │ │ └── get.json │ │ │ │ ├── commit │ │ │ │ └── 1 │ │ │ │ │ ├── approve │ │ │ │ │ └── post.json │ │ │ │ │ ├── statuses │ │ │ │ │ ├── build │ │ │ │ │ │ ├── post.json │ │ │ │ │ │ └── test_status │ │ │ │ │ │ │ ├── get.json │ │ │ │ │ │ │ └── put.json │ │ │ │ │ └── get.json │ │ │ │ │ └── comments │ │ │ │ │ ├── 1 │ │ │ │ │ └── get.json │ │ │ │ │ └── get.json │ │ │ │ ├── patch │ │ │ │ └── 1 │ │ │ │ │ └── get.json │ │ │ │ ├── watchers │ │ │ │ └── get.json │ │ │ │ ├── diff │ │ │ │ └── 1 │ │ │ │ │ └── get.json │ │ │ │ ├── get.json │ │ │ │ └── forks │ │ │ │ └── get.json │ │ └── get.json │ ├── branch_restriction.json │ ├── build_status.json │ ├── profile.json │ ├── users │ │ └── test_owner │ │ │ ├── get.json │ │ │ └── followers │ │ │ └── get.json │ ├── comment.json │ ├── teams │ │ ├── test_team │ │ │ ├── get.json │ │ │ ├── followers │ │ │ │ └── get.json │ │ │ ├── members │ │ │ │ └── get.json │ │ │ ├── projects │ │ │ │ └── myprj │ │ │ │ │ └── get.json │ │ │ └── following │ │ │ │ └── get.json │ │ └── get_role_admin.json │ └── team.json ├── lib │ ├── tinybucket │ │ ├── api │ │ │ ├── diff_api_spec.rb │ │ │ ├── repos_api_spec.rb │ │ │ ├── projects_api_spec.rb │ │ │ ├── tags_api_spec.rb │ │ │ ├── branches_api_spec.rb │ │ │ ├── user_api_spec.rb │ │ │ ├── branch_restrictions_api_spec.rb │ │ │ ├── build_status_api_spec.rb │ │ │ └── team_api_spec.rb │ │ ├── resource │ │ │ ├── base_spec.rb │ │ │ ├── teams_spec.rb │ │ │ ├── team │ │ │ │ ├── members_spec.rb │ │ │ │ ├── repos_spec.rb │ │ │ │ ├── followers_spec.rb │ │ │ │ └── following_spec.rb │ │ │ ├── user │ │ │ │ ├── repos_spec.rb │ │ │ │ ├── followers_spec.rb │ │ │ │ └── following_spec.rb │ │ │ ├── forks_spec.rb │ │ │ ├── watchers_spec.rb │ │ │ ├── pull_request │ │ │ │ ├── commits_spec.rb │ │ │ │ └── comments_spec.rb │ │ │ ├── projects_spec.rb │ │ │ ├── tags_spec.rb │ │ │ ├── commits_spec.rb │ │ │ ├── branches_spec.rb │ │ │ ├── commit │ │ │ │ ├── comments_spec.rb │ │ │ │ └── build_statuses_spec.rb │ │ │ ├── pull_requests_spec.rb │ │ │ ├── branch_restrictions_spec.rb │ │ │ └── repos_spec.rb │ │ ├── api_factory_spec.rb │ │ ├── error │ │ │ └── service_error_spec.rb │ │ ├── model │ │ │ ├── page_spec.rb │ │ │ ├── branch_restriction_spec.rb │ │ │ ├── comment_spec.rb │ │ │ ├── tag_spec.rb │ │ │ ├── project_spec.rb │ │ │ ├── branch_spec.rb │ │ │ ├── profile_spec.rb │ │ │ ├── build_status_spec.rb │ │ │ └── team_spec.rb │ │ ├── null_logger_spec.rb │ │ └── connection_spec.rb │ └── tinybucket_spec.rb ├── support │ ├── fixture_macros.rb │ └── api_response_macros.rb └── spec_helper.rb ├── lib ├── tinybucket │ ├── version.rb │ ├── error │ │ ├── conflict.rb │ │ ├── not_found.rb │ │ ├── base_error.rb │ │ └── service_error.rb │ ├── response.rb │ ├── constants.rb │ ├── error.rb │ ├── parser.rb │ ├── config.rb │ ├── resource │ │ ├── team │ │ │ ├── repos.rb │ │ │ ├── members.rb │ │ │ ├── followers.rb │ │ │ ├── following.rb │ │ │ └── base.rb │ │ ├── user │ │ │ ├── repos.rb │ │ │ ├── followers.rb │ │ │ ├── following.rb │ │ │ └── base.rb │ │ ├── commit │ │ │ ├── base.rb │ │ │ ├── comments.rb │ │ │ └── build_statuses.rb │ │ ├── teams.rb │ │ ├── pull_request │ │ │ ├── base.rb │ │ │ ├── commits.rb │ │ │ └── comments.rb │ │ ├── forks.rb │ │ ├── watchers.rb │ │ ├── tags.rb │ │ ├── repos.rb │ │ ├── branches.rb │ │ ├── base.rb │ │ ├── projects.rb │ │ ├── branch_restrictions.rb │ │ ├── pull_requests.rb │ │ └── commits.rb │ ├── model │ │ ├── concerns.rb │ │ ├── concerns │ │ │ ├── api_callable.rb │ │ │ ├── enumerable.rb │ │ │ ├── reloadable.rb │ │ │ ├── acceptable_attributes.rb │ │ │ └── repository_keys.rb │ │ ├── error_response.rb │ │ ├── project.rb │ │ ├── base.rb │ │ ├── page.rb │ │ ├── tag.rb │ │ ├── branch.rb │ │ ├── branch_restriction.rb │ │ ├── build_status.rb │ │ ├── comment.rb │ │ └── profile.rb │ ├── parser │ │ ├── object_parser.rb │ │ └── collection_parser.rb │ ├── model.rb │ ├── api.rb │ ├── api │ │ ├── helper │ │ │ ├── repos_helper.rb │ │ │ ├── tags_helper.rb │ │ │ ├── projects_helper.rb │ │ │ ├── repo_helper.rb │ │ │ ├── branches_helper.rb │ │ │ ├── branch_restrictions_helper.rb │ │ │ ├── user_helper.rb │ │ │ ├── diff_helper.rb │ │ │ ├── team_helper.rb │ │ │ ├── api_helper.rb │ │ │ ├── commits_helper.rb │ │ │ ├── build_status_helper.rb │ │ │ ├── comments_helper.rb │ │ │ └── pull_requests_helper.rb │ │ ├── helper.rb │ │ ├── projects_api.rb │ │ ├── repos_api.rb │ │ ├── base_api.rb │ │ ├── diff_api.rb │ │ ├── tags_api.rb │ │ ├── branches_api.rb │ │ ├── repo_api.rb │ │ ├── user_api.rb │ │ ├── branch_restrictions_api.rb │ │ ├── build_status_api.rb │ │ └── team_api.rb │ ├── api_factory.rb │ ├── response │ │ └── handler.rb │ ├── null_logger.rb │ ├── enumerator.rb │ ├── resource.rb │ ├── request.rb │ ├── iterator.rb │ └── connection.rb ├── tinybucket.rb └── faraday_middleware │ └── follow_oauth_redirects.rb ├── docs └── _config.yml ├── .codeclimate.yml ├── Guardfile ├── .gitignore ├── Gemfile ├── .github └── workflows │ ├── ci.yml │ └── codeql-analysis.yml ├── LICENSE ├── Rakefile ├── tinybucket.gemspec └── .rubocop.yml /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/pullrequests/1/approve/delete.json: -------------------------------------------------------------------------------- 1 | { 2 | "approved": false 3 | } 4 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/pullrequests/1/approve/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "approved": true 3 | } 4 | -------------------------------------------------------------------------------- /lib/tinybucket/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | VERSION = '1.6.0'.freeze 5 | end 6 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/api/diff_api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Api::DiffApi do 4 | pending 'TODO: add specs' 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/fixture_macros.rb: -------------------------------------------------------------------------------- 1 | module FixtureMacros 2 | def load_json_fixture(suffix) 3 | JSON.load(File.read('spec/fixtures/' + suffix + '.json')) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/tinybucket/error/conflict.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Error 5 | class Conflict < ServiceError 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/tinybucket/error/not_found.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Error 5 | class NotFound < ServiceError 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | encoding: utf-8 2 | title: tinybucket - A ruby client library for Bitbucket Cloud REST APIs 3 | highlighter: rouge 4 | markdown: kramdown 5 | theme: jekyll-theme-cayman 6 | -------------------------------------------------------------------------------- /lib/tinybucket/response.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Response 5 | extend ActiveSupport::Autoload 6 | 7 | autoload :Handler 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/tinybucket/constants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Constants 5 | MISSING_REPOSITORY_KEY = 'This method call require repository keys.'.freeze 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - ruby 8 | ratings: 9 | paths: 10 | - lib/**/*.rb 11 | exclude_paths: 12 | - spec/**/*.rb 13 | - docs/**/* 14 | -------------------------------------------------------------------------------- /lib/tinybucket/error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Error 5 | extend ActiveSupport::Autoload 6 | 7 | autoload :BaseError 8 | autoload :ServiceError 9 | autoload :Conflict 10 | autoload :NotFound 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/tinybucket/parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Parser 5 | extend ActiveSupport::Autoload 6 | 7 | [ 8 | :BaseParser, 9 | :ObjectParser, 10 | :CollectionParser 11 | ].each do |klass_name| 12 | autoload klass_name 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tinybucket/config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | class Config 5 | include ActiveSupport::Configurable 6 | config_accessor :logger, :oauth_token, :oauth_secret, \ 7 | :cache_store_options, :access_token, \ 8 | :client_id, :client_secret, :user_agent 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/team/repos.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module Team 6 | class Repos < Tinybucket::Resource::Team::Base 7 | private 8 | 9 | def enumerator 10 | create_team_enumerator(:repos) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/user/repos.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module User 6 | class Repos < Tinybucket::Resource::User::Base 7 | private 8 | 9 | def enumerator 10 | create_user_enumerator(:repos) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tinybucket/error/base_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Error 5 | class BaseError < StandardError 6 | attr_reader :response_message, :response_headers 7 | 8 | def initialize(message) 9 | super message 10 | @response_message = message 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/commit/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module Commit 6 | class Base < Tinybucket::Resource::Base 7 | def initialize(commit, options) 8 | @commit = commit 9 | @args = [options] 10 | end 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/team/members.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module Team 6 | class Members < Tinybucket::Resource::Team::Base 7 | private 8 | 9 | def enumerator 10 | create_team_enumerator(:members) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/team/followers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module Team 6 | class Followers < Tinybucket::Resource::Team::Base 7 | private 8 | 9 | def enumerator 10 | create_team_enumerator(:followers) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/team/following.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module Team 6 | class Following < Tinybucket::Resource::Team::Base 7 | private 8 | 9 | def enumerator 10 | create_team_enumerator(:following) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/user/followers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module User 6 | class Followers < Tinybucket::Resource::User::Base 7 | private 8 | 9 | def enumerator 10 | create_user_enumerator(:followers) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/user/following.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module User 6 | class Following < Tinybucket::Resource::User::Base 7 | private 8 | 9 | def enumerator 10 | create_user_enumerator(:following) 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/tinybucket/model/concerns.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | module Concerns 6 | extend ActiveSupport::Autoload 7 | 8 | [ 9 | :AcceptableAttributes, 10 | :ApiCallable, 11 | :Enumerable, 12 | :RepositoryKeys, 13 | :Reloadable 14 | ].each do |mod_name| 15 | autoload mod_name 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /Guardfile: -------------------------------------------------------------------------------- 1 | guard 'rspec', cmd: 'rspec --drb' do 2 | watch(/^spec\/.+_spec\.rb$/) 3 | watch(/^lib\/(.+)\.rb$/) { |m| "spec/#{m[1]}_spec.rb" } 4 | watch(/^spec\/(.+)\.rb$/) { |m| "spec/#{m[1]}_spec.rb" } 5 | watch('spec/spec_helper.rb') { 'spec' } 6 | 7 | watch(/^lib\/(.+)\.rb$/) { |m| "spec/#{m[1]}_spec.rb" } 8 | end 9 | 10 | guard :rubocop, all_on_start: true, cmd: ['--format', 'clang', '-c', '.rubocop.yml'] do 11 | watch(/^lib\/(.+)\.rb$/) 12 | watch(/\.rubocop.yml$/) 13 | end 14 | -------------------------------------------------------------------------------- /lib/tinybucket/parser/object_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Parser 5 | class ObjectParser < FaradayMiddleware::ResponseMiddleware 6 | define_parser do |hash, parser_options| 7 | cls = parser_options[:model_class] 8 | throw 'model_class option does not provided' unless cls 9 | cls.new(hash) 10 | end 11 | 12 | def parse_response?(env) 13 | env[:body].is_a? Hash 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/teams.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | class Teams < Base 6 | def initialize(role_name, options = {}) 7 | @role_name = role_name 8 | @args = [options] 9 | end 10 | 11 | private 12 | 13 | def teams_api 14 | create_api('Team') 15 | end 16 | 17 | def enumerator 18 | create_enumerator(teams_api, :list, @role_name, *@args) 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | Gemfile.lock 5 | *.bundle 6 | *.so 7 | *.o 8 | *.a 9 | mkmf.log 10 | app.rb 11 | log/ 12 | *.swp 13 | 14 | features 15 | .envrc 16 | .byebug_history 17 | .ruby-version 18 | 19 | ## from github ignore. 20 | 21 | /.config 22 | /coverage/ 23 | /InstalledFiles 24 | /pkg/ 25 | /tmp/ 26 | 27 | ## Documentation cache and generated files: 28 | /.yardoc/ 29 | /_yardoc/ 30 | /doc/ 31 | /rdoc/ 32 | 33 | ## Environment normalisation: 34 | /.bundle/ 35 | /lib/bundler/man/ 36 | /vendor/ 37 | *.iml -------------------------------------------------------------------------------- /lib/tinybucket/resource/pull_request/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module PullRequest 6 | class Base < Tinybucket::Resource::Base 7 | def initialize(pull_request, options) 8 | @pull_request = pull_request 9 | @args = [options] 10 | end 11 | 12 | protected 13 | 14 | def pull_request_api 15 | create_api('PullRequests', @pull_request.repo_keys) 16 | end 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/tinybucket/parser/collection_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Parser 5 | class CollectionParser < FaradayMiddleware::ResponseMiddleware 6 | define_parser do |hash, parser_options| 7 | cls = parser_options[:model_class] 8 | throw 'model_class option does not provided' unless cls 9 | ::Tinybucket::Model::Page.new(hash, cls) 10 | end 11 | 12 | def parse_response?(env) 13 | env[:body].is_a? Hash 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/tinybucket/model.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | extend ActiveSupport::Autoload 6 | 7 | [ 8 | :Base, 9 | :Branch, 10 | :BranchRestriction, 11 | :BuildStatus, 12 | :Comment, 13 | :Commit, 14 | :ErrorResponse, 15 | :Page, 16 | :Profile, 17 | :Project, 18 | :PullRequest, 19 | :Repository, 20 | :Tag, 21 | :Team 22 | ].each do |klass_name| 23 | autoload klass_name 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/fixtures/branch_restriction.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": [], 3 | "id": 52, 4 | "kind": "delete", 5 | "links": [ 6 | { 7 | "href": "https://api.bitbucket.org/2.0/repositories/manthony/restrictiontest/branch-restrictions/52", 8 | "rel": "self" 9 | }, 10 | { 11 | "href": "https://api.bitbucket.org/2.0/repositories/manthony/restrictiontest/branch-restrictions", 12 | "rel": "parent" 13 | } 14 | ], 15 | "pattern": "foobar/*", 16 | "users": [] 17 | } 18 | -------------------------------------------------------------------------------- /lib/tinybucket/model/concerns/api_callable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | module Concerns 6 | module ApiCallable 7 | protected 8 | 9 | def create_api(name, keys = {}) 10 | api = ApiFactory.create_instance(name) 11 | return api if keys.empty? 12 | 13 | api.tap do |m| 14 | m.repo_owner = keys[:repo_owner] 15 | m.repo_slug = keys[:repo_slug] 16 | end 17 | end 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/tinybucket/model/concerns/enumerable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | module Concerns 6 | module Enumerable 7 | extend ActiveSupport::Concern 8 | 9 | included do 10 | protected 11 | 12 | def enumerator(api_client, method, *args, &block) 13 | iter = Tinybucket::Iterator.new(api_client, method, *args) 14 | Tinybucket::Enumerator.new(iter, block) 15 | end 16 | end 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/pull_request/commits.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module PullRequest 6 | class Commits < Tinybucket::Resource::PullRequest::Base 7 | private 8 | 9 | def enumerator 10 | params = [@pull_request.id].concat(@args) 11 | 12 | create_enumerator(pull_request_api, :commits, *params) do |m| 13 | inject_repo_keys(m, @pull_request.repo_keys) 14 | end 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/forks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | class Forks < Base 6 | def initialize(repo, options) 7 | @repo = repo 8 | @args = [options] 9 | end 10 | 11 | private 12 | 13 | def repo_api 14 | create_api('Repo', @repo.repo_keys) 15 | end 16 | 17 | def enumerator 18 | create_enumerator(repo_api, :forks, *@args) do |m| 19 | inject_repo_keys(m, @repo.repo_keys) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/base_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class TestResource < Tinybucket::Resource::Base 4 | def enumerator 5 | (0..10).to_a.to_enum 6 | end 7 | end 8 | 9 | RSpec.describe Tinybucket::Resource::Base do 10 | let(:resource) { TestResource.new } 11 | 12 | describe 'respond_to?' do 13 | context 'with undefined method call' do 14 | subject { resource.respond_to?(:undefined_method_call) } 15 | it 'should raise NoMethodError' do 16 | expect(subject).to eq(false) 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/tinybucket/api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | extend ActiveSupport::Autoload 6 | 7 | [ 8 | :BaseApi, 9 | :BranchesApi, 10 | :TagsApi, 11 | :BranchRestrictionsApi, 12 | :BuildStatusApi, 13 | :CommitsApi, 14 | :CommentsApi, 15 | :DiffApi, 16 | :ProjectsApi, 17 | :PullRequestsApi, 18 | :ReposApi, 19 | :RepoApi, 20 | :TeamApi, 21 | :UserApi 22 | ].each do |klass_name| 23 | autoload klass_name 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/repos_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module ReposHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_list(options) 12 | owner = options[:owner] 13 | return base_path if owner.blank? 14 | 15 | build_path(base_path, [owner, 'owner']) 16 | end 17 | 18 | def base_path 19 | '/repositories' 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/watchers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | class Watchers < Base 6 | def initialize(repo, options) 7 | @repo = repo 8 | @args = [options] 9 | end 10 | 11 | private 12 | 13 | def repo_api 14 | create_api('Repo', @repo.repo_keys) 15 | end 16 | 17 | def enumerator 18 | create_enumerator(repo_api, :watchers, *@args) do |m| 19 | inject_repo_keys(m, @repo.repo_keys) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/tinybucket/api_factory.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | class ApiFactory 5 | class << self 6 | def create_instance(klass_name) 7 | klass = 8 | begin 9 | name = "#{klass_name}Api".intern 10 | Tinybucket::Api.const_get name 11 | rescue => e 12 | # TODO: log exception 13 | Tinybucket.logger.error e 14 | raise ArgumentError, 'must provide klass to be instantiated' 15 | end 16 | 17 | klass.new 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/branch-restrictions/1/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": [], 3 | "id": 52, 4 | "kind": "delete", 5 | "links": [ 6 | { 7 | "href": "https://api.bitbucket.org/2.0/repositories/manthony/restrictiontest/branch-restrictions/52", 8 | "rel": "self" 9 | }, 10 | { 11 | "href": "https://api.bitbucket.org/2.0/repositories/manthony/restrictiontest/branch-restrictions", 12 | "rel": "parent" 13 | } 14 | ], 15 | "pattern": "foobar/*", 16 | "users": [] 17 | } 18 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/commit/1/approve/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "role": "PARTICIPANT", 3 | "user": { 4 | "username": "evzijst", 5 | "display_name": "Erik van Zijst", 6 | "links": { 7 | "self": { 8 | "href": "https://api.bitbucket.org/2.0/users/evzijst" 9 | }, 10 | "avatar": { 11 | "href": "https://bitbucket-staging-assetroot.s3.amazonaws.com/c/photos/2013/Oct/28/evzijst-avatar-3454044670-3_avatar.png" 12 | } 13 | } 14 | }, 15 | "approved": true 16 | } 17 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/api_factory_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::ApiFactory do 4 | 5 | describe 'create_instance' do 6 | subject { Tinybucket::ApiFactory.create_instance(klass_name) } 7 | 8 | context 'with valid klass_name' do 9 | let(:klass_name) { 'PullRequests' } 10 | it { expect(subject).to be_a_kind_of(Tinybucket::Api::BaseApi) } 11 | end 12 | 13 | context 'with invalid klass_name' do 14 | let(:klass_name) { 'Invalid Klass Name' } 15 | it { expect { subject }.to raise_error(ArgumentError) } 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/team/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module Team 6 | class Base < Tinybucket::Resource::Base 7 | def initialize(team_name, options) 8 | @team_name = team_name 9 | @args = [options] 10 | end 11 | 12 | protected 13 | 14 | def team_api 15 | create_api('Team') 16 | end 17 | 18 | def create_team_enumerator(method) 19 | create_enumerator(team_api, method, @team_name, *@args) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/fixtures/build_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "state": "SUCCESSFUL", 3 | "type": "build", 4 | "key": "test_status", 5 | "name": "Build #34", 6 | "url": "https://example.com/path/to/build", 7 | "description": "Changes by John Doe", 8 | "links": { 9 | "self": { 10 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466/statuses/build/BAMBOO-PROJECT-X" 11 | }, 12 | "commit": { 13 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/user/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module User 6 | class Base < Tinybucket::Resource::Base 7 | def initialize(user_name, options) 8 | @user_name = user_name 9 | @args = [options] 10 | end 11 | 12 | protected 13 | 14 | def user_api 15 | create_api('User').tap do |api| 16 | api.username = @user_name 17 | end 18 | end 19 | 20 | def create_user_enumerator(method) 21 | create_enumerator(user_api, method, *@args) 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | extend ActiveSupport::Autoload 7 | 8 | [ 9 | :ApiHelper, 10 | :BranchesHelper, 11 | :BranchRestrictionsHelper, 12 | :TagsHelper, 13 | :BuildStatusHelper, 14 | :CommitsHelper, 15 | :CommentsHelper, 16 | :DiffHelper, 17 | :ReposHelper, 18 | :RepoHelper, 19 | :PullRequestsHelper, 20 | :ProjectsHelper, 21 | :TeamHelper, 22 | :UserHelper 23 | ].each do |klass_name| 24 | autoload klass_name 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/tinybucket/response/handler.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Response 5 | class Handler < Faraday::Response::Middleware 6 | def on_complete(env) 7 | status_code = env[:status].to_i 8 | 9 | return if status_code < 400 10 | 11 | case status_code 12 | when 404 13 | raise Tinybucket::Error::NotFound.new(env) 14 | when 409 15 | raise Tinybucket::Error::Conflict.new(env) 16 | else 17 | Tinybucket.logger.error "Invalid response code:#{status_code}" 18 | raise Tinybucket::Error::ServiceError.new(env) 19 | end 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/tinybucket/null_logger.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | class NullLogger 5 | attr_accessor :level 6 | 7 | def fatal(_progname = nil, &_block); end 8 | 9 | def fatal? 10 | false 11 | end 12 | 13 | def error(_progname = nil, &_block); end 14 | 15 | def error? 16 | false 17 | end 18 | 19 | def warn(_progname = nil, &_block); end 20 | 21 | def warn? 22 | false 23 | end 24 | 25 | def info(_progname = nil, &_block); end 26 | 27 | def info? 28 | false 29 | end 30 | 31 | def debug(_progname = nil, &_block); end 32 | 33 | def debug? 34 | false 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/commit/1/statuses/build/post.json: -------------------------------------------------------------------------------- 1 | { 2 | "state": "SUCCESSFUL", 3 | "type": "build", 4 | "key": "BAMBOO-PROJECT-X", 5 | "name": "Build #34", 6 | "url": "https://example.com/path/to/build", 7 | "description": "Changes by John Doe", 8 | "links": { 9 | "self": { 10 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466/statuses/build/BAMBOO-PROJECT-X" 11 | }, 12 | "commit": { 13 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/tags_helper.rb: -------------------------------------------------------------------------------- 1 | module Tinybucket 2 | module Api 3 | module Helper 4 | module TagsHelper 5 | include ::Tinybucket::Api::Helper::ApiHelper 6 | 7 | private 8 | 9 | def path_to_list 10 | build_path(base_path) 11 | end 12 | 13 | def path_to_find(tag) 14 | build_path(base_path, 15 | [tag, 'tag']) 16 | end 17 | 18 | def base_path 19 | build_path('/repositories', 20 | [repo_owner, 'repo_owner'], 21 | [repo_slug, 'repo_slug'], 22 | 'refs', 'tags') 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in bitbucket.gemspec 4 | gemspec 5 | 6 | gem 'simple-auth', '~> 0.5.0' 7 | gem 'yard', '~> 0.9.12' 8 | gem 'yardstick', '~> 0.9.9' 9 | 10 | group :development, :test do 11 | gem 'pry' 12 | gem 'pry-stack_explorer' 13 | gem 'pry-byebug' 14 | gem 'pry-rescue' 15 | 16 | gem 'guard-rspec' 17 | gem 'guard-spork' 18 | 19 | gem 'guard-rubocop' 20 | gem 'gem-release' 21 | 22 | gem 'rake', '~> 13.0' 23 | gem 'rspec', '~> 3.4' 24 | gem 'rspec-mocks', '~> 3.4' 25 | gem 'webmock', '~> 3.12.1' 26 | gem 'rubocop', '~> 1.11.0' 27 | end 28 | 29 | group :test do 30 | gem 'simplecov', '~> 0.21.2', require: false 31 | end 32 | -------------------------------------------------------------------------------- /lib/tinybucket/api/projects_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | class ProjectsApi < BaseApi 6 | include Tinybucket::Api::Helper::ProjectsHelper 7 | attr_accessor :owner 8 | 9 | def list(options = {}) 10 | get_path( 11 | path_to_list(owner), 12 | options, 13 | get_parser(:collection, Tinybucket::Model::Project) 14 | ) 15 | end 16 | 17 | def find(project_key, options = {}) 18 | get_path( 19 | path_to_find(owner, project_key), 20 | options, 21 | get_parser(:object, Tinybucket::Model::Project) 22 | ) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/commit/1/statuses/build/test_status/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "state": "SUCCESSFUL", 3 | "type": "build", 4 | "key": "BAMBOO-PROJECT-X", 5 | "name": "Build #34", 6 | "url": "https://example.com/path/to/build", 7 | "description": "Changes by John Doe", 8 | "links": { 9 | "self": { 10 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466/statuses/build/BAMBOO-PROJECT-X" 11 | }, 12 | "commit": { 13 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/commit/1/statuses/build/test_status/put.json: -------------------------------------------------------------------------------- 1 | { 2 | "state": "SUCCESSFUL", 3 | "type": "build", 4 | "key": "BAMBOO-PROJECT-X", 5 | "name": "Build #34", 6 | "url": "https://example.com/path/to/build", 7 | "description": "Changes by John Doe", 8 | "links": { 9 | "self": { 10 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466/statuses/build/BAMBOO-PROJECT-X" 11 | }, 12 | "commit": { 13 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/commit/1/statuses/get.json: -------------------------------------------------------------------------------- 1 | {"pagelen": 10, "values": [{ 2 | "state": "SUCCESSFUL", 3 | "type": "build", 4 | "key": "BAMBOO-PROJECT-X", 5 | "name": "Build #34", 6 | "url": "https://example.com/path/to/build", 7 | "description": "Changes by John Doe", 8 | "links": { 9 | "self": { 10 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466/statuses/build/BAMBOO-PROJECT-X" 11 | }, 12 | "commit": { 13 | "href": "https://api.bitbucket.org/2.0/repositories/emmap1/MyRepo/commits/61d9e64348f9da407e62f64726337fd3bb24b466" 14 | } 15 | } 16 | }]} 17 | -------------------------------------------------------------------------------- /lib/tinybucket/model/error_response.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | # ErrorResponse 6 | # 7 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/meta/uri-uuid#stand-error 8 | # Standardized error responses 9 | # 10 | # @!attribute [rw] message 11 | # @return [String] 12 | # @!attribute [rw] fields 13 | # @return [Hash] 14 | # @!attribute [rw] detail 15 | # @return [String] 16 | # @!attribute [rw] id 17 | # @return [String] 18 | # @!attribute [rw] uuid 19 | # @return [NillClass] 20 | class ErrorResponse 21 | acceptable_attributes :message, :fields, :detail, :id, :uuid 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/projects_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module ProjectsHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_list(owner) 12 | build_path(base_path(owner), '/') 13 | end 14 | 15 | def path_to_find(owner, project_key) 16 | build_path(base_path(owner), 17 | [project_key, 'project_key']) 18 | end 19 | 20 | def base_path(owner) 21 | build_path('/teams', 22 | [owner, 'owner'], 23 | 'projects') 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/repo_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module RepoHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_find 12 | base_path 13 | end 14 | 15 | def path_to_watchers 16 | build_path(base_path, '/watchers') 17 | end 18 | 19 | def path_to_forks 20 | build_path(base_path, '/forks') 21 | end 22 | 23 | def base_path 24 | build_path('/repositories', 25 | [repo_owner, 'repo_owner'], 26 | [repo_slug, 'repo_slug']) 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | 4 | require 'rspec' 5 | require 'webmock/rspec' 6 | 7 | require 'pry' 8 | 9 | require 'simplecov' 10 | SimpleCov.start do 11 | add_filter '.bundle/' 12 | add_filter 'spec' 13 | add_filter 'features' 14 | end 15 | 16 | require 'tinybucket' 17 | 18 | path = Pathname.new(Dir.pwd) 19 | Dir[path.join('spec/support/**/*.rb')].each { |f| require f } 20 | 21 | # configure tinybucket logger. 22 | Dir.mkdir('log') unless Dir.exist?('log') 23 | 24 | logger = Logger.new('log/test.log') 25 | logger.level = Logger::DEBUG 26 | Tinybucket.logger = logger 27 | 28 | RSpec.configure do |config| 29 | config.extend FixtureMacros 30 | config.include FixtureMacros 31 | config.order = 'random' 32 | end 33 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/branches_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module BranchesHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_list 12 | build_path(base_path) 13 | end 14 | 15 | def path_to_find(branch) 16 | build_path(base_path, 17 | [branch, 'branch']) 18 | end 19 | 20 | def base_path 21 | build_path('/repositories', 22 | [repo_owner, 'repo_owner'], 23 | [repo_slug, 'repo_slug'], 24 | 'refs', 'branches') 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/tinybucket/error/service_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Error 5 | class ServiceError < BaseError 6 | attr_accessor :http_headers 7 | attr_reader :http_method, :request_url, :status_code, :response_body 8 | 9 | def initialize(env) 10 | super generate_message(env) 11 | @http_headers = env[:response_headers] 12 | end 13 | 14 | private 15 | 16 | def generate_message(env) 17 | @http_method = env[:method].to_s.upcase 18 | @request_url = env[:url].to_s 19 | @status_code = env[:status] 20 | @response_body = env[:body] 21 | 22 | "#{@http_method} #{@request_url} #{@status_code} #{@response_body}" 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/teams_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::Teams do 4 | include ApiResponseMacros 5 | 6 | let(:role_name) { "admin" } 7 | let(:resource) { Tinybucket::Resource::Teams.new(role_name) } 8 | 9 | describe "Enumerable Methods" do 10 | let(:request_path) { "/teams?role=#{role_name}" } 11 | before { stub_enum_response(:get, request_path) } 12 | 13 | describe "#take(1)" do 14 | subject { resource.take(1) } 15 | it { expect(subject).to be_an_instance_of(Array) } 16 | end 17 | 18 | describe "#each" do 19 | it 'iterate models' do 20 | resource.each do |m| 21 | expect(m).to be_an_instance_of(Tinybucket::Model::Team) 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/branch_restrictions_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module BranchRestrictionsHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_list 12 | base_path 13 | end 14 | 15 | def path_to_find(restriction_id) 16 | build_path(base_path, 17 | [restriction_id, 'restriction_id']) 18 | end 19 | 20 | def base_path 21 | build_path('/repositories', 22 | [repo_owner, 'repo_owner'], 23 | [repo_slug, 'repo_slug'], 24 | 'branch-restrictions') 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/user_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module UserHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_find 12 | base_path 13 | end 14 | 15 | def path_to_followers 16 | build_path(base_path, 'followers') 17 | end 18 | 19 | def path_to_following 20 | build_path(base_path, 'following') 21 | end 22 | 23 | def path_to_repos 24 | build_path('/repositories', [username, 'username']) 25 | end 26 | 27 | def base_path 28 | build_path('/users', [username, 'username']) 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/pullrequests/1/diff/get.txt: -------------------------------------------------------------------------------- 1 | diff --git a/apps/account/forms.py b/apps/account/forms.py 2 | index c2f3d2d..2c27220 100644 3 | --- a/apps/account/forms.py 4 | +++ b/apps/account/forms.py 5 | @@ -553,6 +553,8 @@ class UsernameChangeForm(forms.Form): 6 | raise TypeError("Keyword argument 'request' must be supplied") 7 | super(UsernameChangeForm, self).__init__(*args, **kwargs) 8 | self.request = request 9 | + if request.target_user.get_profile().is_team: 10 | + self.fields['username'].label = _('Team id') 11 | if request.user == request.target_user: 12 | self.fields['username'].help_text = _('You will need to log in ' 13 | 'again after making this ' 14 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/error/service_error_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Error::ServiceError do 4 | context 'initialize' do 5 | let(:env) do 6 | { 7 | response_headers: [], 8 | method: 'POST', 9 | url: 'https://api.example.org/path/to', 10 | status: 500, 11 | body: 'Internal Server Error' 12 | } 13 | end 14 | 15 | subject { Tinybucket::Error::ServiceError.new(env) } 16 | 17 | it { expect(subject).to be_an_instance_of(Tinybucket::Error::ServiceError) } 18 | it { expect(subject.http_method).to eq(env[:method]) } 19 | it { expect(subject.request_url).to eq(env[:url]) } 20 | it { expect(subject.status_code).to eq(env[:status]) } 21 | it { expect(subject.response_body).to eq(env[:body]) } 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/diff_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module DiffHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_find(spec) 12 | build_path(base_path, 13 | 'diff', 14 | [spec, 'spec']) 15 | end 16 | 17 | def path_to_patch(spec) 18 | build_path(base_path, 19 | 'patch', 20 | [spec, 'spec']) 21 | end 22 | 23 | def base_path 24 | build_path('/repositories', 25 | [repo_owner, 'repo_owner'], 26 | [repo_slug, 'repo_slug']) 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/team/members_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::Team::Members do 4 | include ApiResponseMacros 5 | 6 | let(:team) { 'test_team' } 7 | let(:options) { {} } 8 | let(:resource) { Tinybucket::Resource::Team::Members.new(team, options) } 9 | 10 | describe 'Enumerable Methods' do 11 | let(:request_path) { "/teams/#{team}/members" } 12 | before { stub_enum_response(:get, request_path) } 13 | 14 | describe '#take(1)' do 15 | subject { resource.take(1) } 16 | it { expect(subject).to be_an_instance_of(Array) } 17 | end 18 | 19 | describe '#each' do 20 | it 'iterate models' do 21 | resource.each do |m| 22 | expect(m).to be_an_instance_of(Tinybucket::Model::Team) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/team/repos_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::User::Repos do 4 | include ApiResponseMacros 5 | 6 | let(:team) { 'test_team' } 7 | let(:options) { {} } 8 | let(:resource) { Tinybucket::Resource::Team::Repos.new(team, options) } 9 | 10 | describe 'Enumerable Methods' do 11 | let(:request_path) { "/teams/#{team}/repositories" } 12 | before { stub_enum_response(:get, request_path) } 13 | 14 | describe '#take(1)' do 15 | subject { resource.take(1) } 16 | it { expect(subject).to be_an_instance_of(Array) } 17 | end 18 | 19 | describe '#each' do 20 | it 'iterate models' do 21 | resource.each do |m| 22 | expect(m).to be_an_instance_of(Tinybucket::Model::Repository) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/user/repos_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::User::Repos do 4 | include ApiResponseMacros 5 | 6 | let(:owner) { 'test_owner' } 7 | let(:options) { {} } 8 | let(:resource) { Tinybucket::Resource::User::Repos.new(owner, options) } 9 | 10 | describe 'Enumerable Methods' do 11 | let(:request_path) { "/repositories/#{owner}" } 12 | before { stub_enum_response(:get, request_path) } 13 | 14 | describe '#take(1)' do 15 | subject { resource.take(1) } 16 | it { expect(subject).to be_an_instance_of(Array) } 17 | end 18 | 19 | describe '#each' do 20 | it 'iterate models' do 21 | resource.each do |m| 22 | expect(m).to be_an_instance_of(Tinybucket::Model::Repository) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/team/followers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::Team::Followers do 4 | include ApiResponseMacros 5 | 6 | let(:team) { 'test_team' } 7 | let(:options) { {} } 8 | let(:resource) { Tinybucket::Resource::Team::Followers.new(team, options) } 9 | 10 | describe 'Enumerable Methods' do 11 | let(:request_path) { "/teams/#{team}/followers" } 12 | before { stub_enum_response(:get, request_path) } 13 | 14 | describe '#take(1)' do 15 | subject { resource.take(1) } 16 | it { expect(subject).to be_an_instance_of(Array) } 17 | end 18 | 19 | describe '#each' do 20 | it 'iterate models' do 21 | resource.each do |m| 22 | expect(m).to be_an_instance_of(Tinybucket::Model::Team) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/team/following_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::Team::Following do 4 | include ApiResponseMacros 5 | 6 | let(:team) { 'test_team' } 7 | let(:options) { {} } 8 | let(:resource) { Tinybucket::Resource::Team::Following.new(team, options) } 9 | 10 | describe 'Enumerable Methods' do 11 | let(:request_path) { "/teams/#{team}/following" } 12 | before { stub_enum_response(:get, request_path) } 13 | 14 | describe '#take(1)' do 15 | subject { resource.take(1) } 16 | it { expect(subject).to be_an_instance_of(Array) } 17 | end 18 | 19 | describe '#each' do 20 | it 'iterate models' do 21 | resource.each do |m| 22 | expect(m).to be_an_instance_of(Tinybucket::Model::Team) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/tinybucket/api/repos_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | # Repos Api client 6 | class ReposApi < BaseApi 7 | include Tinybucket::Api::Helper::ReposHelper 8 | 9 | # Send 'GET a list of repositories for an account' request 10 | # 11 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories 12 | # GET a list of repositories for an account 13 | # 14 | # @param options [Hash] 15 | # @return [Tinybucket::Model::Page] 16 | def list(options = {}) 17 | opts = options.clone 18 | opts.delete(:owner) 19 | 20 | get_path( 21 | path_to_list(options), 22 | opts, 23 | get_parser(:collection, Tinybucket::Model::Repository) 24 | ) 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/user/followers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::User::Followers do 4 | include ApiResponseMacros 5 | 6 | let(:owner) { 'test_owner' } 7 | let(:options) { {} } 8 | let(:resource) { Tinybucket::Resource::User::Followers.new(owner, options) } 9 | 10 | describe 'Enumerable Methods' do 11 | let(:request_path) { "/users/#{owner}/followers" } 12 | before { stub_enum_response(:get, request_path) } 13 | 14 | describe '#take(1)' do 15 | subject { resource.take(1) } 16 | it { expect(subject).to be_an_instance_of(Array) } 17 | end 18 | 19 | describe '#each' do 20 | it 'iterate models' do 21 | resource.each do |m| 22 | expect(m).to be_an_instance_of(Tinybucket::Model::Profile) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/user/following_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::User::Following do 4 | include ApiResponseMacros 5 | 6 | let(:owner) { 'test_owner' } 7 | let(:options) { {} } 8 | let(:resource) { Tinybucket::Resource::User::Following.new(owner, options) } 9 | 10 | describe 'Enumerable Methods' do 11 | let(:request_path) { "/users/#{owner}/following" } 12 | before { stub_enum_response(:get, request_path) } 13 | 14 | describe '#take(1)' do 15 | subject { resource.take(1) } 16 | it { expect(subject).to be_an_instance_of(Array) } 17 | end 18 | 19 | describe '#each' do 20 | it 'iterate models' do 21 | resource.each do |m| 22 | expect(m).to be_an_instance_of(Tinybucket::Model::Profile) 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/api/repos_api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper.rb' 2 | 3 | RSpec.describe Tinybucket::Api::ReposApi do 4 | include ApiResponseMacros 5 | 6 | let(:api) { Tinybucket::Api::ReposApi.new } 7 | 8 | it { expect(api).to be_a_kind_of(Tinybucket::Api::BaseApi) } 9 | 10 | describe 'list' do 11 | subject { api.list(options) } 12 | 13 | before { stub_apiresponse(:get, request_path) } 14 | 15 | context 'without owner' do 16 | let(:options) { {} } 17 | let(:request_path) { '/repositories' } 18 | it { expect(subject).to be_an_instance_of(Tinybucket::Model::Page) } 19 | end 20 | 21 | context 'with owner' do 22 | let(:options) { { owner: 'test_owner' } } 23 | let(:request_path) { '/repositories/test_owner' } 24 | it { expect(subject).to be_an_instance_of(Tinybucket::Model::Page) } 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/tags.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | class Tags < Tinybucket::Resource::Base 6 | def initialize(repo, options) 7 | @repo = repo 8 | @args = [options] 9 | end 10 | 11 | # Find the tag 12 | # 13 | # @param tag [String] 14 | # @param options [Hash] 15 | # @return [Tinybucket::Model::Tag] 16 | def find(tag, options = {}) 17 | tags_api.find(tag, options).tap do |m| 18 | inject_repo_keys(m, @repo.repo_keys) 19 | end 20 | end 21 | 22 | private 23 | 24 | def tags_api 25 | create_api('Tags', @repo.repo_keys) 26 | end 27 | 28 | def enumerator 29 | create_enumerator(tags_api, :list, *@args) do |m| 30 | inject_repo_keys(m, @repo.repo_keys) 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/tinybucket/model/concerns/reloadable.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | module Concerns 6 | module Reloadable 7 | extend ActiveSupport::Concern 8 | 9 | included do 10 | def load 11 | return true if @_loaded 12 | 13 | self.attributes = load_model.attributes 14 | @_loaded = true 15 | rescue => e 16 | @_loaded = false 17 | Tinybucket.logger.error e 18 | raise e 19 | end 20 | 21 | def loaded? 22 | @_loaded 23 | end 24 | 25 | def reload 26 | @_loaded = false 27 | 28 | self.load 29 | end 30 | 31 | private 32 | 33 | def load_model 34 | raise NotImplementedError 35 | end 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/repos.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | class Repos < Base 6 | def initialize(owner, options) 7 | @owner = owner 8 | @args = [options] 9 | end 10 | 11 | def create(_options) 12 | raise NotImplementedError 13 | end 14 | 15 | def find(_options) 16 | raise NotImplementedError 17 | end 18 | 19 | private 20 | 21 | def user_api 22 | create_api('User').tap do |api| 23 | api.username = @owner 24 | end 25 | end 26 | 27 | def repos_api 28 | create_api('Repos') 29 | end 30 | 31 | def enumerator 32 | if @owner 33 | create_enumerator(user_api, :repos, *@args) 34 | else 35 | create_enumerator(repos_api, :list, *@args) 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/model/page_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Model::Page do 4 | 5 | let(:json) do 6 | text = File.read('spec/fixtures/repositories/get.json') 7 | JSON.parse(text) 8 | end 9 | 10 | let(:klass) { Tinybucket::Model::Repository } 11 | let(:model) { Tinybucket::Model::Page.new(json, klass) } 12 | 13 | describe 'initialize' do 14 | subject { model } 15 | 16 | it 'create instance' do 17 | expect(subject).to be_an_instance_of(Tinybucket::Model::Page) 18 | 19 | expect(subject.attrs[:size]).to eq(json['size']) 20 | expect(subject.attrs[:page]).to eq(json['page']) 21 | expect(subject.attrs[:pagelen]).to eq(json['pagelen']) 22 | expect(subject.attrs[:next]).to eq(json['next']) 23 | expect(subject.attrs[:previous]).to eq(json['previous']) 24 | 25 | expect(subject.items.size).to eq(json['values'].size) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/branches.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | class Branches < Tinybucket::Resource::Base 6 | def initialize(repo, options) 7 | @repo = repo 8 | @args = [options] 9 | end 10 | 11 | # Find the branch 12 | # 13 | # @param branch [String] 14 | # @param options [Hash] 15 | # @return [Tinybucket::Model::Branch] 16 | def find(branch, options = {}) 17 | branches_api.find(branch, options).tap do |m| 18 | inject_repo_keys(m, @repo.repo_keys) 19 | end 20 | end 21 | 22 | private 23 | 24 | def branches_api 25 | create_api('Branches', @repo.repo_keys) 26 | end 27 | 28 | def enumerator 29 | create_enumerator(branches_api, :list, *@args) do |m| 30 | inject_repo_keys(m, @repo.repo_keys) 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/api/projects_api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Api::ProjectsApi do 4 | include ApiResponseMacros 5 | 6 | let(:api) { Tinybucket::Api::ProjectsApi.new } 7 | let(:owner) { 'test_team' } 8 | let(:request_path) { nil } 9 | before do 10 | api.owner = owner 11 | stub_apiresponse(:get, request_path) if request_path 12 | end 13 | 14 | it { expect(api).to be_a_kind_of(Tinybucket::Api::BaseApi) } 15 | 16 | describe 'list' do 17 | subject { api.list() } 18 | let(:request_path) { '/teams/test_team/projects/' } 19 | it { expect(subject).to be_an_instance_of(Tinybucket::Model::Page) } 20 | end 21 | 22 | describe 'find' do 23 | let(:project_key) { 'myprj' } 24 | subject { api.find(project_key) } 25 | 26 | let(:request_path) { "/teams/test_team/projects/#{project_key}" } 27 | it { expect(subject).to be_an_instance_of(Tinybucket::Model::Project) } 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/pull_request/comments.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module PullRequest 6 | class Comments < Tinybucket::Resource::PullRequest::Base 7 | # Get the specific comment on the pull request. 8 | # 9 | # @param comment_id [String] 10 | # @param options [Hash] 11 | # @return [Tinybucket::Model::Comment] 12 | def find(comment_id, options) 13 | comment_api.find(comment_id, options) 14 | end 15 | 16 | private 17 | 18 | def comment_api 19 | create_api('Comments', @pull_request.repo_keys).tap do |api| 20 | api.commented_to = @pull_request 21 | end 22 | end 23 | 24 | def enumerator 25 | create_enumerator(comment_api, :list, *@args) do |m| 26 | inject_repo_keys(m, @pull_request.repo_keys) 27 | end 28 | end 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/tinybucket/model/concerns/acceptable_attributes.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | module Concerns 6 | module AcceptableAttributes 7 | extend ActiveSupport::Concern 8 | 9 | included do 10 | def self.acceptable_attributes(*attrs) 11 | @_acceptable_attributes = attrs.map(&:intern) 12 | 13 | attr_accessor(*attrs) 14 | end 15 | 16 | def self.acceptable_attribute?(key) 17 | return false if @_acceptable_attributes.nil? 18 | 19 | @_acceptable_attributes.include?(key.intern) 20 | end 21 | 22 | protected 23 | 24 | def acceptable_attribute?(key) 25 | self.class.acceptable_attribute?(key) 26 | end 27 | 28 | def acceptable_attributes 29 | self.class.instance_variable_get(:@_acceptable_attributes) || [] 30 | end 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/fixtures/profile.json: -------------------------------------------------------------------------------- 1 | { 2 | "username": "tutorials", 3 | "kind": "user", 4 | "website": "https://tutorials.bitbucket.org/", 5 | "display_name": "first name last", 6 | "links": { 7 | "self": { 8 | "href": "https://api.bitbucket.org/2.0/users/tutorials" 9 | }, 10 | "repositories": { 11 | "href": "https://api.bitbucket.org/2.0/users/tutorials/repositories" 12 | }, 13 | "html": { 14 | "href": "https://api.bitbucket.org/tutorials" 15 | }, 16 | "followers": { 17 | "href": "https://api.bitbucket.org/2.0/users/tutorials/followers" 18 | }, 19 | "avatar": { 20 | "href": "https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Jul/17/tutorials-avatar-1826704565-4_avatar.png" 21 | }, 22 | "following": { 23 | "href": "https://api.bitbucket.org/2.0/users/tutorials/following" 24 | } 25 | }, 26 | "created_on": "2011-12-20T16:34:07.132459+00:00", 27 | "location": "San Francisco, CA", 28 | "type": "user" 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ pull_request, push ] 4 | 5 | jobs: 6 | test: 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | os: [ ubuntu-latest ] 11 | ruby: [ '3.1'] 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up Ruby 17 | uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: ${{ matrix.ruby }} 20 | bundler-cache: true 21 | cache-version: ${{ runner.os }}-${{ runner.ruby }}-${{ hashFiles('Gemfile.lock', 'tinybucket.gemspec') }} 22 | - name: Install dependencies 23 | run: bundle install 24 | - name: Run spec & publish coverage 25 | uses: paambaati/codeclimate-action@v3.2.0 26 | env: 27 | CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} 28 | with: 29 | coverageCommand: bundle exec rake spec 30 | coverageLocations: ${{ github.workspace }}/coverage/coverage.json:simplecov 31 | -------------------------------------------------------------------------------- /spec/fixtures/users/test_owner/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "username": "tutorials", 3 | "kind": "user", 4 | "website": "https://tutorials.bitbucket.org/", 5 | "display_name": "first name last", 6 | "links": { 7 | "self": { 8 | "href": "https://api.bitbucket.org/2.0/users/tutorials" 9 | }, 10 | "repositories": { 11 | "href": "https://api.bitbucket.org/2.0/users/tutorials/repositories" 12 | }, 13 | "html": { 14 | "href": "https://api.bitbucket.org/tutorials" 15 | }, 16 | "followers": { 17 | "href": "https://api.bitbucket.org/2.0/users/tutorials/followers" 18 | }, 19 | "avatar": { 20 | "href": "https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Jul/17/tutorials-avatar-1826704565-4_avatar.png" 21 | }, 22 | "following": { 23 | "href": "https://api.bitbucket.org/2.0/users/tutorials/following" 24 | } 25 | }, 26 | "created_on": "2011-12-20T16:34:07.132459+00:00", 27 | "location": "San Francisco, CA", 28 | "type": "user" 29 | } 30 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/model/branch_restriction_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Model::BranchRestriction do 4 | include ApiResponseMacros 5 | 6 | let(:model_json) { load_json_fixture('branch_restriction') } 7 | 8 | let(:request_method) { :get } 9 | let(:request_path) { nil } 10 | 11 | let(:owner) { 'test_owner' } 12 | let(:slug) { 'test_repo' } 13 | 14 | let(:model) do 15 | m = Tinybucket::Model::BranchRestriction.new(model_json) 16 | m.repo_owner = owner 17 | m.repo_slug = slug 18 | 19 | m 20 | end 21 | 22 | before { stub_apiresponse(:get, request_path, stub_options) if request_path } 23 | 24 | it_behaves_like 'model has acceptable_attributes', 25 | Tinybucket::Model::BranchRestriction, 26 | load_json_fixture('branch_restriction') 27 | 28 | describe '#update' do 29 | pending 'TODO implement method' 30 | end 31 | 32 | describe '#destroy' do 33 | pending 'TODO implement method' 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/model/comment_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Model::Comment do 4 | include ApiResponseMacros 5 | include ModelMacros 6 | 7 | let(:model_json) { load_json_fixture('comment') } 8 | 9 | let(:owner) { 'test_owner' } 10 | let(:slug) { 'test_repo' } 11 | 12 | let(:commit) do 13 | m = Tinybucket::Model::Commit.new({}) 14 | m.repo_owner = owner 15 | m.repo_slug = slug 16 | m.hash = '1' 17 | m 18 | end 19 | 20 | it_behaves_like 'model has acceptable_attributes', 21 | Tinybucket::Model::Comment, 22 | load_json_fixture('comment') 23 | 24 | describe 'model can reloadable' do 25 | let(:comment) do 26 | m = Tinybucket::Model::Comment.new({}) 27 | m.repo_owner = owner 28 | m.repo_slug = slug 29 | m.commented_to = commit 30 | m.id = '1' 31 | m 32 | end 33 | 34 | before { @model = commit } 35 | it_behaves_like 'the model is reloadable' 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | class Base 6 | include Tinybucket::Model::Concerns::ApiCallable 7 | 8 | protected 9 | 10 | def method_missing(method, *args) 11 | enum = enumerator 12 | return super unless enum.respond_to?(method) 13 | 14 | enum.send(method, *args) do |m| 15 | block_given? ? yield(m) : m 16 | end 17 | end 18 | 19 | def respond_to_missing?(symbol, include_all) 20 | enumerator.respond_to?(symbol, include_all) 21 | end 22 | 23 | def create_enumerator(api_client, method, *args, &block) 24 | iter = Tinybucket::Iterator.new(api_client, method, *args) 25 | Tinybucket::Enumerator.new(iter, block) 26 | end 27 | 28 | def inject_repo_keys(model, repo_keys) 29 | return model unless model.respond_to?(:repo_keys=) 30 | 31 | model.tap { |m| m.repo_keys = repo_keys } 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/fixtures/comment.json: -------------------------------------------------------------------------------- 1 | { 2 | "links": { 3 | "self": { 4 | "href": "https://bitbucket.org/api/2.0/repositories/tutorials/tutorials.bitbucket.org/pullrequests/2821/comments/839163" 5 | }, 6 | "html": { 7 | "href": "https://bitbucket.org/tutorials/tutorials.bitbucket.org/pull-request/2821/_/diff#comment-839163" 8 | } 9 | }, 10 | "content": { 11 | "raw": "this is my first comment", 12 | "markup": "markdown", 13 | "html": "
this is my first comment
" 14 | }, 15 | "created_on": "2013-11-19T21:19:24.138375+00:00", 16 | "user": { 17 | "username": "tutorials", 18 | "display_name": "first name last", 19 | "links": { 20 | "self": { 21 | "href": "https://bitbucket.org/api/2.0/users/tutorials" 22 | }, 23 | "avatar": { 24 | "href": "https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Jul/17/tutorials-avatar-1826704565-4_avatar.png" 25 | } 26 | } 27 | }, 28 | "updated_on": "2013-11-19T21:19:24.141013+00:00", 29 | "id": 839163 30 | } 31 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/forks_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::Forks do 4 | include ApiResponseMacros 5 | 6 | let(:owner) { 'test_owner' } 7 | let(:slug) { 'test_repo' } 8 | 9 | let(:options) { {} } 10 | let(:repo) do 11 | Tinybucket::Model::Repository.new({}).tap do |m| 12 | m.repo_owner = owner 13 | m.repo_slug = slug 14 | end 15 | end 16 | 17 | let(:resource) { Tinybucket::Resource::Forks.new(repo, options) } 18 | 19 | describe 'Enumerable Methods' do 20 | let(:request_path) { "/repositories/#{owner}/#{slug}/forks" } 21 | before { stub_enum_response(:get, request_path) } 22 | 23 | describe '#take(1)' do 24 | subject { resource.take(1) } 25 | it { expect(subject).to be_an_instance_of(Array) } 26 | end 27 | 28 | describe '#each' do 29 | it 'iterate models' do 30 | resource.each do |m| 31 | expect(m).to be_an_instance_of(Tinybucket::Model::Repository) 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/commit/comments.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module Commit 6 | class Comments < Tinybucket::Resource::Commit::Base 7 | # Get the specific commit comment which associate with the commit. 8 | # 9 | # @param comment_id [String] 10 | # @param options [Hash] 11 | # @return [Tinybucket::Model::Comment] 12 | def find(comment_id, options = {}) 13 | comments_api.find(comment_id, options).tap do |m| 14 | m.repo_keys = @commit.repo_keys 15 | end 16 | end 17 | 18 | private 19 | 20 | def comments_api 21 | create_api('Comments', @commit.repo_keys).tap do |api| 22 | api.commented_to = @commit 23 | end 24 | end 25 | 26 | def enumerator 27 | create_enumerator(comments_api, :list, *@args) do |m| 28 | inject_repo_keys(m, @commit.repo_keys) 29 | end 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/patch/1/get.json: -------------------------------------------------------------------------------- 1 | # HG changeset patch 2 | # User Juan Borda
19 | + 24 | +"If you're going thru hell, keep going."
22 | + Wiston Churchill 23 | +
this is my first comment
" 14 | }, 15 | "created_on": "2013-11-19T21:19:24.138375+00:00", 16 | "user": { 17 | "username": "tutorials", 18 | "display_name": "first name last", 19 | "links": { 20 | "self": { 21 | "href": "https://bitbucket.org/api/2.0/users/tutorials" 22 | }, 23 | "avatar": { 24 | "href": "https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Jul/17/tutorials-avatar-1826704565-4_avatar.png" 25 | } 26 | } 27 | }, 28 | "updated_on": "2013-11-19T21:19:24.141013+00:00", 29 | "id": 839163 30 | } 31 | -------------------------------------------------------------------------------- /spec/fixtures/teams/test_team/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "username": "1team", 3 | "kind": "team", 4 | "website": "", 5 | "display_name": "the team", 6 | "links": { 7 | "self": { 8 | "href": "https://api.bitbucket.org/2.0/teams/1team" 9 | }, 10 | "repositories": { 11 | "href": "https://api.bitbucket.org/2.0/teams/1team/repositories" 12 | }, 13 | "html": { 14 | "href": "https://api.bitbucket.org/1team" 15 | }, 16 | "followers": { 17 | "href": "https://api.bitbucket.org/2.0/teams/1team/followers" 18 | }, 19 | "avatar": { 20 | "href": "https://secure.gravatar.com/avatar/24b6be68b53e383c5d42bab4fe0bde2b?d=https%3A%2F%2Fd3oaxc4q5k2d6q.cloudfront.net%2Fm%2Fbc85d1577e04%2Fimg%2Fdefault_team_avatar%2F32%2Fteam_blue.png&s=32" 21 | }, 22 | "members": { 23 | "href": "https://api.bitbucket.org/2.0/teams/1team/members" 24 | }, 25 | "following": { 26 | "href": "https://api.bitbucket.org/2.0/teams/1team/following" 27 | } 28 | }, 29 | "created_on": "2013-04-22T18:09:29.103337+00:00", 30 | "location": "", 31 | "type": "team" 32 | } 33 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/watchers_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::Watchers do 4 | include ApiResponseMacros 5 | 6 | let(:owner) { 'test_owner' } 7 | let(:slug) { 'test_repo' } 8 | let(:repo) do 9 | Tinybucket::Model::Repository.new({}).tap do |m| 10 | m.repo_owner = owner 11 | m.repo_slug = slug 12 | end 13 | end 14 | 15 | let(:options) { {} } 16 | let(:resource) { Tinybucket::Resource::Watchers.new(repo, options) } 17 | 18 | describe 'Enumerable Methods' do 19 | let(:params) { {} } 20 | let(:request_path) do 21 | "/repositories/#{owner}/#{slug}/watchers" 22 | end 23 | before { stub_enum_response(:get, request_path) } 24 | 25 | describe '#take(1)' do 26 | subject { resource.take(1) } 27 | it { expect(subject).to be_an_instance_of(Array) } 28 | end 29 | 30 | describe '#each' do 31 | it 'iterate models' do 32 | resource.each do |m| 33 | expect(m).to be_an_instance_of(Tinybucket::Model::Profile) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/team_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module TeamHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_list 12 | base_path 13 | end 14 | 15 | def path_to_find(name) 16 | team_path(name) 17 | end 18 | 19 | def path_to_members(name) 20 | build_path(team_path(name), 'members') 21 | end 22 | 23 | def path_to_followers(name) 24 | build_path(team_path(name), 'followers') 25 | end 26 | 27 | def path_to_following(name) 28 | build_path(team_path(name), 'following') 29 | end 30 | 31 | def path_to_repos(name) 32 | build_path(team_path(name), 'repositories') 33 | end 34 | 35 | def base_path 36 | build_path('/teams') 37 | end 38 | 39 | def team_path(name) 40 | build_path(base_path, [name, 'teamname']) 41 | end 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/watchers/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "pagelen": 2, 3 | "next": "https://api.bitbucket.org/2.0/repositories/test_owner/test_repo/watchers?pagelen=2&page=2", 4 | "values": [ 5 | { 6 | "username": "tutorials", 7 | "display_name": "first name last", 8 | "links": { 9 | "self": { 10 | "href": "https://bitbucket.org/api/2.0/users/tutorials" 11 | }, 12 | "avatar": { 13 | "href": "https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Jul/17/tutorials-avatar-1826704565-4_avatar.png" 14 | } 15 | } 16 | }, 17 | { 18 | "username": "mmangier", 19 | "display_name": "mmangier", 20 | "links": { 21 | "self": { 22 | "href": "https://bitbucket.org/api/2.0/users/mmangier" 23 | }, 24 | "avatar": { 25 | "href": "https://secure.gravatar.com/avatar/16b32ae6fcb078db0a088ce2516f2a05?d=https%3A%2F%2Fd3oaxc4q5k2d6q.cloudfront.net%2Fm%2Ff5a36db3429b%2Fimg%2Fdefault_avatar%2F32%2Fuser_blue.png&s=32" 26 | } 27 | } 28 | } 29 | ], 30 | "page": 1, 31 | "size": 19 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 hirakiuc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | 4 | require "bundler/gem_tasks" 5 | 6 | desc 'cleanup rcov, doc directories' 7 | task :clean do 8 | rm_rf 'coverage' 9 | rm_rf 'doc' 10 | rm_rf 'measurement' 11 | end 12 | 13 | # https://github.com/sferik/twitter/blob/master/Rakefile 14 | require 'rspec/core/rake_task' 15 | RSpec::Core::RakeTask.new(:spec) 16 | task :test => :spec 17 | 18 | if Dir.exist?('./features') 19 | RSpec::Core::RakeTask.new(:features) do |t| 20 | t.pattern = './features/**/*_spec.rb' 21 | t.rspec_opts = '-I ./features' 22 | end 23 | end 24 | 25 | begin 26 | require 'rubocop/rake_task' 27 | RuboCop::RakeTask.new 28 | rescue LoadError 29 | task :rubocop do 30 | $stderr.puts 'Rubocop is disabled.' 31 | end 32 | end 33 | 34 | require 'yard' 35 | YARD::Rake::YardocTask.new 36 | 37 | require 'yardstick/rake/measurement' 38 | Yardstick::Rake::Measurement.new do |measurement| 39 | measurement.output = 'measurement/report.txt' 40 | end 41 | 42 | require 'yardstick/rake/verify' 43 | Yardstick::Rake::Verify.new do |verify| 44 | verify.threshold = 59.6 45 | end 46 | 47 | task :default => [:spec, :rubocop] 48 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/api_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module ApiHelper 7 | private 8 | 9 | def next_proc(method, options) 10 | lambda do |next_options| 11 | send(method, options.merge(next_options)) 12 | end 13 | end 14 | 15 | def urlencode(v, key) 16 | if v.blank? || (escaped = CGI.escape(v.to_s)).blank? 17 | msg = "Invalid #{key} parameter. (#{v})" 18 | raise ArgumentError, msg 19 | end 20 | 21 | escaped 22 | end 23 | 24 | def build_path(base_path, *components) 25 | components.reduce(base_path) do |path, component| 26 | part = if component.is_a?(Array) 27 | urlencode(*component) 28 | else 29 | component.to_s 30 | end 31 | "#{path}/#{part}" 32 | end 33 | rescue ArgumentError => e 34 | raise ArgumentError, "Failed to build request URL: #{e}" 35 | end 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/tinybucket/api/base_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | class Parser 6 | attr_reader :type, :options 7 | 8 | def initialize(type, model_class) 9 | parser_class = 10 | case type 11 | when :collection then Tinybucket::Parser::CollectionParser 12 | when :object then Tinybucket::Parser::ObjectParser 13 | else throw "Unknown parser type: #{type}" 14 | end 15 | 16 | @type = parser_class 17 | @options = { model_class: model_class } 18 | end 19 | end 20 | 21 | class BaseApi 22 | include Tinybucket::Connection 23 | include Tinybucket::Request 24 | 25 | protected 26 | 27 | # 28 | # Get parser 29 | # 30 | # @param type [Symbol] :collection or :object 31 | # @param model_class [Tinybucket::Model::Base] SubClass of Tinybucket::Model::Base 32 | # @return [Hash] 33 | def get_parser(type, model_class) 34 | Tinybucket::Api::Parser.new(type, model_class) 35 | end 36 | 37 | private 38 | 39 | def logger 40 | Tinybucket.logger 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/tinybucket/model/project.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | class Project < Base 6 | acceptable_attributes \ 7 | :type, :description, :links, :uuid, :created_on, 8 | :key, :updated_on, :is_private, :name, :owner 9 | 10 | # Update this project 11 | # 12 | # @param _params [Hash] 13 | # @raise [NotImplementedError] to be implemented 14 | def update(_params) 15 | raise NotImplementedError 16 | end 17 | 18 | # Destroy this project 19 | # 20 | # @raise [NotImplementedError] to be implemented. 21 | def destroy 22 | raise NotImplementedError 23 | end 24 | 25 | # Get repositories 26 | # 27 | # @return [Tinybucket::Resource::Repos] 28 | def repos 29 | repos_resource 30 | end 31 | 32 | private 33 | 34 | def owner_name 35 | raise 'This project is not loaded yet.' if (owner.nil? || owner['username'].nil?) 36 | owner['username'] 37 | end 38 | 39 | def repos_resource 40 | Tinybucket::Resource::Repos.new(owner_name, q: %(project.key="#{key}")) 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/fixtures/team.json: -------------------------------------------------------------------------------- 1 | { 2 | "username": "teamsinspace", 3 | "website": null, 4 | "display_name": "Teams In Space", 5 | "uuid": "{61fc5cf6-d054-47d2-b4a9-061ccf858379}", 6 | "links": { 7 | "self": { 8 | "href": "https://bitbucket.org/api/2.0/teams/teamsinspace" 9 | }, 10 | "repositories": { 11 | "href": "https://bitbucket.org/api/2.0/repositories/teamsinspace" 12 | }, 13 | "html": { 14 | "href": "https://bitbucket.org/teamsinspace" 15 | }, 16 | "followers": { 17 | "href": "https://bitbucket.org/api/2.0/teams/teamsinspace/followers" 18 | }, 19 | "avatar": { 20 | "href": "https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2014/Sep/24/teamsinspace-avatar-3731530358-7_avatar.png" 21 | }, 22 | "members": { 23 | "href": "https://bitbucket.org/api/2.0/teams/teamsinspace/members" 24 | }, 25 | "following": { 26 | "href": "https://bitbucket.org/api/2.0/teams/teamsinspace/following" 27 | } 28 | }, 29 | "created_on": "2014-04-08T00:00:14.070969+00:00", 30 | "location": null, 31 | "type": "team" 32 | } 33 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/diff/1/get.json: -------------------------------------------------------------------------------- 1 | diff --git a/index.html b/index.html 2 | --- a/index.html 3 | +++ b/index.html 4 | @@ -82,7 +82,7 @@ 5 |11 |“Here's to the crazy ones, the misfits, the rebels, the troublemakers, the round pegs in the square holes... the ones who see things differently -- they're not fond of rules... You can quote them, disagree with them, glorify or vilify them, but the only thing you can't do is ignore them because they change things... they push the human race forward, and while some may see them as the crazy ones, we see genius, because the ones who are crazy enough to think that they can change the world, are the ones who do.”
8 | - Steve Jobs, Founder Of Apple Inc. 9 | + Steve Jobs, Founder of Apple Inc. 10 |
20 |“No.”
17 | - Steve Jobs, Founder Of Apple Inc. 18 | + Steve Jobs, Founder of Apple Inc. 19 |
Inline test comment.
" 17 | }, 18 | "created_on": "2013-11-07T23:55:24.486865+00:00", 19 | "user": { 20 | "username": "evzijst", 21 | "display_name": "Erik van Zijst", 22 | "links": { 23 | "self": { 24 | "href": "https://api.bitbucket.org/2.0/users/evzijst" 25 | }, 26 | "avatar": { 27 | "href": "https://secure.gravatar.com/avatar/f6bcbb4e3f665e74455bd8c0b4b3afba?d=https%3A%2F%2Fd3oaxc4q5k2d6q.cloudfront.net%2Fm%2Fbf1e763db20f%2Fimg%2Fdefault_avatar%2F32%2Fuser_blue.png&s=32" 28 | } 29 | } 30 | }, 31 | "inline": { 32 | "to": null, 33 | "from": 381, 34 | "path": "pom.xml" 35 | }, 36 | "updated_on": "2013-11-07T23:55:24.502477+00:00", 37 | "id": 530189 38 | } 39 | -------------------------------------------------------------------------------- /lib/tinybucket/api/helper/pull_requests_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | module Helper 6 | module PullRequestsHelper 7 | include ::Tinybucket::Api::Helper::ApiHelper 8 | 9 | private 10 | 11 | def path_to_list 12 | base_path 13 | end 14 | 15 | def path_to_find(pr_id) 16 | build_path(base_path, 17 | [pr_id, 'pullrequest_id']) 18 | end 19 | 20 | def path_to_commits(pr_id) 21 | build_path(base_path, 22 | [pr_id, 'pullrequest_id'], 23 | 'commits') 24 | end 25 | 26 | def path_to_approve(pr_id) 27 | build_path(base_path, 28 | [pr_id, 'pullrequest_id'], 29 | 'approve') 30 | end 31 | 32 | def path_to_diff(pr_id) 33 | build_path(path_to_find(pr_id), 34 | 'diff') 35 | end 36 | 37 | def path_to_decline(pr_id) 38 | build_path(base_path, 39 | [pr_id, 'pullrequest_id'], 40 | 'decline') 41 | end 42 | 43 | def path_to_merge(pr_id) 44 | build_path(base_path, 45 | [pr_id, 'pullrequest_id'], 46 | 'merge') 47 | end 48 | 49 | def base_path 50 | build_path('/repositories', 51 | [repo_owner, 'repo_owner'], 52 | [repo_slug, 'repo_slug'], 53 | 'pullrequests') 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/model/profile_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Model::Profile do 4 | include ApiResponseMacros 5 | include ModelMacros 6 | 7 | let(:model_json) { load_json_fixture('profile') } 8 | 9 | let(:request_path) { nil } 10 | 11 | let(:username) { 'test_owner' } 12 | 13 | let(:model) do 14 | m = Tinybucket::Model::Profile.new(model_json) 15 | m.username = username 16 | m 17 | end 18 | 19 | before { stub_apiresponse(:get, request_path) if request_path } 20 | 21 | it_behaves_like 'model has acceptable_attributes', 22 | Tinybucket::Model::Profile, 23 | load_json_fixture('profile') 24 | 25 | describe 'model can reloadable' do 26 | let(:profile) do 27 | m = Tinybucket::Model::Profile.new({}) 28 | m.username = username 29 | m 30 | end 31 | before { @model = profile } 32 | it_behaves_like 'the model is reloadable' 33 | end 34 | 35 | describe 'followers' do 36 | let(:request_path) { "/users/#{username}/followers" } 37 | subject { model.followers() } 38 | it { expect(subject).to be_an_instance_of(Tinybucket::Resource::User::Followers) } 39 | end 40 | 41 | describe 'following' do 42 | let(:request_path) { "/users/#{username}/following" } 43 | subject { model.following } 44 | it { expect(subject).to be_an_instance_of(Tinybucket::Resource::User::Following) } 45 | end 46 | 47 | describe 'repos' do 48 | let(:request_path) { "/repositories/#{username}" } 49 | subject { model.repos } 50 | it { expect(subject).to be_an_instance_of(Tinybucket::Resource::User::Repos) } 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/commit/build_statuses.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | module Commit 6 | class BuildStatuses < Tinybucket::Resource::Commit::Base 7 | # Get the build status for the commit 8 | # 9 | # @param key [String] 10 | # @param options [Hash] 11 | # @option options [String] :state 12 | # @option options [String] :key 13 | # @option options [String] :name 14 | # @option options [String] :url 15 | # @option options [String] :description 16 | # @return [Tinybucket::Model::BuildStatus] 17 | def find(key, options = {}) 18 | build_status_api.find(@commit.hash, key, options).tap do |m| 19 | m.revision = @commit.hash 20 | m.repo_keys = @commit.repo_keys 21 | end 22 | end 23 | 24 | # Create a build status for the commit 25 | # 26 | # @param key [String] 27 | # @param options [Hash] 28 | # @return [Tinybucket::Model::BuildStatus] 29 | def create(key, options) 30 | build_status_api.post(@commit.hash, key, options).tap do |m| 31 | m.revision = @commit.hash 32 | m.repo_keys = @commit.repo_keys 33 | end 34 | end 35 | 36 | private 37 | 38 | def build_status_api 39 | create_api('BuildStatus', @commit.repo_keys).tap do |api| 40 | api.revision = @commit.hash 41 | end 42 | end 43 | 44 | def enumerator 45 | create_enumerator(build_status_api, :list, *@args) 46 | end 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/tinybucket/resource/commits.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Resource 5 | class Commits < Base 6 | def initialize(repo, options) 7 | @repo = repo 8 | @args = [options] 9 | end 10 | 11 | # Find the commit 12 | # 13 | # @param revision [String] 14 | # @param options [Hash] 15 | # @return [Tinybucket::Model::Commit] 16 | def find(revision, options = {}) 17 | commits_api.find(revision, options).tap do |m| 18 | inject_repo_keys(m, @repo.repo_keys) 19 | end 20 | end 21 | 22 | # Returns the commits for a specific branch 23 | # 24 | # @param name [String] 25 | # @param options [Hash] 26 | # @return [Tinybucket::Iterator] 27 | def branch(name, options = {}) 28 | create_enumerator(commits_api, :branch, name, options) do |m| 29 | inject_repo_keys(m, @repo.repo_keys) 30 | end 31 | end 32 | 33 | # Returns the commits for a specific tag 34 | # 35 | # @param name [String] 36 | # @param options [Hash] 37 | # @return [Tinybucket::Iterator] 38 | def tag(name, options = {}) 39 | create_enumerator(commits_api, :tag, name, options) do |m| 40 | inject_repo_keys(m, @repo.repo_keys) 41 | end 42 | end 43 | 44 | private 45 | 46 | def commits_api 47 | create_api('Commits', @repo.repo_keys) 48 | end 49 | 50 | def enumerator 51 | create_enumerator(commits_api, :list, *@args) do |m| 52 | inject_repo_keys(m, @repo.repo_keys) 53 | end 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.7 3 | Include: 4 | - 'lib/**/*.rb' 5 | Exclude: 6 | - '*.rb' 7 | - 'spec/**/*' 8 | - 'features/**/*' 9 | - 'test/**/*' 10 | - 'Gemfile' 11 | - 'Guardfile' 12 | - 'Rakefile' 13 | - '*.gemspec' 14 | 15 | Metrics/ClassLength: 16 | Max: 150 17 | 18 | MethodLength: 19 | Max: 30 20 | 21 | Documentation: 22 | Enabled: false 23 | 24 | AndOr: 25 | Enabled: false 26 | 27 | ParenthesesAroundCondition: 28 | Enabled: false 29 | 30 | Style/MethodCallWithoutArgsParentheses: 31 | Enabled: false 32 | 33 | # why use this option ? 34 | #RedundantBegin: 35 | # Enabled: false 36 | 37 | NumericLiterals: 38 | Enabled: false 39 | 40 | CyclomaticComplexity: 41 | Max: 15 42 | 43 | Next: 44 | Enabled: false 45 | 46 | Layout/HashAlignment: 47 | Enabled: false 48 | 49 | Style/RaiseArgs: 50 | Enabled: false 51 | 52 | Style/RedundantFreeze: 53 | Enabled: false 54 | 55 | Metrics/AbcSize: 56 | Max: 18 57 | 58 | Metrics/LineLength: 59 | Max: 100 60 | IgnoredPatterns: [ 61 | '^\s*#', 62 | 'ruby wrapper for the Bitbucket REST API*' 63 | ] 64 | 65 | Style/SymbolArray: 66 | Enabled: false 67 | 68 | Style/FormatStringToken: 69 | Enabled: false 70 | 71 | Style/RescueStandardError: 72 | Enabled: false 73 | 74 | Style/PercentLiteralDelimiters: 75 | Enabled: false 76 | 77 | Layout/SpaceAroundOperators: 78 | Enabled: false 79 | 80 | Layout/ExtraSpacing: 81 | Enabled: false 82 | 83 | Style/SafeNavigation: 84 | Enabled: false 85 | 86 | Lint/DuplicateMethods: 87 | Enabled: false 88 | 89 | Naming/MethodParameterName: 90 | MinNameLength: 1 91 | AllowNamesEndingNumbers: false 92 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/connection_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper.rb' 2 | 3 | RSpec.describe Tinybucket::Connection do 4 | 5 | class MockApi 6 | include Tinybucket::Connection 7 | 8 | def config(key) 9 | nil 10 | end 11 | end 12 | 13 | let(:mock_api){ MockApi.new } 14 | 15 | describe 'clear_cache' do 16 | pending 'TODO: this method is required ?' 17 | end 18 | 19 | describe 'caching?' do 20 | pending 'TODO: this method is required ?' 21 | end 22 | 23 | describe 'connection(options, parser)' do 24 | subject{ mock_api.connection } 25 | 26 | context 'when no params are given ' do 27 | it { expect(subject).to be_instance_of(Faraday::Connection) } 28 | end 29 | end 30 | 31 | describe 'configure_auth' do 32 | subject(:handlers) { mock_api.connection.builder.handlers } 33 | 34 | context 'with Tinybucket.config.access_token' do 35 | before do 36 | allow_any_instance_of(Tinybucket::Config).to \ 37 | receive(:access_token).and_return('test token') 38 | end 39 | 40 | it 'should use oauth2 middleware' do 41 | expect(handlers).to include(FaradayMiddleware::OAuth2) 42 | expect(handlers).not_to include(FaradayMiddleware::OAuth) 43 | end 44 | end 45 | 46 | context 'without Tinybucket.config.access_token' do 47 | before do 48 | allow_any_instance_of(Tinybucket::Config).to \ 49 | receive(:access_token).and_return(nil) 50 | end 51 | 52 | it 'should use oauth middleware' do 53 | expect(handlers).not_to include(FaradayMiddleware::OAuth2) 54 | expect(handlers).to include(FaradayMiddleware::OAuth) 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/tinybucket/api/tags_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | # Tags Api client 6 | # 7 | # @!attribute [rw] repo_owner 8 | # @return [String] repository owner name. 9 | # @!attribute [rw] repo_slug 10 | # @return [String] {https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D repo_slug}. 11 | class TagsApi < BaseApi 12 | include Tinybucket::Api::Helper::TagsHelper 13 | 14 | attr_accessor :repo_owner, :repo_slug 15 | 16 | # Send 'GET a tags list for a repository' request 17 | # 18 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/refs/tags#get 19 | # GET a tags list for a repository 20 | # 21 | # @param options [Hash] 22 | # @return [Tinybucket::Model::Page] 23 | def list(options = {}) 24 | get_path( 25 | path_to_list, 26 | options, 27 | get_parser(:collection, Tinybucket::Model::Tag) 28 | ) 29 | end 30 | 31 | # Send 'GET an individual tag' request 32 | # 33 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/refs/tags/%7Bname%7D#get 34 | # GET an individual tag 35 | # 36 | # @param name [String] The tag name 37 | # @param options [Hash] 38 | # @return [Tinybucket::Model::Tag] 39 | def find(name, options = {}) 40 | get_path( 41 | path_to_find(name), 42 | options, 43 | get_parser(:object, Tinybucket::Model::Tag) 44 | ) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/tinybucket/model/build_status.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | # Build Status 6 | # 7 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/commit/%7Bnode%7D/statuses/build 8 | # statuses/build Resource 9 | # 10 | # @!attribute [rw] state 11 | # @return [String] 12 | # @!attribute [rw] type 13 | # @return [String] 14 | # @!attribute [rw] key 15 | # @return [String] 16 | # @!attribute [rw] name 17 | # @return [String] 18 | # @!attribute [rw] url 19 | # @return [String] 20 | # @!attribute [rw] description 21 | # @return [String] 22 | # @!attribute [rw] links 23 | # @return [Hash] 24 | class BuildStatus < Base 25 | include Tinybucket::Model::Concerns::RepositoryKeys 26 | include Tinybucket::Model::Concerns::Reloadable 27 | 28 | acceptable_attributes \ 29 | :state, :type, :key, :name, :url, :description, :links, \ 30 | :created_on, :updated_on 31 | 32 | attr_accessor :revision 33 | 34 | # Update build status 35 | # 36 | # @param options [Hash] 37 | # @option options [String] :state 38 | # @return [Tinybucket::Model::BuildStatus] 39 | def update(options) 40 | build_status_api.put(revision, key, options).tap do |m| 41 | m.repo_keys = repo_keys 42 | m.revision = revision 43 | end 44 | end 45 | 46 | private 47 | 48 | def build_status_api 49 | create_api('BuildStatus', repo_keys) 50 | end 51 | 52 | def load_model 53 | build_status_api.find(revision, key) 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/commit/1/comments/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [{ 3 | "links": { 4 | "self": { 5 | "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlassian-rest/commit/61d9e64348f9da407e62f64726337fd3bb24b466/comments/530189" 6 | }, 7 | "code": { 8 | "href": "https://api.bitbucket.org/2.0/repositories/atlassian/atlassian-rest/diff/61d9e64348f9da407e62f64726337fd3bb24b466?path=pom.xml" 9 | }, 10 | "html": { 11 | "href": "https://bitbucket.org/atlassian/atlassian-rest/commits/61d9e64348f9da407e62f64726337fd3bb24b466#comment-530189" 12 | } 13 | }, 14 | "content": { 15 | "raw": "Inline test comment.", 16 | "markup": "markdown", 17 | "html": "Inline test comment.
" 18 | }, 19 | "created_on": "2013-11-07T23:55:24.486865+00:00", 20 | "user": { 21 | "username": "evzijst", 22 | "display_name": "Erik van Zijst", 23 | "links": { 24 | "self": { 25 | "href": "https://api.bitbucket.org/2.0/users/evzijst" 26 | }, 27 | "avatar": { 28 | "href": "https://secure.gravatar.com/avatar/f6bcbb4e3f665e74455bd8c0b4b3afba?d=https%3A%2F%2Fd3oaxc4q5k2d6q.cloudfront.net%2Fm%2Fbf1e763db20f%2Fimg%2Fdefault_avatar%2F32%2Fuser_blue.png&s=32" 29 | } 30 | } 31 | }, 32 | "inline": { 33 | "to": null, 34 | "from": 381, 35 | "path": "pom.xml" 36 | }, 37 | "updated_on": "2013-11-07T23:55:24.502477+00:00", 38 | "id": 530189 39 | }] 40 | } 41 | -------------------------------------------------------------------------------- /lib/tinybucket/api/branches_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | # Branches Api client 6 | # 7 | # @!attribute [rw] repo_owner 8 | # @return [String] repository owner name. 9 | # @!attribute [rw] repo_slug 10 | # @return [String] {https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D repo_slug}. 11 | class BranchesApi < BaseApi 12 | include Tinybucket::Api::Helper::BranchesHelper 13 | 14 | attr_accessor :repo_owner, :repo_slug 15 | 16 | # Send 'GET a branches list for a repository' request 17 | # 18 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/refs/branches#get 19 | # GET a branches list for a repository 20 | # 21 | # @param options [Hash] 22 | # @return [Tinybucket::Model::Page] 23 | def list(options = {}) 24 | get_path( 25 | path_to_list, 26 | options, 27 | get_parser(:collection, Tinybucket::Model::Branch) 28 | ) 29 | end 30 | 31 | # Send 'GET an individual branch' request 32 | # 33 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/refs/branches/%7Bname%7D#get 34 | # GET an individual branch 35 | # 36 | # @param name [String] The branch name 37 | # @param options [Hash] 38 | # @return [Tinybucket::Model::Branch] 39 | def find(name, options = {}) 40 | get_path( 41 | path_to_find(name), 42 | options, 43 | get_parser(:object, Tinybucket::Model::Branch) 44 | ) 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /lib/tinybucket/api/repo_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | # Repo Api client 6 | # 7 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories 8 | # repository Resource 9 | # 10 | # @!attribute [rw] repo_owner 11 | # @return [String] repository owner name. 12 | # @!attribute [rw] repo_slug 13 | # @return [String] {https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D repository slug}. 14 | class RepoApi < BaseApi 15 | include Tinybucket::Api::Helper::RepoHelper 16 | 17 | attr_accessor :repo_owner, :repo_slug 18 | 19 | # Send 'GET a repository' request 20 | # 21 | # @param options [Hash] 22 | # @return [Tinybucket::Model::Repository] 23 | def find(options = {}) 24 | get_path( 25 | path_to_find, 26 | options, 27 | get_parser(:object, Tinybucket::Model::Repository) 28 | ) 29 | end 30 | 31 | # Send 'GET a list of watchers' request 32 | # 33 | # @param options [Hash] 34 | # @return [Tinybucket::Model::Page] 35 | def watchers(options = {}) 36 | get_path( 37 | path_to_watchers, 38 | options, 39 | get_parser(:collection, Tinybucket::Model::Profile) 40 | ) 41 | end 42 | 43 | # Send 'GET a list of forks' request 44 | # 45 | # @param options [Hash] 46 | # @return [Tinybucket::Model::Page] 47 | def forks(options = {}) 48 | get_path( 49 | path_to_forks, 50 | options, 51 | get_parser(:collection, Tinybucket::Model::Repository) 52 | ) 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/tinybucket/request.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Request 5 | protected 6 | 7 | def get_path(path, params = {}, parser = nil, options = {}) 8 | request(:get, path, params, parser, options) 9 | end 10 | 11 | def patch_path(path, params = {}, parser = nil, options = {}) 12 | request(:patch, path, params, parser, options) 13 | end 14 | 15 | def post_path(path, params = {}, parser = nil, options = {}) 16 | request(:post, path, params, parser, options) 17 | end 18 | 19 | def put_path(path, params = {}, parser = nil, options = {}) 20 | request(:put, path, params, parser, options) 21 | end 22 | 23 | def delete_path(path, params = {}, parser = nil, options = {}) 24 | request(:delete, path, params, parser, options) 25 | end 26 | 27 | private 28 | 29 | def request(method, path, params, parser, options) 30 | conn = connection(parser, options) 31 | 32 | path = (conn.path_prefix + path).gsub(%r{//}, '/') \ 33 | if conn.path_prefix != '/' 34 | 35 | response = conn.send(method) do |request| 36 | case method.intern 37 | when :get, :delete 38 | request.body = params.delete('data') if params.key?('data') 39 | request.url(path, params) 40 | when :post, :put, :patch 41 | request.path = path 42 | request.body = extract_data_from_params(params) unless params.empty? 43 | else 44 | raise ArgumentError, 'unknown http method: ' + method 45 | end 46 | end 47 | 48 | response.body 49 | end 50 | 51 | def extract_data_from_params(params) 52 | if params.key?('data') && !params['data'].nil? 53 | params['data'] 54 | else 55 | params 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/pull_requests_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::PullRequests do 4 | include ApiResponseMacros 5 | 6 | let(:owner) { 'test_owner' } 7 | let(:slug) { 'test_repo' } 8 | let(:repo) do 9 | Tinybucket::Model::Repository.new({}).tap do |m| 10 | m.repo_owner = owner 11 | m.repo_slug = slug 12 | end 13 | end 14 | 15 | let(:options) { {} } 16 | let(:resource) { Tinybucket::Resource::PullRequests.new(repo, options) } 17 | 18 | describe '#create' do 19 | let(:params) { {} } 20 | subject { resource.create(params) } 21 | it { expect { subject }.to raise_error(NotImplementedError) } 22 | end 23 | 24 | describe '#find' do 25 | let(:pullrequest_id) { '1' } 26 | let(:request_path) do 27 | "/repositories/#{owner}/#{slug}/pullrequests/#{pullrequest_id}" 28 | end 29 | before { stub_apiresponse(:get, request_path) } 30 | subject { resource.find(pullrequest_id) } 31 | it { expect(subject).to be_an_instance_of(Tinybucket::Model::PullRequest) } 32 | end 33 | 34 | describe '#activities' do 35 | let(:params) { {} } 36 | subject { resource.activities(params) } 37 | it { expect { subject }.to raise_error(NotImplementedError) } 38 | end 39 | 40 | describe 'Enumerable Methods' do 41 | let(:request_path) do 42 | "/repositories/#{owner}/#{slug}/pullrequests" 43 | end 44 | before { stub_enum_response(:get, request_path) } 45 | 46 | describe '#take(1)' do 47 | subject { resource.take(1) } 48 | it { expect(subject).to be_an_instance_of(Array) } 49 | end 50 | 51 | describe '#each' do 52 | it 'iterate models' do 53 | resource.each do |m| 54 | expect(m).to be_an_instance_of(Tinybucket::Model::PullRequest) 55 | end 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/resource/branch_restrictions_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Resource::BranchRestrictions do 4 | include ApiResponseMacros 5 | 6 | let(:owner) { 'test_owner' } 7 | let(:slug) { 'test_repo' } 8 | let(:repo) do 9 | Tinybucket::Model::Repository.new({}).tap do |m| 10 | m.repo_owner = owner 11 | m.repo_slug = slug 12 | end 13 | end 14 | 15 | let(:options) { {} } 16 | let(:resource) { Tinybucket::Resource::BranchRestrictions.new(repo, options) } 17 | 18 | describe 'constructor' do 19 | subject { resource } 20 | it 'create new instance' do 21 | expect(subject).to be_an_instance_of( 22 | Tinybucket::Resource::BranchRestrictions) 23 | end 24 | end 25 | 26 | describe '#create' do 27 | subject { resource.create({}) } 28 | it { expect { subject.create }.to raise_error(NotImplementedError) } 29 | end 30 | 31 | describe '#find' do 32 | let(:restriction_id) { '1' } 33 | let(:request_path) do 34 | "/repositories/#{owner}/#{slug}/branch-restrictions/#{restriction_id}" 35 | end 36 | subject { resource.find(restriction_id, {}) } 37 | before { stub_apiresponse(:get, request_path, {}) } 38 | it 'return model' do 39 | expect(subject).to be_an_instance_of(Tinybucket::Model::BranchRestriction) 40 | end 41 | end 42 | 43 | describe 'Enumerable Methods' do 44 | let(:request_path) { "/repositories/#{owner}/#{slug}/branch-restrictions" } 45 | before { stub_enum_response(:get, request_path) } 46 | 47 | describe '#take(1)' do 48 | subject { resource.take(1) } 49 | it { expect(subject).to be_an_instance_of(Array) } 50 | end 51 | 52 | describe '#each' do 53 | it 'iterate models' do 54 | resource.each do |m| 55 | expect(m).to be_an_instance_of(Tinybucket::Model::BranchRestriction) 56 | end 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/tinybucket/api/user_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | # User Api client 6 | # 7 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/users/%7Busername%7D 8 | # users Endpoint 9 | # 10 | # @!attribute [rw] username 11 | # @return [String] 12 | class UserApi < BaseApi 13 | include Tinybucket::Api::Helper::UserHelper 14 | 15 | attr_accessor :username 16 | 17 | # Send 'GET the user profile' request 18 | # 19 | # @param options [Hash] 20 | # @return [Tinybucket::Model::Profile] 21 | def profile(options = {}) 22 | get_path( 23 | path_to_find, 24 | options, 25 | get_parser(:object, Tinybucket::Model::Profile) 26 | ) 27 | end 28 | 29 | # Send 'GET the list of followers' request 30 | # 31 | # @param options [Hash] 32 | # @return [Tinybucket::Model::Page] 33 | def followers(options = {}) 34 | get_path( 35 | path_to_followers, 36 | options, 37 | get_parser(:collection, Tinybucket::Model::Profile) 38 | ) 39 | end 40 | 41 | # Send 'GET a list of accounts the user is following' request 42 | # 43 | # @param options [Hash] 44 | # @return [Tinybucket::Model::Page] 45 | def following(options = {}) 46 | get_path( 47 | path_to_following, 48 | options, 49 | get_parser(:collection, Tinybucket::Model::Profile) 50 | ) 51 | end 52 | 53 | # Send 'GET the user's repositories' request 54 | # 55 | # @param options [Hash] 56 | # @return [Tinybucket::Model::Page] 57 | def repos(options = {}) 58 | get_path( 59 | path_to_repos, 60 | options, 61 | get_parser(:collection, Tinybucket::Model::Repository) 62 | ) 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/api/tags_api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Api::TagsApi do 4 | include ApiResponseMacros 5 | 6 | let(:tag) { "3.8" } 7 | let(:request_path) { nil } 8 | let(:options) { {} } 9 | let(:owner) { 'test_owner' } 10 | let(:slug) { 'test_repo' } 11 | 12 | let(:api) do 13 | api = Tinybucket::Api::TagsApi.new 14 | api.repo_owner = owner 15 | api.repo_slug = slug 16 | api 17 | end 18 | 19 | it { expect(api).to be_a_kind_of(Tinybucket::Api::BaseApi) } 20 | 21 | let(:request_method) { :get } 22 | let(:stub_options) { nil } 23 | 24 | before do 25 | if request_path 26 | opts = stub_options.present? ? stub_options : {} 27 | stub_apiresponse(request_method, request_path, opts) 28 | end 29 | end 30 | 31 | describe 'list' do 32 | subject { api.list(options) } 33 | 34 | context 'without owner' do 35 | let(:owner) { nil } 36 | it { expect { subject }.to raise_error(ArgumentError) } 37 | end 38 | 39 | context 'without slug' do 40 | let(:slug) { nil } 41 | it { expect { subject }.to raise_error(ArgumentError) } 42 | end 43 | 44 | context 'with owner and slug' do 45 | let(:request_path) { "/repositories/#{owner}/#{slug}/refs/tags" } 46 | it { expect(subject).to be_an_instance_of(Tinybucket::Model::Page) } 47 | end 48 | end 49 | 50 | describe 'find' do 51 | subject { api.find(tag, options) } 52 | 53 | context 'without owner' do 54 | let(:owner) { nil } 55 | it { expect { subject }.to raise_error(ArgumentError) } 56 | end 57 | 58 | context 'without slug' do 59 | let(:slug) { nil } 60 | it { expect { subject }.to raise_error(ArgumentError) } 61 | end 62 | 63 | context 'with owner and slug' do 64 | let(:request_path) do 65 | "/repositories/#{owner}/#{slug}/refs/tags/#{tag}" 66 | end 67 | it { expect(subject).to be_instance_of(Tinybucket::Model::Tag) } 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/model/build_status_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Model::BuildStatus do 4 | include ApiResponseMacros 5 | include ModelMacros 6 | 7 | let(:model_json) { load_json_fixture('build_status') } 8 | 9 | let(:request_method) { :get } 10 | let(:request_path) { nil } 11 | let(:stub_options) { nil } 12 | 13 | let(:commit_hash) { '1' } 14 | let(:owner) { 'test_owner' } 15 | let(:slug) { 'test_repo' } 16 | let(:model) do 17 | Tinybucket::Model::BuildStatus.new(model_json).tap do |m| 18 | m.repo_owner = owner 19 | m.repo_slug = slug 20 | m.revision = commit_hash 21 | end 22 | end 23 | 24 | before do 25 | if request_path 26 | opts = stub_options.present? ? stub_options : {} 27 | stub_apiresponse(request_method, request_path, opts) 28 | end 29 | end 30 | 31 | it_behaves_like 'model has acceptable_attributes', 32 | Tinybucket::Model::BuildStatus, 33 | load_json_fixture('build_status') 34 | 35 | describe 'model can reloadable' do 36 | let(:build_status) do 37 | Tinybucket::Model::BuildStatus.new({}).tap do |m| 38 | m.repo_owner = owner 39 | m.repo_slug = slug 40 | m.revision = commit_hash 41 | end 42 | end 43 | 44 | before { @model = build_status } 45 | it_behaves_like 'the model is reloadable' 46 | end 47 | 48 | describe '#update' do 49 | let(:status_key) { 'test_status' } 50 | let(:request_path) do 51 | "/repositories/#{owner}/#{slug}/commit/1/statuses/build/#{status_key}" 52 | end 53 | let(:params) do 54 | { 55 | state: 'SUCCESSFUL', 56 | name: 'test_repo test #10', 57 | url: 'https://example.com/path/to/build/info', 58 | description: 'Changes by test_owner' 59 | } 60 | end 61 | let(:request_method) { :put } 62 | 63 | subject { model.update(params) } 64 | it { expect(subject).to be_an_instance_of(Tinybucket::Model::BuildStatus) } 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/tinybucket/model/comment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | # Comment 6 | # 7 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/commit/%7Bsha%7D/comments 8 | # Comment Resource 9 | # 10 | # @!attribute [rw] links 11 | # @return [Hash] 12 | # @!attribute [rw] id 13 | # @return [Fixnum] 14 | # @!attribute [rw] parent 15 | # @return [Hash] 16 | # @!attribute [rw] filename 17 | # @return [String] 18 | # @!attribute [rw] content 19 | # @return [Hash] 20 | # @!attribute [rw] user 21 | # @return [Hash] 22 | # @!attribute [rw] inline 23 | # @return [Hash] 24 | # @!attribute [rw] created_on 25 | # @return [String] 26 | # @!attribute [rw] updated_on 27 | # @return [String] 28 | # @!attribute [rw] uuid 29 | # @return [NillClass] 30 | class Comment < Base 31 | include Tinybucket::Model::Concerns::RepositoryKeys 32 | 33 | acceptable_attributes \ 34 | :links, :id, :parent, :filename, :content, :user, :inline, \ 35 | :created_on, :updated_on, :uuid 36 | 37 | # @!attribute [rw] commented_to 38 | # @return [Tinybucket::Model::PullRequest, Tinybucket::Model::Commit] 39 | attr_accessor :commented_to 40 | 41 | private 42 | 43 | def commit_api 44 | create_api('Comments', repo_keys) 45 | end 46 | 47 | def pull_request_api 48 | create_api('PullRequests', repo_keys) 49 | end 50 | 51 | def load_model 52 | api = 53 | case commented_to 54 | when Tinybucket::Model::Commit 55 | commit_api 56 | when Tinybucket::Model::PullRequest 57 | pull_request_api 58 | else 59 | raise ArgumentError, 'commented_to was invalid' 60 | end 61 | 62 | api.commented_to = commented_ato 63 | api.find(id, {}) 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /spec/lib/tinybucket/api/branches_api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.describe Tinybucket::Api::BranchesApi do 4 | include ApiResponseMacros 5 | 6 | let(:branch) { "master" } 7 | let(:request_path) { nil } 8 | let(:options) { {} } 9 | let(:owner) { 'test_owner' } 10 | let(:slug) { 'test_repo' } 11 | 12 | let(:api) do 13 | api = Tinybucket::Api::BranchesApi.new 14 | api.repo_owner = owner 15 | api.repo_slug = slug 16 | api 17 | end 18 | 19 | it { expect(api).to be_a_kind_of(Tinybucket::Api::BaseApi) } 20 | 21 | let(:request_method) { :get } 22 | let(:stub_options) { nil } 23 | 24 | before do 25 | if request_path 26 | opts = stub_options.present? ? stub_options : {} 27 | stub_apiresponse(request_method, request_path, opts) 28 | end 29 | end 30 | 31 | describe 'list' do 32 | subject { api.list(options) } 33 | 34 | context 'without owner' do 35 | let(:owner) { nil } 36 | it { expect { subject }.to raise_error(ArgumentError) } 37 | end 38 | 39 | context 'without slug' do 40 | let(:slug) { nil } 41 | it { expect { subject }.to raise_error(ArgumentError) } 42 | end 43 | 44 | context 'with owner and slug' do 45 | let(:request_path) { "/repositories/#{owner}/#{slug}/refs/branches" } 46 | it { expect(subject).to be_an_instance_of(Tinybucket::Model::Page) } 47 | end 48 | end 49 | 50 | describe 'find' do 51 | subject { api.find(branch, options) } 52 | 53 | context 'without owner' do 54 | let(:owner) { nil } 55 | it { expect { subject }.to raise_error(ArgumentError) } 56 | end 57 | 58 | context 'without slug' do 59 | let(:slug) { nil } 60 | it { expect { subject }.to raise_error(ArgumentError) } 61 | end 62 | 63 | context 'with owner and slug' do 64 | let(:request_path) do 65 | "/repositories/#{owner}/#{slug}/refs/branches/#{branch}" 66 | end 67 | it { expect(subject).to be_instance_of(Tinybucket::Model::Branch) } 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/tinybucket.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'tinybucket/version' 4 | 5 | require 'active_support/dependencies/autoload' 6 | 7 | require 'active_support/core_ext/hash' 8 | require 'active_support/configurable' 9 | require 'active_support/inflector' 10 | 11 | require 'faraday' 12 | require 'faraday_middleware' 13 | require 'faraday_middleware/response_middleware' 14 | require 'faraday_middleware/follow_oauth_redirects' 15 | require 'faraday-http-cache' 16 | 17 | require 'active_model' 18 | 19 | require 'logger' 20 | require 'uri' 21 | 22 | require 'tinybucket/enumerator' 23 | require 'tinybucket/iterator' 24 | 25 | require 'tinybucket/config' 26 | require 'tinybucket/null_logger' 27 | require 'tinybucket/api' 28 | require 'tinybucket/api_factory' 29 | require 'tinybucket/api/helper' 30 | require 'tinybucket/connection' 31 | require 'tinybucket/constants' 32 | require 'tinybucket/error' 33 | require 'tinybucket/model/concerns' 34 | require 'tinybucket/model' 35 | require 'tinybucket/parser' 36 | require 'tinybucket/request' 37 | require 'tinybucket/resource' 38 | require 'tinybucket/response' 39 | 40 | require 'tinybucket/client' 41 | 42 | require 'active_support/notifications' 43 | ActiveSupport::Notifications.subscribe('request.faraday') \ 44 | do |_name, start_time, end_time, _, env| 45 | url = env[:url] 46 | http_method = env[:method].to_s.upcase 47 | duration = end_time - start_time 48 | Tinybucket.logger.debug \ 49 | format( 50 | '[%s] %s %s (%.3f s)', 51 | url.host, http_method, url.request_uri, duration 52 | ) 53 | end 54 | 55 | module Tinybucket 56 | class << self 57 | include ActiveSupport::Configurable 58 | attr_accessor :logger, :api_client 59 | 60 | def new 61 | @api_client = Tinybucket::Client.new 62 | end 63 | 64 | def configure 65 | yield(config) 66 | end 67 | 68 | def config 69 | @config ||= Tinybucket::Config.new 70 | end 71 | 72 | def logger 73 | config.logger || Tinybucket::NullLogger.new 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /spec/fixtures/repositories/test_owner/test_repo/pullrequests/1/comments/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "pagelen": 1, 3 | "next": "https://api.bitbucket.org/2.0/repositories/test_owner/test_repo/pullrequests/1/comments?pagelen=1&page=2", 4 | "values": [{ 5 | "parent": { 6 | "id": 25334, 7 | "links": { 8 | "self": { 9 | "href": "https://bitbucket.org/api/2.0/repositories/bitbucket/bitbucket/pullrequests/3767/comments/25334" 10 | } 11 | } 12 | }, 13 | "links": { 14 | "self": { 15 | "href": "https://bitbucket.org/api/2.0/repositories/bitbucket/bitbucket/pullrequests/3767/comments/25337" 16 | }, 17 | "html": { 18 | "href": "https://bitbucket.org/bitbucket/bitbucket/pull-request/3767/_/diff#comment-25337" 19 | } 20 | }, 21 | "content": { 22 | "raw": "If we are leaving the email attached to the team account, Gravatar should still work fine. The problem would be they can't change the Gravatar associated account then.", 23 | "markup": "markdown", 24 | "html": "If we are leaving the email attached to the team account, Gravatar should still work fine. The problem would be they can't change the Gravatar associated account then.
" 25 | }, 26 | "created_on": "2013-11-07T17:11:20.986517+00:00", 27 | "user": { 28 | "username": "mfrauenholtz", 29 | "display_name": "Michael Frauenholtz", 30 | "links": { 31 | "self": { 32 | "href": "https://bitbucket.org/api/2.0/users/mfrauenholtz" 33 | }, 34 | "avatar": { 35 | "href": "https://bitbucket-staging-assetroot.s3.amazonaws.com/c/photos/2013/Aug/24/mfrauenholtz-avatar-1858533797-5_avatar.png" 36 | } 37 | } 38 | }, 39 | "updated_on": "2013-11-07T17:11:20.989028+00:00", 40 | "id": 25337 41 | }], 42 | "page": 1, 43 | "size": 4 44 | } 45 | -------------------------------------------------------------------------------- /lib/tinybucket/api/branch_restrictions_api.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Api 5 | # BranchRestrictions API client 6 | # 7 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/branch-restrictions 8 | # branch-restrictions Resource 9 | # 10 | # @!attribute [rw] repo_owner 11 | # @return [String] repository owner name. 12 | # @!attribute [rw] repo_slug 13 | # @return [String] {https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D repository slug}. 14 | class BranchRestrictionsApi < BaseApi 15 | include Tinybucket::Api::Helper::BranchRestrictionsHelper 16 | 17 | attr_accessor :repo_owner, :repo_slug 18 | 19 | # Send 'GET the branch-restrictions' request. 20 | # 21 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/branch-restrictions#get 22 | # GET the branch-restrictions 23 | # 24 | # @param options [Hash] 25 | # @return [Tinybucket::Model::Page] 26 | def list(options = {}) 27 | get_path( 28 | path_to_list, 29 | options, 30 | get_parser(:collection, Tinybucket::Model::BranchRestriction) 31 | ) 32 | end 33 | 34 | # Send 'GET a specific restriction' request. 35 | # 36 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/repositories/%7Busername%7D/%7Brepo_slug%7D/branch-restrictions/%7Bid%7D#get 37 | # GET a specific restriction 38 | # 39 | # @param restriction_id [String] The restriction's identifier 40 | # @param options [Hash] 41 | # @return [Tinybucket::Model::BranchRestriction] 42 | def find(restriction_id, options = {}) 43 | get_path( 44 | path_to_find(restriction_id), 45 | options, 46 | get_parser(:object, Tinybucket::Model::BranchRestriction) 47 | ) 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/tinybucket/model/profile.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | module Model 5 | # Profile 6 | # 7 | # @see https://developer.atlassian.com/bitbucket/api/2/reference/resource/users/%7Busername%7D 8 | # users Endpoint 9 | # 10 | # @!attribute [rw] username 11 | # @return [String] 12 | # @!attribute [rw] kind 13 | # @return [NillClass] 14 | # @!attribute [rw] website 15 | # @return [String] 16 | # @!attribute [rw] display_name 17 | # @return [String] 18 | # @!attribute [rw] links 19 | # @return [Hash] 20 | # @!attribute [rw] created_on 21 | # @return [String] 22 | # @!attribute [rw] location 23 | # @return [String] 24 | # @!attribute [rw] type 25 | # @return [String] 26 | # @!attribute [rw] uuid 27 | # @return [String] 28 | class Profile < Base 29 | acceptable_attributes \ 30 | :username, :kind, :website, :display_name, 31 | :links, :created_on, :location, :type, :uuid 32 | 33 | # Get this user's followers 34 | # 35 | # @param options [Hash] 36 | # @return [Tinybucket::Resource::User::Followers] 37 | def followers(options = {}) 38 | Tinybucket::Resource::User::Followers.new(username, options) 39 | end 40 | 41 | # Get users which this user is following 42 | # 43 | # @param options [Hash] 44 | # @return [Tinybucket::Resource::User::Following] 45 | def following(options = {}) 46 | Tinybucket::Resource::User::Following.new(username, options) 47 | end 48 | 49 | # Get this user's repositories 50 | # 51 | # @param options [Hash] 52 | # @return [Tinybucket::Resource::User::Repos] 53 | def repos(options = {}) 54 | Tinybucket::Resource::User::Repos.new(username, options) 55 | end 56 | 57 | private 58 | 59 | def user_api 60 | create_api('User').tap do |api| 61 | api.username = username 62 | end 63 | end 64 | 65 | def load_model 66 | user_api.profile 67 | end 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/faraday_middleware/follow_oauth_redirects.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'faraday' 4 | require 'faraday_middleware' 5 | 6 | module FaradayMiddleware 7 | class FollowOAuthRedirects < FollowRedirects 8 | dependency 'simple_oauth' 9 | 10 | AUTH_HEADER = OAuth::AUTH_HEADER 11 | CONTENT_TYPE = OAuth::CONTENT_TYPE 12 | TYPE_URLENCODED = OAuth::TYPE_URLENCODED 13 | 14 | def update_env(env, request_body, response) 15 | env = super(env, request_body, response) 16 | 17 | # update Authentication Header 18 | if oauth_signed_request?(env) 19 | env[:request_headers][OAuth::AUTH_HEADER] = oauth_header(env).to_s 20 | end 21 | 22 | env 23 | end 24 | 25 | def oauth_header(env) 26 | SimpleOAuth::Header.new env[:method], 27 | env[:url].to_s, 28 | signature_params(body_params(env)), 29 | oauth_options(env) 30 | end 31 | 32 | def oauth_signed_request?(env) 33 | env[:request].oauth 34 | end 35 | 36 | def oauth_options(env) 37 | extra = env[:request][:oauth] 38 | if extra.present? and extra.is_a? Hash and !extra.empty? 39 | @options.merge extra 40 | else 41 | @options 42 | end 43 | end 44 | 45 | def body_params(env) 46 | if include_body_params?(env) 47 | if env[:body].respond_to?(:to_str) 48 | ::Faraday::Utils.parse_nested_query env[:body] 49 | else 50 | env[:body] 51 | end 52 | end || {} 53 | end 54 | 55 | def include_body_params?(env) 56 | # see RFC 5849, section 3.4.1.3.1 for details 57 | !(type = env[:request_headers][OAuth::CONTENT_TYPE]) || 58 | type == OAuth::TYPE_URLENCODED 59 | end 60 | 61 | def signature_params(params) 62 | return params if params.empty? 63 | 64 | params.reject { |_k, v| v.respond_to?(:content_type) } 65 | end 66 | end 67 | 68 | if Faraday::Middleware.respond_to? :register_middleware 69 | Faraday::Response.register_middleware \ 70 | follow_oauth_redirects: -> { FollowOAuthRedirects } 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/tinybucket/iterator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Tinybucket 4 | # Iterator 5 | # 6 | # This iterator iterate items by sending request ot Bitbucket Cloud REST API. 7 | class Iterator 8 | # Constructor 9 | # 10 | # @param api_client [Tinybucket::Api::Base] an instance of Api Client class. 11 | # @param method [Symbol] method name to invoke api_client method. 12 | # @param args [Array