testdroid-api-client-updated 0.5.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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/.travis.yml +11 -0
  4. data/.whitesource +15 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +104 -0
  7. data/LICENSE +20 -0
  8. data/README.md +82 -0
  9. data/Rakefile +9 -0
  10. data/lib/testdroid-api-client.rb +36 -0
  11. data/lib/testdroid-api-filter-builder.rb +145 -0
  12. data/lib/testdroid_api/admin.rb +25 -0
  13. data/lib/testdroid_api/admin_device_models.rb +10 -0
  14. data/lib/testdroid_api/admin_device_problems.rb +9 -0
  15. data/lib/testdroid_api/admin_device_statuses.rb +12 -0
  16. data/lib/testdroid_api/admin_devices.rb +10 -0
  17. data/lib/testdroid_api/apikey_client.rb +120 -0
  18. data/lib/testdroid_api/client.rb +161 -0
  19. data/lib/testdroid_api/cloud_list_resource.rb +82 -0
  20. data/lib/testdroid_api/cloud_resource.rb +89 -0
  21. data/lib/testdroid_api/device_groups.rb +12 -0
  22. data/lib/testdroid_api/device_session_connections.rb +10 -0
  23. data/lib/testdroid_api/device_sessions.rb +23 -0
  24. data/lib/testdroid_api/devices.rb +10 -0
  25. data/lib/testdroid_api/files.rb +62 -0
  26. data/lib/testdroid_api/frameworks.rb +11 -0
  27. data/lib/testdroid_api/label_groups.rb +12 -0
  28. data/lib/testdroid_api/labels.rb +12 -0
  29. data/lib/testdroid_api/projects.rb +14 -0
  30. data/lib/testdroid_api/properties.rb +13 -0
  31. data/lib/testdroid_api/runs.rb +24 -0
  32. data/lib/testdroid_api/services.rb +18 -0
  33. data/lib/testdroid_api/user.rb +13 -0
  34. data/sample/BitbarSampleApp.apk +0 -0
  35. data/sample/BitbarSampleAppTest.apk +0 -0
  36. data/sample/sample.rb +39 -0
  37. data/spec/client_spec.rb +10 -0
  38. data/spec/device_groups_spec.rb +41 -0
  39. data/spec/files_spec.rb +53 -0
  40. data/spec/fixtures/apk.apk +0 -0
  41. data/spec/fixtures/cassettes/device_groups_authorize.json +1 -0
  42. data/spec/fixtures/cassettes/device_groups_create.json +1 -0
  43. data/spec/fixtures/cassettes/device_groups_delete.json +1 -0
  44. data/spec/fixtures/cassettes/device_groups_get_all.json +1 -0
  45. data/spec/fixtures/cassettes/device_groups_get_one.json +1 -0
  46. data/spec/fixtures/cassettes/files_authorize.json +1 -0
  47. data/spec/fixtures/cassettes/files_delete.json +1 -0
  48. data/spec/fixtures/cassettes/files_get_all.json +1 -0
  49. data/spec/fixtures/cassettes/files_get_one.json +1 -0
  50. data/spec/fixtures/cassettes/files_upload.json +1 -0
  51. data/spec/fixtures/cassettes/files_upload_with_wait.json +1 -0
  52. data/spec/fixtures/cassettes/label_groups_authorize.json +1 -0
  53. data/spec/fixtures/cassettes/label_groups_create.json +1 -0
  54. data/spec/fixtures/cassettes/label_groups_delete.json +1 -0
  55. data/spec/fixtures/cassettes/label_groups_get_all.json +1 -0
  56. data/spec/fixtures/cassettes/label_groups_get_labels.json +1 -0
  57. data/spec/fixtures/cassettes/label_groups_get_one.json +1 -0
  58. data/spec/fixtures/cassettes/projects_authorize.json +1 -0
  59. data/spec/fixtures/cassettes/projects_create.json +1 -0
  60. data/spec/fixtures/cassettes/projects_delete.json +1 -0
  61. data/spec/fixtures/cassettes/projects_get_all.json +1 -0
  62. data/spec/fixtures/cassettes/projects_get_one.json +1 -0
  63. data/spec/fixtures/cassettes/runs_abort_run.json +1 -0
  64. data/spec/fixtures/cassettes/runs_authorize.json +1 -0
  65. data/spec/fixtures/cassettes/runs_get_project_runs.json +1 -0
  66. data/spec/fixtures/cassettes/runs_start_run.json +1 -0
  67. data/spec/fixtures/data.txt +1 -0
  68. data/spec/fixtures/data2.txt +1 -0
  69. data/spec/label_groups_spec.rb +47 -0
  70. data/spec/projects_spec.rb +41 -0
  71. data/spec/runs_spec.rb +41 -0
  72. data/spec/spec_helper.rb +33 -0
  73. data/testdroid-api-client.gemspec +38 -0
  74. metadata +306 -0
