toolhound-ruby 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS.md +7 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +10 -0
- data/LICENSE +20 -0
- data/Makefile +15 -0
- data/README.md +208 -0
- data/Rakefile +10 -0
- data/lib/toolhound-ruby.rb +50 -0
- data/lib/toolhound-ruby/authentication.rb +64 -0
- data/lib/toolhound-ruby/base.rb +94 -0
- data/lib/toolhound-ruby/client.rb +113 -0
- data/lib/toolhound-ruby/client/account.rb +14 -0
- data/lib/toolhound-ruby/client/attachments.rb +88 -0
- data/lib/toolhound-ruby/client/bookmarks.rb +39 -0
- data/lib/toolhound-ruby/client/categories.rb +72 -0
- data/lib/toolhound-ruby/client/comments.rb +0 -0
- data/lib/toolhound-ruby/client/companies.rb +76 -0
- data/lib/toolhound-ruby/client/incidents.rb +167 -0
- data/lib/toolhound-ruby/client/notifications.rb +31 -0
- data/lib/toolhound-ruby/client/projects.rb +40 -0
- data/lib/toolhound-ruby/client/users.rb +81 -0
- data/lib/toolhound-ruby/configurable.rb +47 -0
- data/lib/toolhound-ruby/core_ext/string.rb +16 -0
- data/lib/toolhound-ruby/default.rb +57 -0
- data/lib/toolhound-ruby/error.rb +186 -0
- data/lib/toolhound-ruby/incident.rb +75 -0
- data/lib/toolhound-ruby/inventory.rb +19 -0
- data/lib/toolhound-ruby/project.rb +17 -0
- data/lib/toolhound-ruby/version.rb +3 -0
- data/script/bootstrap +5 -0
- data/script/cibuild +5 -0
- data/script/console +15 -0
- data/script/package +7 -0
- data/script/release +16 -0
- data/script/test +0 -0
- data/toolhound-ruby.gemspec +33 -0
- metadata +110 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
module Nearmiss
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Methods for the Notifications API
|
5
|
+
#
|
6
|
+
module Notifications
|
7
|
+
|
8
|
+
# List notifications for the current user
|
9
|
+
#
|
10
|
+
# If user is not supplied, repositories for the current
|
11
|
+
# authenticated user are returned.
|
12
|
+
#
|
13
|
+
# @note If the user provided is a GitHub organization, only the
|
14
|
+
# organization's public repositories will be listed. For retrieving
|
15
|
+
# organization repositories the {Organizations#organization_repositories}
|
16
|
+
# method should be used instead.
|
17
|
+
# @see https://developer.github.com/v3/repos/#list-your-repositories
|
18
|
+
# @see https://developer.github.com/v3/repos/#list-user-repositories
|
19
|
+
# @param user [Integer, String] Optional GitHub user login or id for which
|
20
|
+
# to list repos.
|
21
|
+
# @return [Array<Sawyer::Resource>] List of projects
|
22
|
+
def notifications(options = {})
|
23
|
+
paginate "notifications", options
|
24
|
+
end
|
25
|
+
alias :list_notifications :notifications
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Toolhound
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Methods for the Projects API
|
5
|
+
#
|
6
|
+
module Projects
|
7
|
+
|
8
|
+
# List projects
|
9
|
+
#
|
10
|
+
# @note Shows a list of projects for the users organization aka account
|
11
|
+
#
|
12
|
+
# @return [Array<Sawyer::Resource>] List of projects
|
13
|
+
def projects(options = {})
|
14
|
+
# paginate "projects", options
|
15
|
+
|
16
|
+
end
|
17
|
+
# alias :list_projects :projects
|
18
|
+
|
19
|
+
# Get a single project
|
20
|
+
#
|
21
|
+
# @param project [String] UUID of project to fetch
|
22
|
+
# @return [Sawyer::Resource] Project information
|
23
|
+
#
|
24
|
+
def project(project, options = {})
|
25
|
+
# get "#{project_path(project)}", options
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# def project_path(id)
|
31
|
+
# if uuid?(id)
|
32
|
+
# "projects/#{id}"
|
33
|
+
# else
|
34
|
+
# "project/#{id}"
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Toolhound
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Methods for the Users API
|
5
|
+
#
|
6
|
+
module Users
|
7
|
+
|
8
|
+
# List users
|
9
|
+
#
|
10
|
+
# @note Logged in user must be an admin to see all users
|
11
|
+
#
|
12
|
+
# @return [Array<Sawyer::Resource>] List of projects
|
13
|
+
def users(options = {})
|
14
|
+
paginate "users", options
|
15
|
+
end
|
16
|
+
alias :list_users :users
|
17
|
+
|
18
|
+
# Get a single user
|
19
|
+
#
|
20
|
+
# @param user [String] Nearmiss user email or id.
|
21
|
+
# @return [Sawyer::Resource]
|
22
|
+
# @example
|
23
|
+
# Nearmiss.user("31817811-dce4-48c4-aa5f-f49603c5abee") or Nearmiss.user("m4rkuskk+a@gmail.com")
|
24
|
+
def user(user=nil, options = {})
|
25
|
+
if user.nil?
|
26
|
+
get "me", options
|
27
|
+
else
|
28
|
+
get "users/#{user}", options
|
29
|
+
end
|
30
|
+
# get User.path(user), options
|
31
|
+
end
|
32
|
+
|
33
|
+
# Edit a user
|
34
|
+
#
|
35
|
+
# @param options [Hash] User information.
|
36
|
+
# @option options [String] :email Email of user
|
37
|
+
# @option options [String] :name Name of user
|
38
|
+
# @option options [String] :nickname Nickname of user
|
39
|
+
# @option options [Integer] :role Set to admin or not
|
40
|
+
# @option options [String] :phone_number Phone number of user
|
41
|
+
# @option options [String] :image URL of image of user
|
42
|
+
# @option options [String] :language Code "en", "de", "es"
|
43
|
+
# @return
|
44
|
+
# [Sawyer::Resource] Edited user info
|
45
|
+
# @example Update a user
|
46
|
+
# @client.edit_user('some_id', {
|
47
|
+
# email: "mklooth@webcor.com",
|
48
|
+
# name: "Markus Klooth"
|
49
|
+
# })
|
50
|
+
#
|
51
|
+
def edit_user(user, options = {})
|
52
|
+
patch "update_user/#{user}", options
|
53
|
+
end
|
54
|
+
alias :update_user :edit_user
|
55
|
+
|
56
|
+
def update_email(user, options = {})
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def delete_user(user, options = {})
|
62
|
+
delete "users/#{user}", options
|
63
|
+
end
|
64
|
+
|
65
|
+
# Validate user username and password
|
66
|
+
#
|
67
|
+
# @param options [Hash] User credentials
|
68
|
+
# @option options [String] :email Nearmiss login email
|
69
|
+
# @option options [String] :password Nearmiss password
|
70
|
+
# @return [Boolean] True if credentials are valid
|
71
|
+
def validate_credentials(options = {})
|
72
|
+
!self.class.new(options).user.nil?
|
73
|
+
rescue Toolhound::Unauthorized
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Toolhound
|
2
|
+
module Configurable
|
3
|
+
|
4
|
+
# attr_accessor :auto_paginate, :per_page
|
5
|
+
attr_writer :dataserver, :port, :username, :password
|
6
|
+
|
7
|
+
# Define static methods
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def keys
|
11
|
+
@keys ||= [
|
12
|
+
:dataserver,
|
13
|
+
:port,
|
14
|
+
:username,
|
15
|
+
:password
|
16
|
+
]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Set configuration options using a block
|
21
|
+
def configure
|
22
|
+
yield self
|
23
|
+
end
|
24
|
+
|
25
|
+
# Reset configuration options to default values
|
26
|
+
def reset!
|
27
|
+
Toolhound::Configurable.keys.each do |key|
|
28
|
+
instance_variable_set(:"@#{key}", Toolhound::Default.options[key])
|
29
|
+
end
|
30
|
+
self
|
31
|
+
end
|
32
|
+
alias setup reset!
|
33
|
+
|
34
|
+
# def api_endpoint
|
35
|
+
# File.join(@api_endpoint, "")
|
36
|
+
# end
|
37
|
+
|
38
|
+
def options
|
39
|
+
Hash[Toolhound::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class String
|
2
|
+
|
3
|
+
def underscore()
|
4
|
+
camel_cased_word = self
|
5
|
+
acronym_regex = /#{["REST"].join("|")}/
|
6
|
+
return camel_cased_word unless camel_cased_word =~ /[A-Z-]|::/
|
7
|
+
word = camel_cased_word.to_s.gsub('::'.freeze, '/'.freeze)
|
8
|
+
word.gsub!(/(?:(?<=([A-Za-z\d]))|\b)(#{acronym_regex})(?=\b|[^a-z])/) { "#{$1 && '_'.freeze }#{$2.downcase}" }
|
9
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
|
10
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2'.freeze)
|
11
|
+
word.tr!("-".freeze, "_".freeze)
|
12
|
+
word.downcase!
|
13
|
+
word
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# require "bim360/version" unless defined?(Nearmiss::VERSION)
|
2
|
+
# require 'bim360/response/raise_error'
|
3
|
+
|
4
|
+
module Toolhound
|
5
|
+
|
6
|
+
|
7
|
+
# Default configuration options for {Client}
|
8
|
+
module Default
|
9
|
+
|
10
|
+
# Default User Agent header string
|
11
|
+
|
12
|
+
PORT = 1433.freeze
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Configuration options
|
17
|
+
# @return [Hash]
|
18
|
+
def options
|
19
|
+
Hash[Toolhound::Configurable.keys.map{|key| [key, send(key)]}]
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
def config
|
25
|
+
@config ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Default API endpoint from ENV or {API_ENDPOINT}
|
29
|
+
# @return [String]
|
30
|
+
def dataserver
|
31
|
+
ENV['TOOLHOUND_DATASERVER'] || config['dataserver']
|
32
|
+
end
|
33
|
+
|
34
|
+
def port
|
35
|
+
ENV['TOOLHOUND_PORT'] || config['port'] || PORT
|
36
|
+
end
|
37
|
+
|
38
|
+
# Default BIM360-Field username for Basic Auth from ENV
|
39
|
+
# @return [String]
|
40
|
+
def username
|
41
|
+
ENV['TOOLHOUND_USERNAME'] || config['username']
|
42
|
+
# ENV['NEARMISS_EMAIL'] || config['email']
|
43
|
+
end
|
44
|
+
|
45
|
+
# Default BIM360-Field password for Basic Auth from ENV
|
46
|
+
# @return [String]
|
47
|
+
def password
|
48
|
+
ENV['TOOLHOUND_PASSWORD'] || config['password']
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module Toolhound
|
2
|
+
# Custom error class for rescuing from all GitHub errors
|
3
|
+
class Error < StandardError
|
4
|
+
|
5
|
+
# Returns the appropriate Nearmiss::Error sublcass based
|
6
|
+
# on status and response message
|
7
|
+
#
|
8
|
+
# @param [Hash] response HTTP response
|
9
|
+
# @return [Nearmiss::Error]
|
10
|
+
def self.from_response(response)
|
11
|
+
status = response[:status].to_i
|
12
|
+
body = response[:body].to_s
|
13
|
+
headers = response[:response_headers]
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
if klass = case status
|
18
|
+
when 400 then Nearmiss::BadRequest
|
19
|
+
when 401 then Nearmiss::Unauthorized
|
20
|
+
when 403 then Nearmiss::Forbidden
|
21
|
+
when 404 then Nearmiss::NotFound
|
22
|
+
when 405 then Nearmiss::MethodNotAllowed
|
23
|
+
when 406 then Nearmiss::NotAcceptable
|
24
|
+
when 409 then Nearmiss::Conflict
|
25
|
+
when 415 then Nearmiss::UnsupportedMediaType
|
26
|
+
when 422 then Nearmiss::UnprocessableEntity
|
27
|
+
when 400..499 then Nearmiss::ClientError
|
28
|
+
when 500 then Nearmiss::InternalServerError
|
29
|
+
when 501 then Nearmiss::NotImplemented
|
30
|
+
when 502 then Nearmiss::BadGateway
|
31
|
+
when 503 then Nearmiss::ServiceUnavailable
|
32
|
+
when 500..599 then Nearmiss::ServerError
|
33
|
+
end
|
34
|
+
klass.new(response)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(response=nil)
|
39
|
+
@response = response
|
40
|
+
super(build_error_message)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Array of validation errors
|
44
|
+
# @return [Array<Hash>] Error info
|
45
|
+
def errors
|
46
|
+
if data && data.is_a?(Hash)
|
47
|
+
data[:errors] || []
|
48
|
+
else
|
49
|
+
[]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# private
|
54
|
+
|
55
|
+
def data
|
56
|
+
@data ||=
|
57
|
+
if (body = @response[:body]) && !body.empty?
|
58
|
+
if body.is_a?(String) &&
|
59
|
+
@response[:response_headers] &&
|
60
|
+
@response[:response_headers][:content_type] =~ /json/
|
61
|
+
|
62
|
+
# Sawyer::Agent.serializer.decode(body)
|
63
|
+
else
|
64
|
+
body
|
65
|
+
end
|
66
|
+
else
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def response_message
|
72
|
+
case data
|
73
|
+
when Hash
|
74
|
+
data[:message]
|
75
|
+
when String
|
76
|
+
data
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def response_error
|
81
|
+
"Error: #{data[:error]}" if data.is_a?(Hash) && data[:error]
|
82
|
+
end
|
83
|
+
|
84
|
+
def response_error_summary
|
85
|
+
return nil unless data.is_a?(Hash) && !Array(data[:errors]).empty?
|
86
|
+
|
87
|
+
summary = "\nError summary:\n"
|
88
|
+
# summary << data[:errors].map do |hash|
|
89
|
+
# hash.map { |k,v| " #{k}: #{v}" }
|
90
|
+
# end.join("\n")
|
91
|
+
summary << data[:errors].join("\n")
|
92
|
+
summary
|
93
|
+
end
|
94
|
+
|
95
|
+
def build_error_message
|
96
|
+
return nil if @response.nil?
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
message = "#{@response[:method].to_s.upcase} "
|
101
|
+
message << redact_url(@response[:url].to_s) + ": "
|
102
|
+
message << "#{@response[:status]} - "
|
103
|
+
message << "#{response_message}" unless response_message.nil?
|
104
|
+
message << "#{response_error}" unless response_error.nil?
|
105
|
+
message << "#{response_error_summary}" unless response_error_summary.nil?
|
106
|
+
# message << MultiJson.dump(@response)
|
107
|
+
message
|
108
|
+
end
|
109
|
+
|
110
|
+
def redact_url(url_string)
|
111
|
+
%w[client_secret access_token].each do |token|
|
112
|
+
url_string.gsub!(/#{token}=\S+/, "#{token}=(redacted)") if url_string.include? token
|
113
|
+
end
|
114
|
+
url_string
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
# Raised on errors in the 400-499 range
|
120
|
+
class ClientError < Error; end
|
121
|
+
|
122
|
+
# Raised when Nearmiss-Field returns a 400 HTTP status code
|
123
|
+
class BadRequest < ClientError; end
|
124
|
+
|
125
|
+
# Raised when Nearmiss-Field returns a 401 HTTP status code
|
126
|
+
class Unauthorized < ClientError; end
|
127
|
+
# Raised when Nearmiss-Field returns a 403 HTTP status code
|
128
|
+
class Forbidden < ClientError; end
|
129
|
+
|
130
|
+
# Raised when Nearmiss-Field returns a 403 HTTP status code
|
131
|
+
# and body matches 'rate limit exceeded'
|
132
|
+
class TooManyRequests < Forbidden; end
|
133
|
+
|
134
|
+
# Raised when Nearmiss-Field returns a 403 HTTP status code
|
135
|
+
# and body matches 'login attempts exceeded'
|
136
|
+
class TooManyLoginAttempts < Forbidden; end
|
137
|
+
|
138
|
+
# Raised when Nearmiss-Field returns a 404 HTTP status code
|
139
|
+
class NotFound < ClientError; end
|
140
|
+
|
141
|
+
# Raised when Nearmiss-Field returns a 405 HTTP status code
|
142
|
+
class MethodNotAllowed < ClientError; end
|
143
|
+
|
144
|
+
# Raised when Nearmiss-Field returns a 406 HTTP status code
|
145
|
+
class NotAcceptable < ClientError; end
|
146
|
+
|
147
|
+
# Raised when Nearmiss-Field returns a 409 HTTP status code
|
148
|
+
class Conflict < ClientError; end
|
149
|
+
|
150
|
+
# Raised when Nearmiss-Field returns a 414 HTTP status code
|
151
|
+
class UnsupportedMediaType < ClientError; end
|
152
|
+
|
153
|
+
# Raised when Nearmiss-Field returns a 422 HTTP status code
|
154
|
+
class UnprocessableEntity < ClientError; end
|
155
|
+
|
156
|
+
# Raised on errors in the 500-599 range
|
157
|
+
class ServerError < Error; end
|
158
|
+
|
159
|
+
# Raised when Nearmiss-Field returns a 500 HTTP status code
|
160
|
+
class InternalServerError < ServerError; end
|
161
|
+
|
162
|
+
# Raised when Nearmiss-Field returns a 501 HTTP status code
|
163
|
+
class NotImplemented < ServerError; end
|
164
|
+
|
165
|
+
# Raised when Nearmiss-Field returns a 502 HTTP status code
|
166
|
+
class BadGateway < ServerError; end
|
167
|
+
|
168
|
+
# Raised when Nearmiss-Field returns a 503 HTTP status code
|
169
|
+
class ServiceUnavailable < ServerError; end
|
170
|
+
|
171
|
+
# Raised when client fails to provide valid Content-Type
|
172
|
+
class MissingContentType < ArgumentError; end
|
173
|
+
|
174
|
+
# Raised when a method requires an application client_id
|
175
|
+
# and secret but none is provided
|
176
|
+
class ApplicationCredentialsRequired < StandardError; end
|
177
|
+
|
178
|
+
class MissingParams < Error
|
179
|
+
def initialize(msg)
|
180
|
+
|
181
|
+
msg
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
end
|