brat 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +24 -0
  6. data/README.md +103 -0
  7. data/Rakefile +9 -0
  8. data/bin/brat +7 -0
  9. data/brat.gemspec +26 -0
  10. data/lib/brat.rb +37 -0
  11. data/lib/brat/api.rb +17 -0
  12. data/lib/brat/cli.rb +47 -0
  13. data/lib/brat/cli_helpers.rb +175 -0
  14. data/lib/brat/client.rb +18 -0
  15. data/lib/brat/client/branches.rb +79 -0
  16. data/lib/brat/client/groups.rb +88 -0
  17. data/lib/brat/client/issues.rb +92 -0
  18. data/lib/brat/client/merge_requests.rb +107 -0
  19. data/lib/brat/client/milestones.rb +57 -0
  20. data/lib/brat/client/notes.rb +106 -0
  21. data/lib/brat/client/projects.rb +298 -0
  22. data/lib/brat/client/repositories.rb +78 -0
  23. data/lib/brat/client/snippets.rb +86 -0
  24. data/lib/brat/client/system_hooks.rb +58 -0
  25. data/lib/brat/client/users.rb +123 -0
  26. data/lib/brat/configuration.rb +40 -0
  27. data/lib/brat/error.rb +42 -0
  28. data/lib/brat/help.rb +44 -0
  29. data/lib/brat/objectified_hash.rb +24 -0
  30. data/lib/brat/request.rb +101 -0
  31. data/lib/brat/shell.rb +49 -0
  32. data/lib/brat/version.rb +3 -0
  33. data/spec/brat/cli_spec.rb +80 -0
  34. data/spec/brat/client/branches_spec.rb +103 -0
  35. data/spec/brat/client/groups_spec.rb +111 -0
  36. data/spec/brat/client/issues_spec.rb +122 -0
  37. data/spec/brat/client/merge_requests_spec.rb +124 -0
  38. data/spec/brat/client/milestones_spec.rb +66 -0
  39. data/spec/brat/client/notes_spec.rb +156 -0
  40. data/spec/brat/client/projects_spec.rb +357 -0
  41. data/spec/brat/client/repositories_spec.rb +92 -0
  42. data/spec/brat/client/snippets_spec.rb +85 -0
  43. data/spec/brat/client/system_hooks_spec.rb +69 -0
  44. data/spec/brat/client/users_spec.rb +192 -0
  45. data/spec/brat/objectified_hash_spec.rb +23 -0
  46. data/spec/brat/request_spec.rb +48 -0
  47. data/spec/brat_spec.rb +65 -0
  48. data/spec/fixtures/branch.json +1 -0
  49. data/spec/fixtures/branches.json +1 -0
  50. data/spec/fixtures/comment_merge_request.json +1 -0
  51. data/spec/fixtures/create_branch.json +1 -0
  52. data/spec/fixtures/create_merge_request.json +1 -0
  53. data/spec/fixtures/error_already_exists.json +1 -0
  54. data/spec/fixtures/group.json +60 -0
  55. data/spec/fixtures/group_create.json +1 -0
  56. data/spec/fixtures/group_member.json +1 -0
  57. data/spec/fixtures/group_member_delete.json +1 -0
  58. data/spec/fixtures/group_members.json +1 -0
  59. data/spec/fixtures/groups.json +2 -0
  60. data/spec/fixtures/issue.json +1 -0
  61. data/spec/fixtures/issues.json +1 -0
  62. data/spec/fixtures/key.json +1 -0
  63. data/spec/fixtures/keys.json +1 -0
  64. data/spec/fixtures/merge_request.json +1 -0
  65. data/spec/fixtures/merge_request_comments.json +1 -0
  66. data/spec/fixtures/merge_requests.json +1 -0
  67. data/spec/fixtures/milestone.json +1 -0
  68. data/spec/fixtures/milestones.json +1 -0
  69. data/spec/fixtures/note.json +1 -0
  70. data/spec/fixtures/notes.json +1 -0
  71. data/spec/fixtures/project.json +1 -0
  72. data/spec/fixtures/project_commit.json +13 -0
  73. data/spec/fixtures/project_commit_diff.json +10 -0
  74. data/spec/fixtures/project_commits.json +1 -0
  75. data/spec/fixtures/project_delete_key.json +8 -0
  76. data/spec/fixtures/project_events.json +1 -0
  77. data/spec/fixtures/project_for_user.json +1 -0
  78. data/spec/fixtures/project_fork_link.json +1 -0
  79. data/spec/fixtures/project_hook.json +1 -0
  80. data/spec/fixtures/project_hooks.json +1 -0
  81. data/spec/fixtures/project_issues.json +1 -0
  82. data/spec/fixtures/project_key.json +6 -0
  83. data/spec/fixtures/project_keys.json +6 -0
  84. data/spec/fixtures/project_tags.json +1 -0
  85. data/spec/fixtures/projects.json +1 -0
  86. data/spec/fixtures/protect_branch.json +1 -0
  87. data/spec/fixtures/session.json +1 -0
  88. data/spec/fixtures/snippet.json +1 -0
  89. data/spec/fixtures/snippets.json +1 -0
  90. data/spec/fixtures/system_hook.json +1 -0
  91. data/spec/fixtures/system_hook_test.json +1 -0
  92. data/spec/fixtures/system_hooks.json +1 -0
  93. data/spec/fixtures/tag.json +1 -0
  94. data/spec/fixtures/team_member.json +1 -0
  95. data/spec/fixtures/team_members.json +1 -0
  96. data/spec/fixtures/unprotect_branch.json +1 -0
  97. data/spec/fixtures/update_merge_request.json +1 -0
  98. data/spec/fixtures/user.json +1 -0
  99. data/spec/fixtures/users.json +1 -0
  100. data/spec/spec_helper.rb +74 -0
  101. metadata +281 -0
