code42 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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