From 9dc0688fb164800996dc3ad8c22079c08fa01336 Mon Sep 17 00:00:00 2001 From: Erik Straub Date: Tue, 11 Feb 2014 12:03:02 -0500 Subject: [PATCH 1/7] Added Alarm, AlarmTemplate, specs for AlarmTemplate --- lib/device_cloud.rb | 2 + lib/device_cloud/alarm.rb | 59 ++++++++++++ lib/device_cloud/alarm_template.rb | 37 ++++++++ spec/device_cloud/alarm_template_spec.rb | 114 +++++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 lib/device_cloud/alarm.rb create mode 100644 lib/device_cloud/alarm_template.rb create mode 100644 spec/device_cloud/alarm_template_spec.rb diff --git a/lib/device_cloud.rb b/lib/device_cloud.rb index 4492862..13be4c7 100644 --- a/lib/device_cloud.rb +++ b/lib/device_cloud.rb @@ -1,6 +1,8 @@ require 'device_cloud/configuration' require 'device_cloud/utils' require 'device_cloud/version' +require 'device_cloud/alarm' +require 'device_cloud/alarm_template' require 'device_cloud/monitor' require 'device_cloud/push_notification' require 'device_cloud/push_notification/base_notification' diff --git a/lib/device_cloud/alarm.rb b/lib/device_cloud/alarm.rb new file mode 100644 index 0000000..54669f9 --- /dev/null +++ b/lib/device_cloud/alarm.rb @@ -0,0 +1,59 @@ +module DeviceCloud + class Alarm + + attr_accessor :almId, :cstId, :almtId, :grpId, :almName, :almDescription, :almEnabled, :almPriority, :almScopeConfig, :almRuleConfig + attr_reader :error + + DEFAULT_CREATE_OPTIONS = { + + } + + class << self + def all + alarms = DeviceCloud::Request.new('/ws/Alarm/.json').get.to_hash_from_json + + return [] unless alarms['resultSize'].to_i > 0 + + alarms['items'].map { |alarm| + Alarm.new alarm + } + end + + def find(id) + alarms = DeviceCloud::Request.new("/ws/Alarm/#{id}.json").get.to_hash_from_json + + return nil unless alarms['resultSize'].to_i > 0 + + Alarm.new alarms['items'].first + end + end + + def initialize(attributes = {}) + attributes.each do |name, value| + send("#{name}=", value) + end + + @error = nil + + set_defaults + end + + def persist! + @error = nil + almId.nil? ? create : update + end + + private + def set_defaults + puts 'setting defaults...' + end + + def create + puts 'creating...' + end + + def update + puts 'updating... ' + end + end +end diff --git a/lib/device_cloud/alarm_template.rb b/lib/device_cloud/alarm_template.rb new file mode 100644 index 0000000..ff78a82 --- /dev/null +++ b/lib/device_cloud/alarm_template.rb @@ -0,0 +1,37 @@ +module DeviceCloud + class AlarmTemplate + + attr_reader :almtId, :almtName, :almtDescription, :grpId, :almtTopic, :almtScopeOptions, :almtRules, :almtResourceList + + class << self + def all + templates = DeviceCloud::Request.new(path: '/ws/AlarmTemplate/.json').get.to_hash_from_json + + return [] unless templates['resultSize'].to_i > 0 + + templates['items'].map { |template| + AlarmTemplate.new template + } + end + + def find(id) + templates = DeviceCloud::Request.new(path: "/ws/AlarmTemplate/#{id}.json").get.to_hash_from_json + + return nil unless templates['resultSize'].to_i > 0 + + AlarmTemplate.new templates['items'].first + end + end + + def initialize(attributes = {}) + attributes.each do |name, value| + instance_variable_set("@#{name}", value) + end + end + + private + def almtId=(value) + @almtId = value.to_i + end + end +end diff --git a/spec/device_cloud/alarm_template_spec.rb b/spec/device_cloud/alarm_template_spec.rb new file mode 100644 index 0000000..9264df5 --- /dev/null +++ b/spec/device_cloud/alarm_template_spec.rb @@ -0,0 +1,114 @@ +require 'spec_helper' + +describe DeviceCloud::AlarmTemplate do + + its(:almtId) { should be_nil } + its(:almtName) { should be_nil } + its(:almtDescription) { should be_nil } + its(:grpId) { should be_nil } + its(:almtTopic) { should be_nil } + its(:almtScopeOptions) { should be_nil } + its(:almtRules) { should be_nil } + its(:almtResourceList) { should be_nil } + + context 'when attributes given' do + let(:attributes) do + { + almtId: 'foo', + almtName: 'foo', + almtDescription: 'foo', + grpId: 'foo', + almtTopic: 'foo', + almtScopeOptions: 'foo', + almtRules: 'foo', + almtResourceList: 'foo' + } + end + + subject { DeviceCloud::AlarmTemplate.new attributes } + + its(:almtId) { should eq 'foo' } + its(:almtName) { should eq 'foo' } + its(:almtDescription) { should eq 'foo' } + its(:grpId) { should eq 'foo' } + its(:almtTopic) { should eq 'foo' } + its(:almtScopeOptions) { should eq 'foo' } + its(:almtRules) { should eq 'foo' } + its(:almtResourceList) { should eq 'foo' } + end + + describe '::all' do + before(:each) do + stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/AlarmTemplate/.json"). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => response_body, :headers => {}) + end + + subject { DeviceCloud::AlarmTemplate.all } + + context 'when results are found' do + let(:response_body) do + { + "resultTotalRows" => "1", + "requestedStartRow" => "0", + "resultSize" => "1", + "requestedSize" => "1000", + "remainingSize" => "0", + "items" => [ + { + "almtId" => "2", + "almtName" => "Device Offline", + "almtDescription" => "Detects when a device disconnects from Device Cloud and fails to reconnected within the specified time", + "grpId" => "1", + "almtTopic" => "Alarm.DeviceOffline", + "almtScopeOptions" => { + "ScopingOptions" => { + "Scope" => "" + } + }, + "almtRules" => { + "Rules" => { + "FireRule" => { "Variable" => "" }, "ResetRule" => "" } + }, + "almtResourceList" => "DeviceCore,AlarmStatus" + } + ] + }.to_json + end + + it { should be_a(Array) } + its(:first) { should be_a(DeviceCloud::AlarmTemplate) } + its(:size) { should eq 1 } + end + + context 'when results are not found' do + let(:response_body) { "{\n \"resultTotalRows\": \"0\",\n \"requestedStartRow\": \"0\",\n \"resultSize\": \"0\",\n \"requestedSize\": \"1000\",\n \"remainingSize\": \"0\",\n \"items\": [\n ]\n }\n" } + + it { should eq [] } + end + end + + describe '::find' do + before(:each) do + stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/AlarmTemplate/2.json"). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => response_body, :headers => {}) + end + + subject { DeviceCloud::AlarmTemplate.find 2 } + + context 'when result is found' do + let(:response_body) do + "{\n \"resultTotalRows\": \"1\",\n \"requestedStartRow\": \"0\",\n \"resultSize\": \"1\",\n \"requestedSize\": \"1000\",\n \"remainingSize\": \"0\",\n \"items\": [\n{ \"almtId\": \"2\", \"almtName\": \"Device Offline\", \"almtDescription\": \"Detects when a device disconnects from Device Cloud and fails to reconnected within the specified time\", \"grpId\": \"1\", \"almtTopic\": \"Alarm.DeviceOffline\", \"almtScopeOptions\": { \"ScopingOptions\": { \"Scope\": \"\", \"Scope\": \"\"}}, \"almtRules\": { \"Rules\": { \"FireRule\": { \"Variable\": \"\"}, \"ResetRule\": \"\"}}, \"almtResourceList\": \"DeviceCore,AlarmStatus\"}\n ]\n }\n" + end + + it { should be_a(DeviceCloud::AlarmTemplate) } + end + + context 'when result is not found' do + let(:response_body) { "{\n \"error\": [\"GET AlarmTemplate error. Error reading AlarmTemplate entity id='2'\"]\n}" } + + it { should be_nil } + end + end +end From 7a0a361468756438e06f67f4ea6bdc29074676aa Mon Sep 17 00:00:00 2001 From: Erik Straub Date: Wed, 12 Feb 2014 11:10:14 -0500 Subject: [PATCH 2/7] Added nori dependency for XML parsing. --- device_cloud.gemspec | 18 ++++++++++-------- lib/device_cloud.rb | 1 + lib/device_cloud/configuration.rb | 14 ++++++++++++++ lib/device_cloud/response.rb | 4 ++++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/device_cloud.gemspec b/device_cloud.gemspec index a9cc6de..9e9d4a6 100644 --- a/device_cloud.gemspec +++ b/device_cloud.gemspec @@ -4,22 +4,24 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'device_cloud/version' Gem::Specification.new do |spec| - spec.name = "device_cloud" + spec.name = 'device_cloud' spec.version = DeviceCloud::VERSION - spec.authors = ["Erik Straub"] - spec.email = ["erik@madgloryint.com"] + spec.authors = ['Erik Straub'] + spec.email = ['erik@madgloryint.com'] spec.description = %q{A Ruby wrapper for the Etherios Device Cloud} spec.summary = %q{A Ruby wrapper for the Etherios Device Cloud} - spec.homepage = "http://github.com/madgloryint/device_cloud" - spec.license = "MIT" + spec.homepage = 'http://github.com/madgloryint/device_cloud' + spec.license = 'MIT' spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) - spec.require_paths = ["lib"] + spec.require_paths = ['lib'] - spec.add_development_dependency "bundler", "~> 1.3" - spec.add_development_dependency "rake" + spec.add_dependency 'nori', '~> 2.3' + + spec.add_development_dependency 'bundler', '~> 1.3' + spec.add_development_dependency 'rake' spec.add_development_dependency 'rspec' spec.add_development_dependency 'webmock' spec.add_development_dependency 'pry' diff --git a/lib/device_cloud.rb b/lib/device_cloud.rb index 13be4c7..6f07429 100644 --- a/lib/device_cloud.rb +++ b/lib/device_cloud.rb @@ -13,6 +13,7 @@ require 'device_cloud/push_notification/message/file_data' require 'device_cloud/request' require 'device_cloud/response' +require 'nori' module DeviceCloud extend Configuration diff --git a/lib/device_cloud/configuration.rb b/lib/device_cloud/configuration.rb index 35d255d..bfd294e 100644 --- a/lib/device_cloud/configuration.rb +++ b/lib/device_cloud/configuration.rb @@ -110,5 +110,19 @@ def password def logger @logger ||= Logger.new(STDOUT) end + + # DeviceCloud logger + # + # @return the DeviceCloud logger or set the default to stdout + def logger + @logger ||= Logger.new(STDOUT) + end + + # DeviceCloud xml_parser + # + # @return the DeviceCloud xml_parser - default is Nori using rexml + def xml_parser + Nori.new(parser: :rexml) + end end end diff --git a/lib/device_cloud/response.rb b/lib/device_cloud/response.rb index b871023..bb47dd9 100644 --- a/lib/device_cloud/response.rb +++ b/lib/device_cloud/response.rb @@ -10,6 +10,10 @@ def initialize(http_response) @body = original_response.body end + def to_hash_from_xml + DeviceCloud.xml_parser.parse(body) + end + def to_hash_from_json JSON.parse(body) end From 4a6a5d633abc872afd566b319cbec808333de40b Mon Sep 17 00:00:00 2001 From: Erik Straub Date: Wed, 12 Feb 2014 11:14:03 -0500 Subject: [PATCH 3/7] Added Group for getting DC group info --- lib/device_cloud.rb | 1 + lib/device_cloud/group.rb | 39 ++++++++++++++++++ spec/device_cloud/group_spec.rb | 73 +++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 lib/device_cloud/group.rb create mode 100644 spec/device_cloud/group_spec.rb diff --git a/lib/device_cloud.rb b/lib/device_cloud.rb index 6f07429..fe9766a 100644 --- a/lib/device_cloud.rb +++ b/lib/device_cloud.rb @@ -3,6 +3,7 @@ require 'device_cloud/version' require 'device_cloud/alarm' require 'device_cloud/alarm_template' +require 'device_cloud/group' require 'device_cloud/monitor' require 'device_cloud/push_notification' require 'device_cloud/push_notification/base_notification' diff --git a/lib/device_cloud/group.rb b/lib/device_cloud/group.rb new file mode 100644 index 0000000..ea87b83 --- /dev/null +++ b/lib/device_cloud/group.rb @@ -0,0 +1,39 @@ +module DeviceCloud + class Group + attr_reader :grpId, :grpName, :grpDescription, :grpPath, :grpParentId + + ALLOWED_ATTRIBUTES = %w{ + grpId + grpName + grpDescription + grpPath + grpParentId + } + + class << self + def all + groups = DeviceCloud::Request.new(path: '/ws/Group/.json').get.to_hash_from_json + + return [] unless groups['resultSize'].to_i > 0 + + groups['items'].map { |group| + Group.new group + } + end + + def find(group_id) + groups = DeviceCloud::Request.new(path: "/ws/Group/#{group_id}.json").get.to_hash_from_json + + return nil unless groups['resultSize'].to_i > 0 + + Group.new groups['items'].first + end + end + + def initialize(attributes = {}) + ALLOWED_ATTRIBUTES.each do |attr_name| + instance_variable_set("@#{attr_name}", attributes[attr_name]) + end + end + end +end diff --git a/spec/device_cloud/group_spec.rb b/spec/device_cloud/group_spec.rb new file mode 100644 index 0000000..c650877 --- /dev/null +++ b/spec/device_cloud/group_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe DeviceCloud::Group do + describe '::all' do + before(:each) do + stub_request(:get, authenticated_host + '/ws/Group/.json'). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => groups_json, :headers => {}) + end + + subject { DeviceCloud::Group.all } + + context 'with results' do + let(:groups_json) do + "{\"resultTotalRows\": \"2\",\"requestedStartRow\": \"0\",\"resultSize\": \"2\",\"requestedSize\": \"1000\",\"remainingSize\": \"0\",\"items\": [{ \"grpId\": \"4946\", \"grpName\": \"Your_Company\", \"grpDescription\": \"Your_Company root group\", \"grpPath\": \"/Your_Company/\", \"grpParentId\": \"1\"},{ \"grpId\": \"9769\", \"grpName\": \"Sub_Group\", \"grpPath\": \"/Your_Company/Sub_Group/\", \"grpParentId\": \"4946\"}] }" + end + + it { should be_a(Array) } + its(:size) { should eq 2 } + its(:first) { should be_a(DeviceCloud::Group) } + end + + context 'with no results' do + let(:groups_json) do + "{\"resultTotalRows\": \"0\",\"requestedStartRow\": \"0\",\"resultSize\": \"0\",\"requestedSize\": \"1000\",\"remainingSize\": \"0\",\"items\": [] }" + end + + it { should eq [] } + end + end + + describe '::find' do + before(:each) do + stub_request(:get, authenticated_host + '/ws/Group/123.json'). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => group_json, :headers => {}) + end + + subject { DeviceCloud::Group.find 123 } + + context 'with result' do + let(:group_result) do + {"resultTotalRows"=>"1", + "requestedStartRow"=>"0", + "resultSize"=>"1", + "requestedSize"=>"1000", + "remainingSize"=>"0", + "items"=> + [{"grpId"=>"9769", + "grpDescription"=>"Describing this group.", + "grpName"=>"Your_Company", + "grpPath"=>"/Your_Company/Your_Group/", + "grpParentId"=>"4946"}]} + end + let(:group_json) { group_result.to_json } + + it { should be_a(DeviceCloud::Group) } + its(:grpId) { should eq group_result['items'].first['grpId'] } + its(:grpDescription) { should eq group_result['items'].first['grpDescription'] } + its(:grpName) { should eq group_result['items'].first['grpName'] } + its(:grpPath) { should eq group_result['items'].first['grpPath'] } + its(:grpParentId) { should eq group_result['items'].first['grpParentId'] } + end + + context 'with no results' do + let(:group_json) do + {"error"=>["GET Group error. Error reading Group entity id='12123'"]}.to_json + end + + it { should be_nil } + end + end +end From 33e8be02b49b5b7daf500b476194106c3740b46c Mon Sep 17 00:00:00 2001 From: Erik Straub Date: Wed, 12 Feb 2014 11:35:55 -0500 Subject: [PATCH 4/7] Added ability to search groups by grpParentId --- lib/device_cloud/group.rb | 14 ++++++++++ spec/device_cloud/group_spec.rb | 47 ++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/lib/device_cloud/group.rb b/lib/device_cloud/group.rb index ea87b83..2102f30 100644 --- a/lib/device_cloud/group.rb +++ b/lib/device_cloud/group.rb @@ -28,6 +28,20 @@ def find(group_id) Group.new groups['items'].first end + + def find_all_by_parent_id(parent_id) + parent_id = parent_id.to_i + + path = "/ws/Group/.json?condition=grpParentId='#{parent_id}'" + + groups = DeviceCloud::Request.new(path: path).get.to_hash_from_json + + return [] unless groups['resultSize'].to_i > 0 + + groups['items'].map { |group| + Group.new group + } + end end def initialize(attributes = {}) diff --git a/spec/device_cloud/group_spec.rb b/spec/device_cloud/group_spec.rb index c650877..5107152 100644 --- a/spec/device_cloud/group_spec.rb +++ b/spec/device_cloud/group_spec.rb @@ -4,8 +4,8 @@ describe '::all' do before(:each) do stub_request(:get, authenticated_host + '/ws/Group/.json'). - with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). - to_return(:status => 200, :body => groups_json, :headers => {}) + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => groups_json, :headers => {}) end subject { DeviceCloud::Group.all } @@ -32,8 +32,8 @@ describe '::find' do before(:each) do stub_request(:get, authenticated_host + '/ws/Group/123.json'). - with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). - to_return(:status => 200, :body => group_json, :headers => {}) + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => group_json, :headers => {}) end subject { DeviceCloud::Group.find 123 } @@ -70,4 +70,43 @@ it { should be_nil } end end + + describe '::find_all_by_parent_id' do + before(:each) do + stub_request(:get, authenticated_host + '/ws/Group/.json?condition=grpParentId=\'1\''). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => group_json, :headers => {}) + end + + subject { DeviceCloud::Group.find_all_by_parent_id 1 } + + context 'with result' do + let(:group_result) do + {"resultTotalRows"=>"1", + "requestedStartRow"=>"0", + "resultSize"=>"1", + "requestedSize"=>"1000", + "remainingSize"=>"0", + "items"=> + [{"grpId"=>"9769", + "grpDescription"=>"Describing this group.", + "grpName"=>"Your_Company", + "grpPath"=>"/Your_Company/Your_Group/", + "grpParentId"=>"1"}]} + end + let(:group_json) { group_result.to_json } + + it { should be_a(Array) } + its(:first) { should be_a(DeviceCloud::Group) } + its(:size) { should eq 1 } + end + + context 'with no results' do + let(:group_json) do + "{\"resultTotalRows\": \"0\",\"requestedStartRow\": \"0\",\"resultSize\": \"0\",\"requestedSize\": \"1000\",\"remainingSize\": \"0\",\"items\": [] }" + end + + it { should eq [] } + end + end end From 1c273dae35eb0f7c7b160a160019a77cb862ea1f Mon Sep 17 00:00:00 2001 From: Erik Straub Date: Fri, 14 Feb 2014 14:45:29 -0500 Subject: [PATCH 5/7] AlarmTemplate now retrieves XML info, over JSON --- lib/device_cloud/alarm_template.rb | 31 +++---- spec/device_cloud/alarm_template_spec.rb | 109 ++++++++++++++++------- 2 files changed, 94 insertions(+), 46 deletions(-) diff --git a/lib/device_cloud/alarm_template.rb b/lib/device_cloud/alarm_template.rb index ff78a82..9d7a255 100644 --- a/lib/device_cloud/alarm_template.rb +++ b/lib/device_cloud/alarm_template.rb @@ -5,21 +5,27 @@ class AlarmTemplate class << self def all - templates = DeviceCloud::Request.new(path: '/ws/AlarmTemplate/.json').get.to_hash_from_json - - return [] unless templates['resultSize'].to_i > 0 - - templates['items'].map { |template| - AlarmTemplate.new template - } + response = DeviceCloud::Request.new(path: '/ws/AlarmTemplate').get + templates = response.to_hash_from_xml + + return [] unless response.code == '200' && templates['result']['resultSize'].to_i > 0 + + if templates['result']['resultSize'].to_i == 1 + [AlarmTemplate.new(templates['result']['AlarmTemplate'])] + else + templates['result']['AlarmTemplate'].map { |template| + AlarmTemplate.new template + } + end end def find(id) - templates = DeviceCloud::Request.new(path: "/ws/AlarmTemplate/#{id}.json").get.to_hash_from_json + response = DeviceCloud::Request.new(path: "/ws/AlarmTemplate/#{id}").get + templates = response.to_hash_from_xml - return nil unless templates['resultSize'].to_i > 0 + return nil unless response.code == '200' && templates['result']['resultSize'].to_i == 1 - AlarmTemplate.new templates['items'].first + AlarmTemplate.new(templates['result']['AlarmTemplate']) end end @@ -28,10 +34,5 @@ def initialize(attributes = {}) instance_variable_set("@#{name}", value) end end - - private - def almtId=(value) - @almtId = value.to_i - end end end diff --git a/spec/device_cloud/alarm_template_spec.rb b/spec/device_cloud/alarm_template_spec.rb index 9264df5..3f270d9 100644 --- a/spec/device_cloud/alarm_template_spec.rb +++ b/spec/device_cloud/alarm_template_spec.rb @@ -39,7 +39,7 @@ describe '::all' do before(:each) do - stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/AlarmTemplate/.json"). + stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/AlarmTemplate"). with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). to_return(:status => 200, :body => response_body, :headers => {}) end @@ -48,32 +48,36 @@ context 'when results are found' do let(:response_body) do - { - "resultTotalRows" => "1", - "requestedStartRow" => "0", - "resultSize" => "1", - "requestedSize" => "1000", - "remainingSize" => "0", - "items" => [ - { - "almtId" => "2", - "almtName" => "Device Offline", - "almtDescription" => "Detects when a device disconnects from Device Cloud and fails to reconnected within the specified time", - "grpId" => "1", - "almtTopic" => "Alarm.DeviceOffline", - "almtScopeOptions" => { - "ScopingOptions" => { - "Scope" => "" - } - }, - "almtRules" => { - "Rules" => { - "FireRule" => { "Variable" => "" }, "ResetRule" => "" } - }, - "almtResourceList" => "DeviceCore,AlarmStatus" - } - ] - }.to_json + %{ + + 1 + 0 + 1 + 1000 + 0 + + 2 + Device Offline + Detects when a device disconnects from Device Cloud and fails to reconnected within the specified time + 1 + Alarm.DeviceOffline + + + + + + + + + + + + + + + DeviceCore,AlarmStatus + + } end it { should be_a(Array) } @@ -82,7 +86,16 @@ end context 'when results are not found' do - let(:response_body) { "{\n \"resultTotalRows\": \"0\",\n \"requestedStartRow\": \"0\",\n \"resultSize\": \"0\",\n \"requestedSize\": \"1000\",\n \"remainingSize\": \"0\",\n \"items\": [\n ]\n }\n" } + let(:response_body) do + %{ + + 0 + 0 + 0 + 1000 + 0 + } + end it { should eq [] } end @@ -90,7 +103,7 @@ describe '::find' do before(:each) do - stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/AlarmTemplate/2.json"). + stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/AlarmTemplate/2"). with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). to_return(:status => 200, :body => response_body, :headers => {}) end @@ -99,14 +112,48 @@ context 'when result is found' do let(:response_body) do - "{\n \"resultTotalRows\": \"1\",\n \"requestedStartRow\": \"0\",\n \"resultSize\": \"1\",\n \"requestedSize\": \"1000\",\n \"remainingSize\": \"0\",\n \"items\": [\n{ \"almtId\": \"2\", \"almtName\": \"Device Offline\", \"almtDescription\": \"Detects when a device disconnects from Device Cloud and fails to reconnected within the specified time\", \"grpId\": \"1\", \"almtTopic\": \"Alarm.DeviceOffline\", \"almtScopeOptions\": { \"ScopingOptions\": { \"Scope\": \"\", \"Scope\": \"\"}}, \"almtRules\": { \"Rules\": { \"FireRule\": { \"Variable\": \"\"}, \"ResetRule\": \"\"}}, \"almtResourceList\": \"DeviceCore,AlarmStatus\"}\n ]\n }\n" + %{ + + 1 + 0 + 1 + 1000 + 0 + + 2 + Device Offline + Detects when a device disconnects from Device Cloud and fails to reconnected within the specified time + 1 + Alarm.DeviceOffline + + + + + + + + + + + + + + + DeviceCore,AlarmStatus + + } end it { should be_a(DeviceCloud::AlarmTemplate) } end context 'when result is not found' do - let(:response_body) { "{\n \"error\": [\"GET AlarmTemplate error. Error reading AlarmTemplate entity id='2'\"]\n}" } + let(:response_body) do + %{ + + GET AlarmTemplate error. Error reading AlarmTemplate entity id='212' + } + end it { should be_nil } end From 357489b29f6f1b5b318a82f56a2c70c563ef748a Mon Sep 17 00:00:00 2001 From: Erik Straub Date: Mon, 17 Feb 2014 17:37:14 -0500 Subject: [PATCH 6/7] Added Alarm specs --- lib/device_cloud/alarm.rb | 160 ++++++++++++++++++--- spec/device_cloud/alarm_spec.rb | 247 ++++++++++++++++++++++++++++++++ 2 files changed, 384 insertions(+), 23 deletions(-) create mode 100644 spec/device_cloud/alarm_spec.rb diff --git a/lib/device_cloud/alarm.rb b/lib/device_cloud/alarm.rb index 54669f9..1652f4b 100644 --- a/lib/device_cloud/alarm.rb +++ b/lib/device_cloud/alarm.rb @@ -1,59 +1,173 @@ module DeviceCloud class Alarm - attr_accessor :almId, :cstId, :almtId, :grpId, :almName, :almDescription, :almEnabled, :almPriority, :almScopeConfig, :almRuleConfig - attr_reader :error - - DEFAULT_CREATE_OPTIONS = { - - } + attr_accessor :almId, :cstId, :almtId, :grpId, :almName, :almDescription, :almScopeConfig, :almRuleConfig + attr_reader :error, :almEnabled, :almPriority class << self def all - alarms = DeviceCloud::Request.new('/ws/Alarm/.json').get.to_hash_from_json - - return [] unless alarms['resultSize'].to_i > 0 - - alarms['items'].map { |alarm| - Alarm.new alarm - } + response = DeviceCloud::Request.new(path: '/ws/Alarm').get + alarms = response.to_hash_from_xml + + return [] unless response.code == '200' && alarms['result']['resultSize'].to_i > 0 + + if alarms['result']['resultSize'].to_i == 1 + [initialize_proper_alarm_type(alarms['result']['Alarm'])] + else + alarms['result']['Alarm'].map { |alarm| + initialize_proper_alarm_type(alarm) + } + end end def find(id) - alarms = DeviceCloud::Request.new("/ws/Alarm/#{id}.json").get.to_hash_from_json + response = DeviceCloud::Request.new(path: "/ws/Alarm/#{id}").get + alarms = response.to_hash_from_xml - return nil unless alarms['resultSize'].to_i > 0 + return nil unless response.code == '200' && alarms['result']['resultSize'].to_i > 0 - Alarm.new alarms['items'].first + if alarms['result']['resultSize'].to_i == 1 + initialize_proper_alarm_type(alarms['result']['Alarm']) + else + initialize_proper_alarm_type(alarms['result']['Alarm'].first) + end + end + + protected + def initialize_proper_alarm_type(alarm) + case alarm['almtId'] + when '2' then DeviceDisconnectAlarm.new(alarm) + else Alarm.new(alarm) + end end end def initialize(attributes = {}) + set_defaults + attributes.each do |name, value| send("#{name}=", value) end + end - @error = nil + def almEnabled=(value) + @almEnabled = !!value + end - set_defaults + def almPriority=(value) + raise DeviceCloud::Error, 'almPriority must be 0 (high), 1 (medium), or 2 (low)' unless [0,1,2].include?(value.to_i) + @almPriority = value.to_i end def persist! - @error = nil + remove_instance_variable '@error' if error almId.nil? ? create : update end - private + def destroy! + return false if almId.nil? + + response = DeviceCloud::Request.new(path: "/ws/Alarm/#{almId}").delete + + response.code == '200' ? true : false + end + + def attributes + available_attributes.inject({}) do |memo, attr_name| + memo[attr_name] = send(attr_name) unless send(attr_name).nil? + memo + end + end + + protected def set_defaults - puts 'setting defaults...' + # no-op + end + + def available_attributes + %w{ + almtId + almName + almDescription + almPriority + almEnabled + grpId + } end def create - puts 'creating...' + response = DeviceCloud::Request.new(path: '/ws/Alarm', body: to_xml).post + xml_result = response.to_hash_from_xml['result'] + + if response.code == '201' + self.almId = xml_result['location'].sub /Alarm\//, '' + return true + else + @error = xml_result['error'] + return false + end end def update - puts 'updating... ' + response = DeviceCloud::Request.new(path: "/ws/Alarm/#{almId}", body: to_xml).put + + if response.code == '200' + return true + else + @error = error_from_response_xml(response) + return false + end + end + + def to_xml + xml = '' + attributes.each do |key,value| + xml << "<#{key}>#{value}" + end + xml << alarm_scope_config_xml + xml << alarm_rule_config_xml + xml << '' + end + + # Method to set the Alarm's scope + # + # Example: + # + # + # + # + # + def alarm_scope_config_xml + raise NotImplementedError + end + + # Method to set the Alarm's rules + # + # Example: + # + # + # + # + # + # + # + # + # + # + # + # + # + # + # + # + # + # + def alarm_rule_config_xml + raise NotImplementedError + end + + def error_from_response_xml(response) + response.body.match(/(.*)<\/error>/)[1] end end end diff --git a/spec/device_cloud/alarm_spec.rb b/spec/device_cloud/alarm_spec.rb new file mode 100644 index 0000000..1f86a87 --- /dev/null +++ b/spec/device_cloud/alarm_spec.rb @@ -0,0 +1,247 @@ +require 'spec_helper' + +describe DeviceCloud::Alarm do + its(:almId) { should be_nil } + its(:cstId) { should be_nil } + its(:almtId) { should be_nil } + its(:grpId) { should be_nil } + its(:almName) { should be_nil } + its(:almDescription) { should be_nil } + its(:almScopeConfig) { should be_nil } + its(:almRuleConfig) { should be_nil } + its(:almEnabled) { should be_nil } + its(:almPriority) { should be_nil } + its(:error) { should be_nil } + + context 'when attributes given' do + let(:attributes) do + { + almId: 'foo', + cstId: 'foo', + almtId: 'foo', + grpId: 'foo', + almName: 'foo', + almDescription: 'foo', + almScopeConfig: 'foo', + almRuleConfig: 'foo', + almEnabled: true, + almPriority: 2 + } + end + + subject { DeviceCloud::Alarm.new attributes} + + its(:almId) { should eq 'foo' } + its(:cstId) { should eq 'foo' } + its(:almtId) { should eq 'foo' } + its(:grpId) { should eq 'foo' } + its(:almName) { should eq 'foo' } + its(:almDescription) { should eq 'foo' } + its(:almScopeConfig) { should eq 'foo' } + its(:almRuleConfig) { should eq 'foo' } + its(:almEnabled) { should eq true } + its(:almPriority) { should eq 2 } + end + + describe '::all' do + let(:group_body) do + %{{"resultTotalRows": "1","requestedStartRow": "0","resultSize": "1","requestedSize": "1000","remainingSize": "0","items": [{ "grpId": "9769", "grpName": "Staging", "grpPath": "/4044_MadGlory_Interactive/Staging/", "grpParentId": "4946"}] }} + end + + before(:each) do + stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/Alarm"). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => response_body, :headers => {}) + end + + subject { DeviceCloud::Alarm.all } + + context 'when results are found' do + before(:each) do + stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/Group/9769.json"). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => group_body, :headers => {}) + end + + let(:response_body) do + %{ + + 1 + 0 + 1 + 1000 + 0 + + 1898 + 4044 + #{almtId} + 9769 + Staging Device Disconnect + Detects when a device disconnects from Device Cloud and fails to reconnected within the specified time + true + 1 + + + + + + + + + + + + + + + } + end + + + context 'result has a known almtId' do + let(:almtId) { 2 } + + it { should be_a(Array) } + its(:first) { should be_a(DeviceCloud::DeviceDisconnectAlarm) } + end + + context 'result has unknown almtId' do + let(:almtId) { 26 } + + it { should be_a(Array) } + its(:first) { should be_a(DeviceCloud::Alarm) } + end + end + + context 'when results are not found' do + let(:response_body) do + %{ + + 0 + 0 + 0 + 1000 + 0 + } + end + + it { should eq [] } + end + end + + describe '::find' do + before(:each) do + stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/Alarm/1898"). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => response_body, :headers => {}) + end + + subject { DeviceCloud::Alarm.find 1898 } + + context 'when result is found' do + let(:group_body) do + %{{"resultTotalRows": "1","requestedStartRow": "0","resultSize": "1","requestedSize": "1000","remainingSize": "0","items": [{ "grpId": "9769", "grpName": "Staging", "grpPath": "/4044_MadGlory_Interactive/Staging/", "grpParentId": "4946"}] }} + end + + before(:each) do + stub_request(:get, "https://foouser:barpass@my.idigi.com/ws/Group/9769.json"). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => 200, :body => group_body, :headers => {}) + end + + let(:response_body) do + %{ + + 1 + 0 + 1 + 1000 + 0 + + 1898 + 4044 + #{almtId} + 9769 + Staging Device Disconnect + Detects when a device disconnects from Device Cloud and fails to reconnected within the specified time + true + 1 + + + + + + + + + + + + + + + } + end + + context 'with a known almtId' do + let(:almtId) { 2 } + it { should be_a(DeviceCloud::DeviceDisconnectAlarm) } + end + + context 'with an unknown almtId' do + let(:almtId) { 26 } + it { should be_a(DeviceCloud::Alarm)} + end + end + + context 'when result is not found' do + let(:response_body) do + %{ + + GET AlarmTemplate error. Error reading AlarmTemplate entity id='212' + } + end + + it { should be_nil } + end + end + + describe '#persist!' do + subject { DeviceCloud::Alarm.new } + + it 'raises NotImplementedError' do + expect { subject.persist! }.to raise_error(NotImplementedError) + end + end + + describe '#destroy!' do + context 'when almId is nil' do + its(:destroy!) { should be_false } + end + + context 'when almId is not nil' do + let(:existing_alarm) { DeviceCloud::Alarm.new almId: 12345 } + before(:each) do + stub_request(:delete, "https://foouser:barpass@my.idigi.com/ws/Alarm/12345"). + with(:headers => {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}). + to_return(:status => response_status, :body => response_body, :headers => {}) + end + + subject { existing_alarm.destroy! } + + context 'when successful' do + let(:response_body) { "\n\n 1 items deleted\n" } + let(:response_status) { 200 } + + it { should be_true } + end + + context 'when unsuccessful' do + let(:response_body) { "\n\n DELETE Alarm error. Invalid request. For input string: \"gigo\"\n" } + let(:response_status) { 400 } + + it { should be_false } + end + end + end +end From c3c700bc0f856f1941ebaf4c7c797454d6dce368 Mon Sep 17 00:00:00 2001 From: Erik Straub Date: Mon, 17 Feb 2014 17:43:01 -0500 Subject: [PATCH 7/7] Added DeviceDisconnectAlarm pending specs --- lib/device_cloud.rb | 1 + lib/device_cloud/device_disconnect_alarm.rb | 86 +++++++++++++++++++ .../device_disconnect_alarm_spec.rb | 5 ++ 3 files changed, 92 insertions(+) create mode 100644 lib/device_cloud/device_disconnect_alarm.rb create mode 100644 spec/device_cloud/device_disconnect_alarm_spec.rb diff --git a/lib/device_cloud.rb b/lib/device_cloud.rb index fe9766a..233294d 100644 --- a/lib/device_cloud.rb +++ b/lib/device_cloud.rb @@ -2,6 +2,7 @@ require 'device_cloud/utils' require 'device_cloud/version' require 'device_cloud/alarm' +require 'device_cloud/device_disconnect_alarm' require 'device_cloud/alarm_template' require 'device_cloud/group' require 'device_cloud/monitor' diff --git a/lib/device_cloud/device_disconnect_alarm.rb b/lib/device_cloud/device_disconnect_alarm.rb new file mode 100644 index 0000000..43e7461 --- /dev/null +++ b/lib/device_cloud/device_disconnect_alarm.rb @@ -0,0 +1,86 @@ +module DeviceCloud + class DeviceDisconnectAlarm < Alarm + attr_accessor :reconnectWindowDuration, :reset_on_reconnect, :device_id, :group + attr_reader :group + + def group=(group_object) + raise DeviceCloud::Error, 'Must be a DeviceCloud::Group' unless group_object.is_a?(DeviceCloud::Group) + + @group = group_object + self.grpId = group.grpId + end + + def initialize(attributes = {}) + super + load_group + load_device_id + self.almtId = 2 + end + + def persist! + raise DeviceCloud::Error, 'Must specify @group or @device_id.' unless group || device_id + super + end + private + def default_create_options + { + 'reconnectWindowDuration' => '5', + 'reset_on_reconnect' => true + } + end + + def set_defaults + super + default_create_options.each do |k,v| + send("#{k}=",v) + end + end + + def alarm_scope_config_xml + return '' if group.nil? && device_id.nil? + "\n#{scope_xml}\n" + end + + def scope_xml + group.nil? ? device_scope_xml : group_scope_xml + end + + def group_scope_xml + "" + end + + def device_scope_xml + "" + end + + def alarm_rule_config_xml + xml = "\n" + xml << fire_rule_xml + xml << reset_rule_xml + xml << "\n" + end + + def reset_rule_xml + xml = '\n" + end + + def fire_rule_xml + xml = '' + xml << '\n" + end + + def load_group + return if almScopeConfig.nil? || almScopeConfig['ScopingOptions']['Scope']['@name'] != 'Group' + @group ||= DeviceCloud::Group.find(grpId) + end + + def load_device_id + return if almScopeConfig.nil? || almScopeConfig['ScopingOptions']['Scope']['@name'] != 'Device' + @device_id ||= almScopeConfig['ScopingOptions']['Scope']['@value'] + end + end +end diff --git a/spec/device_cloud/device_disconnect_alarm_spec.rb b/spec/device_cloud/device_disconnect_alarm_spec.rb new file mode 100644 index 0000000..2c97a91 --- /dev/null +++ b/spec/device_cloud/device_disconnect_alarm_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe DeviceCloud::DeviceDisconnectAlarm do + pending 'Add specs' +end