amee 4.3.2 → 4.4.0
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.
- data/CHANGELOG.txt +6 -0
- data/Gemfile +7 -1
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/amee.gemspec +57 -6
- data/amee_test_credentials.example.yml +11 -0
- data/cassettes/AMEE_Connection/v2/raising_unhandled_errors.yml +36 -0
- data/cassettes/AMEE_Connection/v3/retries/502.yml +36 -0
- data/cassettes/AMEE_Connection/v3/retries/503.yml +36 -0
- data/cassettes/AMEE_Connection/v3/retries/504.yml +36 -0
- data/cassettes/AMEE_Connection/v3/retries/AMEE_TimeOut.yml +36 -0
- data/cassettes/AMEE_Connection/v3/should_be_able_to_get_from_meta_server.yml +30 -0
- data/cassettes/AMEE_Connection/v3/should_be_able_to_handle_failed_gets_from_meta_server.yml +30 -0
- data/cassettes/AMEE_Connection/v3/should_be_able_to_post_to_meta_server.yml +59 -0
- data/cassettes/AMEE_Connection/v3/should_have_a_connection_to_meta_server.yml +36 -0
- data/cassettes/AMEE_Connection/v3/should_login_and_know_the_path_to_the_server.yml +36 -0
- data/cassettes/AMEE_Connection_Caching_Off/authenticating.yml +36 -0
- data/cassettes/AMEE_Connection_Caching_Off/first_request.yml +40 -0
- data/cassettes/AMEE_Connection_Caching_Off/second_request.yml +40 -0
- data/cassettes/AMEE_Connection_Caching_On/authenticating.yml +36 -0
- data/cassettes/AMEE_Connection_Caching_On/first_request.yml +40 -0
- data/cassettes/AMEE_Connection_Caching_clear_all/second_request.yml +40 -0
- data/cassettes/AMEE_Connection_Caching_clear_manually/second_request.yml +40 -0
- data/cassettes/AMEE_Connection_Caching_further_down_tree/second_request.yml +79 -0
- data/cassettes/AMEE_Connection_with_authentication/handling_404s.yml +69 -0
- data/cassettes/AMEE_Connection_with_authentication/hitting_private_urls.yml +75 -0
- data/cassettes/AMEE_Connection_with_authentication/raising_errors_if_permission_denied.yml +69 -0
- data/cassettes/AMEE_Connection_with_authentication/should_re-authenticate_and_refresh_authtoken_when_authtoken_expires.yml +104 -0
- data/cassettes/AMEE_Connection_with_authentication/should_refresh_authtoken_when_authtoken_is_changed.yml +114 -0
- data/cassettes/AMEE_Connection_with_authentication/using_a_v1_key.yml +71 -0
- data/cassettes/AMEE_Connection_with_authentication/using_a_v2_key/detects_the_API_version_for_JSON.yml +36 -0
- data/cassettes/AMEE_Connection_with_authentication/using_a_v2_key/detects_the_API_version_for_XML.yml +36 -0
- data/cassettes/AMEE_Connection_with_authentication_doing_write-requests.yml +75 -0
- data/cassettes/AMEE_Connection_with_authentication_doing_write-requests/working_with_an_existing_profile/deleting_existing_items.yml +108 -0
- data/cassettes/AMEE_Connection_with_authentication_doing_write-requests/working_with_an_existing_profile/sending_updates_to_existing_items.yml +108 -0
- data/cassettes/AMEE_Connection_with_bad_authentication_information/hitting_a_private_url.yml +29 -0
- data/cassettes/AMEE_Connection_with_bad_authentication_information/hitting_a_public_url.yml +32 -0
- data/cassettes/AMEE_Connection_with_incorrect_server_name.yml +16 -0
- data/cassettes/AMEE_Connection_with_retry_enabled.yml +36 -0
- data/lib/amee/connection.rb +234 -110
- data/lib/amee/data_category.rb +2 -2
- data/lib/amee/data_item.rb +45 -2
- data/lib/amee/data_item_value.rb +1 -1
- data/lib/amee/exceptions.rb +5 -2
- data/lib/amee/parse_helper.rb +2 -0
- data/lib/amee/profile_item.rb +6 -2
- data/lib/amee/v3/connection.rb +77 -70
- data/lib/amee/v3/item_value_definition.rb +1 -1
- data/lib/amee/v3/return_value_definition.rb +1 -1
- data/spec/cache_spec.rb +107 -48
- data/spec/connection_spec.rb +224 -183
- data/spec/data_item_spec.rb +12 -0
- data/spec/data_item_value_history_spec.rb +4 -4
- data/spec/data_item_value_spec.rb +2 -2
- data/spec/fixtures/AD63A83B4D41.json +1 -1
- data/spec/fixtures/AD63A83B4D41.xml +1 -1
- data/spec/profile_item_spec.rb +14 -10
- data/spec/spec_helper.rb +29 -0
- data/spec/v3/connection_spec.rb +77 -65
- data/spec/v3/item_value_definition_spec.rb +1 -0
- data/spec/v3/return_value_definition_spec.rb +1 -1
- metadata +140 -24
- data/Gemfile.lock +0 -63
data/lib/amee/data_category.rb
CHANGED
@@ -168,8 +168,8 @@ module AMEE
|
|
168
168
|
# Send data to path
|
169
169
|
options[:newObjectType] = "DC"
|
170
170
|
response = connection.post(path, options)
|
171
|
-
if response['Location']
|
172
|
-
location = response['Location'].match("https??://.*?(/.*)")[1]
|
171
|
+
if response.headers_hash.has_key?('Location') && response.headers_hash['Location']
|
172
|
+
location = response.headers_hash['Location'].match("https??://.*?(/.*)")[1]
|
173
173
|
else
|
174
174
|
category = Category.parse(connection, response.body)
|
175
175
|
location = category.full_path
|
data/lib/amee/data_item.rb
CHANGED
@@ -14,6 +14,8 @@ module AMEE
|
|
14
14
|
@item_definition_uid = data[:item_definition]
|
15
15
|
@total_amount = data[:total_amount]
|
16
16
|
@total_amount_unit = data[:total_amount_unit]
|
17
|
+
@amounts = data[:amounts] || []
|
18
|
+
@notes = data[:notes] || []
|
17
19
|
@start_date = data[:start_date]
|
18
20
|
@category_uid = data[:category_uid]
|
19
21
|
super
|
@@ -24,6 +26,8 @@ module AMEE
|
|
24
26
|
attr_reader :label
|
25
27
|
attr_reader :total_amount
|
26
28
|
attr_reader :total_amount_unit
|
29
|
+
attr_reader :amounts
|
30
|
+
attr_reader :notes
|
27
31
|
attr_reader :start_date
|
28
32
|
attr_reader :category_uid
|
29
33
|
attr_reader :item_definition_uid
|
@@ -55,6 +59,28 @@ module AMEE
|
|
55
59
|
data[:total_amount] = doc['amountPerMonth'] rescue nil
|
56
60
|
data[:total_amount_unit] = "kg/month"
|
57
61
|
end
|
62
|
+
# Read amounts
|
63
|
+
if doc['amounts']
|
64
|
+
if doc['amounts']['amount']
|
65
|
+
data[:amounts] = doc['amounts']['amount'].map do |item|
|
66
|
+
{
|
67
|
+
:type => item['type'],
|
68
|
+
:value => item['value'].to_f,
|
69
|
+
:unit => item['unit'],
|
70
|
+
:per_unit => item['perUnit'],
|
71
|
+
:default => (item['default'] == 'true'),
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
if doc['amounts']['note']
|
76
|
+
data[:notes] = doc['amounts']['note'].map do |item|
|
77
|
+
{
|
78
|
+
:type => item['type'],
|
79
|
+
:value => item['value'],
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
58
84
|
# Get values
|
59
85
|
data[:values] = []
|
60
86
|
doc['dataItem']['itemValues'].each do |value|
|
@@ -103,6 +129,23 @@ module AMEE
|
|
103
129
|
data[:total_amount] = REXML::XPath.first(doc, '/Resources/DataItemResource/AmountPerMonth').text.to_f rescue nil
|
104
130
|
data[:total_amount_unit] = "kg/month"
|
105
131
|
end
|
132
|
+
data[:amounts] = []
|
133
|
+
REXML::XPath.each(doc,'/Resources/DataItemResource/Amounts/Amount') do |amount|
|
134
|
+
amount_data = {}
|
135
|
+
amount_data[:type] = amount.attribute('type').value
|
136
|
+
amount_data[:value] = amount.text.to_f
|
137
|
+
amount_data[:unit] = amount.attribute('unit').value
|
138
|
+
amount_data[:per_unit] = amount.attribute('perUnit').value if amount.attribute('perUnit')
|
139
|
+
amount_data[:default] = (amount.attribute('default').value == 'true') if amount.attribute('default')
|
140
|
+
data[:amounts] << amount_data
|
141
|
+
end
|
142
|
+
data[:notes] = []
|
143
|
+
REXML::XPath.each(doc,'/Resources/DataItemResource/Amounts/Note') do |note|
|
144
|
+
note_data = {}
|
145
|
+
note_data[:type] = note.attribute('type').value
|
146
|
+
note_data[:value] = note.text
|
147
|
+
data[:notes] << note_data
|
148
|
+
end
|
106
149
|
# Get values
|
107
150
|
data[:values] = []
|
108
151
|
REXML::XPath.each(doc, '/Resources/DataItemResource/DataItem/ItemValues/ItemValue') do |value|
|
@@ -188,8 +231,8 @@ module AMEE
|
|
188
231
|
options[:newObjectType] = "DI"
|
189
232
|
# Send data to path
|
190
233
|
response = connection.post(path, options)
|
191
|
-
if response['Location']
|
192
|
-
location = response['Location'].match("https??://.*?(/.*)")[1]
|
234
|
+
if response.headers_hash.has_key?('Location') && response.headers_hash['Location']
|
235
|
+
location = response.headers_hash['Location'].match("https??://.*?(/.*)")[1]
|
193
236
|
else
|
194
237
|
category = Category.parse(connection, response.body)
|
195
238
|
location = category.full_path + "/" + category.items[0][:path]
|
data/lib/amee/data_item_value.rb
CHANGED
@@ -175,7 +175,7 @@ module AMEE
|
|
175
175
|
end
|
176
176
|
|
177
177
|
response = data_item.connection.post(data_item.full_path, options)
|
178
|
-
location = response['Location'].match("https??://.*?(/.*)")[1]
|
178
|
+
location = response.headers_hash['Location'].match("https??://.*?(/.*)")[1]
|
179
179
|
if get_item == true
|
180
180
|
get_options = {}
|
181
181
|
get_options[:format] = format if format
|
data/lib/amee/exceptions.rb
CHANGED
@@ -20,7 +20,7 @@ module AMEE
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
class BadRequest < Exception
|
25
25
|
end
|
26
26
|
|
@@ -32,7 +32,10 @@ module AMEE
|
|
32
32
|
|
33
33
|
class ConnectionFailed < Exception
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
|
+
class TimeOut < Exception
|
37
|
+
end
|
38
|
+
|
36
39
|
class NotFound < Exception
|
37
40
|
end
|
38
41
|
|
data/lib/amee/parse_helper.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
3
|
|
4
4
|
module ParseHelper
|
5
|
+
|
5
6
|
def x(xpath,options = {})
|
6
7
|
doc = options[:doc] || @doc
|
7
8
|
preamble = options[:meta] == true ? metaxmlpathpreamble : xmlpathpreamble
|
@@ -36,6 +37,7 @@ module ParseHelper
|
|
36
37
|
def xmlpathpreamble
|
37
38
|
''
|
38
39
|
end
|
40
|
+
|
39
41
|
def load_xml_doc(xml)
|
40
42
|
doc = Nokogiri.XML(xml) { |config| config.strict }
|
41
43
|
doc.remove_namespaces!
|
data/lib/amee/profile_item.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# Copyright (C) 2008-2011 AMEE UK Ltd. - http://www.amee.com
|
2
2
|
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
|
3
3
|
|
4
|
+
require 'active_support/core_ext/hash/conversions'
|
5
|
+
require 'active_support/inflector'
|
6
|
+
|
4
7
|
module AMEE
|
5
8
|
module Profile
|
6
9
|
class Item < AMEE::Profile::Object
|
@@ -314,8 +317,8 @@ module AMEE
|
|
314
317
|
response = connection.post(path, options)
|
315
318
|
# Parse response
|
316
319
|
category = response.body.empty? ? nil : Category.parse(connection, response.body, options)
|
317
|
-
if response['Location']
|
318
|
-
location = response['Location'].match("https??://.*?(/.*)")[1]
|
320
|
+
if response.headers_hash.has_key?('Location') && response.headers_hash['Location']
|
321
|
+
location = response.headers_hash['Location'].match("https??://.*?(/.*)")[1]
|
319
322
|
else
|
320
323
|
location = category.full_path + "/" + category.items[0][:path]
|
321
324
|
end
|
@@ -335,6 +338,7 @@ module AMEE
|
|
335
338
|
values << v.merge(:path => k.to_s)
|
336
339
|
end
|
337
340
|
item[:values] = values
|
341
|
+
item[:path] = category.path + '/' + item[:path]
|
338
342
|
return AMEE::Profile::Item.new(item)
|
339
343
|
else
|
340
344
|
get_options = {}
|
data/lib/amee/v3/connection.rb
CHANGED
@@ -6,61 +6,97 @@ require 'active_support/core_ext/string'
|
|
6
6
|
module AMEE
|
7
7
|
class Connection
|
8
8
|
|
9
|
+
# API version used in URLs
|
9
10
|
def self.api_version
|
10
11
|
'3'
|
11
12
|
end
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
@v3_http = Net::HTTP.new(v3_hostname, @port)
|
16
|
-
if @ssl == true
|
17
|
-
@v3_http.use_ssl = true
|
18
|
-
if File.exists? RootCA
|
19
|
-
@v3_http.ca_file = RootCA
|
20
|
-
@v3_http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
21
|
-
@v3_http.verify_depth = 5
|
22
|
-
end
|
23
|
-
end
|
24
|
-
@v3_http.set_debug_output($stdout) if @debug
|
25
|
-
@v3_http
|
26
|
-
end
|
27
|
-
end
|
13
|
+
|
14
|
+
# Perform a GET request
|
15
|
+
# options hash should contain query parameters
|
28
16
|
def v3_get(path, options = {})
|
29
|
-
# Create
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
17
|
+
# Create request parameters
|
18
|
+
get_params = {
|
19
|
+
:method => "get"
|
20
|
+
}
|
21
|
+
get_params[:params] = options unless options.empty?
|
22
|
+
# Send request (with caching)
|
23
|
+
v3_do_request(get_params, path, :cache => true)
|
34
24
|
end
|
25
|
+
|
26
|
+
# Perform a PUT request
|
27
|
+
# options hash should contain request body parameters
|
35
28
|
def v3_put(path, options = {})
|
29
|
+
# Expire cached objects from parent on down
|
36
30
|
expire_matching "#{parent_path(path)}.*"
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
31
|
+
# Create request parameters
|
32
|
+
put_params = {
|
33
|
+
:method => "put",
|
34
|
+
:body => options[:body] ? options[:body] : form_encode(options)
|
35
|
+
}
|
36
|
+
if options[:content_type]
|
37
|
+
put_params[:headers] = {
|
38
|
+
:'Content-Type' => content_type(options[:content_type])
|
39
|
+
}
|
44
40
|
end
|
45
|
-
|
41
|
+
# Request
|
42
|
+
v3_do_request(put_params, path)
|
46
43
|
end
|
44
|
+
|
45
|
+
# Perform a POST request
|
46
|
+
# options hash should contain request body parameters
|
47
|
+
# It can also contain a :returnobj parameter which will cause
|
48
|
+
# a full reponse object to be returned instead of just the body
|
47
49
|
def v3_post(path, options = {})
|
50
|
+
# Expire cached objects from here on down
|
48
51
|
expire_matching "#{raw_path(path)}.*"
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
# Get 'return full response object' flag
|
53
|
+
return_obj = options.delete(:returnobj) || false
|
54
|
+
# Create request parameters
|
55
|
+
post_params = {
|
56
|
+
:method => "post",
|
57
|
+
:body => form_encode(options)
|
58
|
+
}
|
59
|
+
if options[:content_type]
|
60
|
+
post_params[:headers] = {
|
61
|
+
:'Content-Type' => content_type(options[:content_type])
|
62
|
+
}
|
63
|
+
end
|
64
|
+
# Request
|
65
|
+
v3_do_request(post_params, path, :return_obj => return_obj)
|
53
66
|
end
|
54
|
-
|
67
|
+
|
68
|
+
# Perform a POST request
|
69
|
+
def v3_delete(path)
|
70
|
+
# Expire cached objects from here on down
|
55
71
|
expire_matching "#{parent_path(path)}.*"
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
|
72
|
+
# Create request parameters
|
73
|
+
delete_params = {
|
74
|
+
:method => "delete"
|
75
|
+
}
|
76
|
+
# Request
|
77
|
+
v3_do_request(delete_params, path)
|
62
78
|
end
|
79
|
+
|
63
80
|
private
|
81
|
+
|
82
|
+
# Default request parameters
|
83
|
+
def v3_defaults
|
84
|
+
{
|
85
|
+
:verbose => DEBUG,
|
86
|
+
:follow_location => true,
|
87
|
+
:username => @username,
|
88
|
+
:password => @password
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
# Wrap up parameters into a request and execute it
|
93
|
+
def v3_do_request(params, path, options = {})
|
94
|
+
req = Typhoeus::Request.new("https://#{v3_hostname}#{path}", v3_defaults.merge(params))
|
95
|
+
response = do_request(req, :xml, options)
|
96
|
+
options[:return_obj]==true ? response : response.body
|
97
|
+
end
|
98
|
+
|
99
|
+
# Work out v3 hostname corresponding to v2 hostname
|
64
100
|
def v3_hostname
|
65
101
|
unless @server.starts_with?("platform-api-")
|
66
102
|
if @server.starts_with?("platform-")
|
@@ -72,35 +108,6 @@ module AMEE
|
|
72
108
|
@server
|
73
109
|
end
|
74
110
|
end
|
75
|
-
|
76
|
-
# Open HTTP connection
|
77
|
-
v3_connection.start
|
78
|
-
# Set auth
|
79
|
-
req.basic_auth *v3_auth
|
80
|
-
# Do request
|
81
|
-
timethen=Time.now
|
82
|
-
response = send_request(v3_connection, req, :xml)
|
83
|
-
Logger.log.debug("Requested #{req.class} at #{req.path} with #{req.body}, taking #{(Time.now-timethen)*1000} miliseconds")
|
84
|
-
v3_response_ok? response, req
|
85
|
-
returnobj ? response : response.body
|
86
|
-
ensure
|
87
|
-
v3_connection.finish if v3_connection.started?
|
88
|
-
end
|
89
|
-
|
90
|
-
def v3_response_ok?(response, request)
|
91
|
-
case response.code
|
92
|
-
when '200', '201', '204'
|
93
|
-
return true
|
94
|
-
when '404'
|
95
|
-
raise AMEE::NotFound.new("The URL was not found on the server.\nRequest: #{request.method} #{request.path}")
|
96
|
-
when '403'
|
97
|
-
raise AMEE::PermissionDenied.new("You do not have permission to perform the requested operation.\nRequest: #{request.method} #{request.path}\n#{request.body}\Response: #{response.body}")
|
98
|
-
when '401'
|
99
|
-
raise AMEE::AuthFailed.new("Authentication failed. Please check your username and password.")
|
100
|
-
when '400'
|
101
|
-
raise AMEE::BadRequest.new("Bad request. This is probably due to malformed input data.\nRequest: #{request.method} #{request.path}\n#{request.body}\Response: #{response.body}")
|
102
|
-
end
|
103
|
-
raise AMEE::UnknownError.new("An error occurred while talking to AMEE: HTTP response code #{response.code}.\nRequest: #{request.method} #{request.path}\n#{request.body}\Response: #{response.body}")
|
104
|
-
end
|
111
|
+
|
105
112
|
end
|
106
113
|
end
|
@@ -179,7 +179,7 @@ module AMEE
|
|
179
179
|
|
180
180
|
options.merge!(:returnobj=>true)
|
181
181
|
response = connection.v3_post("/#{AMEE::Connection.api_version}/definitions/#{itemdefuid}/returnvalues", options)
|
182
|
-
return ReturnValueDefinition.load(connection,itemdefuid , response['Location'].split('/')[7])
|
182
|
+
return ReturnValueDefinition.load(connection,itemdefuid , response.headers_hash['Location'].split('/')[7])
|
183
183
|
rescue
|
184
184
|
raise AMEE::BadData.new("Couldn't create ReturnValueDefinition. Check that your information is correct.\n#{response}")
|
185
185
|
end
|
data/spec/cache_spec.rb
CHANGED
@@ -9,14 +9,35 @@ describe AMEE::Connection do
|
|
9
9
|
describe 'without caching' do
|
10
10
|
|
11
11
|
it "doesn't cache GET requests" do
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
VCR.use_cassette("AMEE_Connection_Caching_Off/logging_in") do
|
13
|
+
@connection = AMEE::Connection.new("stage.amee.com", "amee_ruby_vcr_v2", "8nkj8rm7")
|
14
|
+
end
|
15
|
+
|
16
|
+
VCR.use_cassette("AMEE_Connection_Caching_Off/authenticating") do
|
17
|
+
@connection.authenticate
|
18
|
+
end
|
19
|
+
|
20
|
+
VCR.use_cassette("AMEE_Connection_Caching_Off/first_request") do
|
21
|
+
@first_response = AMEE::Data::Category.get(@connection, '/data/home/energy/quantity')
|
22
|
+
end
|
23
|
+
|
24
|
+
VCR.use_cassette("AMEE_Connection_Caching_Off/second_request") do
|
25
|
+
@second_response = AMEE::Data::Category.get(@connection, '/data/home/energy/quantity')
|
26
|
+
end
|
27
|
+
|
28
|
+
# We're checking to see if we actually have a returned object.
|
29
|
+
# If there's no response in the VCR cassette, then we can't build the object
|
30
|
+
# to check if it responds to the methods below
|
31
|
+
@first_response.should respond_to(:uid)
|
32
|
+
@first_response.should respond_to(:name)
|
33
|
+
|
34
|
+
# Likewise with the second response. There needs to be a yaml file to read from to
|
35
|
+
# build the object.
|
36
|
+
File.exists?('cassettes/AMEE_Connection_Caching_Off/second_request.yml').should be true
|
37
|
+
# Then we check for the same content to see we can build the object needed.
|
38
|
+
@second_response.should respond_to(:uid)
|
39
|
+
@second_response.should respond_to(:name)
|
40
|
+
|
20
41
|
end
|
21
42
|
|
22
43
|
end
|
@@ -24,61 +45,86 @@ describe AMEE::Connection do
|
|
24
45
|
describe 'with caching' do
|
25
46
|
|
26
47
|
def setup_connection
|
27
|
-
|
48
|
+
VCR.use_cassette("AMEE_Connection_Caching_On/logging_in") do
|
49
|
+
@connection = AMEE::Connection.new("stage.amee.com", "amee_ruby_vcr_v2", "8nkj8rm7", :cache => :memory_store)
|
50
|
+
end
|
51
|
+
|
52
|
+
VCR.use_cassette("AMEE_Connection_Caching_On/authenticating") do
|
53
|
+
@connection.authenticate
|
54
|
+
end
|
55
|
+
|
56
|
+
VCR.use_cassette("AMEE_Connection_Caching_On/first_request") do
|
57
|
+
@first_response = AMEE::Data::Category.get(@connection, '/data/home/energy/quantity')
|
58
|
+
end
|
28
59
|
end
|
29
60
|
|
30
61
|
it "caches GET requests" do
|
31
|
-
|
32
|
-
mock.should_receive(:start => nil)
|
33
|
-
mock.should_receive(:request).once.and_return(OpenStruct.new(:code => '200', :body => fixture('data_home_energy_quantity.xml')))
|
34
|
-
mock.should_receive(:finish => nil)
|
35
|
-
end
|
62
|
+
|
36
63
|
setup_connection
|
37
|
-
|
38
|
-
|
64
|
+
|
65
|
+
VCR.use_cassette("AMEE_Connection_Caching_On/second_request") do
|
66
|
+
@second_response = AMEE::Data::Category.get(@connection, '/data/home/energy/quantity')
|
67
|
+
end
|
68
|
+
|
69
|
+
# We're checking to see if we actually have a returned object.
|
70
|
+
# If there's no response in the VCR cassette, then we can't build the object
|
71
|
+
# to check if it responds to the methods below
|
72
|
+
@first_response.should respond_to(:uid)
|
73
|
+
@first_response.should respond_to(:name)
|
74
|
+
|
75
|
+
# Likewise with the second response. When caching is on, we don't want to see a second
|
76
|
+
# request being recorded, but we still want to see the object being built
|
77
|
+
File.exists?('cassettes/AMEE_Connection_Caching_On/second_request.yml').should be false
|
78
|
+
# Then we check for the same content to see we can build the object needed.
|
79
|
+
@second_response.should respond_to(:uid)
|
80
|
+
@second_response.should respond_to(:name)
|
39
81
|
end
|
40
82
|
|
41
83
|
it "allows complete cache clear" do
|
42
|
-
|
43
|
-
mock.should_receive(:start => nil)
|
44
|
-
mock.should_receive(:request).twice.and_return(OpenStruct.new(:code => '200', :body => fixture('data_home_energy_quantity.xml')))
|
45
|
-
mock.should_receive(:finish => nil)
|
46
|
-
end
|
84
|
+
|
47
85
|
setup_connection
|
48
|
-
c = AMEE::Data::Category.get(@connection, '/data/home/energy/quantity')
|
49
86
|
@connection.expire_all
|
50
|
-
|
87
|
+
|
88
|
+
VCR.use_cassette("AMEE_Connection_Caching_clear_all/second_request") do
|
89
|
+
@second_response = AMEE::Data::Category.get(@connection, '/data/home/energy/quantity')
|
90
|
+
end
|
91
|
+
|
92
|
+
File.exists?('cassettes/AMEE_Connection_Caching_clear_all/second_request.yml').should be true
|
93
|
+
@second_response.should respond_to(:uid)
|
94
|
+
@second_response.should respond_to(:name)
|
51
95
|
end
|
52
96
|
|
53
97
|
it "allows manual cache expiry for objects" do
|
54
|
-
flexmock(Net::HTTP).new_instances do |mock|
|
55
|
-
mock.should_receive(:start => nil)
|
56
|
-
mock.should_receive(:request).twice.and_return(OpenStruct.new(:code => '200', :body => fixture('data_home_energy_quantity.xml')))
|
57
|
-
mock.should_receive(:finish => nil)
|
58
|
-
end
|
59
98
|
setup_connection
|
60
|
-
|
61
|
-
c.expire_cache
|
62
|
-
c = AMEE::Data::Category.get(@connection, '/data/home/energy/quantity')
|
63
|
-
end
|
99
|
+
@first_response.expire_cache
|
64
100
|
|
65
|
-
|
66
|
-
|
67
|
-
mock.should_receive(:start => nil)
|
68
|
-
mock.should_receive(:request).once.and_return(OpenStruct.new(:code => '200', :body => fixture('data_home_energy_quantity.xml')))
|
69
|
-
mock.should_receive(:request).twice.and_return(OpenStruct.new(:code => '200', :body => fixture('data_home_energy_quantity_biodiesel.xml')))
|
70
|
-
mock.should_receive(:finish => nil)
|
101
|
+
VCR.use_cassette("AMEE_Connection_Caching_clear_manually/second_request") do
|
102
|
+
@second_response = AMEE::Data::Category.get(@connection, '/data/home/energy/quantity')
|
71
103
|
end
|
104
|
+
|
105
|
+
File.exists?('cassettes/AMEE_Connection_Caching_clear_manually/second_request.yml').should be true
|
106
|
+
@second_response.should respond_to(:uid)
|
107
|
+
@second_response.should respond_to(:name)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "object expiry invalidates objects further down the tree" do
|
72
111
|
setup_connection
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
112
|
+
|
113
|
+
VCR.use_cassette("AMEE_Connection_Caching_further_down_tree/second_request") do
|
114
|
+
@second_response = @first_response.item :label => 'biodiesel'
|
115
|
+
@first_response.expire_cache
|
116
|
+
@third_response = @first_response.item :label => 'biodiesel'
|
117
|
+
end
|
118
|
+
|
119
|
+
File.exists?('cassettes/AMEE_Connection_Caching_further_down_tree/second_request.yml').should be true
|
120
|
+
@second_response.label.should == "biodiesel"
|
121
|
+
@third_response.label.should == "biodiesel"
|
122
|
+
|
77
123
|
end
|
78
124
|
|
79
125
|
it "removes special characters from cache keys, include slashes" do
|
80
126
|
setup_connection
|
81
|
-
@connection.send(:cache_key, "/%cache/$4/%20test").should eql '
|
127
|
+
@connection.send(:cache_key, "/%cache/$4/%20test").should eql 'stage.amee.com_cache_4_20test'
|
82
128
|
end
|
83
129
|
|
84
130
|
it "trims cache keys to correct length for filenames, allowing for lock extension" do
|
@@ -99,14 +145,12 @@ describe AMEE::Connection do
|
|
99
145
|
describe 'and automatic invalidation' do
|
100
146
|
|
101
147
|
def test_invalidation_sequence(interactions)
|
102
|
-
|
103
|
-
|
148
|
+
setup_connection
|
149
|
+
flexmock(@connection) do |mock|
|
104
150
|
interactions.each do |path, action, result|
|
105
|
-
mock.should_receive(:
|
151
|
+
mock.should_receive(:run_request).once.and_return(OpenStruct.new(:code => '200', :body => path)) if result
|
106
152
|
end
|
107
|
-
mock.should_receive(:finish => nil)
|
108
153
|
end
|
109
|
-
setup_connection
|
110
154
|
interactions.each do |path, action, result|
|
111
155
|
if action
|
112
156
|
@connection.send(action, path).body.should == path
|
@@ -115,14 +159,18 @@ describe AMEE::Connection do
|
|
115
159
|
end
|
116
160
|
|
117
161
|
it "handles PUT requests" do
|
162
|
+
VCR.use_cassette("AMEE_Connection_Caching/automatic_invalidation_for_put") do
|
118
163
|
test_invalidation_sequence([
|
164
|
+
# Fill the cache
|
119
165
|
["/parent/object", :get, true],
|
120
166
|
["/parent", :get, true],
|
121
167
|
["/parent/object/child", :get, true],
|
122
168
|
["/parent/sibling", :get, true],
|
123
169
|
["/uncle/cousin", :get, true],
|
124
170
|
["/uncle", :get, true],
|
171
|
+
# Do a PUT
|
125
172
|
["/parent/object", :put, true],
|
173
|
+
# Check that cache is cleared in the right places
|
126
174
|
["/parent/object", :get, true],
|
127
175
|
["/parent", :get, true],
|
128
176
|
["/parent/object/child", :get, true],
|
@@ -130,17 +178,22 @@ describe AMEE::Connection do
|
|
130
178
|
["/uncle/cousin", :get, false],
|
131
179
|
["/uncle", :get, false],
|
132
180
|
])
|
181
|
+
end
|
133
182
|
end
|
134
183
|
|
135
184
|
it "handles POST requests" do
|
185
|
+
VCR.use_cassette("AMEE_Connection_Caching/automatic_invalidation_for_post") do
|
136
186
|
test_invalidation_sequence([
|
187
|
+
# Fill the cache
|
137
188
|
["/parent/object", :get, true],
|
138
189
|
["/parent", :get, true],
|
139
190
|
["/parent/object/child", :get, true],
|
140
191
|
["/parent/sibling", :get, true],
|
141
192
|
["/uncle/cousin", :get, true],
|
142
193
|
["/uncle", :get, true],
|
194
|
+
# Do a POST
|
143
195
|
["/parent/object", :post, true],
|
196
|
+
# Check that cache is cleared in the right places
|
144
197
|
["/parent/object", :get, true],
|
145
198
|
["/parent", :get, false],
|
146
199
|
["/parent/object/child", :get, true],
|
@@ -149,16 +202,21 @@ describe AMEE::Connection do
|
|
149
202
|
["/uncle", :get, false],
|
150
203
|
])
|
151
204
|
end
|
205
|
+
end
|
152
206
|
|
153
207
|
it "handles DELETE requests" do
|
208
|
+
VCR.use_cassette("AMEE_Connection_Caching/automatic_invalidation_for_delete") do
|
154
209
|
test_invalidation_sequence([
|
210
|
+
# Fill the cache
|
155
211
|
["/parent/object", :get, true],
|
156
212
|
["/parent", :get, true],
|
157
213
|
["/parent/object/child", :get, true],
|
158
214
|
["/parent/sibling", :get, true],
|
159
215
|
["/uncle/cousin", :get, true],
|
160
216
|
["/uncle", :get, true],
|
217
|
+
# Do a DELETE
|
161
218
|
["/parent/object", :delete, true],
|
219
|
+
# Check that cache is cleared in the right places
|
162
220
|
["/parent/object", :get, true],
|
163
221
|
["/parent", :get, true],
|
164
222
|
["/parent/object/child", :get, true],
|
@@ -167,6 +225,7 @@ describe AMEE::Connection do
|
|
167
225
|
["/uncle", :get, false],
|
168
226
|
])
|
169
227
|
end
|
228
|
+
end
|
170
229
|
|
171
230
|
end
|
172
231
|
|