@@ -0,0 +1,25 @@
1
+ module TestdroidAPI
2
+ class Admin < CloudResource
3
+ def initialize(uri, client, params = {})
4
+ super uri, client, "admin", params
5
+ @uri, @client = uri, client
6
+ end
7
+
8
+ def device_statuses
9
+ AdminDeviceStatuses.new(@uri + "/device/statuses", @client)
10
+ end
11
+
12
+ def device_problems
13
+ AdminDeviceProblems.new(@uri + "/device-problems", @client)
14
+ end
15
+
16
+ def device_models
17
+ AdminDeviceModels.new(@uri + "/device-models", @client)
18
+ end
19
+
20
+ def devices
21
+ AdminDevices.new(@uri + "/devices", @client)
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ module TestdroidAPI
2
+ class AdminDeviceModels < CloudListResource
3
+ end
4
+ class AdminDeviceModel < CloudResource
5
+ def initialize(uri, client, params = {})
6
+ super uri, client, "adminDeviceModel", params
7
+ @uri, @client = uri, client
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ module TestdroidAPI
2
+ class AdminDeviceProblems < CloudListResource
3
+ end
4
+ class AdminDeviceProblem < CloudResource
5
+ def initialize(uri, client, params = {})
6
+ super uri, client, "adminDeviceProblem", params
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module TestdroidAPI
2
+ class AdminDeviceStatuses < CloudListResource
3
+ def initialize(uri, client)
4
+ super uri, client, "AdminDeviceStatus"
5
+ end
6
+ end
7
+ class AdminDeviceStatus < CloudResource
8
+ def initialize(uri, client, params = {})
9
+ super uri, client, "adminDeviceStatus", params
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module TestdroidAPI
2
+ class AdminDevices < CloudListResource
3
+ end
4
+ class AdminDevice < CloudResource
5
+ def initialize(uri, client, params = {})
6
+ super uri, client, "adminDevice", params
7
+ @uri, @client = uri, client
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,120 @@
1
+ module TestdroidAPI
2
+ class ApikeyClient < Client
3
+
4
+ attr_reader :config
5
+ attr_accessor :logger
6
+
7
+ API_VERSION = 'api/v2'
8
+ CLOUD_ENDPOINT = 'https://cloud.bitbar.com'
9
+
10
+ def initialize(api_key, cloud_url = CLOUD_ENDPOINT, logger = nil)
11
+ # Instance variables
12
+ @api_key = api_key
13
+ @cloud_url = cloud_url
14
+ @logger = logger
15
+
16
+ if @logger.nil?
17
+ @logger = Logger.new(STDOUT)
18
+ @logger.info("Logger is not defined => output to STDOUT")
19
+ end
20
+ end
21
+
22
+ def authorize
23
+
24
+ if @cloud_user.nil?
25
+ @cloud_user = TestdroidAPI::User.new("/#{API_VERSION}/me", self).refresh
26
+ @cloud_user = TestdroidAPI::User.new("/#{API_VERSION}/users/#{@cloud_user.id}", self).refresh
27
+
28
+ end
29
+ @cloud_user
30
+ end
31
+
32
+ # Basic methods
33
+
34
+ def request_factory(method, uri, http_params = {}, headers = {})
35
+ default_http_params = {
36
+ :method => method,
37
+ :url => uri,
38
+ :user => @api_key,
39
+ :password => "",
40
+ :headers => headers
41
+ }
42
+ request_http_params = default_http_params.deep_merge!(http_params)
43
+
44
+ RestClient::Request.new(request_http_params)
45
+ end
46
+
47
+ def get(uri, params = {}, http_params = {})
48
+ uri = @cloud_url + uri
49
+ begin
50
+ http_params = http_params.deep_merge({:headers => {:params => params}})
51
+ request = self.request_factory(:get, uri, http_params)
52
+ resp = request.execute
53
+ rescue => e
54
+ @logger.error "Failed to get resource #{uri} #{e}"
55
+ return nil
56
+ end
57
+ JSON.parse(resp.body)
58
+ end
59
+
60
+ def post(uri, params = {}, http_params = {})
61
+ uri = @cloud_url + uri
62
+ begin
63
+ payload = params.fetch(:body, params)
64
+ headers = params.fetch(:headers, {})
65
+ http_params = http_params.deep_merge({:payload => payload})
66
+ request = self.request_factory(:post, uri, http_params, headers)
67
+ resp = request.execute
68
+ rescue => e
69
+ @logger.error "Failed to post resource #{uri} #{e}"
70
+ return nil
71
+ end
72
+
73
+ if resp.body.nil? || resp.body.length == 0
74
+ return nil
75
+ end
76
+
77
+ JSON.parse(resp.body)
78
+ end
79
+
80
+ def delete(uri)
81
+ uri = @cloud_url + uri
82
+ begin
83
+ request = self.request_factory(:delete, uri)
84
+ resp = request.execute
85
+ rescue => e
86
+ @logger.error "Failed to delete resource #{uri} #{e}"
87
+ return nil
88
+ end
89
+
90
+ if resp.code != 204
91
+ @logger.error "Failed to delete resource #{uri} #{e}"
92
+ else
93
+ @logger.info "response: #{resp.code}"
94
+ end
95
+ end
96
+
97
+ def upload(uri, file_name)
98
+ self.post(uri, {
99
+ :multipart => true,
100
+ :file => ::File.new(file_name, 'rb')
101
+ })
102
+ end
103
+
104
+ def download(uri, file_name, params = {}, http_params = {})
105
+ begin
106
+ ::File.open(file_name, "w+b") do |file|
107
+ full_uri = uri.start_with?(@cloud_url) ? uri : @cloud_url + uri
108
+ http_params = http_params.deep_merge({:headers => {:params => params}})
109
+ request = self.request_factory(:get, full_uri, http_params)
110
+ resp = request.execute
111
+ file.write(resp.body)
112
+ end
113
+ rescue => e
114
+ @logger.error "Failed to get resource #{uri} #{e}"
115
+ return nil
116
+ end
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,161 @@
1
+ module TestdroidAPI
2
+
3
+ class Client
4
+ attr_reader :config
5
+ attr_accessor :logger
6
+ attr_reader :token
7
+
8
+ API_VERSION = 'api/v2'
9
+ CLOUD_ENDPOINT = 'https://cloud.bitbar.com'
10
+
11
+ def initialize(username, password, cloud_url = CLOUD_ENDPOINT, logger = nil)
12
+ # Instance variables
13
+ @username = username
14
+ @password = password
15
+ @cloud_url = cloud_url
16
+ @logger = logger
17
+
18
+ if @logger.nil?
19
+ @logger = Logger.new(STDOUT)
20
+ @logger.info("Logger is not defined => output to STDOUT")
21
+ end
22
+ end
23
+
24
+ def authorize
25
+ @client = OAuth2::Client.new(
26
+ 'testdroid-cloud-api', nil, :site => @cloud_url, :token_url => 'oauth/token') do |faraday|
27
+ faraday.request :multipart
28
+ faraday.request :url_encoded
29
+ faraday.response :logger, @logger
30
+ faraday.adapter Faraday.default_adapter
31
+ end
32
+
33
+ @token = @client.password.get_token(@username, @password)
34
+
35
+ if @cloud_user.nil?
36
+ @cloud_user = TestdroidAPI::User.new("/#{API_VERSION}/me", self).refresh
37
+ @cloud_user = TestdroidAPI::User.new("/#{API_VERSION}/users/#{@cloud_user.id}", self).refresh
38
+
39
+ end
40
+ @cloud_user
41
+ end
42
+
43
+ def mime_for(path)
44
+ mime = MIME::Types.type_for path
45
+ mime.empty? ? 'text/plain' : mime[0].content_type
46
+ end
47
+
48
+ def upload(uri, filename)
49
+ begin
50
+ @token = @token.refresh! if @token.expired?
51
+ connection = @token.client.connection
52
+ payload = {:file => Faraday::UploadIO.new(filename, mime_for(filename))}
53
+ response = connection.post(@cloud_url + "#{uri}", payload, @token.headers)
54
+ rescue => e
55
+ @logger.error e
56
+ return nil
57
+ end
58
+ JSON.parse(response.body)
59
+ end
60
+
61
+ def post(uri, params = {})
62
+
63
+ @token = @token.refresh! if @token.expired?
64
+
65
+ begin
66
+ resp = @token.post("#{@cloud_url}#{uri}", params)
67
+ rescue => e
68
+ @logger.error "Failed to post resource #{uri} #{e}"
69
+ return nil
70
+ end
71
+
72
+ if resp.body.nil? || resp.body.length == 0
73
+ return nil
74
+ end
75
+
76
+ JSON.parse(resp.body)
77
+ end
78
+
79
+ def get(uri, params = {})
80
+
81
+ @token = @token.refresh! if @token.expired?
82
+
83
+ begin
84
+ resp = @token.get(@cloud_url + "#{uri}", :params => params)
85
+ rescue => e
86
+ @logger.error "Failed to get resource #{uri} #{e}"
87
+ return nil
88
+ end
89
+ JSON.parse(resp.body)
90
+ end
91
+
92
+ def delete(uri)
93
+
94
+ @token = @token.refresh! if @token.expired?
95
+
96
+ begin
97
+ resp = @token.delete(@cloud_url + "#{uri}")
98
+ rescue => e
99
+ @logger.error "Failed to delete resource #{uri} #{e}"
100
+ return nil
101
+ end
102
+
103
+ if resp.status != 204
104
+ @logger.error "Failed to delete resource #{uri} #{e}"
105
+ else
106
+ @logger.info "response: #{resp.status}"
107
+ end
108
+ end
109
+
110
+ def download(uri, file_name)
111
+ begin
112
+ @token = @token.refresh! if @token.expired?
113
+ ::File.open(file_name, "w+b") do |file|
114
+ full_uri = uri.start_with?(@cloud_url) ? uri : @cloud_url + uri
115
+ resp = @token.get(full_uri)
116
+ file.write(resp.body)
117
+ end
118
+ rescue => e
119
+ @logger.error "Failed to get resource #{uri} #{e}"
120
+ return nil
121
+ end
122
+ end
123
+
124
+ # Resources
125
+
126
+ # public read-only
127
+
128
+ def info
129
+ TestdroidAPI::CloudResource.new("/#{API_VERSION}/info", self, "info")
130
+ end
131
+
132
+ def devices
133
+ TestdroidAPI::Devices.new("/#{API_VERSION}/devices", self)
134
+ end
135
+
136
+ def label_groups
137
+ TestdroidAPI::LabelGroups.new("/#{API_VERSION}/label-groups", self)
138
+ end
139
+
140
+ # user read-write
141
+
142
+ def me
143
+ TestdroidAPI::User.new("/#{API_VERSION}/me", self).load
144
+ end
145
+
146
+ def properties
147
+ TestdroidAPI::Properties.new("/#{API_VERSION}/properties", self)
148
+ end
149
+
150
+ def device_session_connections
151
+ TestdroidAPI::DeviceSessionConnections.new("/#{API_VERSION}/device-session-connections", self)
152
+ end
153
+
154
+ # admin only
155
+
156
+ def admin
157
+ TestdroidAPI::Admin.new("/#{API_VERSION}/admin", self)
158
+ end
159
+
160
+ end
161
+ end
@@ -0,0 +1,82 @@
1
+ module TestdroidAPI
2
+ class CloudListResource
3
+
4
+ def initialize(uri, client, instance_class = nil)
5
+ @uri, @client = uri, client
6
+ resource_name = self.class.name.split('::')[-1]
7
+ @instance_class = TestdroidAPI.const_get(instance_class.nil? ? resource_name.chop : instance_class)
8
+ @list_key, @instance_id_key = resource_name.gsub!(/\b\w/) {$&.downcase}, 'id'
9
+ end
10
+
11
+ def get(resource_id)
12
+ @instance_class.new("#{@uri}/#{resource_id}", @client)
13
+ end
14
+
15
+ def total
16
+ @client.get(@uri)['total']
17
+ end
18
+
19
+ def create(params = {})
20
+ raise "Can't create a resource without a REST Client" unless @client
21
+ response = @client.post @uri, params
22
+ @instance_class.new "#{@uri}/#{response[@instance_id_key]}", @client, response
23
+ end
24
+
25
+ def list_all()
26
+ raise "Can't get a resource list without a REST Client" unless @client
27
+
28
+ response = @client.get(@uri, {:limit => 0})
29
+
30
+ class_list = []
31
+ if response['data'].is_a?(Array)
32
+ instance_uri = @uri
33
+ response['data'].each do |val|
34
+ class_list << @instance_class.new("#{instance_uri}/#{val[@instance_id_key]}", @client, val)
35
+ end
36
+ end
37
+ class_list
38
+ end
39
+
40
+ def list(params = {}, full_uri = false)
41
+ raise "Can't get a resource list without a REST Client" unless @client
42
+ @uri = full_uri ? @uri.split(@client.instance_variable_get(:@cloud_url))[1] : @uri
43
+ response = @client.get(@uri, params)
44
+ class_list = []
45
+ if response['data'].is_a?(Array)
46
+ client = @client
47
+ list_class = self.class
48
+ instance_uri = full_uri ? @uri.split('?')[0] : @uri
49
+ response['data'].each do |val|
50
+
51
+ class_list << @instance_class.new("#{instance_uri}/#{val[@instance_id_key]}", @client, val)
52
+ end
53
+ class_list.instance_eval do
54
+ eigenclass = class << self;
55
+ self;
56
+ end
57
+
58
+ eigenclass.send :define_method, :offset, &lambda {response['offset']}
59
+ eigenclass.send :define_method, :limit, &lambda {response['limit']}
60
+ eigenclass.send :define_method, :total, &lambda {response['total']}
61
+ eigenclass.send :define_method, :next_page, &lambda {
62
+ if response['next']
63
+
64
+ list_class.new(response['next'], client).list({}, true)
65
+ else
66
+ []
67
+ end
68
+ }
69
+ eigenclass.send :define_method, :previous_page, &lambda {
70
+ if response['previous']
71
+
72
+ list_class.new(response['previous'], client).list({}, true)
73
+ else
74
+ []
75
+ end
76
+ }
77
+ end
78
+ end
79
+ class_list
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,89 @@
1
+ module TestdroidAPI
2
+ class CloudResource
3
+ def initialize(uri, client, resource_name = nil, params = {})
4
+
5
+ @uri, @client, @resource_name = uri, client, resource_name
6
+ set_up_properties_from(params)
7
+ end
8
+
9
+ def inspect # :nodoc:
10
+ "<#{self.class} @uri=#{@uri}>"
11
+ end
12
+
13
+ def sub_items(*items)
14
+ items.each do |item|
15
+ resource = camel_case_it item
16
+ uri = "#{@uri}/#{item.to_s.gsub('_', '-') }"
17
+ resource_class = TestdroidAPI.const_get resource
18
+ new_class = resource_class.new(uri, @client)
19
+ instance_variable_set("@#{item}", new_class)
20
+ end
21
+ self.class.instance_eval {attr_reader *items}
22
+ end
23
+
24
+ def method_missing(method, *args)
25
+ super if @updated
26
+ set_up_properties_from(@client.get(@uri))
27
+ self.send method, *args
28
+ end
29
+
30
+ def update(params = {})
31
+ raise "Can't update a resource without a REST Client" unless @client
32
+ set_up_properties_from(@client.post(@uri, params))
33
+ self
34
+ end
35
+
36
+ def set_up_properties_from(hash)
37
+
38
+ eigenclass = class << self;
39
+ self;
40
+ end
41
+ hash.each do |p, v|
42
+ property = snake_case_it p
43
+
44
+ unless ['uri', 'client', 'updated'].include? property
45
+ eigenclass.send :define_method, property.to_sym, &lambda {v}
46
+ end
47
+ end
48
+ @updated = !hash.keys.empty?
49
+ end
50
+
51
+ def refresh
52
+ raise "Can't refresh a resource without a REST Client" unless @client
53
+ @updated = false
54
+ set_up_properties_from(@client.get(@uri))
55
+ self
56
+ end
57
+
58
+ alias_method :load, :refresh
59
+
60
+ def download_file(file_resource_name, target_file_name = nil)
61
+ raise "Can't refresh a resource without a REST Client" unless @client
62
+ target_file_name = file_resource_name unless target_file_name
63
+ @client.download("#{@uri}/#{file_resource_name}", target_file_name)
64
+ end
65
+
66
+ def camel_case_it(something)
67
+ if something.is_a? Hash
68
+ Hash[*something.to_a.map {|a| [camel_case_it(a[0]).to_sym, a[1]]}.flatten]
69
+ else
70
+ something.to_s.split('_').map do |s|
71
+ [s[0, 1].capitalize, s[1..-1]].join
72
+ end.join
73
+ end
74
+ end
75
+
76
+ def snake_case_it(something)
77
+ if something.is_a? Hash
78
+ Hash[*something.to_a.map {|pair| [snake_case_it(pair[0]).to_sym, pair[1]]}.flatten]
79
+ else
80
+ something.to_s.gsub(/[A-Z][a-z]*/) {|s| "_#{s.downcase}"}.gsub(/^_/, '')
81
+ end
82
+ end
83
+
84
+ def delete
85
+ raise "Can't delete a resource without a REST Client" unless @client
86
+ @client.delete(@uri)
87
+ end
88
+ end
89
+ end