code42 0.1.2

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 (80) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +203 -0
  5. data/README.md +182 -0
  6. data/Rakefile +1 -0
  7. data/code42.gemspec +26 -0
  8. data/examples/Gemfile +6 -0
  9. data/examples/computers.rb +88 -0
  10. data/examples/diagnostic.rb +82 -0
  11. data/examples/orgs.rb +88 -0
  12. data/examples/parser.rb +51 -0
  13. data/examples/parser_test.rb +64 -0
  14. data/examples/users.rb +88 -0
  15. data/lib/code42.rb +46 -0
  16. data/lib/code42/attribute.rb +13 -0
  17. data/lib/code42/attribute_serializer.rb +129 -0
  18. data/lib/code42/client.rb +301 -0
  19. data/lib/code42/computer.rb +15 -0
  20. data/lib/code42/connection.rb +124 -0
  21. data/lib/code42/diagnostic.rb +8 -0
  22. data/lib/code42/error.rb +21 -0
  23. data/lib/code42/org.rb +38 -0
  24. data/lib/code42/ping.rb +14 -0
  25. data/lib/code42/resource.rb +58 -0
  26. data/lib/code42/role.rb +26 -0
  27. data/lib/code42/role_collection.rb +35 -0
  28. data/lib/code42/settings.rb +42 -0
  29. data/lib/code42/token.rb +31 -0
  30. data/lib/code42/token_validation.rb +10 -0
  31. data/lib/code42/user.rb +40 -0
  32. data/lib/code42/version.rb +3 -0
  33. data/spec/cassettes/Code42_Client/_create_org/returns_created_org.yml +47 -0
  34. data/spec/cassettes/Code42_Client/_create_user/returns_created_user.yml +44 -0
  35. data/spec/cassettes/Code42_Client/_create_user/when_sending_an_invalid_email/raises_an_exception.yml +37 -0
  36. data/spec/cassettes/Code42_Client/_find_org_by_name/returns_the_org_with_the_specified_name.yml +101 -0
  37. data/spec/cassettes/Code42_Client/_get_token/returns_valid_tokens.yml +38 -0
  38. data/spec/cassettes/Code42_Client/_get_token/when_providing_invalid_credentials/should_raise_an_exception.yml +37 -0
  39. data/spec/cassettes/Code42_Client/_org/when_ID_is_not_passed/returns_my_org.yml +54 -0
  40. data/spec/cassettes/Code42_Client/_org/when_ID_is_passed_in/returns_a_specific_org.yml +54 -0
  41. data/spec/cassettes/Code42_Client/_ping/returns_a_ping.yml +48 -0
  42. data/spec/cassettes/Code42_Client/_user/when_ID_is_not_passed/returns_my_user.yml +53 -0
  43. data/spec/cassettes/Code42_Client/_user/when_ID_is_passed_in/returns_a_specific_user.yml +53 -0
  44. data/spec/cassettes/Code42_Client/_user_roles/returns_an_enumerable.yml +50 -0
  45. data/spec/cassettes/Code42_Client/_validate_token/returns_a_valid_response.yml +83 -0
  46. data/spec/cassettes/Crashplan_Client/_create_org/returns_created_org.yml +47 -0
  47. data/spec/cassettes/Crashplan_Client/_create_user/returns_created_user.yml +44 -0
  48. data/spec/cassettes/Crashplan_Client/_create_user/when_sending_an_invalid_email/raises_an_exception.yml +37 -0
  49. data/spec/cassettes/Crashplan_Client/_find_org_by_name/returns_the_org_with_the_specified_name.yml +55 -0
  50. data/spec/cassettes/Crashplan_Client/_get_token/returns_valid_tokens.yml +38 -0
  51. data/spec/cassettes/Crashplan_Client/_get_token/when_providing_invalid_credentials/should_raise_an_exception.yml +37 -0
  52. data/spec/cassettes/Crashplan_Client/_org/when_ID_is_not_passed/returns_my_org.yml +54 -0
  53. data/spec/cassettes/Crashplan_Client/_org/when_ID_is_passed_in/returns_a_specific_org.yml +54 -0
  54. data/spec/cassettes/Crashplan_Client/_ping/returns_a_ping.yml +48 -0
  55. data/spec/cassettes/Crashplan_Client/_user/when_ID_is_not_passed/returns_my_user.yml +99 -0
  56. data/spec/cassettes/Crashplan_Client/_user/when_ID_is_passed_in/returns_a_specific_user.yml +53 -0
  57. data/spec/cassettes/Crashplan_Client/_user_roles/returns_an_enumerable.yml +50 -0
  58. data/spec/cassettes/Crashplan_Client/_validate_token/returns_a_valid_response.yml +128 -0
  59. data/spec/crashplan/client_spec.rb +135 -0
  60. data/spec/crashplan/connection_spec.rb +45 -0
  61. data/spec/crashplan/org_spec.rb +63 -0
  62. data/spec/crashplan/ping_spec.rb +14 -0
  63. data/spec/crashplan/resource_spec.rb +56 -0
  64. data/spec/crashplan/role_spec.rb +28 -0
  65. data/spec/crashplan/settings_spec.rb +118 -0
  66. data/spec/crashplan/token_spec.rb +33 -0
  67. data/spec/crashplan/user_spec.rb +21 -0
  68. data/spec/fixtures/auth/bad_password.json +1 -0
  69. data/spec/fixtures/authToken.json +10 -0
  70. data/spec/fixtures/org.1.json +36 -0
  71. data/spec/fixtures/org.create.json +36 -0
  72. data/spec/fixtures/org.my.json +36 -0
  73. data/spec/fixtures/ping.json +7 -0
  74. data/spec/fixtures/user.1.json +27 -0
  75. data/spec/fixtures/user.create.json +27 -0
  76. data/spec/fixtures/user.my.json +27 -0
  77. data/spec/fixtures/user_roles.json +32 -0
  78. data/spec/fixtures/validate_token.json +10 -0
  79. data/spec/spec_helper.rb +67 -0
  80. metadata +268 -0
