rainforest 1.0.7 → 2.0.0

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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -6
  3. data/.travis.yml +13 -1
  4. data/Gemfile +6 -0
  5. data/README.md +72 -21
  6. data/Rakefile +6 -13
  7. data/VERSION +1 -1
  8. data/bin/rainforest-console +1 -1
  9. data/gemfiles/default-with-activesupport.gemfile +8 -1
  10. data/gemfiles/json.gemfile +10 -2
  11. data/gemfiles/yajl.gemfile +10 -2
  12. data/lib/rainforest.rb +61 -240
  13. data/lib/rainforest/apibits/api_client.rb +28 -0
  14. data/lib/rainforest/apibits/api_endpoint.rb +11 -0
  15. data/lib/rainforest/apibits/api_list.rb +88 -0
  16. data/lib/rainforest/apibits/api_method.rb +95 -0
  17. data/lib/rainforest/apibits/api_object.rb +52 -0
  18. data/lib/rainforest/apibits/api_resource.rb +153 -0
  19. data/lib/rainforest/apibits/headers_builder.rb +47 -0
  20. data/lib/rainforest/apibits/params_builder.rb +27 -0
  21. data/lib/rainforest/apibits/path_builder.rb +38 -0
  22. data/lib/rainforest/apibits/requester.rb +104 -0
  23. data/lib/rainforest/apibits/util.rb +51 -0
  24. data/lib/rainforest/clients/default_client.rb +59 -0
  25. data/lib/rainforest/endpoints/client_stats_endpoint.rb +11 -0
  26. data/lib/rainforest/endpoints/environment_runs_endpoint.rb +11 -0
  27. data/lib/rainforest/endpoints/environments_endpoint.rb +48 -0
  28. data/lib/rainforest/endpoints/generator_rows_endpoint.rb +17 -0
  29. data/lib/rainforest/endpoints/generators_endpoint.rb +48 -0
  30. data/lib/rainforest/endpoints/integrations_endpoint.rb +33 -0
  31. data/lib/rainforest/endpoints/run_tests_endpoint.rb +20 -0
  32. data/lib/rainforest/endpoints/runs_endpoint.rb +48 -0
  33. data/lib/rainforest/endpoints/schedules_endpoint.rb +48 -0
  34. data/lib/rainforest/endpoints/site_environments_endpoint.rb +24 -0
  35. data/lib/rainforest/endpoints/sites_endpoint.rb +39 -0
  36. data/lib/rainforest/endpoints/tests_endpoint.rb +57 -0
  37. data/lib/rainforest/endpoints/users_endpoint.rb +48 -0
  38. data/lib/rainforest/errors/api_connection_error.rb +1 -1
  39. data/lib/rainforest/errors/api_error.rb +32 -1
  40. data/lib/rainforest/errors/authentication_error.rb +1 -1
  41. data/lib/rainforest/errors/rainforest_error.rb +2 -9
  42. data/lib/rainforest/resources/client_stats.rb +31 -0
  43. data/lib/rainforest/resources/environment.rb +73 -0
  44. data/lib/rainforest/resources/generator.rb +73 -0
  45. data/lib/rainforest/resources/integration.rb +52 -0
  46. data/lib/rainforest/resources/run.rb +93 -0
  47. data/lib/rainforest/resources/schedule.rb +63 -0
  48. data/lib/rainforest/resources/site.rb +53 -0
  49. data/lib/rainforest/resources/site_environment.rb +40 -0
  50. data/lib/rainforest/resources/test.rb +98 -0
  51. data/lib/rainforest/resources/user.rb +75 -0
  52. data/rainforest.gemspec +8 -6
  53. data/test/rainforest/api_client_test.rb +51 -0
  54. data/test/rainforest/api_endpoint_test.rb +13 -0
  55. data/test/rainforest/api_list_test.rb +49 -0
  56. data/test/rainforest/api_method_test.rb +78 -0
  57. data/test/rainforest/headers_builder_test.rb +28 -0
  58. data/test/rainforest/params_builder_test.rb +57 -0
  59. data/test/rainforest/path_builder_test.rb +50 -0
  60. data/test/rainforest/requester_test.rb +86 -0
  61. data/test/rainforest/util_test.rb +41 -19
  62. data/test/test_data.rb +72 -0
  63. data/test/test_helper.rb +17 -134
  64. metadata +99 -59
  65. data/CONTRIBUTORS +0 -2
  66. data/History.txt +0 -4
  67. data/LICENSE +0 -21
  68. data/lib/data/ca-certificates.crt +0 -3918
  69. data/lib/rainforest/api_operations/create.rb +0 -16
  70. data/lib/rainforest/api_operations/delete.rb +0 -11
  71. data/lib/rainforest/api_operations/list.rb +0 -16
  72. data/lib/rainforest/api_operations/update.rb +0 -61
  73. data/lib/rainforest/api_resource.rb +0 -33
  74. data/lib/rainforest/errors/invalid_request_error.rb +0 -10
  75. data/lib/rainforest/json.rb +0 -21
  76. data/lib/rainforest/list_object.rb +0 -35
  77. data/lib/rainforest/rainforest_object.rb +0 -165
  78. data/lib/rainforest/run.rb +0 -8
  79. data/lib/rainforest/singleton_api_resource.rb +0 -20
  80. data/lib/rainforest/util.rb +0 -100
  81. data/test/rainforest/api_resource_test.rb +0 -11
  82. data/test/rainforest/auth_test.rb +0 -24
  83. data/test/rainforest/list_object_test.rb +0 -7
  84. data/test/rainforest/metadata_test.rb +0 -7
  85. data/test/rainforest/run_test.rb +0 -36
