device_cloud 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MGExNTcxYzU4ZTZiNDdkYjkzODcwNjM5YzA4OTBkYTZiOTQ5ZjI4MA==
5
+ data.tar.gz: !binary |-
6
+ YTlkYTdmZjMzMGM5ZWFjZjEwY2FjYjc0M2FkNGNhNDMyOTdjNDBhNw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YjhiYzU1Mzk3ODhjMWVmMTViODM4OWJkMDNiZjJkOTY1NDRmYTA3ZTU0OWJk
10
+ YjhhNDU0YTUyMTA5MDdhNzA3NWJkYzczMWNjODc0YmVhNGQ5ZTdkNzBlNDk5
11
+ MDQxY2QxN2FjMDU3YjExODk0NGIzMDY2MGQ0N2RjNzEwM2MzNzU=
12
+ data.tar.gz: !binary |-
13
+ YzE5NThhZmM2NjJiMzdkY2ZjZDllMGYxNzNkNWEzNmNlZGZhNTI5NzNkODY3
14
+ YzMzOWIwNjYzY2ZiYWU2YmJlMjRjNzU2YjY2NjU1ODk4NmExMzVmNzQ5ZGQz
15
+ MDFmNzk4ZTRlODA3YWU4MzhiYTZlNmJkYjU1MzU0OGMzMTlkNjE=
@@ -0,0 +1 @@
1
+ device_cloud
@@ -1 +1 @@
1
- 1.9.3-p392
1
+ 1.9.3-p484
data/README.md CHANGED
@@ -1,11 +1,15 @@
1
1
  # DeviceCloud
2
2
 
