device_cloud 0.2.3 → 0.2.4

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.
@@ -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