@@ -0,0 +1,28 @@
1
+ module Rainforest
2
+ class ApiClient
3
+ attr_accessor :headers, :params
4
+
5
+ def initialize(headers, params)
6
+ self.refresh_from(headers, params)
7
+ end
8
+
9
+ def refresh_from(headers, params)
10
+ @headers = headers
11
+ @params = params
12
+ self
13
+ end
14
+
15
+ def execute(api_method)
16
+ api_method.headers = ParamsBuilder.merge(api_method.headers, @headers)
17
+ api_method.params = ParamsBuilder.merge(api_method.params, @params)
18
+ api_method.execute
19
+ end
20
+
21
+ def inspect
22
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}> Headers: " +
23
+ JSON.pretty_generate(@headers) + ", Params: " +
24
+ JSON.pretty_generate(@params)
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module Rainforest
2
+ class ApiEndpoint
3
+ attr_accessor :client
4
+ attr_accessor :parent
5
+
6
+ def initialize(client, parent=nil)
7
+ @client = client
8
+ @parent = parent
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,88 @@
1
+ module Rainforest
2
+ class ApiList < ApiResource
3
+ include Enumerable
4
+
5
+ attr_reader :api_method
6
+ attr_reader :client
7
+ attr_reader :data
8
+ attr_reader :klass
9
+
10
+ def initialize(klass, json={}, api_method=nil, client=nil)
11
+ if klass.is_a?(Class)
12
+ @klass = klass
13
+ else
14
+ @klass = Util.constantize(klass)
15
+ end
16
+
17
+ refresh_from(json, api_method, client)
18
+ end
19
+
20
+ def refresh_from(json, api_method=nil, client=nil)
21
+ unless json.is_a?(Hash)
22
+ json = {
23
+ :data => json
24
+ }
25
+ end
26
+ json = Util.symbolize_keys(json)
27
+
28
+ clear_api_attributes
29
+ @api_method = api_method
30
+ @client = client
31
+ @data = []
32
+ @json = Util.sorta_deep_clone(json)
33
+
34
+ json.each do |k, v|
35
+ if k.to_sym == :data
36
+ if v.respond_to?(:map)
37
+ instance_variable_set("@#{k}", v.map{ |i| @klass.new(i, @api_method, @client) })
38
+ else
39
+ instance_variable_set("@#{k}", v || [])
40
+ end
41
+ elsif self.class.api_attribute_names.include?(k)
42
+ instance_variable_set("@#{k}", determine_api_attribute_value(k, v))
43
+ end
44
+ end
45
+ self
46
+ end
47
+
48
+ def [](k)
49
+ data[k]
50
+ end
51
+
52
+ def []=(k, v)
53
+ data[k]=v
54
+ end
55
+
56
+ def last
57
+ data.last
58
+ end
59
+
60
+ def length
61
+ data.length
62
+ end
63
+
64
+ def each(&blk)
65
+ data.each(&blk)
66
+ end
67
+
68
+ def inspect
69
+ "#<#{self.class}[#{@klass}]:0x#{self.object_id.to_s(16)}> Data: " + JSON.pretty_generate(inspect_data)
70
+ end
71
+
72
+ def inspect_data
73
+ ret = []
74
+ data.each do |d|
75
+ if d.respond_to?(:inspect_nested)
76
+ ret << d.inspect_nested
77
+ else
78
+ ret << d
79
+ end
80
+ end
81
+ ret
82
+ end
83
+
84
+ @api_attributes = {
85
+ :data => { :readonly => true }
86
+ }
87
+ end
88
+ end
@@ -0,0 +1,95 @@
1
+ module Rainforest
2
+ class ApiMethod
3
+
4
+ attr_accessor :path
5
+ attr_accessor :method
6
+ attr_accessor :params
7
+ attr_accessor :headers
8
+
9
+ attr_accessor :response_body
10
+ attr_accessor :response_code
11
+ attr_accessor :error
12
+
13
+ attr_accessor :api_base
14
+
15
+ def initialize(method, path, params, headers, object)
16
+ @api_base = api_base || Rainforest.api_base
17
+
18
+ @method = method.to_sym
19
+ @path = PathBuilder.build(path, object, params)
20
+ @params = ParamsBuilder.build(params)
21
+ @headers = HeadersBuilder.build(headers)
22
+ end
23
+
24
+ def execute
25
+ begin
26
+ response = Requester.request(method, url, params, headers)
27
+ @response_body = response.body
28
+ @response_code = response.code
29
+ rescue StandardError => e
30
+ @response_body = e.http_body if e.respond_to?(:http_body)
31
+ @response_code = e.http_code if e.respond_to?(:http_code)
32
+ @error = compose_error(e)
33
+ raise @error
34
+ end
35
+
36
+
37
+ response_json
38
+ end
39
+
40
+ def url
41
+ "#{api_base}#{@path}"
42
+ end
43
+
44
+ def response_json
45
+ return @json if @json
46
+ begin
47
+ @json = Util.symbolize_keys(JSON.parse(@response_body))
48
+ rescue JSON::ParserError
49
+ @error = ApiError.new("Unable to parse the server response as JSON.", self)
50
+ raise @error
51
+ end
52
+ @json
53
+ end
54
+
55
+ def compose_error(error)
56
+ msg = "An error occured while making the API call."
57
+
58
+ case error
59
+ when RestClient::ExceptionWithResponse
60
+ return error_with_response(error)
61
+
62
+ when RestClient::RequestTimeout
63
+ msg = "The request timed out while making the API call."
64
+
65
+ when RestClient::ServerBrokeConnection
66
+ msg = "The connection to the server broke before the request completed."
67
+
68
+ when SocketError
69
+ msg = "An unexpected error occured while trying to connect to " \
70
+ "the API. You may be seeing this message because your DNS is " \
71
+ "not working. To check, try running 'host #{Rainforest.api_base}' "\
72
+ "from the command line."
73
+
74
+ else
75
+ msg = "An unexpected error occured. If this problem persists let us " \
76
+ "know at #{Rainforest.support_email}."
77
+ end
78
+
79
+ return ApiConnectionError.new(msg, self)
80
+ end
81
+
82
+ # Handle a few common cases.
83
+ def error_with_response(error)
84
+ case @response_code
85
+ when 400, 404
86
+ return ApiError.new(@response_body || "Invalid request. Please check the URL and parameters.", self)
87
+ when 401
88
+ return AuthenticationError.new(@response_body || "Authentication failed.", self)
89
+ else
90
+ return ApiError.new(@response_body || "An error occured while making the API call.", self)
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,52 @@
1
+ module Rainforest
2
+ class ApiObject
3
+ include Enumerable
4
+ attr_reader :json
5
+
6
+ def self.construct(json)
7
+ if json.is_a?(Array)
8
+ return json.map{ |a| ApiObject.construct(a) }
9
+ elsif json.is_a?(Hash)
10
+ return ApiObject.new(json)
11
+ else
12
+ return json
13
+ end
14
+ end
15
+
16
+ def initialize(json=nil)
17
+ refresh_from(json)
18
+ end
19
+
20
+ def refresh_from(json={})
21
+ @json = Util.sorta_deep_clone(json)
22
+ @json.each do |k, v|
23
+ @json[k] = ApiObject.construct(v)
24
+ end
25
+ self
26
+ end
27
+
28
+ def inspect
29
+ @json.inspect
30
+ end
31
+
32
+ def to_json(*args)
33
+ JSON.generate(@json)
34
+ end
35
+
36
+ def method_missing(name, *args, &blk)
37
+ if name.to_s.end_with?('=')
38
+ attr = name.to_s[0...-1].to_sym
39
+ @json[attr] = args[0]
40
+ else
41
+ if @json.respond_to?(name)
42
+ @json.send(name, *args, &blk)
43
+ elsif @json.has_key?(name.to_sym)
44
+ return @json[name.to_sym]
45
+ else
46
+ super
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,153 @@
1
+ module Rainforest
2
+ class ApiResource
3
+ attr_reader :api_method
4
+ attr_reader :json
5
+ attr_reader :client
6
+
7
+ def initialize(json=nil, api_method=nil, client=nil)
8
+ refresh_from(json, api_method, client)
9
+ end
10
+
11
+ def refresh_from(json={}, api_method=nil, client=nil)
12
+ unless json.is_a?(Hash)
13
+ json = { :id => json }
14
+ end
15
+ json = Util.symbolize_keys(json)
16
+
17
+ # Clear or write over any old data
18
+ clear_api_attributes
19
+ @api_method = api_method
20
+ @json = Util.sorta_deep_clone(json)
21
+ @client = client || Rainforest.default_client
22
+
23
+ # Use json (not the @json, the cloned copy)
24
+ json.each do |k, v|
25
+ unless self.class.api_attribute_names.include?(k.to_sym)
26
+ self.class.add_api_attribute(k.to_sym)
27
+ end
28
+ instance_variable_set("@#{k}", determine_api_attribute_value(k, v))
29
+ end
30
+ self
31
+ end
32
+
33
+ # Returns the default client
34
+ def self.client
35
+ Rainforest.default_client
36
+ end
37
+
38
+ def inspect
39
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
40
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> Attributes: " + JSON.pretty_generate(inspect_api_attributes)
41
+ end
42
+
43
+ def inspect_nested
44
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
45
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}>"
46
+ end
47
+
48
+ def to_json(*args)
49
+ JSON.generate(api_attributes)
50
+ end
51
+
52
+ def self.api_attribute_names
53
+ @api_attributes.map(&:first)
54
+ end
55
+
56
+ def self.editable_api_attribute_names
57
+ @api_attributes.select do |k, v|
58
+ v[:editable]
59
+ end.map(&:first)
60
+ end
61
+
62
+ def self.add_api_attribute(name)
63
+ attr_accessor name.to_sym
64
+ @api_attributes[name.to_sym] = {}
65
+ end
66
+
67
+ def api_attributes
68
+ ret = {}
69
+ self.class.api_attribute_names.each do |attribute|
70
+ ret[attribute] = self.send(attribute)
71
+ end
72
+ ret
73
+ end
74
+
75
+ def inspect_api_attributes
76
+ ret = {}
77
+ api_attributes.each do |k, v|
78
+ if v.is_a?(ApiResource)
79
+ ret[k] = v.inspect_nested
80
+ else
81
+ ret[k] = v
82
+ end
83
+ end
84
+ ret
85
+ end
86
+
87
+ # TODO(joncalhoun): Fix this (not currently working as intended)
88
+ def changed_api_attributes
89
+ ret = {}
90
+ self.api_attributes.each do |name, value|
91
+ if @json[name] != value
92
+ ret[name] = value
93
+ end
94
+ end
95
+ ret
96
+ end
97
+
98
+ def editable_api_attributes
99
+ ret = {}
100
+ self.class.editable_api_attribute_names.each do |name|
101
+ ret[name] = self.send(name)
102
+ end
103
+ ret
104
+ end
105
+
106
+ def clear_api_attributes
107
+ self.class.api_attribute_names.each do |name|
108
+ instance_variable_set("@#{name}", nil)
109
+ end
110
+ end
111
+
112
+ def self.determine_api_attribute_value(name, raw_value)
113
+ if @api_attributes[name] && @api_attributes[name].has_key?(:constructor)
114
+ klass = Util.constantize(@api_attributes[name][:constructor])
115
+ if(klass.respond_to?(:construct))
116
+ klass.construct(raw_value)
117
+ else
118
+ klass.new(raw_value)
119
+ end
120
+ else
121
+ ApiObject.construct(raw_value)
122
+ end
123
+ end
124
+
125
+ def determine_api_attribute_value(name, raw_value)
126
+ self.class.determine_api_attribute_value(name, raw_value)
127
+ end
128
+
129
+
130
+ def self.api_subclasses
131
+ return @api_subclasses ||= Set.new
132
+ end
133
+
134
+ def self.api_subclass_fetch(name)
135
+ @api_subclasses_hash ||= {}
136
+ if @api_subclasses_hash.has_key?(name)
137
+ @api_subclasses_hash[name]
138
+ end
139
+ end
140
+
141
+ def self.register_api_subclass(subclass, name=nil)
142
+ @api_subclasses ||= Set.new
143
+ @api_subclasses << subclass
144
+
145
+ unless name.nil?
146
+ @api_subclasses_hash ||= {}
147
+ @api_subclasses_hash[name] = subclass
148
+ end
149
+ end
150
+
151
+ @api_attributes = {}
152
+ end
153
+ end
@@ -0,0 +1,47 @@
1
+ module Rainforest
2
+ module HeadersBuilder
3
+
4
+ def self.build(headers)
5
+ headers ||= {}
6
+ default_headers.merge(headers)
7
+ end
8
+
9
+ def self.default_headers
10
+ headers = {
11
+ :user_agent => "Rainforest/#{Rainforest.api_version} RubyBindings/#{Rainforest::VERSION}",
12
+ }
13
+
14
+ begin
15
+ headers.update({
16
+ :x_rainforest_client_user_agent => JSON.generate(user_agent)
17
+ })
18
+ rescue => e
19
+ headers.update({
20
+ :x_rainforest_client_raw_user_agent => user_agent.inspect,
21
+ :error => "#{e} (#{e.class})"
22
+ })
23
+ end
24
+ headers
25
+ end
26
+
27
+ def self.user_agent
28
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
29
+
30
+ {
31
+ :bindings_version => Rainforest::VERSION,
32
+ :lang => 'ruby',
33
+ :lang_version => lang_version,
34
+ :platform => RUBY_PLATFORM,
35
+ :publisher => 'rainforest',
36
+ :uname => get_uname
37
+ }
38
+ end
39
+
40
+ def self.get_uname
41
+ `uname -a 2>/dev/null`.strip if RUBY_PLATFORM =~ /linux|darwin/i
42
+ rescue Errno::ENOMEM => ex # couldn't create subprocess
43
+ "uname lookup failed"
44
+ end
45
+
46
+ end
47
+ end