gds-api-adapters 4.8.0 → 4.9.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/lib/gds_api/content_api.rb +31 -2
- data/lib/gds_api/content_api/list_response.rb +3 -3
- data/lib/gds_api/content_api/response.rb +56 -0
- data/lib/gds_api/json_client.rb +14 -4
- data/lib/gds_api/test_helpers/content_api.rb +1 -0
- data/lib/gds_api/version.rb +1 -1
- data/test/content_api_response_test.rb +97 -0
- data/test/content_api_test.rb +64 -0
- data/test/json_client_test.rb +31 -5
- metadata +7 -4
data/lib/gds_api/content_api.rb
CHANGED
@@ -1,10 +1,21 @@
|
|
1
1
|
require_relative 'base'
|
2
2
|
require_relative 'exceptions'
|
3
|
+
require 'gds_api/content_api/response'
|
3
4
|
require 'gds_api/content_api/list_response'
|
4
5
|
|
5
6
|
class GdsApi::ContentApi < GdsApi::Base
|
6
7
|
include GdsApi::ExceptionHandling
|
7
8
|
|
9
|
+
def initialize(endpoint_url, options = {})
|
10
|
+
# If the `web_urls_relative_to` option is given, the adapter will convert
|
11
|
+
# any `web_url` values to relative URLs if they are from the same host.
|
12
|
+
#
|
13
|
+
# For example: "https://www.gov.uk"
|
14
|
+
|
15
|
+
@web_urls_relative_to = options.delete(:web_urls_relative_to)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
8
19
|
def sections
|
9
20
|
get_list!("#{base_url}/tags.json?type=section")
|
10
21
|
end
|
@@ -86,11 +97,29 @@ class GdsApi::ContentApi < GdsApi::Base
|
|
86
97
|
end
|
87
98
|
|
88
99
|
def get_list!(url)
|
89
|
-
get_json!(url) { |r|
|
100
|
+
get_json!(url) { |r|
|
101
|
+
ListResponse.new(r, self, web_urls_relative_to: @web_urls_relative_to)
|
102
|
+
}
|
90
103
|
end
|
91
104
|
|
92
105
|
def get_list(url)
|
93
|
-
get_json(url) { |r|
|
106
|
+
get_json(url) { |r|
|
107
|
+
ListResponse.new(r, self, web_urls_relative_to: @web_urls_relative_to)
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_json(url, &create_response)
|
112
|
+
create_response = create_response || Proc.new { |r|
|
113
|
+
GdsApi::ContentApi::Response.new(r, web_urls_relative_to: @web_urls_relative_to)
|
114
|
+
}
|
115
|
+
super(url, &create_response)
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_json!(url, &create_response)
|
119
|
+
create_response = create_response || Proc.new { |r|
|
120
|
+
GdsApi::ContentApi::Response.new(r, web_urls_relative_to: @web_urls_relative_to)
|
121
|
+
}
|
122
|
+
super(url, &create_response)
|
94
123
|
end
|
95
124
|
|
96
125
|
def countries
|
@@ -9,12 +9,12 @@ class GdsApi::ContentApi < GdsApi::Base
|
|
9
9
|
# These responses are in a common format, with the list of results contained
|
10
10
|
# under the `results` key. The response may also have previous and subsequent
|
11
11
|
# pages, indicated by entries in the response's `Link` header.
|
12
|
-
class ListResponse <
|
12
|
+
class ListResponse < Response
|
13
13
|
|
14
14
|
# The ListResponse is instantiated with a reference back to the API client,
|
15
15
|
# so it can make requests for the subsequent pages
|
16
|
-
def initialize(response, api_client)
|
17
|
-
super(response)
|
16
|
+
def initialize(response, api_client, options = {})
|
17
|
+
super(response, options)
|
18
18
|
@api_client = api_client
|
19
19
|
end
|
20
20
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module GdsApi
|
2
|
+
class ContentApi < GdsApi::Base
|
3
|
+
class Response < GdsApi::Response
|
4
|
+
# Responses from the content API can be configured to use relative URLs
|
5
|
+
# for `web_url` properties. This is useful on non-canonical frontends,
|
6
|
+
# such as those in staging environments.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# r = Response.new(response, web_urls_relative_to: "https://www.gov.uk")
|
11
|
+
# r.results[0].web_url
|
12
|
+
# => "/bank-holidays"
|
13
|
+
|
14
|
+
WEB_URL_KEYS = ["web_url"]
|
15
|
+
|
16
|
+
def initialize(http_response, options = {})
|
17
|
+
if options[:web_urls_relative_to]
|
18
|
+
@web_urls_relative_to = URI.parse(options[:web_urls_relative_to])
|
19
|
+
else
|
20
|
+
@web_urls_relative_to = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
super(http_response)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
@parsed ||= transform_parsed(JSON.parse(@http_response.body))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def transform_parsed(value)
|
32
|
+
case value
|
33
|
+
when Hash
|
34
|
+
Hash[value.map { |k, v|
|
35
|
+
# NOTE: Don't bother transforming if the value is nil
|
36
|
+
if @web_urls_relative_to && WEB_URL_KEYS.include?(k) && v
|
37
|
+
# Use relative URLs to route when the web_url value is on the
|
38
|
+
# same domain as the site root. Note that we can't just use the
|
39
|
+
# `route_to` method, as this would give us technically correct
|
40
|
+
# but potentially confusing `//host/path` URLs for URLs with the
|
41
|
+
# same scheme but different hosts.
|
42
|
+
relative_url = @web_urls_relative_to.route_to(v)
|
43
|
+
[k, relative_url.host ? v : relative_url.to_s]
|
44
|
+
else
|
45
|
+
[k, transform_parsed(v)]
|
46
|
+
end
|
47
|
+
}]
|
48
|
+
when Array
|
49
|
+
value.map { |v| transform_parsed(v) }
|
50
|
+
else
|
51
|
+
value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/gds_api/json_client.rb
CHANGED
@@ -49,15 +49,15 @@ module GdsApi
|
|
49
49
|
# and return nil.
|
50
50
|
[:get, :post, :put, :delete].each do |http_method|
|
51
51
|
method_name = "#{http_method}_json"
|
52
|
-
define_method method_name do |url, *args|
|
52
|
+
define_method method_name do |url, *args, &block|
|
53
53
|
ignoring GdsApi::HTTPNotFound do
|
54
|
-
send (method_name + "!"), url, *args
|
54
|
+
send (method_name + "!"), url, *args, &block
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
59
|
def get_json!(url, &create_response)
|
60
|
-
|
60
|
+
do_json_request(:get, url, nil, &create_response)
|
61
61
|
end
|
62
62
|
|
63
63
|
def post_json!(url, params)
|
@@ -92,7 +92,7 @@ module GdsApi
|
|
92
92
|
def do_json_request(method, url, params = nil, &create_response)
|
93
93
|
|
94
94
|
begin
|
95
|
-
response =
|
95
|
+
response = do_request_with_cache(method, url, params)
|
96
96
|
|
97
97
|
rescue RestClient::ResourceNotFound => e
|
98
98
|
raise GdsApi::HTTPNotFound.new(e.http_code)
|
@@ -149,6 +149,16 @@ module GdsApi
|
|
149
149
|
)
|
150
150
|
end
|
151
151
|
|
152
|
+
def do_request_with_cache(method, url, params = nil)
|
153
|
+
# Only read GET requests from the cache: any other request methods should
|
154
|
+
# always be passed through
|
155
|
+
if method == :get
|
156
|
+
@cache[url] ||= do_request(method, url, params)
|
157
|
+
else
|
158
|
+
do_request(method, url, params)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
152
162
|
def do_request(method, url, params = nil)
|
153
163
|
loggable = {request_uri: url, start_time: Time.now.to_f}
|
154
164
|
start_logging = loggable.merge(action: 'start')
|
data/lib/gds_api/version.rb
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "gds_api/content_api"
|
3
|
+
|
4
|
+
describe "GdsApi::ContentApi::Response" do
|
5
|
+
|
6
|
+
DummyNetResponse = Struct.new(:body)
|
7
|
+
|
8
|
+
def response_for(net_response)
|
9
|
+
root = "https://www.gov.uk"
|
10
|
+
GdsApi::ContentApi::Response.new(net_response, web_urls_relative_to: root)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should map web URLs" do
|
14
|
+
body = {
|
15
|
+
"web_url" => "https://www.gov.uk/test"
|
16
|
+
}.to_json
|
17
|
+
assert_equal "/test", response_for(DummyNetResponse.new(body)).web_url
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should leave other properties alone" do
|
21
|
+
body = {
|
22
|
+
"title" => "Title",
|
23
|
+
"description" => "Description"
|
24
|
+
}.to_json
|
25
|
+
response = response_for(DummyNetResponse.new(body))
|
26
|
+
assert_equal "Title", response.title
|
27
|
+
assert_equal "Description", response.description
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should traverse into arrays" do
|
31
|
+
body = {
|
32
|
+
"other_urls" => [
|
33
|
+
{ "title" => "Pies", "web_url" => "https://www.gov.uk/pies" },
|
34
|
+
{ "title" => "Cheese", "web_url" => "https://www.gov.uk/cheese" }
|
35
|
+
]
|
36
|
+
}.to_json
|
37
|
+
|
38
|
+
response = response_for(DummyNetResponse.new(body))
|
39
|
+
assert_equal "/pies", response.other_urls[0].web_url
|
40
|
+
assert_equal "/cheese", response.other_urls[1].web_url
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should traverse into hashes" do
|
44
|
+
body = {
|
45
|
+
"details" => {
|
46
|
+
"chirality" => "widdershins",
|
47
|
+
"web_url" => "https://www.gov.uk/left"
|
48
|
+
}
|
49
|
+
}.to_json
|
50
|
+
|
51
|
+
response = response_for(DummyNetResponse.new(body))
|
52
|
+
assert_equal "/left", response.details.web_url
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should handle nil values" do
|
56
|
+
body = {"web_url" => nil}.to_json
|
57
|
+
|
58
|
+
response = response_for(DummyNetResponse.new(body))
|
59
|
+
assert_nil response.web_url
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should handle query parameters" do
|
63
|
+
body = {
|
64
|
+
"web_url" => "https://www.gov.uk/thing?does=stuff"
|
65
|
+
}.to_json
|
66
|
+
|
67
|
+
response = response_for(DummyNetResponse.new(body))
|
68
|
+
assert_equal "/thing?does=stuff", response.web_url
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should handle fragments" do
|
72
|
+
body = {
|
73
|
+
"web_url" => "https://www.gov.uk/thing#part-2"
|
74
|
+
}.to_json
|
75
|
+
|
76
|
+
response = response_for(DummyNetResponse.new(body))
|
77
|
+
assert_equal "/thing#part-2", response.web_url
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should keep URLs from other domains absolute" do
|
81
|
+
body = {
|
82
|
+
"web_url" => "https://www.example.com/example"
|
83
|
+
}.to_json
|
84
|
+
|
85
|
+
response = response_for(DummyNetResponse.new(body))
|
86
|
+
assert_equal "https://www.example.com/example", response.web_url
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should keep URLs with other schemes absolute" do
|
90
|
+
body = {
|
91
|
+
"web_url" => "http://www.example.com/example"
|
92
|
+
}.to_json
|
93
|
+
|
94
|
+
response = response_for(DummyNetResponse.new(body))
|
95
|
+
assert_equal "http://www.example.com/example", response.web_url
|
96
|
+
end
|
97
|
+
end
|
data/test/content_api_test.rb
CHANGED
@@ -10,6 +10,70 @@ describe GdsApi::ContentApi do
|
|
10
10
|
@api = GdsApi::ContentApi.new(@base_api_url)
|
11
11
|
end
|
12
12
|
|
13
|
+
describe "when asked for relative web URLs" do
|
14
|
+
before do
|
15
|
+
@api = GdsApi::ContentApi.new(
|
16
|
+
@base_api_url,
|
17
|
+
web_urls_relative_to: "http://www.test.gov.uk"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should use relative URLs for an artefact" do
|
22
|
+
artefact_response = artefact_for_slug_in_a_section("bank-holidays", "cheese")
|
23
|
+
|
24
|
+
# Rewrite the web_url fields to have a common prefix
|
25
|
+
# The helper's default is to point the web_url for an artefact at the
|
26
|
+
# frontend app, and the web_url for a tag's content to www: to test the
|
27
|
+
# rewriting properly, they need to be the same
|
28
|
+
artefact_response["web_url"] = "http://www.test.gov.uk/bank-holidays"
|
29
|
+
section_tag_content = artefact_response["tags"][0]["content_with_tag"]
|
30
|
+
section_tag_content["web_url"] = "http://www.test.gov.uk/browse/cheese"
|
31
|
+
|
32
|
+
content_api_has_an_artefact("bank-holidays", artefact_response)
|
33
|
+
artefact = @api.artefact("bank-holidays")
|
34
|
+
|
35
|
+
assert_equal "Bank holidays", artefact.title
|
36
|
+
assert_equal "/bank-holidays", artefact.web_url
|
37
|
+
|
38
|
+
assert_equal "/browse/cheese", artefact.tags[0].content_with_tag.web_url
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should use relative URLs for tag listings" do
|
42
|
+
content_api_has_root_sections %w(housing benefits tax)
|
43
|
+
tags = @api.root_sections
|
44
|
+
|
45
|
+
assert_equal 3, tags.count
|
46
|
+
tags.each do |tag|
|
47
|
+
web_url = tag.content_with_tag.web_url
|
48
|
+
assert web_url.start_with?("/browse/"), web_url
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "with caching enabled" do
|
53
|
+
before do
|
54
|
+
@original_cache = GdsApi::JsonClient.cache
|
55
|
+
GdsApi::JsonClient.cache = LRUCache.new(max_size: 10, ttl: 10)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should not pollute the cache with relative URLs" do
|
59
|
+
artefact_response = artefact_for_slug("bank-holidays")
|
60
|
+
artefact_response["web_url"] = "http://www.test.gov.uk/bank-holidays"
|
61
|
+
content_api_has_an_artefact("bank-holidays", artefact_response)
|
62
|
+
|
63
|
+
assert_equal "/bank-holidays", @api.artefact("bank-holidays").web_url
|
64
|
+
|
65
|
+
clean_api = GdsApi::ContentApi.new(@base_api_url)
|
66
|
+
clean_artefact = clean_api.artefact("bank-holidays")
|
67
|
+
|
68
|
+
assert_equal "http://www.test.gov.uk/bank-holidays", clean_artefact.web_url
|
69
|
+
end
|
70
|
+
|
71
|
+
after do
|
72
|
+
GdsApi::JsonClient.cache = @original_cache
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
13
77
|
describe "sections" do
|
14
78
|
it "should show a list of sections" do
|
15
79
|
content_api_has_root_sections(["crime"])
|
data/test/json_client_test.rb
CHANGED
@@ -73,7 +73,7 @@ class JsonClientTest < MiniTest::Spec
|
|
73
73
|
stub_request(:get, url).to_return(:body => JSON.dump(result), :status => 200)
|
74
74
|
response_a = GdsApi::JsonClient.new.get_json(url)
|
75
75
|
response_b = GdsApi::JsonClient.new.get_json(url)
|
76
|
-
assert_equal response_a.
|
76
|
+
assert_equal response_a.to_hash, response_b.to_hash
|
77
77
|
assert_requested :get, url, times: 1
|
78
78
|
end
|
79
79
|
|
@@ -131,13 +131,13 @@ class JsonClientTest < MiniTest::Spec
|
|
131
131
|
response_b = GdsApi::JsonClient.new.get_json(url)
|
132
132
|
|
133
133
|
assert_requested :get, url, times: 1
|
134
|
-
assert_equal response_a.
|
134
|
+
assert_equal response_a.to_hash, response_b.to_hash
|
135
135
|
|
136
136
|
Timecop.travel( 15 * 60 - 30) do # now + 14 mins 30 secs
|
137
137
|
response_c = GdsApi::JsonClient.new.get_json(url)
|
138
138
|
|
139
139
|
assert_requested :get, url, times: 1
|
140
|
-
|
140
|
+
assert_equal response_a.to_hash, response_c.to_hash
|
141
141
|
end
|
142
142
|
|
143
143
|
Timecop.travel( 15 * 60 + 30) do # now + 15 mins 30 secs
|
@@ -158,13 +158,13 @@ class JsonClientTest < MiniTest::Spec
|
|
158
158
|
response_b = GdsApi::JsonClient.new.get_json(url)
|
159
159
|
|
160
160
|
assert_requested :get, url, times: 1
|
161
|
-
assert_equal response_a.
|
161
|
+
assert_equal response_a.to_hash, response_b.to_hash
|
162
162
|
|
163
163
|
Timecop.travel( 5 * 60 - 30) do # now + 4 mins 30 secs
|
164
164
|
response_c = GdsApi::JsonClient.new.get_json(url)
|
165
165
|
|
166
166
|
assert_requested :get, url, times: 1
|
167
|
-
|
167
|
+
assert_equal response_a.to_hash, response_c.to_hash
|
168
168
|
end
|
169
169
|
|
170
170
|
Timecop.travel( 5 * 60 + 30) do # now + 5 mins 30 secs
|
@@ -347,6 +347,32 @@ class JsonClientTest < MiniTest::Spec
|
|
347
347
|
assert_equal({}, @client.put_json(url, payload).to_hash)
|
348
348
|
end
|
349
349
|
|
350
|
+
def test_can_build_custom_response_object
|
351
|
+
url = "http://some.endpoint/some.json"
|
352
|
+
stub_request(:get, url).to_return(:body => "Hello there!")
|
353
|
+
|
354
|
+
response = @client.get_json(url) { |http_response| http_response.body }
|
355
|
+
assert response.is_a? String
|
356
|
+
assert_equal "Hello there!", response
|
357
|
+
end
|
358
|
+
|
359
|
+
def test_responds_with_nil_on_custom_response_404
|
360
|
+
url = "http://some.endpoint/some.json"
|
361
|
+
stub_request(:get, url).to_return(:body => "", :status => 404)
|
362
|
+
|
363
|
+
response = @client.get_json(url) { |http_response| http_response.body }
|
364
|
+
assert_nil response
|
365
|
+
end
|
366
|
+
|
367
|
+
def test_can_build_custom_response_object_in_bang_method
|
368
|
+
url = "http://some.endpoint/some.json"
|
369
|
+
stub_request(:get, url).to_return(:body => "Hello there!")
|
370
|
+
|
371
|
+
response = @client.get_json!(url) { |http_response| http_response.body }
|
372
|
+
assert response.is_a? String
|
373
|
+
assert_equal "Hello there!", response
|
374
|
+
end
|
375
|
+
|
350
376
|
def test_can_convert_response_to_ostruct
|
351
377
|
url = "http://some.endpoint/some.json"
|
352
378
|
payload = {a: 1}
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: gds-api-adapters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 4.
|
5
|
+
version: 4.9.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- James Stewart
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2013-02-
|
13
|
+
date: 2013-02-18 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: plek
|
@@ -201,6 +201,7 @@ files:
|
|
201
201
|
- lib/gds_api/core-ext/openstruct.rb
|
202
202
|
- lib/gds_api/response.rb
|
203
203
|
- lib/gds_api/content_api/list_response.rb
|
204
|
+
- lib/gds_api/content_api/response.rb
|
204
205
|
- lib/gds_api/publisher.rb
|
205
206
|
- lib/gds_api/test_helpers/licence_application.rb
|
206
207
|
- lib/gds_api/test_helpers/imminence.rb
|
@@ -217,6 +218,7 @@ files:
|
|
217
218
|
- Rakefile
|
218
219
|
- test/rummager_test.rb
|
219
220
|
- test/publisher_api_test.rb
|
221
|
+
- test/content_api_response_test.rb
|
220
222
|
- test/licence_application_api_test.rb
|
221
223
|
- test/mapit_test.rb
|
222
224
|
- test/panopticon_api_test.rb
|
@@ -239,7 +241,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
239
241
|
requirements:
|
240
242
|
- - ">="
|
241
243
|
- !ruby/object:Gem::Version
|
242
|
-
hash:
|
244
|
+
hash: -2287437040986270059
|
243
245
|
segments:
|
244
246
|
- 0
|
245
247
|
version: "0"
|
@@ -248,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
248
250
|
requirements:
|
249
251
|
- - ">="
|
250
252
|
- !ruby/object:Gem::Version
|
251
|
-
hash:
|
253
|
+
hash: -2287437040986270059
|
252
254
|
segments:
|
253
255
|
- 0
|
254
256
|
version: "0"
|
@@ -262,6 +264,7 @@ summary: Adapters to work with GDS APIs
|
|
262
264
|
test_files:
|
263
265
|
- test/rummager_test.rb
|
264
266
|
- test/publisher_api_test.rb
|
267
|
+
- test/content_api_response_test.rb
|
265
268
|
- test/licence_application_api_test.rb
|
266
269
|
- test/mapit_test.rb
|
267
270
|
- test/panopticon_api_test.rb
|