@@ -0,0 +1,46 @@
1
+ require "date"
2
+ require "active_support/core_ext/array/extract_options"
3
+ require "active_support/core_ext/hash/keys"
4
+ require "active_support/core_ext/string/inflections"
5
+ require "active_support/core_ext/object/blank"
6
+ require "active_support/core_ext/object/try"
7
+ require "code42/version"
8
+ require "code42/client"
9
+ require "code42/settings"
10
+ require "code42/error"
11
+ require "code42/connection"
12
+ require "code42/attribute"
13
+ require "code42/attribute_serializer"
14
+ require "code42/resource"
15
+ require "code42/org"
16
+ require "code42/user"
17
+ require "code42/computer"
18
+ require "code42/diagnostic"
19
+ require "code42/ping"
20
+ require "code42/token"
21
+ require "code42/role"
22
+ require "code42/role_collection"
23
+ require "code42/token_validation"
24
+
25
+ module Code42
26
+ class << self
27
+ def client
28
+ @client ||= Code42::Client.new
29
+ end
30
+
31
+ def configure
32
+ yield self.settings
33
+ self
34
+ end
35
+
36
+ def respond_to_missing?(method_name, include_private=false); client.respond_to?(method_name, include_private); end if RUBY_VERSION >= "1.9"
37
+ def respond_to?(method_name, include_private=false); client.respond_to?(method_name, include_private) || super; end if RUBY_VERSION < "1.9"
38
+
39
+ private
40
+
41
+ def method_missing(method_name, *args, &block)
42
+ return super unless client.respond_to?(method_name)
43
+ client.send(method_name, *args, &block)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ module Code42
2
+ class Attribute
3
+ attr_reader :name, :from, :as, :collection
4
+ alias_method :to, :name
5
+
6
+ def initialize(name, options = {})
7
+ @name = name.to_sym
8
+ @from = (options[:from] || name.to_s.camelize(:lower)).to_s
9
+ @as = options[:as]
10
+ @collection = options[:collection]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,129 @@
1
+ module Code42
2
+ class AttributeSerializer
3
+ def add_exception(from, to)
4
+ exceptions << AttributeSerializerException.new(from, to)
5
+ end
6
+
7
+ def attributes
8
+ @attributes ||= []
9
+ end
10
+
11
+ def <<(attribute)
12
+ add_exception attribute.from, attribute.to
13
+ attributes << attribute
14
+ end
15
+
16
+ def exceptions
17
+ @exceptions ||= AttributeSerializerExceptions.new
18
+ end
19
+
20
+ def serialize(key, value)
21
+ Hash[serialize_key(key), serialize_value(value)]
22
+ end
23
+
24
+ def serialize_value(value)
25
+ if value.respond_to? :serialize
26
+ value.serialize
27
+ elsif value.is_a? DateTime
28
+ value.to_s
29
+ elsif value.is_a? Hash
30
+ value.inject({}) do |h,a|
31
+ h.merge! serialize(a[0], a[1])
32
+ end
33
+ elsif value.is_a? Array
34
+ value.map do |item|
35
+ serialize_value(item)
36
+ end
37
+ else
38
+ value
39
+ end
40
+ end
41
+
42
+ def deserialize(key, value)
43
+ Hash[deserialize_key(key), deserialize_value(key, value)]
44
+ end
45
+
46
+ def deserialize_key(key)
47
+ if exception = exception_from(key)
48
+ exception.to
49
+ else
50
+ key.underscore.to_sym
51
+ end
52
+ end
53
+
54
+ def deserialize_value(key, value)
55
+ if klass = attribute_for(key).try(:as)
56
+ if attribute_for(key).try(:collection)
57
+ klass.collection_from_response(value)
58
+ elsif klass.respond_to?(:from_response)
59
+ klass.from_response(value)
60
+ elsif klass.respond_to?(:parse)
61
+ klass.parse(value)
62
+ else
63
+ value
64
+ end
65
+ else
66
+ if value.is_a?(Hash)
67
+ value.inject({}) do |h,a|
68
+ h.merge! deserialize(a[0], a[1])
69
+ end
70
+ else
71
+ value
72
+ end
73
+ end
74
+ end
75
+
76
+ def serialize_key(key)
77
+ if exception = exception_to(key)
78
+ exception.from
79
+ else
80
+ key.to_s.camelize(:lower)
81
+ end
82
+ end
83
+
84
+ def attribute_for(key)
85
+ key = key.to_s
86
+ attributes.detect { |a| a.to.to_s == key.to_s || a.from.to_s == key.to_s }
87
+ end
88
+
89
+ def exception_to(key)
90
+ exceptions.to(key)
91
+ end
92
+
93
+ def exception_from(key)
94
+ exceptions.from(key)
95
+ end
96
+ end
97
+
98
+ class AttributeSerializerExceptions
99
+ include Enumerable
100
+
101
+ def initialize(exceptions = [])
102
+ @exceptions = exceptions
103
+ end
104
+
105
+ def <<(exception)
106
+ @exceptions << exception
107
+ end
108
+
109
+ def from(value)
110
+ detect { |e| e.from == value }
111
+ end
112
+
113
+ def to(value)
114
+ detect { |e| e.to == value }
115
+ end
116
+
117
+ def each(&block)
118
+ @exceptions.each do |exception|
119
+ if block_given?
120
+ block.call exception
121
+ else
122
+ yield exception
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ class AttributeSerializerException < Struct.new(:from, :to); end
129
+ end
@@ -0,0 +1,301 @@
1
+ require 'json'
2
+
3
+ module Code42
4
+ class Client
5
+ attr_accessor :settings
6
+
7
+ def initialize(options = {})
8
+ self.settings = options
9
+ end
10
+
11
+ def settings=(options)
12
+ @settings = Settings.new(options)
13
+ end
14
+
15
+ # Pings the server
16
+ # @return [Boolean] A boolean result (this should always return true)
17
+ def ping
18
+ get('ping')['data']['success']
19
+ end
20
+
21
+ # Use basic authentication for future requests
22
+ # @param username [String] The username to authenticate with
23
+ # @param password [String] The password to authenticate with
24
+ def use_basic_auth(username, password)
25
+ settings.token = nil
26
+ settings.username = username
27
+ settings.password = password
28
+ end
29
+
30
+ # Use token authentication for future requests
31
+ # @param token [Code42::Token, String] The token to authenticate with
32
+ def use_token_auth(token)
33
+ settings.token = token.to_s
34
+ end
35
+
36
+ ### Authentication With Tokens :post, :get, :delete ###
37
+
38
+ # Gets a token for the currently authorized user
39
+ def get_token
40
+ object_from_response(Token, :post, "authToken")
41
+ end
42
+
43
+ # Returns LoginToken and ServerUrl
44
+ # @return [CrashPlan::Token] Token to pass to ServerUrl's AuthToken resource
45
+ def get_login_token
46
+ object_from_response(Token, :post, "loginToken")
47
+ end
48
+
49
+ # Validates an authorization token
50
+ # @return [Code42::TokenValidation]
51
+ # @param token [Code42::Token, String] The token to validate
52
+ def validate_token(token)
53
+ object_from_response(TokenValidation, :get, "authToken/#{token.to_s}")
54
+ end
55
+
56
+ # Manually expires a token
57
+ # @param token [Code42::Token, String] A token to expire (leave blank to expire currently used token)
58
+ def delete_token(token = nil)
59
+ token = token || settings.token
60
+ delete "authToken/#{token.to_s}"
61
+ end
62
+
63
+ ### Users :post, :get ###
64
+
65
+ # Creates a user
66
+ # @return [Code42::User] The created user
67
+ # @param attrs [Hash] A hash of attributes to assign to created user
68
+ # @example
69
+ # client.create_user(:username => 'testuser', password: 'letmein', email: 'test@example.com', org_id: 3)
70
+ def create_user(attrs = {})
71
+ object_from_response(User, :post, "user", attrs)
72
+ end
73
+
74
+ # Returns information for a given user
75
+ # @return [Code42::User] The requested user
76
+ # @param id_or_username [String, Integer] A code42 user ID or username
77
+ def user(id_or_username = "my", params = {})
78
+ if id_or_username.is_a?(Fixnum) || id_or_username == 'my'
79
+ find_user_by_id id_or_username, params
80
+ else
81
+ find_user_by_username id_or_username, params
82
+ end
83
+ end
84
+
85
+ # Returns a user for a given id
86
+ def find_user_by_id(id = 'my', params = {})
87
+ object_from_response(User, :get, "user/#{id}", params)
88
+ end
89
+
90
+ # Returns a user for a given username
91
+ def find_user_by_username(username, params = {})
92
+ params.merge!(username: username)
93
+ users(params).first
94
+ end
95
+
96
+ # Returns a user for a given channel id
97
+ # @return [Code42::User] The requested user
98
+ # @param channel_id [String, Integer] A code42 User
99
+ def find_user_by_channel_id(channel_id = 1)
100
+ object_from_response(User, :get, "userChannel?channelCustomerId=#{channel_id}")
101
+ end
102
+
103
+ # Returns a list of up to 100 users
104
+ # @return [Array] An array of matching users
105
+ # @param params [Hash] A hash of parameters to match results against
106
+ def users(params = {})
107
+ params.merge!(key: 'users')
108
+ objects_from_response(User, :get, 'user', params)
109
+ end
110
+
111
+ # Check if user exists with given username.
112
+ def user_exists?(username)
113
+ users(username: username).present?
114
+ end
115
+
116
+ ### Roles :post, :get ###
117
+
118
+ # Assigns a role to a user
119
+ # @return [Code42::Role] The assigned role
120
+ # @param attrs [Hash] A hash of attributes for assigning a user role
121
+ # @example
122
+ # client.assign_role(:user_id => 2, :role_name => 'Admin')
123
+ def assign_role(attrs = {})
124
+ object_from_response(Role, :post, 'UserRole', attrs)
125
+ end
126
+
127
+ # Returns a list of roles for a given user
128
+ # @return [Code42::RoleCollection] A collection of matching roles
129
+ # @param id [String, Integer] The id of the user to return roles for
130
+ def user_roles(id = 'my')
131
+ collection_from_response(RoleCollection, Role, :get, "userRole/#{id}")
132
+ end
133
+
134
+ ### Orgs :post, :get, :put, :delete ###
135
+
136
+ # Creates blue org as well as user for the org
137
+ # @return [Code42::Org] The created org
138
+ # @param attrs [Hash] A hash of attributes to assign to created org
139
+ # @example
140
+ # client.create_org(:company => "test", :email => "test@test.com", :firstname => "test", :lastname => "test")
141
+ def create_pro_org(attrs = {})
142
+ object_from_response(Org, :post, "proOrgChannel", attrs)
143
+ end
144
+
145
+ # Creates an org
146
+ # @return [Code42::Org] The created org
147
+ # @param attrs [Hash] A hash of attributes to assign to created org
148
+ # @example
149
+ # client.create_org(:name => 'Acme Org', :parent_id => 2)
150
+ def create_org(attrs = {})
151
+ object_from_response(Org, :post, "org", attrs)
152
+ end
153
+
154
+ # Returns information for a given org
155
+ # @return [Code42::Org] The requested org
156
+ # @param id [String, Integer] A code42 user ID
157
+ def org(id = "my", params = {})
158
+ object_from_response(Org, :get, "org/#{id}", params)
159
+ end
160
+
161
+ # Returns an org for a given name
162
+ # @return [Code42::Org] The requested org
163
+ # @param name [String] A Code42 org name
164
+ # FIXME: This needs to change when the API implements a better way.
165
+ def find_org_by_name(name)
166
+ search_orgs(name).select { |o| o.name == name }.first
167
+ end
168
+
169
+ # Searches orgs for a query string
170
+ # @return [Array] An array of matching orgs
171
+ # @param query [String] A string to search for
172
+ def search_orgs(query)
173
+ orgs(q: query)
174
+ end
175
+
176
+ # Creates a user
177
+ # @return [Code42::User] The created user
178
+ # @param attrs [Hash] A hash of attributes to assign to created user
179
+ # @example
180
+ # client.create_user(:username => 'testuser', :password => 'letmein', :email => 'test@example.com', :org_id => 3)
181
+ def create_user(attrs = {})
182
+ object_from_response(User, :post, "user", attrs)
183
+ end
184
+
185
+ # Returns a list of up to 100 orgs
186
+ # @return [Array] An array of matching orgs
187
+ # @param params [Hash] A hash of parameters to match results against
188
+ def orgs(params = {})
189
+ params.merge!(key: 'orgs')
190
+ objects_from_response(Org, :get, 'org', params)
191
+ end
192
+
193
+ def update_org(id = 'my', attrs = {})
194
+ object_from_response(Org, :put, "org/#{id}", attrs)
195
+ end
196
+
197
+ ### Computers :get, :put ###
198
+
199
+ # Returns one computer or http status 404
200
+ # @return [Code42::Computer] The requested computer
201
+ # @param id [String, Integer] A computer ID
202
+ def computer(id, params = {})
203
+ object_from_response(Computer, :get, "computer/#{id}", params)
204
+ end
205
+
206
+ # Returns a list of computers
207
+ # @return [Array] The list of computers
208
+ # @param params [Hash] A hash of valid search parameters for computers
209
+ def computers(params = {})
210
+ params.merge!(key: 'computers')
211
+ objects_from_response(Computer, :get, 'computer', params)
212
+ end
213
+
214
+ def diagnostic
215
+ object_from_response(Diagnostic, :get, 'diagnostic')
216
+ end
217
+
218
+ # Block a computer from backing up
219
+ # @return [Code42::Computer] The blocked computer
220
+ # @params id [Integer, String] The computer ID you want to block
221
+ def block_computer(id)
222
+ put("computerblock/#{id}")
223
+ end
224
+
225
+ def unblock_computer(id)
226
+ delete("computerblock/#{id}")
227
+ end
228
+
229
+ def connection
230
+ @connection = Connection.new(
231
+ host: settings.host,
232
+ port: settings.port,
233
+ scheme: settings.scheme,
234
+ path_prefix: settings.api_root,
235
+ verify_https: settings.verify_https,
236
+ )
237
+ if settings.debug
238
+ @connection.use Faraday::Response::Logger
239
+ end
240
+ if settings.username && settings.password
241
+ @connection.username = settings.username
242
+ @connection.password = settings.password
243
+ end
244
+ if settings.token
245
+ @connection.token = settings.token
246
+ end
247
+ @connection
248
+ end
249
+
250
+ private
251
+
252
+ def object_from_response(klass, request_method, path, options = {})
253
+ options = klass.serialize(options)
254
+ response = send(request_method.to_sym, path, options)
255
+ return nil unless response_has_data?(response['data'])
256
+ klass.from_response(response['data'], self)
257
+ end
258
+
259
+ def objects_from_response(klass, request_method, path, options = {})
260
+ key = options.delete(:key)
261
+ options = klass.serialize(options)
262
+ response = send(request_method.to_sym, path, options)
263
+ return nil unless response_has_data?(response)
264
+ response = response['data']
265
+ response = response[key] if key
266
+ objects_from_array(klass, response)
267
+ end
268
+
269
+ def response_has_data?(response)
270
+ !response.nil?
271
+ end
272
+
273
+ def collection_from_response(collection_klass, object_klass, request_method, path, options = {})
274
+ collection_klass.new objects_from_response(object_klass, request_method, path, options)
275
+ end
276
+
277
+ def objects_from_array(klass, array)
278
+ array.map { |element| klass.deserialize_and_initialize(element, self) }
279
+ end
280
+
281
+ def get(path, data = {})
282
+ make_request(:get, path, data)
283
+ end
284
+
285
+ def post(path, data = {})
286
+ make_request(:post, path, data)
287
+ end
288
+
289
+ def put(path, data = {})
290
+ make_request(:put, path, data)
291
+ end
292
+
293
+ def delete(path, data = {})
294
+ make_request(:delete, path, data)
295
+ end
296
+
297
+ def make_request(method, *args)
298
+ connection.make_request(method, *args)
299
+ end
300
+ end
301
+ end