europeana-api 0.3.6 → 0.4.1
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.
- checksums.yaml +4 -4
- data/.ruby-style.yml +1 -1
- data/.travis.yml +4 -1
- data/README.md +4 -12
- data/europeana-api.gemspec +1 -1
- data/lib/europeana/api.rb +15 -4
- data/lib/europeana/api/record.rb +26 -80
- data/lib/europeana/api/record/hierarchy.rb +48 -0
- data/lib/europeana/api/record/hierarchy/ancestor_self_siblings.rb +12 -0
- data/lib/europeana/api/record/hierarchy/base.rb +32 -0
- data/lib/europeana/api/record/hierarchy/children.rb +12 -0
- data/lib/europeana/api/record/hierarchy/following_siblings.rb +12 -0
- data/lib/europeana/api/record/hierarchy/parent.rb +12 -0
- data/lib/europeana/api/record/hierarchy/preceding_siblings.rb +15 -0
- data/lib/europeana/api/record/hierarchy/self.rb +12 -0
- data/lib/europeana/api/requestable.rb +104 -0
- data/lib/europeana/api/search.rb +16 -44
- data/lib/europeana/api/version.rb +1 -1
- data/spec/europeana/api/record/hierarchy_spec.rb +15 -0
- data/spec/europeana/api/record_spec.rb +99 -99
- data/spec/spec_helper.rb +1 -1
- data/spec/support/shared_examples/record_request.rb +18 -23
- data/spec/support/webmock.rb +14 -0
- metadata +16 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 11327d8d43595f2888b4d3f501875fd45894abe5
|
4
|
+
data.tar.gz: 80e3e9ff21ab54e2bedcf70d8245fc7b84b30f50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec133494538ec61c5a45a01d9fb975f635b58b30136a5dd48c5f7c4efd5a3d5545ef36a238873b8ebb16b087628354e630c9903e1fe014cddd856412a1e3f35a
|
7
|
+
data.tar.gz: ca2870267f8f896def561a84ff9b087f3d73463f516c7c34b2080b5ec7da513a18804288c07bcfc6956ac10c9cc5e08df521f7888ca7cc7d417c3f4da841962a
|
data/.ruby-style.yml
CHANGED
@@ -496,7 +496,7 @@ Metrics/CyclomaticComplexity:
|
|
496
496
|
Metrics/LineLength:
|
497
497
|
Description: Limit lines to 80 characters.
|
498
498
|
StyleGuide: https://github.com/bbatsov/ruby-style-guide#80-character-limits
|
499
|
-
Enabled:
|
499
|
+
Enabled: false
|
500
500
|
Max: 80
|
501
501
|
AllowURI: true
|
502
502
|
URISchemes:
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Europeana
|
2
2
|
|
3
|
-
[](https://travis-ci.org/europeana/europeana-api-client-ruby) [](https://coveralls.io/github/europeana/europeana-api-client-ruby?branch=master) [](https://hakiri.io/github/europeana/europeana-api-client-ruby/master) [](https://gemnasium.com/europeana/europeana-api-client-ruby)
|
4
4
|
|
5
5
|
Ruby client library for the search and retrieval of records from the [Europeana
|
6
6
|
REST API](http://labs.europeana.eu/api/introduction/).
|
@@ -33,7 +33,7 @@ Or install it yourself as:
|
|
33
33
|
|
34
34
|
Authentication is *required* for all requests to the Europeana API.
|
35
35
|
|
36
|
-
Only Basic Authentication (by API key) is supported.
|
36
|
+
Only Basic Authentication (by API key) is supported.
|
37
37
|
|
38
38
|
Sign up for an API key at: http://labs.europeana.eu/api/registration/
|
39
39
|
|
@@ -58,17 +58,9 @@ the search response.
|
|
58
58
|
### Record
|
59
59
|
|
60
60
|
```ruby
|
61
|
-
record = Europeana::API.record('abc/1234') # => { "success" => true, "object" => { ... }, ... }
|
62
|
-
record['object'] # => { "title" => "...", "proxies" => [ ... ], "aggregations" => [ ... ]
|
61
|
+
record = Europeana::API.record('/abc/1234') # => { "success" => true, "object" => { ... }, ... }
|
62
|
+
record['object'] # => { "title" => "...", "proxies" => [ ... ], "aggregations" => [ ... ], ... }
|
63
63
|
```
|
64
64
|
|
65
65
|
See http://labs.europeana.eu/api/record/ for details of the data returned in
|
66
66
|
the record response.
|
67
|
-
|
68
|
-
## Contributing
|
69
|
-
|
70
|
-
1. Fork it
|
71
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
72
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
73
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
74
|
-
5. Create new Pull Request
|
data/europeana-api.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.required_ruby_version = '>=
|
21
|
+
spec.required_ruby_version = '>= 2.0.0'
|
22
22
|
|
23
23
|
spec.add_dependency 'activesupport', '>= 3.0'
|
24
24
|
spec.add_dependency 'multi_json', '~> 1.0'
|
data/lib/europeana/api.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
require 'active_support/cache'
|
1
2
|
require 'active_support/core_ext/object'
|
3
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
4
|
+
require 'active_support/core_ext/hash/slice'
|
2
5
|
require 'active_support/hash_with_indifferent_access'
|
6
|
+
require 'active_support/inflector/methods'
|
3
7
|
require 'europeana/api/version'
|
4
8
|
require 'logger'
|
5
9
|
require 'uri'
|
@@ -8,10 +12,11 @@ module Europeana
|
|
8
12
|
##
|
9
13
|
# Europeana REST API client
|
10
14
|
module API
|
11
|
-
autoload :Errors,
|
12
|
-
autoload :Record,
|
13
|
-
autoload :Request,
|
14
|
-
autoload :
|
15
|
+
autoload :Errors, 'europeana/api/errors'
|
16
|
+
autoload :Record, 'europeana/api/record'
|
17
|
+
autoload :Request, 'europeana/api/request'
|
18
|
+
autoload :Requestable, 'europeana/api/requestable'
|
19
|
+
autoload :Search, 'europeana/api/search'
|
15
20
|
|
16
21
|
class << self
|
17
22
|
##
|
@@ -46,12 +51,18 @@ module Europeana
|
|
46
51
|
# @return [Logger]
|
47
52
|
attr_writer :logger
|
48
53
|
|
54
|
+
attr_accessor :cache_store
|
55
|
+
|
56
|
+
attr_accessor :cache_expires_in
|
57
|
+
|
49
58
|
##
|
50
59
|
# Sets configuration values to their defaults
|
51
60
|
def defaults!
|
52
61
|
self.url = 'http://www.europeana.eu/api/v2'
|
53
62
|
self.max_retries = 5
|
54
63
|
self.retry_delay = 10
|
64
|
+
self.cache_store = ActiveSupport::Cache::NullStore.new
|
65
|
+
self.cache_expires_in = 24.hours
|
55
66
|
end
|
56
67
|
|
57
68
|
##
|
data/lib/europeana/api/record.rb
CHANGED
@@ -5,31 +5,22 @@ module Europeana
|
|
5
5
|
#
|
6
6
|
# @see http://labs.europeana.eu/api/record/
|
7
7
|
class Record
|
8
|
+
autoload :Hierarchy, 'europeana/api/record/hierarchy'
|
9
|
+
|
10
|
+
include Requestable
|
11
|
+
|
8
12
|
# Europeana ID of the record
|
9
|
-
|
13
|
+
attr_reader :id
|
10
14
|
|
11
15
|
# Request parameters to send to the API
|
12
|
-
|
16
|
+
attr_reader :params
|
13
17
|
|
14
18
|
##
|
15
19
|
# @param [String] id Europeana ID of the record
|
16
20
|
# @param [Hash] params Request parameters
|
17
21
|
def initialize(id, params = {})
|
18
|
-
|
19
|
-
|
20
|
-
@hierarchy = HashWithIndifferentAccess.new
|
21
|
-
end
|
22
|
-
|
23
|
-
##
|
24
|
-
# Returns query params with API key added
|
25
|
-
#
|
26
|
-
# @return [Hash]
|
27
|
-
def params_with_authentication
|
28
|
-
return params if params.key?(:wskey) && params[:wskey].present?
|
29
|
-
unless Europeana::API.api_key.present?
|
30
|
-
fail Errors::MissingAPIKeyError
|
31
|
-
end
|
32
|
-
params.merge(wskey: Europeana::API.api_key)
|
22
|
+
@id = id
|
23
|
+
@params = params
|
33
24
|
end
|
34
25
|
|
35
26
|
##
|
@@ -60,92 +51,47 @@ module Europeana
|
|
60
51
|
end
|
61
52
|
|
62
53
|
##
|
63
|
-
# Gets the
|
54
|
+
# Gets the URL for this Record request
|
64
55
|
#
|
65
56
|
# @param [Hash{Symbol => Object}] options
|
66
57
|
# @option options [Boolean] :ld (false)
|
67
58
|
# Request JSON-LD
|
68
|
-
# @return [
|
69
|
-
def
|
70
|
-
|
71
|
-
url
|
72
|
-
|
73
|
-
|
74
|
-
uri
|
59
|
+
# @return [String]
|
60
|
+
def request_url(options = {})
|
61
|
+
options.assert_valid_keys(:ld)
|
62
|
+
(Europeana::API.url + "/record#{@id}.json").tap do |url|
|
63
|
+
url << 'ld' if options[:ld]
|
64
|
+
end
|
75
65
|
end
|
76
66
|
|
67
|
+
alias_method :get, :execute_request
|
68
|
+
|
77
69
|
##
|
78
|
-
#
|
70
|
+
# Examines the `success` and `error` fields of the response for failure
|
79
71
|
#
|
80
|
-
# @param [Hash{Symbol => Object}] options
|
81
|
-
# @option options [Boolean] :ld (false)
|
82
|
-
# Request JSON-LD
|
83
|
-
# @return [Hash]
|
84
|
-
# @raise [Europeana::Errors::ResponseError] if API response could not be
|
85
|
-
# parsed as JSON
|
86
72
|
# @raise [Europeana::Errors::RequestError] if API response has
|
87
73
|
# `success:false`
|
88
74
|
# @raise [Europeana::Errors::RequestError] if API response has 404 status
|
89
75
|
# code
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
fail Errors::RequestError, (body.key?('error') ? body['error'] : response.code)
|
96
|
-
end
|
97
|
-
body
|
98
|
-
rescue JSON::ParserError
|
99
|
-
if response.code.to_i == 404
|
100
|
-
# Handle HTML 404 responses on malformed record ID, emulating API's
|
101
|
-
# JSON response.
|
102
|
-
raise Errors::RequestError, "Invalid record identifier: #{@id}"
|
103
|
-
else
|
104
|
-
raise Errors::ResponseError
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
##
|
109
|
-
# Gets hierarchy data about this and related records from
|
110
|
-
# ancestor-self-siblings.json API method
|
111
|
-
#
|
112
|
-
# @note Proof of concept implementation for demo purposes.
|
113
|
-
# @todo Refactor if functionality to be retained.
|
114
|
-
def hierarchy(*args)
|
115
|
-
args = [:self] if args.blank?
|
116
|
-
|
117
|
-
data = {}
|
118
|
-
args.each do |method|
|
119
|
-
unless @hierarchy.key?(method)
|
120
|
-
@hierarchy[method] = hierarchical_data(method).select do |_k, v|
|
121
|
-
v.is_a?(Enumerable)
|
122
|
-
end
|
76
|
+
# @see Requestable#parse_response
|
77
|
+
def parse_response(response, options = {})
|
78
|
+
super.tap do |body|
|
79
|
+
if (options[:ld] && !(200..299).include?(response.code.to_i)) || (!options[:ld] && !body[:success])
|
80
|
+
fail Errors::RequestError, (body.key?(:error) ? body[:error] : response.code)
|
123
81
|
end
|
124
|
-
data.merge!(@hierarchy[method])
|
125
82
|
end
|
126
|
-
data
|
127
|
-
end
|
128
|
-
|
129
|
-
def hierarchical_data(method = :self)
|
130
|
-
request = Request.new(hierarchical_data_uri(method))
|
131
|
-
response = request.execute
|
132
|
-
body = JSON.parse(response.body)
|
133
|
-
fail Errors::RequestError, body['message'] unless body['success']
|
134
|
-
body
|
135
83
|
rescue JSON::ParserError
|
136
84
|
if response.code.to_i == 404
|
137
85
|
# Handle HTML 404 responses on malformed record ID, emulating API's
|
138
86
|
# JSON response.
|
139
87
|
raise Errors::RequestError, "Invalid record identifier: #{@id}"
|
140
88
|
else
|
141
|
-
raise
|
89
|
+
raise
|
142
90
|
end
|
143
91
|
end
|
144
92
|
|
145
|
-
def
|
146
|
-
|
147
|
-
uri.query = params_with_authentication.to_query
|
148
|
-
uri
|
93
|
+
def hierarchy
|
94
|
+
@hierarchy ||= Hierarchy.new(id)
|
149
95
|
end
|
150
96
|
end
|
151
97
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Europeana
|
2
|
+
module API
|
3
|
+
class Record
|
4
|
+
##
|
5
|
+
# Retrieve record hierarchies over the Europeana API
|
6
|
+
class Hierarchy
|
7
|
+
autoload :AncestorSelfSiblings, 'europeana/api/record/hierarchy/ancestor_self_siblings'
|
8
|
+
autoload :Base, 'europeana/api/record/hierarchy/base'
|
9
|
+
autoload :Children, 'europeana/api/record/hierarchy/children'
|
10
|
+
autoload :FollowingSiblings, 'europeana/api/record/hierarchy/following_siblings'
|
11
|
+
autoload :Parent, 'europeana/api/record/hierarchy/parent'
|
12
|
+
autoload :PrecedingSiblings, 'europeana/api/record/hierarchy/preceding_siblings'
|
13
|
+
autoload :Self, 'europeana/api/record/hierarchy/self'
|
14
|
+
|
15
|
+
def initialize(id)
|
16
|
+
@id = id
|
17
|
+
end
|
18
|
+
|
19
|
+
# bad idea having a method named self, but this is just a minimal
|
20
|
+
# helper class, so going with it for consistency with the API
|
21
|
+
def self(params = {})
|
22
|
+
Self.new(@id, params).execute_request
|
23
|
+
end
|
24
|
+
|
25
|
+
def parent(params = {})
|
26
|
+
Parent.new(@id, params).execute_request
|
27
|
+
end
|
28
|
+
|
29
|
+
def children(params = {})
|
30
|
+
Children.new(@id, params).execute_request
|
31
|
+
end
|
32
|
+
|
33
|
+
def preceding_siblings(params = {})
|
34
|
+
PrecedingSiblings.new(@id, params).execute_request
|
35
|
+
end
|
36
|
+
alias_method :preceeding_siblings, :preceding_siblings
|
37
|
+
|
38
|
+
def following_siblings(params = {})
|
39
|
+
FollowingSiblings.new(@id, params).execute_request
|
40
|
+
end
|
41
|
+
|
42
|
+
def ancestor_self_siblings(params = {})
|
43
|
+
AncestorSelfSiblings.new(@id, params).execute_request
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Europeana
|
2
|
+
module API
|
3
|
+
class Record
|
4
|
+
class Hierarchy
|
5
|
+
##
|
6
|
+
# Base class for common heirarchy API behaviour
|
7
|
+
class Base
|
8
|
+
include Requestable
|
9
|
+
|
10
|
+
attr_accessor :params
|
11
|
+
|
12
|
+
def initialize(id, params = {})
|
13
|
+
@id = id
|
14
|
+
@params = params
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_response(response, options = {})
|
18
|
+
super.slice(:self, :children, :parent, 'preceeding-siblings', 'following-siblings', 'ancestors')
|
19
|
+
end
|
20
|
+
|
21
|
+
def request_url(_options = {})
|
22
|
+
Europeana::API.url + "/record#{@id}/#{api_method}.json"
|
23
|
+
end
|
24
|
+
|
25
|
+
def api_method
|
26
|
+
self.class.to_s.demodulize.underscore.dasherize
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Europeana
|
2
|
+
module API
|
3
|
+
class Record
|
4
|
+
class Hierarchy
|
5
|
+
##
|
6
|
+
# Retrieve record preceding siblings hierarchy data over the Europeana API
|
7
|
+
class PrecedingSiblings < Base
|
8
|
+
def api_method
|
9
|
+
'preceeding-siblings' # mis-spelt on the API
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Europeana
|
2
|
+
module API
|
3
|
+
##
|
4
|
+
# Mixin for classes that need to make Europeana API requests
|
5
|
+
#
|
6
|
+
# Class needs to implement {#request_url}
|
7
|
+
module Requestable
|
8
|
+
##
|
9
|
+
# Request-specific params, to be overriden in including class
|
10
|
+
#
|
11
|
+
# @return [Hash]
|
12
|
+
def params
|
13
|
+
{}
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Query params with API key added
|
18
|
+
#
|
19
|
+
# @return [Hash]
|
20
|
+
def params_with_authentication
|
21
|
+
return params if params.key?(:wskey) && params[:wskey].present?
|
22
|
+
unless Europeana::API.api_key.present?
|
23
|
+
fail Europeana::API::Errors::MissingAPIKeyError
|
24
|
+
end
|
25
|
+
params.merge(wskey: Europeana::API.api_key)
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Execute the API request
|
30
|
+
#
|
31
|
+
# @param options [Hash] Options sent on to {#request_uri} and {#parse_response}
|
32
|
+
# @return (see #parse_response)
|
33
|
+
# @raise [Europeana::Errors::ResponseError] if API response could not be
|
34
|
+
# parsed as JSON
|
35
|
+
def execute_request(options = {})
|
36
|
+
uri = request_uri(options)
|
37
|
+
cache_response_body(uri) do
|
38
|
+
response = Request.new(uri).execute
|
39
|
+
parse_response(response, options)
|
40
|
+
end
|
41
|
+
rescue JSON::ParserError
|
42
|
+
raise Errors::ResponseError
|
43
|
+
end
|
44
|
+
|
45
|
+
def cache_key(uri)
|
46
|
+
"Europeana/API/#{uri}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def cache_response_body(uri)
|
50
|
+
Europeana::API.cache_store.fetch(cache_key(uri), expires_in: Europeana::API.cache_expires_in) do
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Parses a JSON response from the API
|
57
|
+
#
|
58
|
+
# @param response (see Net::HTTP#request)
|
59
|
+
# @param options [Hash] Options used by including class's implementation
|
60
|
+
# of this method
|
61
|
+
# @return [HashWithIndifferentAccess] Parsed body of API response
|
62
|
+
def parse_response(response, _options = {})
|
63
|
+
JSON.parse(response.body).with_indifferent_access
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# URI query param string
|
68
|
+
#
|
69
|
+
# @return [String]
|
70
|
+
def request_uri_query
|
71
|
+
''.tap do |uri_query|
|
72
|
+
params_with_authentication.each_pair do |name, value|
|
73
|
+
[value].flatten.each do |v|
|
74
|
+
uri_query << '&' unless uri_query.blank?
|
75
|
+
uri_query << CGI.escape(name.to_s) + '=' + CGI.escape(v.to_s)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# Gets the URI for this request, with any query parameters
|
83
|
+
#
|
84
|
+
# @param [Hash{Symbol => Object}] options passed to {#request_url}
|
85
|
+
# @return [String]
|
86
|
+
def request_uri(options = {})
|
87
|
+
URI.parse(request_url(options)).tap do |uri|
|
88
|
+
uri.query = request_uri_query
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# URL for the request, without query params
|
94
|
+
#
|
95
|
+
# To be implemented by the including class
|
96
|
+
#
|
97
|
+
# @param options [Hash] Options used by implementation
|
98
|
+
# @return [String] Request URL
|
99
|
+
def request_url(_options = {})
|
100
|
+
fail NotImplementedError, "Requestable class #{self.class} does not implement #request_url"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/europeana/api/search.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/hash'
|
2
|
-
|
3
1
|
module Europeana
|
4
2
|
module API
|
5
3
|
##
|
@@ -7,6 +5,8 @@ module Europeana
|
|
7
5
|
class Search
|
8
6
|
autoload :Fields, 'europeana/api/search/fields'
|
9
7
|
|
8
|
+
include Requestable
|
9
|
+
|
10
10
|
# Query params
|
11
11
|
attr_accessor :params
|
12
12
|
|
@@ -17,6 +17,7 @@ module Europeana
|
|
17
17
|
# @param [String] text Text to escape
|
18
18
|
# @return [String] Escaped text
|
19
19
|
def escape(text)
|
20
|
+
fail ArgumentError, "Expected String, got #{text.class}" unless text.is_a?(String)
|
20
21
|
specials = %w<\\ + - & | ! ( ) { } [ ] ^ " ~ * ? : / >
|
21
22
|
specials.each_with_object(text.dup) do |char, unescaped|
|
22
23
|
unescaped.gsub!(char, '\\\\' + char) # prepends *one* backslash
|
@@ -31,56 +32,27 @@ module Europeana
|
|
31
32
|
end
|
32
33
|
|
33
34
|
##
|
34
|
-
#
|
35
|
+
# Base URL for a Search request
|
35
36
|
#
|
36
|
-
# @return [
|
37
|
-
|
38
|
-
|
39
|
-
# @raise [Europeana::Errors::RequestError] if API response has
|
40
|
-
# `success:false`
|
41
|
-
def execute
|
42
|
-
request = Request.new(request_uri)
|
43
|
-
response = request.execute
|
44
|
-
body = JSON.parse(response.body)
|
45
|
-
unless body['success']
|
46
|
-
fail Errors::RequestError, (body.key?('error') ? body['error'] : response.code)
|
47
|
-
end
|
48
|
-
HashWithIndifferentAccess.new(body)
|
49
|
-
rescue JSON::ParserError
|
50
|
-
raise Errors::ResponseError
|
37
|
+
# @return [URI]
|
38
|
+
def request_url(_options = {})
|
39
|
+
Europeana::API.url + '/search.json'
|
51
40
|
end
|
52
41
|
|
53
|
-
|
54
|
-
# Returns query params with API key added
|
55
|
-
#
|
56
|
-
# @return [Hash]
|
57
|
-
def params_with_authentication
|
58
|
-
return params if params.key?(:wskey) && params[:wskey].present?
|
59
|
-
unless Europeana::API.api_key.present?
|
60
|
-
fail Errors::MissingAPIKeyError
|
61
|
-
end
|
62
|
-
params.merge(wskey: Europeana::API.api_key)
|
63
|
-
end
|
42
|
+
alias_method :execute, :execute_request
|
64
43
|
|
65
44
|
##
|
66
|
-
#
|
45
|
+
# Examines the `success` and `error` fields of the response for failure
|
67
46
|
#
|
68
|
-
# @
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
def request_uri_query
|
76
|
-
uri_query = ''
|
77
|
-
params_with_authentication.each_pair do |name, value|
|
78
|
-
[value].flatten.each do |v|
|
79
|
-
uri_query << '&' unless uri_query.blank?
|
80
|
-
uri_query << CGI.escape(name.to_s) + '=' + CGI.escape(v.to_s)
|
47
|
+
# @raise [Europeana::Errors::RequestError] if API response has
|
48
|
+
# `success:false`
|
49
|
+
# @see Requestable#parse_response
|
50
|
+
def parse_response(response, _options = {})
|
51
|
+
super.tap do |body|
|
52
|
+
unless body[:success]
|
53
|
+
fail Errors::RequestError, (body.key?(:error) ? body[:error] : response.code)
|
81
54
|
end
|
82
55
|
end
|
83
|
-
uri_query
|
84
56
|
end
|
85
57
|
end
|
86
58
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe Europeana::API::Record::Hierarchy do
|
2
|
+
let(:record_id) { '/abc/1234' }
|
3
|
+
let(:params) { { callback: 'doSomething();' } }
|
4
|
+
|
5
|
+
subject { described_class.new(record_id) }
|
6
|
+
|
7
|
+
%w(self parent children).each do |relation|
|
8
|
+
describe "##{relation}" do
|
9
|
+
it "should retrieve #{relation} hierarchy data from the API" do
|
10
|
+
subject.send(relation)
|
11
|
+
expect(a_request(:get, %r{www.europeana.eu/api/v2/record#{record_id}/#{relation}.json})).to have_been_made.once
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,133 +1,133 @@
|
|
1
|
-
|
1
|
+
RSpec.describe Europeana::API::Record do
|
2
|
+
let(:api_key) { 'xyz' }
|
3
|
+
let(:record_id) { '/abc/1234' }
|
4
|
+
let(:params) { { callback: 'doSomething();' } }
|
2
5
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
let(:api_key) { 'xyz' }
|
7
|
-
let(:record_id) { '/abc/1234' }
|
8
|
-
let(:params) { { callback: 'doSomething();' } }
|
6
|
+
before do
|
7
|
+
Europeana::API.api_key = api_key
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
describe '#new' do
|
11
|
+
context 'without record ID' do
|
12
|
+
it 'raises error' do
|
13
|
+
expect { subject }.to raise_error(ArgumentError)
|
12
14
|
end
|
15
|
+
end
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
expect { subject }.to raise_error(ArgumentError)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context "with record ID" do
|
22
|
-
context "without params" do
|
23
|
-
subject { described_class.new(record_id) }
|
24
|
-
|
25
|
-
it "should not raise error" do
|
26
|
-
expect { subject }.to_not raise_error
|
27
|
-
end
|
17
|
+
context 'with record ID' do
|
18
|
+
context 'without params' do
|
19
|
+
subject { described_class.new(record_id) }
|
28
20
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
21
|
+
it 'should not raise error' do
|
22
|
+
expect { subject }.to_not raise_error
|
23
|
+
end
|
33
24
|
|
34
|
-
|
35
|
-
|
25
|
+
it 'sets id attribute' do
|
26
|
+
expect(subject.instance_variable_get(:@id)).to eq(record_id)
|
27
|
+
end
|
28
|
+
end
|
36
29
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
30
|
+
context 'with params' do
|
31
|
+
subject { described_class.new(record_id, params) }
|
40
32
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
33
|
+
it 'should not raise error' do
|
34
|
+
expect { subject }.to_not raise_error
|
45
35
|
end
|
46
|
-
end
|
47
36
|
|
48
|
-
|
49
|
-
|
50
|
-
it "gets id attribute" do
|
51
|
-
expect(subject.id).to eq(subject.instance_variable_get(:@id))
|
37
|
+
it 'sets params attribute' do
|
38
|
+
expect(subject.instance_variable_get(:@params)).to eq(params)
|
52
39
|
end
|
53
40
|
end
|
41
|
+
end
|
42
|
+
end
|
54
43
|
|
55
|
-
|
56
|
-
|
44
|
+
describe '#id' do
|
45
|
+
subject { described_class.new(record_id) }
|
46
|
+
it 'gets id attribute' do
|
47
|
+
expect(subject.id).to eq(subject.instance_variable_get(:@id))
|
48
|
+
end
|
49
|
+
end
|
57
50
|
|
58
|
-
|
59
|
-
|
60
|
-
subject.id = "/xyz/5678"
|
61
|
-
expect(subject.instance_variable_get(:@id)).to eq("/xyz/5678")
|
62
|
-
end
|
63
|
-
end
|
51
|
+
describe '#id=' do
|
52
|
+
subject { described_class.new(record_id) }
|
64
53
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
54
|
+
context 'with valid ID' do
|
55
|
+
it 'sets id attribute' do
|
56
|
+
subject.id = '/xyz/5678'
|
57
|
+
expect(subject.instance_variable_get(:@id)).to eq('/xyz/5678')
|
70
58
|
end
|
59
|
+
end
|
71
60
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
expect(subject.params).to eq(subject.instance_variable_get(:@params))
|
76
|
-
end
|
61
|
+
context 'invalid ID' do
|
62
|
+
it 'raises error' do
|
63
|
+
expect { subject.id = 'invalid' }.to raise_error('Invalid Europeana record ID: "invalid"')
|
77
64
|
end
|
65
|
+
end
|
66
|
+
end
|
78
67
|
|
79
|
-
|
80
|
-
|
68
|
+
describe '#params' do
|
69
|
+
subject { described_class.new(record_id, params) }
|
70
|
+
it 'gets params attribute' do
|
71
|
+
expect(subject.params).to eq(subject.instance_variable_get(:@params))
|
72
|
+
end
|
73
|
+
end
|
81
74
|
|
82
|
-
|
83
|
-
|
84
|
-
subject.params = params
|
85
|
-
expect(subject.instance_variable_get(:@params)).to eq(params)
|
86
|
-
end
|
87
|
-
end
|
75
|
+
describe '#params=' do
|
76
|
+
subject { described_class.new(record_id, {}) }
|
88
77
|
|
89
|
-
|
90
|
-
|
91
|
-
|
78
|
+
context 'valid params' do
|
79
|
+
it 'sets params attribute' do
|
80
|
+
subject.params = params
|
81
|
+
expect(subject.instance_variable_get(:@params)).to eq(params)
|
92
82
|
end
|
83
|
+
end
|
93
84
|
|
94
|
-
|
95
|
-
|
85
|
+
it 'validates param names' do
|
86
|
+
expect { subject.params = { invalid: 'parameter' } }.to raise_error(/Unknown key: :?invalid/)
|
87
|
+
end
|
88
|
+
end
|
96
89
|
|
97
|
-
|
98
|
-
|
99
|
-
expect(subject.params_with_authentication).to eq(params.merge(:wskey => api_key))
|
100
|
-
end
|
101
|
-
end
|
90
|
+
describe '#params_with_authentication' do
|
91
|
+
subject { described_class.new(record_id, params) }
|
102
92
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
expect { subject.params_with_authentication }.to raise_error(Europeana::API::Errors::MissingAPIKeyError)
|
107
|
-
end
|
108
|
-
end
|
93
|
+
context 'with API key' do
|
94
|
+
it 'adds API key to params' do
|
95
|
+
expect(subject.params_with_authentication).to eq(params.merge(wskey: api_key))
|
109
96
|
end
|
97
|
+
end
|
110
98
|
|
111
|
-
|
112
|
-
|
99
|
+
context 'without API key' do
|
100
|
+
it 'raises an error' do
|
101
|
+
Europeana::API.api_key = nil
|
102
|
+
expect { subject.params_with_authentication }.to raise_error(Europeana::API::Errors::MissingAPIKeyError)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
113
106
|
|
114
|
-
|
115
|
-
|
116
|
-
end
|
107
|
+
describe '#request_uri' do
|
108
|
+
subject { described_class.new(record_id, params) }
|
117
109
|
|
118
|
-
|
119
|
-
|
120
|
-
|
110
|
+
it 'returns a URI' do
|
111
|
+
expect(subject.request_uri).to be_a(URI)
|
112
|
+
end
|
121
113
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end
|
114
|
+
it 'includes the record ID' do
|
115
|
+
expect(subject.request_uri.to_s).to include(record_id)
|
116
|
+
end
|
126
117
|
|
127
|
-
|
128
|
-
|
129
|
-
it_behaves_like "record request"
|
130
|
-
end
|
118
|
+
it 'includes the query params' do
|
119
|
+
expect(subject.request_uri.to_s).to include(params.to_query)
|
131
120
|
end
|
132
121
|
end
|
122
|
+
|
123
|
+
describe '#get' do
|
124
|
+
subject { described_class.new(record_id, params).get }
|
125
|
+
it_behaves_like 'record request'
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '#hierarchy' do
|
129
|
+
let(:record) { described_class.new(record_id, params) }
|
130
|
+
subject { record.hierarchy }
|
131
|
+
it { is_expected.to be_a(Europeana::API::Record::Hierarchy) }
|
132
|
+
end
|
133
133
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,31 +1,26 @@
|
|
1
|
-
shared_examples
|
1
|
+
shared_examples 'record request' do
|
2
|
+
let(:api_record_endpoint) { %r{www.europeana.eu/api/v2/record#{record_id}\.json} }
|
3
|
+
let(:api_key) { 'xyz' }
|
4
|
+
let(:record_id) { '/abc/1234' }
|
5
|
+
let(:params) { { callback: 'doSomething();' } }
|
6
|
+
|
2
7
|
before(:each) do
|
3
|
-
stub_request(:get,
|
4
|
-
|
8
|
+
stub_request(:get, api_record_endpoint).to_return(body: '{"success":true}')
|
9
|
+
Europeana::API.api_key = api_key
|
5
10
|
end
|
6
11
|
|
7
|
-
it_behaves_like
|
8
|
-
|
9
|
-
context "with API key" do
|
10
|
-
before(:each) do
|
11
|
-
Europeana::API.api_key = api_key
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:api_key) { 'xyz' }
|
15
|
-
let(:record_id) { '/abc/1234' }
|
16
|
-
let(:params) { { callback: 'doSomething();' } }
|
12
|
+
it_behaves_like 'API request'
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
it 'sends a Record request to the API' do
|
15
|
+
subject
|
16
|
+
expect(a_request(:get, api_record_endpoint)).to have_been_made.once
|
17
|
+
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
19
|
+
context 'when record ID is invalid' do
|
20
|
+
it 'raises RequestError from HTML 404 response' do
|
21
|
+
stub_request(:get, api_record_endpoint).
|
22
|
+
to_return(body: '<html></html>', headers: { 'Content-Type' => 'text/html' }, status: 404)
|
23
|
+
expect { subject }.to raise_error(Europeana::API::Errors::RequestError, "Invalid record identifier: #{record_id}")
|
29
24
|
end
|
30
25
|
end
|
31
26
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.before do
|
3
|
+
Europeana::API.api_key = 'xyz'
|
4
|
+
|
5
|
+
stub_request(:get, %r{www.europeana.eu/api/v2/record/[^/]+/[^/]+/self\.json}).
|
6
|
+
to_return(body: lambda { |_| '{"success":true, "self":{"id":"' + record_id + '", "childrenCount":0, "hasChildren":false}}' })
|
7
|
+
|
8
|
+
stub_request(:get, %r{www.europeana.eu/api/v2/record/[^/]+/[^/]+/parent\.json}).
|
9
|
+
to_return(body: lambda { |_| '{"success":true, "self":{"id":"' + record_id + '", "childrenCount":5, "hasChildren":false}, "parent":{}}' })
|
10
|
+
|
11
|
+
stub_request(:get, %r{www.europeana.eu/api/v2/record/[^/]+/[^/]+/children\.json}).
|
12
|
+
to_return(body: lambda { |_| '{"success":true, "self":{"id":"' + record_id + '", "childrenCount":5, "hasChildren":false}, "children":[]}' })
|
13
|
+
end
|
14
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: europeana-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Doe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -115,11 +115,21 @@ files:
|
|
115
115
|
- lib/europeana/api.rb
|
116
116
|
- lib/europeana/api/errors.rb
|
117
117
|
- lib/europeana/api/record.rb
|
118
|
+
- lib/europeana/api/record/hierarchy.rb
|
119
|
+
- lib/europeana/api/record/hierarchy/ancestor_self_siblings.rb
|
120
|
+
- lib/europeana/api/record/hierarchy/base.rb
|
121
|
+
- lib/europeana/api/record/hierarchy/children.rb
|
122
|
+
- lib/europeana/api/record/hierarchy/following_siblings.rb
|
123
|
+
- lib/europeana/api/record/hierarchy/parent.rb
|
124
|
+
- lib/europeana/api/record/hierarchy/preceding_siblings.rb
|
125
|
+
- lib/europeana/api/record/hierarchy/self.rb
|
118
126
|
- lib/europeana/api/request.rb
|
127
|
+
- lib/europeana/api/requestable.rb
|
119
128
|
- lib/europeana/api/search.rb
|
120
129
|
- lib/europeana/api/search/fields.rb
|
121
130
|
- lib/europeana/api/version.rb
|
122
131
|
- spec/europeana/api/errors_spec.rb
|
132
|
+
- spec/europeana/api/record/hierarchy_spec.rb
|
123
133
|
- spec/europeana/api/record_spec.rb
|
124
134
|
- spec/europeana/api/search_spec.rb
|
125
135
|
- spec/europeana/api_spec.rb
|
@@ -127,6 +137,7 @@ files:
|
|
127
137
|
- spec/support/shared_examples/api_request.rb
|
128
138
|
- spec/support/shared_examples/record_request.rb
|
129
139
|
- spec/support/shared_examples/search_request.rb
|
140
|
+
- spec/support/webmock.rb
|
130
141
|
homepage: https://github.com/europeana/europeana-api-client-ruby
|
131
142
|
licenses:
|
132
143
|
- EUPL V.1.1
|
@@ -139,7 +150,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
139
150
|
requirements:
|
140
151
|
- - ">="
|
141
152
|
- !ruby/object:Gem::Version
|
142
|
-
version:
|
153
|
+
version: 2.0.0
|
143
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
155
|
requirements:
|
145
156
|
- - ">="
|
@@ -153,6 +164,7 @@ specification_version: 4
|
|
153
164
|
summary: Ruby client library for the Europeana API
|
154
165
|
test_files:
|
155
166
|
- spec/europeana/api/errors_spec.rb
|
167
|
+
- spec/europeana/api/record/hierarchy_spec.rb
|
156
168
|
- spec/europeana/api/record_spec.rb
|
157
169
|
- spec/europeana/api/search_spec.rb
|
158
170
|
- spec/europeana/api_spec.rb
|
@@ -160,3 +172,4 @@ test_files:
|
|
160
172
|
- spec/support/shared_examples/api_request.rb
|
161
173
|
- spec/support/shared_examples/record_request.rb
|
162
174
|
- spec/support/shared_examples/search_request.rb
|
175
|
+
- spec/support/webmock.rb
|