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.
- checksums.yaml +4 -4
- data/.gitignore +3 -6
- data/.travis.yml +13 -1
- data/Gemfile +6 -0
- data/README.md +72 -21
- data/Rakefile +6 -13
- data/VERSION +1 -1
- data/bin/rainforest-console +1 -1
- data/gemfiles/default-with-activesupport.gemfile +8 -1
- data/gemfiles/json.gemfile +10 -2
- data/gemfiles/yajl.gemfile +10 -2
- data/lib/rainforest.rb +61 -240
- data/lib/rainforest/apibits/api_client.rb +28 -0
- data/lib/rainforest/apibits/api_endpoint.rb +11 -0
- data/lib/rainforest/apibits/api_list.rb +88 -0
- data/lib/rainforest/apibits/api_method.rb +95 -0
- data/lib/rainforest/apibits/api_object.rb +52 -0
- data/lib/rainforest/apibits/api_resource.rb +153 -0
- data/lib/rainforest/apibits/headers_builder.rb +47 -0
- data/lib/rainforest/apibits/params_builder.rb +27 -0
- data/lib/rainforest/apibits/path_builder.rb +38 -0
- data/lib/rainforest/apibits/requester.rb +104 -0
- data/lib/rainforest/apibits/util.rb +51 -0
- data/lib/rainforest/clients/default_client.rb +59 -0
- data/lib/rainforest/endpoints/client_stats_endpoint.rb +11 -0
- data/lib/rainforest/endpoints/environment_runs_endpoint.rb +11 -0
- data/lib/rainforest/endpoints/environments_endpoint.rb +48 -0
- data/lib/rainforest/endpoints/generator_rows_endpoint.rb +17 -0
- data/lib/rainforest/endpoints/generators_endpoint.rb +48 -0
- data/lib/rainforest/endpoints/integrations_endpoint.rb +33 -0
- data/lib/rainforest/endpoints/run_tests_endpoint.rb +20 -0
- data/lib/rainforest/endpoints/runs_endpoint.rb +48 -0
- data/lib/rainforest/endpoints/schedules_endpoint.rb +48 -0
- data/lib/rainforest/endpoints/site_environments_endpoint.rb +24 -0
- data/lib/rainforest/endpoints/sites_endpoint.rb +39 -0
- data/lib/rainforest/endpoints/tests_endpoint.rb +57 -0
- data/lib/rainforest/endpoints/users_endpoint.rb +48 -0
- data/lib/rainforest/errors/api_connection_error.rb +1 -1
- data/lib/rainforest/errors/api_error.rb +32 -1
- data/lib/rainforest/errors/authentication_error.rb +1 -1
- data/lib/rainforest/errors/rainforest_error.rb +2 -9
- data/lib/rainforest/resources/client_stats.rb +31 -0
- data/lib/rainforest/resources/environment.rb +73 -0
- data/lib/rainforest/resources/generator.rb +73 -0
- data/lib/rainforest/resources/integration.rb +52 -0
- data/lib/rainforest/resources/run.rb +93 -0
- data/lib/rainforest/resources/schedule.rb +63 -0
- data/lib/rainforest/resources/site.rb +53 -0
- data/lib/rainforest/resources/site_environment.rb +40 -0
- data/lib/rainforest/resources/test.rb +98 -0
- data/lib/rainforest/resources/user.rb +75 -0
- data/rainforest.gemspec +8 -6
- data/test/rainforest/api_client_test.rb +51 -0
- data/test/rainforest/api_endpoint_test.rb +13 -0
- data/test/rainforest/api_list_test.rb +49 -0
- data/test/rainforest/api_method_test.rb +78 -0
- data/test/rainforest/headers_builder_test.rb +28 -0
- data/test/rainforest/params_builder_test.rb +57 -0
- data/test/rainforest/path_builder_test.rb +50 -0
- data/test/rainforest/requester_test.rb +86 -0
- data/test/rainforest/util_test.rb +41 -19
- data/test/test_data.rb +72 -0
- data/test/test_helper.rb +17 -134
- metadata +99 -59
- data/CONTRIBUTORS +0 -2
- data/History.txt +0 -4
- data/LICENSE +0 -21
- data/lib/data/ca-certificates.crt +0 -3918
- data/lib/rainforest/api_operations/create.rb +0 -16
- data/lib/rainforest/api_operations/delete.rb +0 -11
- data/lib/rainforest/api_operations/list.rb +0 -16
- data/lib/rainforest/api_operations/update.rb +0 -61
- data/lib/rainforest/api_resource.rb +0 -33
- data/lib/rainforest/errors/invalid_request_error.rb +0 -10
- data/lib/rainforest/json.rb +0 -21
- data/lib/rainforest/list_object.rb +0 -35
- data/lib/rainforest/rainforest_object.rb +0 -165
- data/lib/rainforest/run.rb +0 -8
- data/lib/rainforest/singleton_api_resource.rb +0 -20
- data/lib/rainforest/util.rb +0 -100
- data/test/rainforest/api_resource_test.rb +0 -11
- data/test/rainforest/auth_test.rb +0 -24
- data/test/rainforest/list_object_test.rb +0 -7
- data/test/rainforest/metadata_test.rb +0 -7
- data/test/rainforest/run_test.rb +0 -36
@@ -0,0 +1,27 @@
|
|
1
|
+
module Rainforest
|
2
|
+
module ParamsBuilder
|
3
|
+
|
4
|
+
def self.clean(params)
|
5
|
+
Util.symbolize_keys(params || {})
|
6
|
+
end
|
7
|
+
|
8
|
+
# Clean the params, and the hash to_merge, and then merge them.
|
9
|
+
# This ensures that we dont get something like { "id" => 123, :id => 321 }.
|
10
|
+
def self.merge(*args)
|
11
|
+
ret = {}
|
12
|
+
args.each do |arg|
|
13
|
+
ret = ret.merge(clean(arg))
|
14
|
+
end
|
15
|
+
ret
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.build(params, api_key=nil, auth_key=nil)
|
19
|
+
default_params.merge(clean(params))
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.default_params
|
23
|
+
params = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Rainforest
|
2
|
+
module PathBuilder
|
3
|
+
|
4
|
+
# Take a path like:
|
5
|
+
# ":path/:id/dogs/:dog_id"
|
6
|
+
# and convert it to:
|
7
|
+
# "#{object.path}/#{object.id}/dogs/#{params[:id]}" => "/objects/1/dogs/2"
|
8
|
+
#
|
9
|
+
# Path priority is:
|
10
|
+
# 1. Object - this will be a class or an instance of a class.
|
11
|
+
# 2. Params - this is a hash of key values. All keys *must* be symbolized.
|
12
|
+
def self.build(path, object, params)
|
13
|
+
ret = path.dup
|
14
|
+
if ret.include?(":")
|
15
|
+
matches = ret.scan(/:([^\/]*)/).flatten.map(&:to_sym)
|
16
|
+
missing = Set.new(matches)
|
17
|
+
|
18
|
+
matches.each do |match|
|
19
|
+
value = determine_value(match, object, params)
|
20
|
+
missing.delete(match) unless value.nil?
|
21
|
+
ret.sub!(match.inspect, "#{value}")
|
22
|
+
end
|
23
|
+
|
24
|
+
if missing.any?
|
25
|
+
raise ArgumentError.new("Could not determine the full URL. The following values of the path are missing: #{missing.to_a.join(', ')}. Try setting them in your params.")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
ret
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.determine_value(match, object, params)
|
32
|
+
value = object.send(match) if object && object.respond_to?(match)
|
33
|
+
value ||= params[match] if params && params.has_key?(match)
|
34
|
+
value
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Rainforest
|
2
|
+
module Requester
|
3
|
+
|
4
|
+
def self.request(method, url, params={}, headers={})
|
5
|
+
method = method.to_sym
|
6
|
+
url, params = prepare_params(method, url, params, headers)
|
7
|
+
request_opts = {
|
8
|
+
:method => method,
|
9
|
+
:url => url,
|
10
|
+
:headers => headers,
|
11
|
+
:payload => params,
|
12
|
+
|
13
|
+
:verify_ssl => false,
|
14
|
+
:open_timeout => 30,
|
15
|
+
:timeout => 60
|
16
|
+
}
|
17
|
+
|
18
|
+
execute_request(request_opts)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.execute_request(opts)
|
22
|
+
RestClient::Request.execute(opts)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.get(url, params, headers)
|
26
|
+
self.request(:get, url, params, headers)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.delete(url, params, headers)
|
30
|
+
self.request(:delete, url, params, headers)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.put(url, params, headers)
|
34
|
+
self.request(:put, url, params, headers)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.post(url, params, headers)
|
38
|
+
self.request(:post, url, params, headers)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.prepare_params(method, url, params={}, headers={})
|
42
|
+
if [:get, :head, :delete].include?(method)
|
43
|
+
unless params.empty?
|
44
|
+
url += URI.parse(url).query ? '&' : '?' + query_string(params)
|
45
|
+
end
|
46
|
+
params = nil
|
47
|
+
else
|
48
|
+
if headers["Content-Type"] == "application/json" || headers[:"Content-Type"] == "application/json"
|
49
|
+
params = JSON.generate(params)
|
50
|
+
else
|
51
|
+
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
52
|
+
if !RestClient::Payload.has_file?(params)
|
53
|
+
params = query_string(params)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
[url, params]
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.query_string(params)
|
61
|
+
params ||= {}
|
62
|
+
if params.any?
|
63
|
+
query_array(params).join('&')
|
64
|
+
else
|
65
|
+
""
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Three major use cases (and nesting of them needs to be supported):
|
70
|
+
# { :a => { :b => "bvalue" } } => ["a[b]=bvalue"]
|
71
|
+
# { :a => [1, 2] } => ["a[]=1", "a[]=2"]
|
72
|
+
# { :a => "value" } => ["a=value"]
|
73
|
+
def self.query_array(params, key_prefix=nil)
|
74
|
+
ret = []
|
75
|
+
params.each do |key, value|
|
76
|
+
if params.is_a?(Array)
|
77
|
+
value = key
|
78
|
+
key = ''
|
79
|
+
end
|
80
|
+
key_suffix = escape(key)
|
81
|
+
full_key = key_prefix ? "#{key_prefix}[#{key_suffix}]" : key_suffix
|
82
|
+
|
83
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
84
|
+
# Handles the following cases:
|
85
|
+
# { :a => { :b => "bvalue" } } => ["a[b]=bvalue"]
|
86
|
+
# { :a => [1, 2] } => ["a[]=1", "a[]=2"]
|
87
|
+
ret += query_array(value, full_key)
|
88
|
+
elsif value.is_a?(ApiObject)
|
89
|
+
ret += query_array(value.json, full_key)
|
90
|
+
else
|
91
|
+
# Handles the base case with just key and value:
|
92
|
+
# { :a => "value" } => ["a=value"]
|
93
|
+
ret << "#{full_key}=#{escape(value)}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
ret
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.escape(val)
|
100
|
+
URI.escape(val.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Rainforest
|
2
|
+
module Util
|
3
|
+
|
4
|
+
def self.symbolize_keys(obj)
|
5
|
+
if obj.is_a?(Hash)
|
6
|
+
ret = {}
|
7
|
+
obj.each do |key, value|
|
8
|
+
ret[(key.to_sym rescue key) || key] = symbolize_keys(value)
|
9
|
+
end
|
10
|
+
return ret
|
11
|
+
elsif obj.is_a?(Array)
|
12
|
+
return obj.map{ |value| symbolize_keys(value) }
|
13
|
+
else
|
14
|
+
return obj
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.sorta_deep_clone(json)
|
19
|
+
if json.is_a?(Hash)
|
20
|
+
ret = {}
|
21
|
+
json.each do |k, v|
|
22
|
+
ret[k] = sorta_deep_clone(v)
|
23
|
+
end
|
24
|
+
ret
|
25
|
+
elsif json.is_a?(Array)
|
26
|
+
json.map{ |j| sorta_deep_clone(j) }
|
27
|
+
else
|
28
|
+
begin
|
29
|
+
json.dup
|
30
|
+
rescue
|
31
|
+
json
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.constantize(str, prefix=false)
|
37
|
+
str = str.to_s
|
38
|
+
begin
|
39
|
+
str.split('::').reduce(Rainforest, :const_get)
|
40
|
+
rescue NameError => e
|
41
|
+
if prefix
|
42
|
+
raise e
|
43
|
+
else
|
44
|
+
p = "#{self.name}".split("::").first
|
45
|
+
constantize("#{p}::#{str}", true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Rainforest
|
2
|
+
class DefaultClient < ApiClient
|
3
|
+
|
4
|
+
def initialize(api_key)
|
5
|
+
self.refresh_from(api_key)
|
6
|
+
end
|
7
|
+
|
8
|
+
def refresh_from(api_key)
|
9
|
+
headers = {
|
10
|
+
:Accept => "application/json",
|
11
|
+
:"Content-Type" => "application/x-www-form-urlencoded",
|
12
|
+
:CLIENT_TOKEN => api_key,
|
13
|
+
}
|
14
|
+
params = {}
|
15
|
+
super(headers, params)
|
16
|
+
end
|
17
|
+
|
18
|
+
def client_stats
|
19
|
+
@client_stats ||= ClientStatsEndpoint.new(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
def environments
|
23
|
+
@environments ||= EnvironmentsEndpoint.new(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def generators
|
27
|
+
@generators ||= GeneratorsEndpoint.new(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def integrations
|
31
|
+
@integrations ||= IntegrationsEndpoint.new(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def runs
|
35
|
+
@runs ||= RunsEndpoint.new(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
def schedules
|
39
|
+
@schedules ||= SchedulesEndpoint.new(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def site_environments
|
43
|
+
@site_environments ||= SiteEnvironmentsEndpoint.new(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def sites
|
47
|
+
@sites ||= SitesEndpoint.new(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def tests
|
51
|
+
@tests ||= TestsEndpoint.new(self)
|
52
|
+
end
|
53
|
+
|
54
|
+
def users
|
55
|
+
@users ||= UsersEndpoint.new(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Rainforest
|
2
|
+
class ClientStatsEndpoint < ApiEndpoint
|
3
|
+
|
4
|
+
def retrieve(params={}, headers={})
|
5
|
+
method = ApiMethod.new(:get, "/clients/stats", params, headers, @parent)
|
6
|
+
json = @client.execute(method)
|
7
|
+
ClientStats.new(json, method)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Rainforest
|
2
|
+
class EnvironmentRunsEndpoint < ApiEndpoint
|
3
|
+
|
4
|
+
def all(params={}, headers={})
|
5
|
+
method = ApiMethod.new(:get, "/environments/:id/runs", params, headers, @parent)
|
6
|
+
json = @client.execute(method)
|
7
|
+
ApiList.new(:Run, json, method)
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Rainforest
|
2
|
+
class EnvironmentsEndpoint < ApiEndpoint
|
3
|
+
|
4
|
+
def new(id)
|
5
|
+
Environment.new({:id => id}, nil, @client)
|
6
|
+
end
|
7
|
+
|
8
|
+
def all(params={}, headers={})
|
9
|
+
method = ApiMethod.new(:get, "/environments", params, headers, @parent)
|
10
|
+
json = @client.execute(method)
|
11
|
+
ApiList.new(:Environment, json, method)
|
12
|
+
end
|
13
|
+
|
14
|
+
def retrieve(environment_id, params={}, headers={})
|
15
|
+
params = ParamsBuilder.merge({
|
16
|
+
:environment_id => environment_id,
|
17
|
+
}, params)
|
18
|
+
method = ApiMethod.new(:get, "/environments/:environment_id", params, headers, @parent)
|
19
|
+
json = @client.execute(method)
|
20
|
+
Environment.new(json, method)
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(environment_id, params={}, headers={})
|
24
|
+
params = ParamsBuilder.merge({
|
25
|
+
:environment_id => environment_id,
|
26
|
+
}, params)
|
27
|
+
method = ApiMethod.new(:delete, "/environments/:environment_id", params, headers, @parent)
|
28
|
+
json = @client.execute(method)
|
29
|
+
json
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(environment_id, params={}, headers={})
|
33
|
+
params = ParamsBuilder.merge({
|
34
|
+
:environment_id => environment_id,
|
35
|
+
}, params)
|
36
|
+
method = ApiMethod.new(:put, "/environments/:environment_id", params, headers, @parent)
|
37
|
+
json = @client.execute(method)
|
38
|
+
Environment.new(json, method)
|
39
|
+
end
|
40
|
+
|
41
|
+
def create(params={}, headers={})
|
42
|
+
method = ApiMethod.new(:post, "/environments", params, headers, @parent)
|
43
|
+
json = @client.execute(method)
|
44
|
+
Environment.new(json, method)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rainforest
|
2
|
+
class GeneratorRowsEndpoint < ApiEndpoint
|
3
|
+
|
4
|
+
def all(params={}, headers={})
|
5
|
+
method = ApiMethod.new(:get, "/generators/:id/rows", params, headers, @parent)
|
6
|
+
json = @client.execute(method)
|
7
|
+
json
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(params={}, headers={})
|
11
|
+
method = ApiMethod.new(:post, "/generators/:id/rows", params, headers, @parent)
|
12
|
+
json = @client.execute(method)
|
13
|
+
json
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Rainforest
|
2
|
+
class GeneratorsEndpoint < ApiEndpoint
|
3
|
+
|
4
|
+
def new(id)
|
5
|
+
Generator.new({:id => id}, nil, @client)
|
6
|
+
end
|
7
|
+
|
8
|
+
def all(params={}, headers={})
|
9
|
+
method = ApiMethod.new(:get, "/generators", params, headers, @parent)
|
10
|
+
json = @client.execute(method)
|
11
|
+
ApiList.new(:Generator, json, method)
|
12
|
+
end
|
13
|
+
|
14
|
+
def retrieve(generator_id, params={}, headers={})
|
15
|
+
params = ParamsBuilder.merge({
|
16
|
+
:generator_id => generator_id,
|
17
|
+
}, params)
|
18
|
+
method = ApiMethod.new(:get, "/generators/:generator_id", params, headers, @parent)
|
19
|
+
json = @client.execute(method)
|
20
|
+
Generator.new(json, method)
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(generator_id, params={}, headers={})
|
24
|
+
params = ParamsBuilder.merge({
|
25
|
+
:generator_id => generator_id,
|
26
|
+
}, params)
|
27
|
+
method = ApiMethod.new(:delete, "/generators/:generator_id", params, headers, @parent)
|
28
|
+
json = @client.execute(method)
|
29
|
+
Generator.new(json, method)
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(generator_id, params={}, headers={})
|
33
|
+
params = ParamsBuilder.merge({
|
34
|
+
:generator_id => generator_id,
|
35
|
+
}, params)
|
36
|
+
method = ApiMethod.new(:put, "/generators/:generator_id", params, headers, @parent)
|
37
|
+
json = @client.execute(method)
|
38
|
+
Generator.new(json, method)
|
39
|
+
end
|
40
|
+
|
41
|
+
def create(params={}, headers={})
|
42
|
+
method = ApiMethod.new(:post, "/generators", params, headers, @parent)
|
43
|
+
json = @client.execute(method)
|
44
|
+
Generator.new(json, method)
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rainforest
|
2
|
+
class IntegrationsEndpoint < ApiEndpoint
|
3
|
+
|
4
|
+
def new(id)
|
5
|
+
Integration.new({:id => id}, nil, @client)
|
6
|
+
end
|
7
|
+
|
8
|
+
def all(params={}, headers={})
|
9
|
+
method = ApiMethod.new(:get, "/integrations", params, headers, @parent)
|
10
|
+
json = @client.execute(method)
|
11
|
+
ApiList.new(:Integration, json, method)
|
12
|
+
end
|
13
|
+
|
14
|
+
def retrieve(integration_id, params={}, headers={})
|
15
|
+
params = ParamsBuilder.merge({
|
16
|
+
:integration_id => integration_id,
|
17
|
+
}, params)
|
18
|
+
method = ApiMethod.new(:get, "/integrations/:integration_id", params, headers, @parent)
|
19
|
+
json = @client.execute(method)
|
20
|
+
Integration.new(json, method)
|
21
|
+
end
|
22
|
+
|
23
|
+
def update(integration_id, params={}, headers={})
|
24
|
+
params = ParamsBuilder.merge({
|
25
|
+
:integration_id => integration_id,
|
26
|
+
}, params)
|
27
|
+
method = ApiMethod.new(:put, "/integrations/:integration_id", params, headers, @parent)
|
28
|
+
json = @client.execute(method)
|
29
|
+
Integration.new(json, method)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|