3
- TODO:
4
- - remove any assumptions about Device Cloud FileData contents .. probably should only parse them if asked
5
- - add code for maintaining monitors
3
+ [![Gem Version](https://badge.fury.io/rb/device_cloud.png)](http://badge.fury.io/rb/device_cloud)
6
4
 
7
- ## Installation
5
+ [![wercker status](https://app.wercker.com/status/88a596a14228b6d5b8e7a57dd5d6db55/m/ "wercker status")](https://app.wercker.com/project/bykey/88a596a14228b6d5b8e7a57dd5d6db55)
6
+
7
+ #####TODO:
8
8
 
9
+ * Add code for maintaining devices
10
+ * Add code for maintaining alarms
11
+
12
+ ## Installation
9
13
  Add this line to your application's Gemfile:
10
14
 
11
15
  gem 'device_cloud'
@@ -20,25 +24,86 @@ Or install it yourself as:
20
24
 
21
25
  ## Usage
22
26
 
23
- TODO: Write usage instructions here
27
+ ### Configuration
28
+
29
+ ```ruby
30
+ DeviceCloud.configure do |config|
31
+ config.username = 'your idigi username'
32
+ config.password = 'your idigi password'
33
+ end
34
+ # =>
35
+ # {
36
+ # username: 'your idigi username,
37
+ # password: 'your idigi password',
38
+ # root_url: 'https://my.idigi.com',
39
+ # host: 'my.idigi.com',
40
+ # alert_notification_handler: nil,
41
+ # empty_alert_notification_handler: nil,
42
+ # data_notification_handler: nil,
43
+ # empty_data_notification_handler: nil,
44
+ # event_notification_handler: nil,
45
+ # empty_event_notification_handler: nil,
46
+ # logger: Logger.new(STDOUT) # default
47
+ # }
48
+ ```
49
+
50
+
51
+ ### Push Notifications
52
+
53
+ `device_cloud` is currently only able to handle JSON push notifications via http, so your monitor must be set up accordingly. Read more about Monitors below.
54
+
55
+ When your application receives an HTTP push notification, it can be passed to the DeviceCloud gem using the following:
56
+
57
+ ```ruby
58
+ push_notification = DeviceCloud::PushNotification( http_response_body )
59
+ # where http_response_body is a hash of the parsed JSON body content sent by DeviceCloud
60
+
61
+ push_notification.handle_each!
62
+ ```
63
+
64
+ The event will then be handled by one of your defined Notification Handlers.
65
+
66
+ #### Notification Handlers
67
+
68
+ Notification handlers take a Proc, and are called with a relevant alert or event object. A list of supported topic handlers are listed as follows:
69
+
70
+ alert_notification_handler
71
+ data_notification_handler
72
+ event_notification_handler
73
+
74
+ The following empty notification handlers are also available:
75
+
76
+ empty_alert_notification_handler
77
+ empty_data_notification_handler
78
+ empty_event_notification_handler
79
+
80
+ An example definition may look like the following
81
+
82
+ ```ruby
83
+ DeviceCloud.alert_notification_handler = Proc.new do |alert|
84
+ puts "#{alert.type} Alert: for device #{alert.device_id}"
85
+ puts "Base 64 encoded data #{alert.raw_data}"
86
+ end
87
+ ```
88
+
89
+ #### Example Push Notification
24
90
 
25
- ## Example Push Notification
26
91
 
27
92
  ```json
28
93
  {
29
94
  "Document": {
30
95
  "Msg": {
31
96
  "timestamp": "2013-10-21T19:34:56Z",
32
- "topic": "4044/FileData/db/4044_MadGlory_Interactive/00000000-00000000-001395FF-FF0E6017/event/parking_lot_event_exit-0966595cdcdd11e2abf50013950e6017.json",
97
+ "topic": "device/event/event.json",
33
98
  "FileData": {
34
99
  "id": {
35
- "fdPath": "/db/4044_MadGlory_Interactive/00000000-00000000-001395FF-FF0E6017/event/",
36
- "fdName": "parking_lot_event_exit-0966595cdcdd11e2abf50013950e6017.json"
100
+ "fdPath": "/device/event/",
101
+ "fdName": "event.json"
37
102
  },
38
103
  "fdLastModifiedDate": "2013-10-21T19:34:56Z",
39
104
  "fdSize": 545,
40
105
  "fdContentType": "application/json",
41
- "fdData": "eyJ2YWx1ZSI6eyJwbGF0ZSI6Ijk0MUdWVCIsImNvbmZpZGVuY2UiOiI5OSIsImNvdW50cnkiOiJVUyIsInRvd2FyZHNfY2FtZXJhIjoiZmFsc2UiLCJ0aW1lc3RhbXAiOiIyMDEzLTEwLTIxVDE0OjM0OjQwWiIsIm92ZXJ2aWV3X2ltYWdlX2lkIjoibG90X292ZXJ2aWV3X2NhcF8xMF8yMV8yMDEzXzE0MzQ0MC5qcGciLCJzdGF0ZSI6Ik1OIiwicGF0Y2hfaW1hZ2VfaWQiOiJsb3RfcGF0Y2hfY2FwXzEwXzIxXzIwMTNfMTQzNDQwLmpwZyJ9LCJjbGFzcyI6ImV2ZW50IiwicXVldWVkX2R0IjoiMjAxMy0xMC0yMVQxOTozNDo1NloiLCJ0eXBlIjoicGFya2luZ19sb3RfZXZlbnRfZXhpdCIsImlkIjoiZGQ5ZDU3MzYzYTg3MTFlM2E5YmQwMDEzOTUwZTYwMTciLCJkZXZpY2VfaWQiOiJtOjAwMTM5NTBFNjAxNyJ9",
106
+ "fdData": "eW91IGhhdmUgdG9vIG11Y2ggZnJlZSB0aW1l\n",
42
107
  "fdArchive": false,
43
108
  "cstId": 4044,
44
109
  "fdType": "event",
@@ -52,27 +117,9 @@ TODO: Write usage instructions here
52
117
  }
53
118
  ```
54
119
 
55
- That notification's unencoded fdData:
120
+ ### Monitors
56
121
 
57
- ```json
58
- {
59
- "value": {
60
- "plate": "941GVT",
61
- "confidence": "99",
62
- "country": "US",
63
- "towards_camera": "false",
64
- "timestamp": "2013-10-21T14:34:40Z",
65
- "overview_image_id": "lot_overview_cap_10_21_2013_143440.jpg",
66
- "state": "MN",
67
- "patch_image_id": "lot_patch_cap_10_21_2013_143440.jpg"
68
- },
69
- "class": "event",
70
- "queued_dt": "2013-10-21T19:34:56Z",
71
- "type": "parking_lot_event_exit",
72
- "id": "dd9d57363a8711e3a9bd0013950e6017",
73
- "device_id": "m:0013950E6017"
74
- }
75
- ```
122
+ TODO: write about Monitor class
76
123
 
77
124
  ## Contributing
78
125
 
@@ -81,3 +128,13 @@ That notification's unencoded fdData:
81
128
  3. Commit your changes (`git commit -am 'Add some feature'`)
82
129
  4. Push to the branch (`git push origin my-new-feature`)
83
130
  5. Create new Pull Request
131
+
132
+ ### Development
133
+
134
+ You can get a pry console by using the `:console` Rake task
135
+
136
+ $ rake console
137
+
138
+ You can also optionally pass your Device Cloud username and password like so
139
+
140
+ $ IDIGI_USERNAME=youruser IDIGI_PASSWORD=yourpass rake console
data/Rakefile CHANGED
@@ -1 +1,20 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ desc 'Run a console with device_cloud loaded'
9
+ task :console do
10
+ require 'device_cloud'
11
+ require 'pry'
12
+ require 'awesome_print'
13
+
14
+ DeviceCloud.configure do |config|
15
+ config.username = ENV['IDIGI_USERNAME']
16
+ config.password = ENV['IDIGI_PASSWORD']
17
+ end
18
+
19
+ Pry.start
20
+ end
@@ -7,10 +7,10 @@ Gem::Specification.new do |spec|
7
7
  spec.name = "device_cloud"
8
8
  spec.version = DeviceCloud::VERSION
9
9
  spec.authors = ["Erik Straub"]
10
- spec.email = ["erik@madgloryint.com"]
10
+ spec.email = ["erik@madglory.com"]
11
11
  spec.description = %q{A Ruby wrapper for the Etherios Device Cloud}
12
12
  spec.summary = %q{A Ruby wrapper for the Etherios Device Cloud}
13
- spec.homepage = "http://github.com/madgloryint/device_cloud"
13
+ spec.homepage = "http://github.com/madglory/device_cloud"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
-
24
23
  spec.add_development_dependency 'rspec'
25
- end
24
+ spec.add_development_dependency 'webmock'
25
+ spec.add_development_dependency 'pry'
26
+ spec.add_development_dependency 'awesome_print'
27
+ end
@@ -1,6 +1,7 @@
1
1
  require 'device_cloud/configuration'
2
2
  require 'device_cloud/utils'
3
3
  require 'device_cloud/version'
4
+ require 'device_cloud/monitor'
4
5
  require 'device_cloud/push_notification'
5
6
  require 'device_cloud/push_notification/base_notification'
6
7
  require 'device_cloud/push_notification/alert_notification'
@@ -8,8 +9,11 @@ require 'device_cloud/push_notification/event_notification'
8
9
  require 'device_cloud/push_notification/data_notification'
9
10
  require 'device_cloud/push_notification/message'
10
11
  require 'device_cloud/push_notification/message/file_data'
12
+ require 'device_cloud/request'
13
+ require 'device_cloud/response'
11
14
 
12
15
  module DeviceCloud
13
16
  extend Configuration
14
17
  extend Utils
15
- end
18
+ class Error < StandardError; end
19
+ end
@@ -3,9 +3,6 @@ require 'logger'
3
3
  module DeviceCloud
4
4
  module Configuration
5
5
 
6
- # DeviceCloud account root_url
7
- attr_writer :root_url
8
-
9
6
  # DeviceCloud account username
10
7
  attr_writer :username
11
8
 
@@ -53,11 +50,12 @@ module DeviceCloud
53
50
  #
54
51
  # Example:
55
52
  #
56
- # ActivityFeed.configure do |configuration|
53
+ # DeviceCloud.configure do |configuration|
57
54
  # configuration.root_url = 'https://example.com'
58
55
  # end
59
56
  def configure
60
57
  yield self
58
+ config
61
59
  end
62
60
 
63
61
  def config
@@ -65,6 +63,7 @@ module DeviceCloud
65
63
  username: username,
66
64
  password: password,
67
65
  root_url: root_url,
66
+ host: host,
68
67
  alert_notification_handler: @alert_notification_handler,
69
68
  empty_alert_notification_handler: @empty_alert_notification_handler,
70
69
  data_notification_handler: @data_notification_handler,
@@ -77,16 +76,23 @@ module DeviceCloud
77
76
 
78
77
  # DeviceCloud url
79
78
  #
80
- # @return the DeviceCloud url or the default of 'https://my.idigi.com' if not set.
79
+ # @return the DeviceCloud url - 'https://my.idigi.com'
81
80
  def root_url
82
- @root_url ||= 'https://my.idigi.com'
81
+ 'https://my.idigi.com'
82
+ end
83
+
84
+ # DeviceCloud url
85
+ #
86
+ # @return the DeviceCloud host - 'my.idigi.com'
87
+ def host
88
+ 'my.idigi.com'
83
89
  end
84
90
 
85
91
  # DeviceCloud username
86
92
  #
87
93
  # @return the DeviceCloud username or raises an error
88
94
  def username
89
- raise 'DeviceCloud username is blank' if @username.blank?
95
+ raise 'DeviceCloud username is blank' if @username.nil? || @password == ''
90
96
  @username
91
97
  end
92
98
 
@@ -94,7 +100,7 @@ module DeviceCloud
94
100
  #
95
101
  # @return the DeviceCloud password or raises an error
96
102
  def password
97
- raise 'DeviceCloud password is blank' if @password.blank?
103
+ raise 'DeviceCloud password is blank' if @password.nil? || @password == ''
98
104
  @password
99
105
  end
100
106
 
@@ -105,4 +111,4 @@ module DeviceCloud
105
111
  @logger ||= Logger.new(STDOUT)
106
112
  end
107
113
  end
108
- end
114
+ end
@@ -0,0 +1,140 @@
1
+ module DeviceCloud
2
+ class Monitor
3
+ attr_accessor :monId, :cstId, :monLastConnect, :monLastSent, :monTopic, :monTransportType, :monTransportToken, :monTransportUrl, :monFormatType, :monBatchSize, :monCompression, :monStatus, :monBatchDuration, :monAutoReplayOnConnect, :monLastSentUuid
4
+ attr_reader :error
5
+
6
+ DEFAULT_CREATE_OPTIONS = {
7
+ 'monTransportType' => 'http',
8
+ 'monTransportUrl' => "http://www.example.com/push_notifications",
9
+ 'monTransportToken' => "USERNAME:PASSWORD",
10
+ 'monFormatType' => 'json',
11
+ 'monBatchSize' => '1',
12
+ 'monCompression' => 'none',
13
+ 'monBatchDuration' => '0',
14
+ 'monAutoReplayOnConnect' => 'true'
15
+ }
16
+
17
+ class << self
18
+ def all
19
+ monitors = DeviceCloud::Request.new(path: '/ws/Monitor/.json').get.to_hash_from_json
20
+
21
+ return [] unless monitors['resultSize'].to_i > 0
22
+
23
+ monitors['items'].map { |monitor|
24
+ Monitor.new monitor
25
+ }
26
+ end
27
+
28
+ def find(monitor_id)
29
+ monitors = DeviceCloud::Request.new(path: "/ws/Monitor/#{monitor_id}.json").get.to_hash_from_json
30
+
31
+ return nil unless monitors['resultSize'].to_i > 0
32
+
33
+ Monitor.new monitors['items'].first
34
+ end
35
+ end
36
+
37
+ def initialize(attributes = {})
38
+ attributes.each do |name, value|
39
+ send("#{name}=", value)
40
+ end
41
+
42
+ @error = nil
43
+
44
+ set_defaults
45
+ end
46
+
47
+ def persist!
48
+ @error = nil
49
+ monId.nil? ? create : update
50
+ end
51
+
52
+ def reset!
53
+ @error = nil
54
+ if monId.nil?
55
+ @error = 'monId is nil'
56
+ return false
57
+ else
58
+ reset_monitor!
59
+ end
60
+ end
61
+
62
+ def destroy!
63
+ return false if monId.nil?
64
+
65
+ response = DeviceCloud::Request.new(path: "/ws/Monitor/#{monId}").delete
66
+
67
+ response.code == '200' ? true : false
68
+ end
69
+
70
+ def attributes
71
+ {
72
+ 'monTopic' => monTopic,
73
+ 'monTransportType' => monTransportType,
74
+ 'monTransportToken' => monTransportToken,
75
+ 'monTransportUrl' => monTransportUrl,
76
+ 'monFormatType' => monFormatType,
77
+ 'monBatchSize' => monBatchSize,
78
+ 'monCompression' => monCompression,
79
+ 'monBatchDuration' => monBatchDuration,
80
+ 'monAutoReplayOnConnect' => monAutoReplayOnConnect
81
+ }
82
+ end
83
+
84
+ private
85
+ def set_defaults
86
+ DEFAULT_CREATE_OPTIONS.each do |option, value|
87
+ send("#{option}=", value) if attributes[:option].nil?
88
+ end
89
+ end
90
+
91
+ def create
92
+ response = DeviceCloud::Request.new(path: '/ws/Monitor', body: to_xml).post
93
+ if response.code == '201'
94
+ self.monId = response.headers['location'][0].sub(/Monitor\//, '')
95
+ return true
96
+ else
97
+ @error = error_from_response_xml(response)
98
+ return false
99
+ end
100
+ end
101
+
102
+ def update
103
+ response = DeviceCloud::Request.new(path: "/ws/Monitor/#{monId}", body: to_xml).put
104
+
105
+ if response.code == '200'
106
+ return true
107
+ else
108
+ @error = error_from_response_xml(response)
109
+ return false
110
+ end
111
+ end
112
+
113
+ def reset_monitor!
114
+ response = DeviceCloud::Request.new(path: "/ws/Monitor", body: reset_xml).put
115
+
116
+ if response.code == '200'
117
+ return true
118
+ else
119
+ @error = error_from_response_xml(response)
120
+ return false
121
+ end
122
+ end
123
+
124
+ def reset_xml
125
+ "<Monitor><monId>#{monId}</monId></Monitor>"
126
+ end
127
+
128
+ def to_xml
129
+ xml = '<Monitor>'
130
+ attributes.each do |key,value|
131
+ xml << "<#{key}>#{value}</#{key}>"
132
+ end
133
+ xml << '</Monitor>'
134
+ end
135
+
136
+ def error_from_response_xml(response)
137
+ response.body.match(/<error>(.*)<\/error>/)[1]
138
+ end
139
+ end
140
+ end
@@ -2,8 +2,8 @@ module DeviceCloud
2
2
  class PushNotification
3
3
  attr_reader :messages
4
4
 
5
- def initialize(raw_messages)
6
- @messages = DeviceCloud::PushNotification::Message.parse_raw_messages(raw_messages)
5
+ def initialize(response_body)
6
+ @messages = DeviceCloud::PushNotification::Message.parse_raw_messages(response_body)
7
7
  end
8
8
 
9
9
  def handle_each!
@@ -20,4 +20,4 @@ module DeviceCloud
20
20
  DeviceCloud.constantize "DeviceCloud::PushNotification::#{class_name.capitalize}Notification"
21
21
  end
22
22
  end
23
- end
23
+ end
@@ -0,0 +1,79 @@
1
+ require 'net/http'
2
+
3
+ module DeviceCloud
4
+ # Public: Used to send Net::HTTP requests.
5
+ #
6
+ # Examples:
7
+ #
8
+ # get_response = DeviceCloud::Request.new("/ws/FileData").get
9
+ # post_response = DeviceCloud::Request.new("/ws/sci").post(data)
10
+ class Request
11
+ attr_reader :path, :body
12
+
13
+ # Public: Create a new instance of Request.
14
+ #
15
+ # path - The path String to use.
16
+ def initialize(options = {})
17
+ @path = options[:path]
18
+ @body = options[:body]
19
+ end
20
+
21
+ # Public: Send a GET request.
22
+ #
23
+ # Returns a DeviceCloud::Response instance.
24
+ def get
25
+ make_request do
26
+ Net::HTTP::Get.new request_uri
27
+ end
28
+ end
29
+
30
+ # Public: Send a POST request.
31
+ #
32
+ # body
33
+ #
34
+ # Returns a DeviceCloud::Response instance.
35
+ def post
36
+ make_request do
37
+ Net::HTTP::Post.new request_uri
38
+ end
39
+ end
40
+
41
+ # Public: Send a PUT request.
42
+ #
43
+ # body
44
+ #
45
+ # Returns a DeviceCloud::Response instance.
46
+ def put
47
+ make_request do
48
+ Net::HTTP::Put.new request_uri
49
+ end
50
+ end
51
+
52
+ # Public: Send a DELETE request.
53
+ #
54
+ # Returns a DeviceCloud::Response instance.
55
+ def delete
56
+ make_request do
57
+ Net::HTTP::Delete.new request_uri
58
+ end
59
+ end
60
+
61
+ private
62
+ def request_uri
63
+ uri.request_uri
64
+ end
65
+
66
+ def uri
67
+ @uri ||= URI.parse(DeviceCloud.root_url + path)
68
+ end
69
+
70
+ def make_request
71
+ Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
72
+ request = yield
73
+ request.basic_auth DeviceCloud.username, DeviceCloud.password
74
+
75
+ DeviceCloud::Response.new http.request(request, body)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,17 @@
1
+ module DeviceCloud
2
+ class Response
3
+ attr_reader :code, :message, :headers, :body, :original_response
4
+
5
+ def initialize(http_response)
6
+ @original_response = http_response
7
+ @code = original_response.code
8
+ @message = original_response.message
9
+ @headers = original_response.header.to_hash
10
+ @body = original_response.body
11
+ end
12
+
13
+ def to_hash_from_json
14
+ JSON.parse(body)
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module DeviceCloud
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -0,0 +1,277 @@
1
+ require 'spec_helper'
2
+
3
+ describe DeviceCloud::Monitor do
4
+
5
+ describe '::all' do
6
+ before(:each) do
7
+ stub_request(:get, authenticated_host + '/ws/Monitor/.json').
8
+ with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
9
+ to_return(:status => 200, :body => monitors_json, :headers => {})
10
+ end
11
+
12
+ subject { DeviceCloud::Monitor.all }
13
+
14
+ context 'with results' do
15
+ let(:monitors_json) do
16
+ {
17
+ "resultTotalRows" => "1",
18
+ "requestedStartRow" => "0",
19
+ "resultSize" => "1",
20
+ "requestedSize" => "1000",
21
+ "remainingSize" => "0",
22
+ "items" => [
23
+ {
24
+ "monId" => "109012",
25
+ "cstId" => "4044",
26
+ "monLastConnect" => "2014-02-05T18:57:49.603Z",
27
+ "monLastSent" => "2014-02-11T02:38:46.770Z",
28
+ "monTopic" => "[group=Staging]FileData/*.*",
29
+ "monTransportType" => "http",
30
+ "monTransportUrl" => "http://example.com/device_cloud/push_notifications",
31
+ "monFormatType" => "json",
32
+ "monBatchSize" => "1",
33
+ "monCompression" => "none",
34
+ "monStatus" => "ACTIVE",
35
+ "monBatchDuration" => "0",
36
+ "monAutoReplayOnConnect" => "true",
37
+ "monLastSentUuid" => "a18afa9d-92c5-11e3-a346-bc764e1023af"
38
+ }
39
+ ]
40
+ }.to_json
41
+ end
42
+
43
+ it { should be_a(Array) }
44
+ its(:size) { should eq 1 }
45
+ its(:first) { should be_a(DeviceCloud::Monitor) }
46
+ end
47
+
48
+ context 'with no results' do
49
+ let(:monitors_json) do
50
+ {
51
+ "resultTotalRows"=>"0",
52
+ "requestedStartRow"=>"0",
53
+ "resultSize"=>"0",
54
+ "requestedSize"=>"1000",
55
+ "remainingSize"=>"0",
56
+ "items"=>[]
57
+ }.to_json
58
+ end
59
+
60
+ it { should eq [] }
61
+ end
62
+ end
63
+
64
+ describe '::find' do
65
+ before(:each) do
66
+ stub_request(:get, authenticated_host + '/ws/Monitor/123.json').
67
+ with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
68
+ to_return(:status => 200, :body => monitor_json, :headers => {})
69
+ end
70
+
71
+ subject { DeviceCloud::Monitor.find 123 }
72
+
73
+ context 'with result' do
74
+ let(:monitor_json) do
75
+ {"resultTotalRows"=>"1",
76
+ "requestedStartRow"=>"0",
77
+ "resultSize"=>"1",
78
+ "requestedSize"=>"1000",
79
+ "remainingSize"=>"0",
80
+ "items"=>
81
+ [{"monId"=>"109012",
82
+ "cstId"=>"4044",
83
+ "monLastConnect"=>"2014-02-05T18:57:49.603Z",
84
+ "monLastSent"=>"2014-02-11T02:54:07.143Z",
85
+ "monTopic"=>"[group=Staging]FileData/*.*",
86
+ "monTransportType"=>"http",
87
+ "monTransportUrl"=>
88
+ "http://example.com/device_cloud/push_notifications",
89
+ "monFormatType"=>"json",
90
+ "monBatchSize"=>"1",
91
+ "monCompression"=>"none",
92
+ "monStatus"=>"ACTIVE",
93
+ "monBatchDuration"=>"0",
94
+ "monAutoReplayOnConnect"=>"true",
95
+ "monLastSentUuid"=>"7b4b2515-92c7-11e3-a346-bc764e1023af"}]}.to_json
96
+ end
97
+
98
+ it { should be_a(DeviceCloud::Monitor) }
99
+ end
100
+
101
+ context 'with no results' do
102
+ let(:monitor_json) do
103
+ {"error"=>["GET Monitor error. Error reading Monitor entity id='12123'"]}.to_json
104
+ end
105
+
106
+ it { should be_nil }
107
+ end
108
+ end
109
+
110
+ describe '#persist!' do
111
+ context 'when monId is nil' do
112
+ let(:request_body) { new_monitor.send(:to_xml) }
113
+ before(:each) do
114
+ stub_request(:post, authenticated_host + '/ws/Monitor').
115
+ with(:body => request_body,
116
+ :headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
117
+ to_return(:status => response_status, :body => response_body, :headers => response_headers)
118
+ end
119
+
120
+ subject { new_monitor }
121
+
122
+ context 'when successful' do
123
+ let(:new_monitor) { DeviceCloud::Monitor.new monTopic: 'AlarmStatus' }
124
+ let(:response_body) { "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<result>\n <location>Monitor/115848</location>\n</result>" }
125
+ let(:response_status) { 201 }
126
+ let(:response_headers) do
127
+ { 'location' => ['Monitor/1234567'] }
128
+ end
129
+
130
+
131
+ its(:persist!) { should be_true }
132
+
133
+ context 'after #persist!' do
134
+ before(:each) { subject.persist! }
135
+ its(:monId) { should_not be_nil }
136
+ its(:error) { should be_nil }
137
+ end
138
+ end
139
+
140
+ context 'when unsuccessful' do
141
+ let(:new_monitor) { DeviceCloud::Monitor.new monTopic: 'Invalid' }
142
+ let(:response_body) { "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<result>\n <error>POST Monitor error. Invalid request. Invalid monTopic specified</error>\n</result>" }
143
+ let(:response_status) { 400 }
144
+ let(:response_headers) { Hash.new }
145
+
146
+ its(:persist!) { should be_false }
147
+
148
+ context 'after #persist!' do
149
+ before(:each) { subject.persist! }
150
+ its(:monId) { should be_nil }
151
+ its(:error) { should eq "POST Monitor error. Invalid request. Invalid monTopic specified" }
152
+ end
153
+ end
154
+ end
155
+
156
+ context 'when monId is not nil' do
157
+ let(:request_body) { existing_monitor.send(:to_xml) }
158
+ before(:each) do
159
+ stub_request(:put, "https://foouser:barpass@my.idigi.com/ws/Monitor/12345").
160
+ with(:body => request_body,
161
+ :headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
162
+ to_return(:status => response_status, :body => response_body, :headers => {})
163
+ end
164
+
165
+ subject { existing_monitor }
166
+
167
+ context 'when successful' do
168
+ let(:existing_monitor) { DeviceCloud::Monitor.new monTopic: 'FileData', monId: 12345 }
169
+ let(:response_body) { "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<result/>" }
170
+ let(:response_status) { 200 }
171
+
172
+ its(:persist!) { should be_true }
173
+
174
+ context 'after #persist!' do
175
+ before(:each) { subject.persist! }
176
+ its(:error) { should be_nil }
177
+ end
178
+ end
179
+
180
+ context 'when unsuccessful' do
181
+ let(:existing_monitor) { DeviceCloud::Monitor.new monTopic: 'Invalid', monId: 12345 }
182
+ let(:response_body) { "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<result>\n <error>PUT Monitor error. Invalid request. Invalid monTopic specified</error>\n</result>" }
183
+ let(:response_status) { 400 }
184
+
185
+ its(:persist!) { should be_false }
186
+
187
+ context 'after #persist!' do
188
+ before(:each) { subject.persist! }
189
+ its(:error) { should eq "PUT Monitor error. Invalid request. Invalid monTopic specified" }
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ describe '#destroy!' do
196
+ context 'when monId is nil' do
197
+ its(:destroy!) { should be_false }
198
+ end
199
+
200
+ context 'when monId is not nil' do
201
+ let(:existing_monitor) { DeviceCloud::Monitor.new monId: 12345 }
202
+ before(:each) do
203
+ stub_request(:delete, "https://foouser:barpass@my.idigi.com/ws/Monitor/12345").
204
+ with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
205
+ to_return(:status => response_status, :body => response_body, :headers => {})
206
+ end
207
+
208
+ subject { existing_monitor.destroy! }
209
+
210
+ context 'when successful' do
211
+ let(:response_body) { "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<result>\n <message>1 items deleted</message>\n</result>" }
212
+ let(:response_status) { 200 }
213
+
214
+ it { should be_true }
215
+ end
216
+
217
+ context 'when unsuccessful' do
218
+ let(:response_body) { "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<result>\n <error>DELETE Monitor error. Invalid request. For input string: \"gigo\"</error>\n</result>" }
219
+ let(:response_status) { 400 }
220
+
221
+ it { should be_false }
222
+ end
223
+ end
224
+ end
225
+
226
+ describe '#reset!' do
227
+ context 'when monId is nil' do
228
+ its(:reset!) { should be_false }
229
+
230
+ context 'after #reset!' do
231
+ before(:each) { subject.reset! }
232
+
233
+ its(:error) { should eq 'monId is nil'}
234
+ end
235
+ end
236
+
237
+ context 'when monId is not nil' do
238
+ before(:each) do
239
+ stub_request(:put, "https://foouser:barpass@my.idigi.com/ws/Monitor").
240
+ with(:body => request_body,
241
+ :headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
242
+ to_return(:status => response_status, :body => response_body, :headers => {})
243
+ end
244
+
245
+
246
+ context 'when successful' do
247
+ subject { DeviceCloud::Monitor.new monId: 12345 }
248
+ let(:request_body) { '<Monitor><monId>12345</monId></Monitor>'}
249
+ let(:response_body) { "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<result/>" }
250
+ let(:response_status) { 200 }
251
+
252
+ its(:reset!) { should be_true }
253
+
254
+ context 'after #reset!' do
255
+ before(:each) { subject.reset! }
256
+
257
+ its(:error) { should be_nil }
258
+ end
259
+ end
260
+
261
+ context 'when unsuccessful' do
262
+ subject { DeviceCloud::Monitor.new monId: 1 }
263
+ let(:request_body) { '<Monitor><monId>1</monId></Monitor>' }
264
+ let(:response_body) { "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n<result>\n <error>PUT Monitor error. Error reading Monitor entity id='&lt;monId&gt;1&lt;/monId&gt;'</error>\n</result>" }
265
+ let(:response_status) { 400 }
266
+
267
+ its(:reset!) { should be_false }
268
+
269
+ context 'after #reset!' do
270
+ before(:each) { subject.reset! }
271
+
272
+ its(:error) { should eq "PUT Monitor error. Error reading Monitor entity id='&lt;monId&gt;1&lt;/monId&gt;'"}
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ describe DeviceCloud::Request do
4
+ let(:path) { '/the/road/less/travelled' }
5
+ let(:body) { 'badguts' }
6
+ let(:request_uri) { authenticated_host + path }
7
+
8
+ its(:path) { should be_nil }
9
+ its(:body) { should be_nil }
10
+
11
+ context 'when passed options' do
12
+ subject { DeviceCloud::Request.new path: '/boom/boom/boom', body: 'HEYO' }
13
+
14
+ its(:path) { should eq '/boom/boom/boom' }
15
+ its(:body) { should eq 'HEYO' }
16
+ end
17
+
18
+ describe '#get' do
19
+ before(:each) do
20
+ stub_request(:get, request_uri).
21
+ with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
22
+ to_return(:status => 200, :body => "", :headers => {})
23
+ end
24
+
25
+ subject { DeviceCloud::Request.new(path: path) }
26
+
27
+ its(:get) { should be_a(DeviceCloud::Response) }
28
+ end
29
+
30
+ describe '#post' do
31
+ before(:each) do
32
+ stub_request(:post, request_uri).
33
+ with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}, body: body).
34
+ to_return(:status => 200, :body => "", :headers => {})
35
+ end
36
+
37
+ subject { DeviceCloud::Request.new(path: path, body: body) }
38
+
39
+ its(:post) { should be_a(DeviceCloud::Response) }
40
+ end
41
+
42
+ describe '#put' do
43
+ before(:each) do
44
+ stub_request(:put, request_uri).
45
+ with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}, body: body).
46
+ to_return(:status => 200, :body => "", :headers => {})
47
+ end
48
+
49
+ subject { DeviceCloud::Request.new(path: path, body: body) }
50
+
51
+ its(:put) { should be_a(DeviceCloud::Response) }
52
+ end
53
+
54
+ describe '#delete' do
55
+ before(:each) do
56
+ stub_request(:delete, request_uri).
57
+ with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
58
+ to_return(:status => 200, :body => "", :headers => {})
59
+ end
60
+
61
+ subject { DeviceCloud::Request.new(path: path) }
62
+
63
+ its(:delete) { should be_a(DeviceCloud::Response) }
64
+ end
65
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe DeviceCloud::Response do
4
+ let(:headers_hash) do
5
+ { 'location' => ['here'] }
6
+ end
7
+ let(:header) do
8
+ OpenStruct.new to_hash: headers_hash
9
+ end
10
+ let(:message) { 'OK' }
11
+ let(:code) { 200 }
12
+ let(:body) { 'HEYO' }
13
+ let(:http_response) do
14
+ OpenStruct.new(
15
+ code: code,
16
+ message: message,
17
+ header: header,
18
+ body: body
19
+ )
20
+ end
21
+
22
+ subject { DeviceCloud::Response.new http_response }
23
+
24
+ its(:code) { should eq code }
25
+ its(:message) { should eq message }
26
+ its(:body) { should eq body }
27
+ its(:headers) { should eq headers_hash }
28
+
29
+ describe '#to_hash_from_json' do
30
+ context 'when valid JSON' do
31
+ let(:json) do
32
+ {
33
+ 'hello' => 'world'
34
+ }
35
+ end
36
+ let(:body) { json.to_json }
37
+
38
+ its(:to_hash_from_json) { should eq json }
39
+ end
40
+
41
+ context 'when not valid json' do
42
+ it 'raises an error' do
43
+ expect{ subject.to_hash_from_json }.to raise_error(JSON::ParserError)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,13 +1,28 @@
1
1
  require 'device_cloud'
2
2
  require 'ostruct'
3
+ require 'webmock/rspec'
4
+ require 'pry'
5
+ require 'awesome_print'
6
+
7
+ def idigi_username
8
+ 'foouser'
9
+ end
10
+
11
+ def idigi_password
12
+ 'barpass'
13
+ end
14
+
15
+ def authenticated_host
16
+ "https://#{DeviceCloud.username}:#{DeviceCloud.password}@#{DeviceCloud.host}"
17
+ end
3
18
 
4
19
  RSpec.configure do |config|
5
20
  config.mock_with :rspec
6
21
 
7
22
  config.before(:each) do
8
23
  DeviceCloud.configure do |configuration|
9
- configuration.username = 'a username'
10
- configuration.password = 'a password'
24
+ configuration.username = idigi_username
25
+ configuration.password = idigi_password
11
26
  end
12
27
  end
13
- end
28
+ end
@@ -2,6 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe 'DeviceCloud::VERSION' do
4
4
  it 'should be the correct version' do
5
- DeviceCloud::VERSION.should == '0.2.3'
5
+ DeviceCloud::VERSION.should == '0.2.4'
6
6
  end
7
- end
7
+ end
@@ -0,0 +1,8 @@
1
+ box: wercker/rvm
2
+
3
+ build:
4
+ steps:
5
+ - bundle-install
6
+ - script:
7
+ name: rspec
8
+ code: bundle exec rspec
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: device_cloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
5
- prerelease:
4
+ version: 0.2.4
6
5
  platform: ruby
7
6
  authors:
8
7
  - Erik Straub
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-10-23 00:00:00.000000000 Z
11
+ date: 2014-09-02 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: bundler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rake
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ! '>='
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ! '>='
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ! '>='
52
46
  - !ruby/object:Gem::Version
@@ -54,20 +48,62 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: awesome_print
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
58
93
  requirements:
59
94
  - - ! '>='
60
95
  - !ruby/object:Gem::Version
61
96
  version: '0'
62
97
  description: A Ruby wrapper for the Etherios Device Cloud
63
98
  email:
64
- - erik@madgloryint.com
99
+ - erik@madglory.com
65
100
  executables: []
66
101
  extensions: []
67
102
  extra_rdoc_files: []
68
103
  files:
69
104
  - .gitignore
70
105
  - .rspec
106
+ - .ruby-gemset
71
107
  - .ruby-version
72
108
  - Gemfile
73
109
  - LICENSE.txt
@@ -76,6 +112,7 @@ files:
76
112
  - device_cloud.gemspec
77
113
  - lib/device_cloud.rb
78
114
  - lib/device_cloud/configuration.rb
115
+ - lib/device_cloud/monitor.rb
79
116
  - lib/device_cloud/push_notification.rb
80
117
  - lib/device_cloud/push_notification/alert_notification.rb
81
118
  - lib/device_cloud/push_notification/base_notification.rb
@@ -83,8 +120,11 @@ files:
83
120
  - lib/device_cloud/push_notification/event_notification.rb
84
121
  - lib/device_cloud/push_notification/message.rb
85
122
  - lib/device_cloud/push_notification/message/file_data.rb
123
+ - lib/device_cloud/request.rb
124
+ - lib/device_cloud/response.rb
86
125
  - lib/device_cloud/utils.rb
87
126
  - lib/device_cloud/version.rb
127
+ - spec/device_cloud/monitor_spec.rb
88
128
  - spec/device_cloud/push_notification/alert_notification_spec.rb
89
129
  - spec/device_cloud/push_notification/base_notification_spec.rb
90
130
  - spec/device_cloud/push_notification/data_notification_spec.rb
@@ -92,34 +132,37 @@ files:
92
132
  - spec/device_cloud/push_notification/message/file_data_spec.rb
93
133
  - spec/device_cloud/push_notification/message_spec.rb
94
134
  - spec/device_cloud/push_notification_spec.rb
135
+ - spec/device_cloud/request_spec.rb
136
+ - spec/device_cloud/response_spec.rb
95
137
  - spec/spec_helper.rb
96
138
  - spec/version_spec.rb
97
- homepage: http://github.com/madgloryint/device_cloud
139
+ - wercker.yml
140
+ homepage: http://github.com/madglory/device_cloud
98
141
  licenses:
99
142
  - MIT
143
+ metadata: {}
100
144
  post_install_message:
101
145
  rdoc_options: []
102
146
  require_paths:
103
147
  - lib
104
148
  required_ruby_version: !ruby/object:Gem::Requirement
105
- none: false
106
149
  requirements:
107
150
  - - ! '>='
108
151
  - !ruby/object:Gem::Version
109
152
  version: '0'
110
153
  required_rubygems_version: !ruby/object:Gem::Requirement
111
- none: false
112
154
  requirements:
113
155
  - - ! '>='
114
156
  - !ruby/object:Gem::Version
115
157
  version: '0'
116
158
  requirements: []
117
159
  rubyforge_project:
118
- rubygems_version: 1.8.23
160
+ rubygems_version: 2.2.1
119
161
  signing_key:
120
- specification_version: 3
162
+ specification_version: 4
121
163
  summary: A Ruby wrapper for the Etherios Device Cloud
122
164
  test_files:
165
+ - spec/device_cloud/monitor_spec.rb
123
166
  - spec/device_cloud/push_notification/alert_notification_spec.rb
124
167
  - spec/device_cloud/push_notification/base_notification_spec.rb
125
168
  - spec/device_cloud/push_notification/data_notification_spec.rb
@@ -127,5 +170,7 @@ test_files:
127
170
  - spec/device_cloud/push_notification/message/file_data_spec.rb
128
171
  - spec/device_cloud/push_notification/message_spec.rb
129
172
  - spec/device_cloud/push_notification_spec.rb
173
+ - spec/device_cloud/request_spec.rb
174
+ - spec/device_cloud/response_spec.rb
130
175
  - spec/spec_helper.rb
131
176
  - spec/version_spec.rb