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