@@ -0,0 +1,58 @@
1
+ class Brat::Client
2
+ # Defines methods related to system hooks.
3
+ module SystemHooks
4
+ # Gets a list of system hooks.
5
+ #
6
+ # @example
7
+ # Brat.hooks
8
+ # Brat.system_hooks
9
+ #
10
+ # @param [Hash] options A customizable set of options.
11
+ # @option options [Integer] :page The page number.
12
+ # @option options [Integer] :per_page The number of results per page.
13
+ # @return [Array<Brat::ObjectifiedHash>]
14
+ def hooks(options={})
15
+ get("/hooks", query: options)
16
+ end
17
+ alias_method :system_hooks, :hooks
18
+
19
+ # Adds a new system hook.
20
+ #
21
+ # @example
22
+ # Brat.add_hook('http://example.com/hook')
23
+ # Brat.add_system_hook('https://api.example.net/v1/hook')
24
+ #
25
+ # @param [String] url The hook URL.
26
+ # @return [Brat::ObjectifiedHash]
27
+ def add_hook(url)
28
+ post("/hooks", :body => {:url => url})
29
+ end
30
+ alias_method :add_system_hook, :add_hook
31
+
32
+ # Tests a system hook.
33
+ #
34
+ # @example
35
+ # Brat.hook(3)
36
+ # Brat.system_hook(12)
37
+ #
38
+ # @param [Integer] id The ID of a system hook.
39
+ # @return [Array<Brat::ObjectifiedHash>]
40
+ def hook(id)
41
+ get("/hooks/#{id}")
42
+ end
43
+ alias_method :system_hook, :hook
44
+
45
+ # Deletes a new system hook.
46
+ #
47
+ # @example
48
+ # Brat.delete_hook(3)
49
+ # Brat.delete_system_hook(12)
50
+ #
51
+ # @param [Integer] id The ID of a system hook.
52
+ # @return [Brat::ObjectifiedHash]
53
+ def delete_hook(id)
54
+ delete("/hooks/#{id}")
55
+ end
56
+ alias_method :delete_system_hook, :delete_hook
57
+ end
58
+ end
@@ -0,0 +1,123 @@
1
+ class Brat::Client
2
+ # Defines methods related to users.
3
+ module Users
4
+ # Gets a list of users.
5
+ #
6
+ # @example
7
+ # Brat.users
8
+ #
9
+ # @param [Hash] options A customizable set of options.
10
+ # @option options [Integer] :page The page number.
11
+ # @option options [Integer] :per_page The number of results per page.
12
+ # @return [Array<Brat::ObjectifiedHash>]
13
+ def users(options={})
14
+ get("/users", :query => options)
15
+ end
16
+
17
+ # Gets information about a user.
18
+ # Will return information about an authorized user if no ID passed.
19
+ #
20
+ # @example
21
+ # Brat.user
22
+ # Brat.user(2)
23
+ #
24
+ # @param [Integer] id The ID of a user.
25
+ # @return [Brat::ObjectifiedHash]
26
+ def user(id=nil)
27
+ id.to_i.zero? ? get("/user") : get("/users/#{id}")
28
+ end
29
+
30
+ # Creates a new user.
31
+ # Requires authentication from an admin account.
32
+ #
33
+ # @param [String] email The email of a user.
34
+ # @param [String] password The password of a user.
35
+ # @param [Hash] options A customizable set of options.
36
+ # @option options [String] :name The name of a user. Defaults to email.
37
+ # @option options [String] :skype The skype of a user.
38
+ # @option options [String] :linkedin The linkedin of a user.
39
+ # @option options [String] :twitter The twitter of a user.
40
+ # @option options [Integer] :projects_limit The limit of projects for a user.
41
+ # @return [Brat::ObjectifiedHash] Information about created user.
42
+ def create_user(email, password, options={})
43
+ body = {:email => email, :password => password, :name => email}.merge(options)
44
+ post("/users", :body => body)
45
+ end
46
+
47
+ # Updates a user.
48
+ #
49
+ # @param [Integer] id The ID of a user.
50
+ # @param [Hash] options A customizable set of options.
51
+ # @option options [String] email The email of a user.
52
+ # @option options [String] password The password of a user.
53
+ # @option options [String] :name The name of a user. Defaults to email.
54
+ # @option options [String] :skype The skype of a user.
55
+ # @option options [String] :linkedin The linkedin of a user.
56
+ # @option options [String] :twitter The twitter of a user.
57
+ # @option options [Integer] :projects_limit The limit of projects for a user.
58
+ # @return [Brat::ObjectifiedHash] Information about created user.
59
+ def edit_user(user_id, options={})
60
+ put("/users/#{user_id}", :body => options)
61
+ end
62
+
63
+ # Creates a new user session.
64
+ #
65
+ # @example
66
+ # Brat.session('jack@example.com', 'secret12345')
67
+ #
68
+ # @param [String] email The email of a user.
69
+ # @param [String] password The password of a user.
70
+ # @return [Brat::ObjectifiedHash]
71
+ # @note This method doesn't require private_token to be set.
72
+ def session(email, password)
73
+ post("/session", :body => {:email => email, :password => password})
74
+ end
75
+
76
+ # Gets a list of user's SSH keys.
77
+ #
78
+ # @example
79
+ # Brat.ssh_keys
80
+ #
81
+ # @param [Hash] options A customizable set of options.
82
+ # @option options [Integer] :page The page number.
83
+ # @option options [Integer] :per_page The number of results per page.
84
+ # @return [Array<Brat::ObjectifiedHash>]
85
+ def ssh_keys(options={})
86
+ get("/user/keys", :query => options)
87
+ end
88
+
89
+ # Gets information about SSH key.
90
+ #
91
+ # @example
92
+ # Brat.ssh_key(1)
93
+ #
94
+ # @param [Integer] id The ID of a user's SSH key.
95
+ # @return [Brat::ObjectifiedHash]
96
+ def ssh_key(id)
97
+ get("/user/keys/#{id}")
98
+ end
99
+
100
+ # Creates a new SSH key.
101
+ #
102
+ # @example
103
+ # Brat.create_ssh_key('key title', 'key body')
104
+ #
105
+ # @param [String] title The title of an SSH key.
106
+ # @param [String] key The SSH key body.
107
+ # @return [Brat::ObjectifiedHash] Information about created SSH key.
108
+ def create_ssh_key(title, key)
109
+ post("/user/keys", :body => {:title => title, :key => key})
110
+ end
111
+
112
+ # Deletes an SSH key.
113
+ #
114
+ # @example
115
+ # Brat.delete_ssh_key(1)
116
+ #
117
+ # @param [Integer] id The ID of a user's SSH key.
118
+ # @return [Brat::ObjectifiedHash] Information about deleted SSH key.
119
+ def delete_ssh_key(id)
120
+ delete("/user/keys/#{id}")
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,40 @@
1
+ module Brat
2
+ # Defines constants and methods related to configuration.
3
+ module Configuration
4
+ # An array of valid keys in the options hash when configuring a Brat::API.
5
+ VALID_OPTIONS_KEYS = [:endpoint, :private_token, :user_agent, :sudo,
6
+ :namespace, :api_version, :host].freeze
7
+
8
+ # The user agent that will be sent to the API endpoint if none is set.
9
+ DEFAULT_USER_AGENT = "Brat Ruby Gem #{Brat::VERSION}".freeze
10
+
11
+ # @private
12
+ attr_accessor(*VALID_OPTIONS_KEYS)
13
+
14
+ # Sets all configuration options to their default values
15
+ # when this module is extended.
16
+ def self.extended(base)
17
+ base.reset
18
+ end
19
+
20
+ # Convenience method to allow configuration options to be set in a block.
21
+ def configure
22
+ yield self
23
+ end
24
+
25
+ # Creates a hash of options and their values.
26
+ def options
27
+ VALID_OPTIONS_KEYS.inject({}) do |option, key|
28
+ option.merge!(key => send(key))
29
+ end
30
+ end
31
+
32
+ # Resets all configuration options to the defaults.
33
+ def reset
34
+ self.endpoint = 'http://brat.io/api/v3'
35
+ self.private_token = ENV['BRAT_API_PRIVATE_TOKEN']
36
+ self.sudo = nil
37
+ self.user_agent = DEFAULT_USER_AGENT
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ module Brat
2
+ module Error
3
+ # Custom error class for rescuing from all Brat errors.
4
+ class Error < StandardError; end
5
+
6
+ # Raise when attributes are missing.
7
+ class MissingAttributes < Error; end
8
+
9
+ # Raised when API endpoint credentials not configured.
10
+ class MissingCredentials < Error; end
11
+
12
+ # Raised when impossible to parse response body.
13
+ class Parsing < Error; end
14
+
15
+ # Raised when API endpoint returns the HTTP status code 400.
16
+ class BadRequest < Error; end
17
+
18
+ # Raised when API endpoint returns the HTTP status code 401.
19
+ class Unauthorized < Error; end
20
+
21
+ # Raised when API endpoint returns the HTTP status code 403.
22
+ class Forbidden < Error; end
23
+
24
+ # Raised when API endpoint returns the HTTP status code 404.
25
+ class NotFound < Error; end
26
+
27
+ # Raised when API endpoint returns the HTTP status code 405.
28
+ class MethodNotAllowed < Error; end
29
+
30
+ # Raised when API endpoint returns the HTTP status code 409.
31
+ class Conflict < Error; end
32
+
33
+ # Raised when API endpoint returns the HTTP status code 500.
34
+ class InternalServerError < Error; end
35
+
36
+ # Raised when API endpoint returns the HTTP status code 502.
37
+ class BadGateway < Error; end
38
+
39
+ # Raised when API endpoint returns the HTTP status code 503.
40
+ class ServiceUnavailable < Error; end
41
+ end
42
+ end
@@ -0,0 +1,44 @@
1
+ require 'brat'
2
+ require 'brat/cli_helpers'
3
+
4
+ module Brat::Help
5
+ extend Brat::CLI::Helpers
6
+
7
+ def self.get_help(methods,cmd=nil)
8
+ help = ''
9
+
10
+ if cmd.nil? || cmd == 'help'
11
+ help = actions_table
12
+ else
13
+ ri_cmd = `which ri`.chomp
14
+
15
+ if $? == 0
16
+ namespace = methods.select {|m| m[:name] === cmd }.map {|m| m[:owner]+'.'+m[:name] }.shift
17
+
18
+ if namespace
19
+ begin
20
+ ri_output = `#{ri_cmd} -T #{namespace} 2>&1`.chomp
21
+
22
+ if $? == 0
23
+ ri_output.gsub!(/#{cmd}\((.*)\)/, cmd+' \1')
24
+ ri_output.gsub!(/Brat\./, 'brat> ')
25
+ ri_output.gsub!(/Brat\..+$/, '')
26
+ ri_output.gsub!(/\,/, '')
27
+ help = ri_output
28
+ else
29
+ help = "Ri docs not found for #{namespace}, please install the docs to use 'help'"
30
+ end
31
+ rescue => e
32
+ puts e.message
33
+ end
34
+ else
35
+ help = "Unknown command: #{cmd}"
36
+ end
37
+ else
38
+ help = "'ri' tool not found in your PATH, please install it to use the help."
39
+ end
40
+ end
41
+
42
+ puts help
43
+ end
44
+ end
@@ -0,0 +1,24 @@
1
+ module Brat
2
+ # Converts hashes to the objects.
3
+ class ObjectifiedHash
4
+ # Creates a new ObjectifiedHash object.
5
+ def initialize(hash)
6
+ @hash = hash
7
+ @data = hash.inject({}) do |data, (key,value)|
8
+ value = ObjectifiedHash.new(value) if value.is_a? Hash
9
+ data[key.to_s] = value
10
+ data
11
+ end
12
+ end
13
+
14
+ def to_hash
15
+ @hash
16
+ end
17
+ alias_method :to_h, :to_hash
18
+
19
+ # Delegate to ObjectifiedHash.
20
+ def method_missing(key)
21
+ @data.key?(key.to_s) ? @data[key.to_s] : nil
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,101 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module Brat
5
+ # @private
6
+ class Request
7
+ include HTTParty
8
+ format :json
9
+ headers 'Accept' => 'application/json'
10
+ parser Proc.new { |body, _| parse(body) }
11
+
12
+ attr_accessor :private_token
13
+
14
+ # Converts the response body to an ObjectifiedHash.
15
+ def self.parse(body)
16
+ body = decode(body)
17
+
18
+ if body.is_a? Hash
19
+ ObjectifiedHash.new body
20
+ elsif body.is_a? Array
21
+ body.collect! { |e| ObjectifiedHash.new(e) }
22
+ else
23
+ raise Error::Parsing.new "Couldn't parse a response body"
24
+ end
25
+ end
26
+
27
+ # Decodes a JSON response into Ruby object.
28
+ def self.decode(response)
29
+ begin
30
+ JSON.load response
31
+ rescue JSON::ParserError
32
+ raise Error::Parsing.new "The response is not a valid JSON"
33
+ end
34
+ end
35
+
36
+ def get(path, options={})
37
+ set_private_token_header(options)
38
+ validate self.class.get(path, options)
39
+ end
40
+
41
+ def post(path, options={})
42
+ set_private_token_header(options, path)
43
+ validate self.class.post(path, options)
44
+ end
45
+
46
+ def put(path, options={})
47
+ set_private_token_header(options)
48
+ validate self.class.put(path, options)
49
+ end
50
+
51
+ def delete(path, options={})
52
+ set_private_token_header(options)
53
+ validate self.class.delete(path, options)
54
+ end
55
+
56
+ # Checks the response code for common errors.
57
+ # Returns parsed response for successful requests.
58
+ def validate(response)
59
+ case response.code
60
+ when 400; raise Error::BadRequest.new error_message(response)
61
+ when 401; raise Error::Unauthorized.new error_message(response)
62
+ when 403; raise Error::Forbidden.new error_message(response)
63
+ when 404; raise Error::NotFound.new error_message(response)
64
+ when 405; raise Error::MethodNotAllowed.new error_message(response)
65
+ when 409; raise Error::Conflict.new error_message(response)
66
+ when 500; raise Error::InternalServerError.new error_message(response)
67
+ when 502; raise Error::BadGateway.new error_message(response)
68
+ when 503; raise Error::ServiceUnavailable.new error_message(response)
69
+ end
70
+
71
+ response.parsed_response
72
+ end
73
+
74
+ # Sets a base_uri and default_params for requests.
75
+ # @raise [Error::MissingCredentials] if endpoint not set.
76
+ def set_request_defaults(endpoint, private_token, sudo=nil)
77
+ raise Error::MissingCredentials.new("Please set an endpoint to API") unless endpoint
78
+ @private_token = private_token
79
+
80
+ self.class.base_uri endpoint
81
+ self.class.default_params :sudo => sudo
82
+ self.class.default_params.delete(:sudo) if sudo.nil?
83
+ end
84
+
85
+ private
86
+
87
+ # Sets a PRIVATE-TOKEN header for requests.
88
+ # @raise [Error::MissingCredentials] if private_token not set.
89
+ def set_private_token_header(options, path=nil)
90
+ unless path == '/session'
91
+ raise Error::MissingCredentials.new("Please set a private_token for user") unless @private_token
92
+ options[:headers] = {'PRIVATE-TOKEN' => @private_token}
93
+ end
94
+ end
95
+
96
+ def error_message(response)
97
+ "Server responded with code #{response.code}, message: #{response.parsed_response.message}. " \
98
+ "Request URI: #{response.request.base_uri}#{response.request.path}"
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,49 @@
1
+ require 'brat'
2
+ require 'brat/help'
3
+ require 'brat/cli_helpers'
4
+ require 'readline'
5
+
6
+ class Brat::Shell
7
+ extend Brat::CLI::Helpers
8
+
9
+ def self.start
10
+ actions = Brat.actions
11
+
12
+ comp = proc { |s| actions.map(&:to_s).grep(/^#{Regexp.escape(s)}/) }
13
+
14
+ Readline.completion_proc = comp
15
+ Readline.completion_append_character = ' '
16
+
17
+ client = Brat::Client.new(endpoint: '')
18
+
19
+ while buf = Readline.readline("brat> ", true)
20
+ next if buf.nil? || buf.empty?
21
+ buf = buf.split.map(&:chomp)
22
+ cmd = buf.shift
23
+ args = buf.count > 0 ? buf : []
24
+
25
+ if cmd == 'help'
26
+ methods = []
27
+
28
+ actions.each do |action|
29
+ methods << {
30
+ name: action.to_s,
31
+ owner: client.method(action).owner.to_s
32
+ }
33
+ end
34
+
35
+ args[0].nil? ? Brat::Help.get_help(methods) : Brat::Help.get_help(methods, args[0])
36
+ next
37
+ end
38
+
39
+ data = if actions.include?(cmd.to_sym)
40
+ confirm_command(cmd)
41
+ brat_helper(cmd, args)
42
+ else
43
+ "'#{cmd}' is not a valid command. See the 'help' for a list of valid commands."
44
+ end
45
+
46
+ output_table(cmd, args, data)
47
+ end
48
+ end
49
+ end