europeana-api 0.5.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +4 -2
- data/Gemfile +1 -8
- data/README.md +103 -21
- data/Rakefile +2 -1
- data/bin/console +21 -0
- data/europeana-api.gemspec +12 -1
- data/lib/europeana/api.rb +44 -68
- data/lib/europeana/api/annotation.rb +20 -0
- data/lib/europeana/api/client.rb +51 -0
- data/lib/europeana/api/entity.rb +16 -0
- data/lib/europeana/api/errors.rb +25 -41
- data/lib/europeana/api/faraday_middleware.rb +26 -0
- data/lib/europeana/api/faraday_middleware/request/authenticated_request.rb +25 -0
- data/lib/europeana/api/faraday_middleware/request/parameter_repetition.rb +25 -0
- data/lib/europeana/api/faraday_middleware/response/handle_text.rb +19 -0
- data/lib/europeana/api/faraday_middleware/response/parse_json_to_various.rb +52 -0
- data/lib/europeana/api/logger.rb +10 -0
- data/lib/europeana/api/queue.rb +47 -0
- data/lib/europeana/api/record.rb +29 -83
- data/lib/europeana/api/request.rb +77 -38
- data/lib/europeana/api/resource.rb +30 -0
- data/lib/europeana/api/response.rb +47 -0
- data/lib/europeana/api/version.rb +2 -1
- data/spec/europeana/api/annotation_spec.rb +77 -0
- data/spec/europeana/api/client_spec.rb +46 -0
- data/spec/europeana/api/entity_spec.rb +6 -0
- data/spec/europeana/api/faraday_middleware/request/authenticated_request_spec.rb +22 -0
- data/spec/europeana/api/queue_spec.rb +4 -0
- data/spec/europeana/api/record_spec.rb +54 -104
- data/spec/europeana/api/request_spec.rb +3 -0
- data/spec/europeana/api/resource_spec.rb +47 -0
- data/spec/europeana/api/response_spec.rb +10 -0
- data/spec/europeana/api_spec.rb +34 -84
- data/spec/spec_helper.rb +8 -0
- data/spec/support/shared_examples/resource_endpoint.rb +11 -0
- metadata +158 -34
- data/lib/europeana/api/record/hierarchy.rb +0 -48
- data/lib/europeana/api/record/hierarchy/ancestor_self_siblings.rb +0 -12
- data/lib/europeana/api/record/hierarchy/base.rb +0 -30
- data/lib/europeana/api/record/hierarchy/children.rb +0 -12
- data/lib/europeana/api/record/hierarchy/following_siblings.rb +0 -12
- data/lib/europeana/api/record/hierarchy/parent.rb +0 -12
- data/lib/europeana/api/record/hierarchy/preceding_siblings.rb +0 -15
- data/lib/europeana/api/record/hierarchy/self.rb +0 -12
- data/lib/europeana/api/requestable.rb +0 -118
- data/lib/europeana/api/search.rb +0 -64
- data/lib/europeana/api/search/fields.rb +0 -112
- data/spec/europeana/api/errors_spec.rb +0 -23
- data/spec/europeana/api/record/hierarchy_spec.rb +0 -15
- data/spec/europeana/api/search_spec.rb +0 -97
- data/spec/support/shared_examples/api_request.rb +0 -65
- data/spec/support/shared_examples/record_request.rb +0 -26
- data/spec/support/shared_examples/search_request.rb +0 -42
- data/spec/support/webmock.rb +0 -14
@@ -1,48 +0,0 @@
|
|
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
|
@@ -1,30 +0,0 @@
|
|
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
|
-
def initialize(id, params = {})
|
11
|
-
@id = id
|
12
|
-
self.request_params = params
|
13
|
-
end
|
14
|
-
|
15
|
-
def parse_response(response, options = {})
|
16
|
-
super.slice(:self, :children, :parent, 'preceding-siblings', 'preceeding-siblings', 'following-siblings', 'ancestors')
|
17
|
-
end
|
18
|
-
|
19
|
-
def request_url(_options = {})
|
20
|
-
api_url + "/record#{@id}/#{api_method}.json"
|
21
|
-
end
|
22
|
-
|
23
|
-
def api_method
|
24
|
-
self.class.to_s.demodulize.underscore.dasherize
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,15 +0,0 @@
|
|
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
|
@@ -1,118 +0,0 @@
|
|
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
|
-
def extract_api_url(params)
|
9
|
-
@api_url = params.delete(:api_url)
|
10
|
-
end
|
11
|
-
|
12
|
-
def api_url
|
13
|
-
@api_url || Europeana::API.url
|
14
|
-
end
|
15
|
-
|
16
|
-
##
|
17
|
-
# Request-specific params, to be overriden in including class
|
18
|
-
#
|
19
|
-
# @return [Hash]
|
20
|
-
def params
|
21
|
-
{}
|
22
|
-
end
|
23
|
-
|
24
|
-
def request_params=(params)
|
25
|
-
params = params.deep_dup
|
26
|
-
extract_api_url(params)
|
27
|
-
@params = HashWithIndifferentAccess.new(params)
|
28
|
-
end
|
29
|
-
|
30
|
-
##
|
31
|
-
# Query params with API key added
|
32
|
-
#
|
33
|
-
# @return [Hash]
|
34
|
-
def params_with_authentication
|
35
|
-
return params if params.key?(:wskey) && params[:wskey].present?
|
36
|
-
unless Europeana::API.api_key.present?
|
37
|
-
fail Europeana::API::Errors::MissingAPIKeyError
|
38
|
-
end
|
39
|
-
params.merge(wskey: Europeana::API.api_key)
|
40
|
-
end
|
41
|
-
|
42
|
-
##
|
43
|
-
# Execute the API request
|
44
|
-
#
|
45
|
-
# @param options [Hash] Options sent on to {#request_uri} and {#parse_response}
|
46
|
-
# @return (see #parse_response)
|
47
|
-
# @raise [Europeana::Errors::ResponseError] if API response could not be
|
48
|
-
# parsed as JSON
|
49
|
-
def execute_request(options = {})
|
50
|
-
uri = request_uri(options)
|
51
|
-
cache_response_body(uri) do
|
52
|
-
response = Request.new(uri).execute
|
53
|
-
parse_response(response, options)
|
54
|
-
end
|
55
|
-
rescue JSON::ParserError
|
56
|
-
raise Errors::ResponseError
|
57
|
-
end
|
58
|
-
|
59
|
-
def cache_key(uri)
|
60
|
-
"Europeana/API/#{uri}"
|
61
|
-
end
|
62
|
-
|
63
|
-
def cache_response_body(uri)
|
64
|
-
Europeana::API.cache_store.fetch(cache_key(uri), expires_in: Europeana::API.cache_expires_in) do
|
65
|
-
yield
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
##
|
70
|
-
# Parses a JSON response from the API
|
71
|
-
#
|
72
|
-
# @param response (see Net::HTTP#request)
|
73
|
-
# @param options [Hash] Options used by including class's implementation
|
74
|
-
# of this method
|
75
|
-
# @return [HashWithIndifferentAccess] Parsed body of API response
|
76
|
-
def parse_response(response, _options = {})
|
77
|
-
JSON.parse(response.body).with_indifferent_access
|
78
|
-
end
|
79
|
-
|
80
|
-
##
|
81
|
-
# URI query param string
|
82
|
-
#
|
83
|
-
# @return [String]
|
84
|
-
def request_uri_query
|
85
|
-
''.tap do |uri_query|
|
86
|
-
params_with_authentication.each_pair do |name, value|
|
87
|
-
[value].flatten.each do |v|
|
88
|
-
uri_query << '&' unless uri_query.blank?
|
89
|
-
uri_query << CGI.escape(name.to_s) + '=' + CGI.escape(v.to_s)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
##
|
96
|
-
# Gets the URI for this request, with any query parameters
|
97
|
-
#
|
98
|
-
# @param [Hash{Symbol => Object}] options passed to {#request_url}
|
99
|
-
# @return [String]
|
100
|
-
def request_uri(options = {})
|
101
|
-
URI.parse(request_url(options)).tap do |uri|
|
102
|
-
uri.query = request_uri_query
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
##
|
107
|
-
# URL for the request, without query params
|
108
|
-
#
|
109
|
-
# To be implemented by the including class
|
110
|
-
#
|
111
|
-
# @param options [Hash] Options used by implementation
|
112
|
-
# @return [String] Request URL
|
113
|
-
def request_url(_options = {})
|
114
|
-
fail NotImplementedError, "Requestable class #{self.class} does not implement #request_url"
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
data/lib/europeana/api/search.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
module Europeana
|
2
|
-
module API
|
3
|
-
##
|
4
|
-
# Interface to Europeana API's Search method
|
5
|
-
class Search
|
6
|
-
autoload :Fields, 'europeana/api/search/fields'
|
7
|
-
|
8
|
-
include Requestable
|
9
|
-
|
10
|
-
# Query params
|
11
|
-
attr_accessor :params
|
12
|
-
|
13
|
-
class << self
|
14
|
-
##
|
15
|
-
# Escapes Lucene syntax special characters for use in query parameters
|
16
|
-
#
|
17
|
-
# @param [String] text Text to escape
|
18
|
-
# @return [String] Escaped text
|
19
|
-
def escape(text)
|
20
|
-
fail ArgumentError, "Expected String, got #{text.class}" unless text.is_a?(String)
|
21
|
-
specials = %w<\\ + - & | ! ( ) { } [ ] ^ " ~ * ? : / >
|
22
|
-
specials.each_with_object(text.dup) do |char, unescaped|
|
23
|
-
unescaped.gsub!(char, '\\\\' + char) # prepends *one* backslash
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
##
|
29
|
-
# @param [Hash] params Europeana API request parameters for Search query
|
30
|
-
def initialize(params = {})
|
31
|
-
self.request_params = params
|
32
|
-
end
|
33
|
-
|
34
|
-
##
|
35
|
-
# Base URL for a Search request
|
36
|
-
#
|
37
|
-
# @return [URI]
|
38
|
-
def request_url(_options = {})
|
39
|
-
api_url + '/search.json'
|
40
|
-
end
|
41
|
-
|
42
|
-
alias_method :execute, :execute_request
|
43
|
-
|
44
|
-
##
|
45
|
-
# Examines the `success` and `error` fields of the response for failure
|
46
|
-
#
|
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
|
-
klass = if body.key?(:error) && body[:error] =~ /1000 search results/
|
54
|
-
Errors::Request::PaginationError
|
55
|
-
else
|
56
|
-
Errors::RequestError
|
57
|
-
end
|
58
|
-
fail klass, (body.key?(:error) ? body[:error] : response.code)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
@@ -1,112 +0,0 @@
|
|
1
|
-
module Europeana
|
2
|
-
module API
|
3
|
-
class Search
|
4
|
-
##
|
5
|
-
# API field information and validation in search context
|
6
|
-
#
|
7
|
-
# @see http://labs.europeana.eu/api/data-fields/
|
8
|
-
module Fields
|
9
|
-
EDM_PROVIDED_CHO = %w(edm_europeana_proxy proxy_dc_contributor
|
10
|
-
proxy_dc_coverage proxy_dc_creator proxy_dc_date proxy_dc_description
|
11
|
-
proxy_dc_format proxy_dc_identifier proxy_dc_language
|
12
|
-
proxy_dc_publisher proxy_dc_relation proxy_dc_rights proxy_dc_source
|
13
|
-
proxy_dc_subject proxy_dc_title proxy_dc_type
|
14
|
-
proxy_dcterms_alternative proxy_dcterms_conformsTo
|
15
|
-
proxy_dcterms_created proxy_dcterms_extent proxy_dcterms_hasFormat
|
16
|
-
proxy_dcterms_hasPart proxy_dcterms_hasVersion
|
17
|
-
proxy_dcterms_isFormatOf proxy_dcterms_isPartOf
|
18
|
-
proxy_dcterms_isReferencedBy proxy_dcterms_isReplacedBy
|
19
|
-
proxy_dcterms_isRequiredBy proxy_dcterms_issued
|
20
|
-
proxy_dcterms_isVersionOf proxy_dcterms_medium
|
21
|
-
proxy_dcterms_provenance proxy_dcterms_references
|
22
|
-
proxy_dcterms_replaces proxy_dcterms_requires proxy_dcterms_spatial
|
23
|
-
proxy_dcterms_tableOfContents proxy_dcterms_temporal
|
24
|
-
proxy_edm_currentLocation proxy_edm_currentLocation_lat
|
25
|
-
proxy_edm_currentLocation_lon proxy_edm_hasMet proxy_edm_hasType
|
26
|
-
proxy_edm_incorporates proxy_edm_isDerivativeOf
|
27
|
-
proxy_edm_isNextInSequence proxy_edm_isRelatedTo
|
28
|
-
proxy_edm_isRepresentationOf proxy_edm_isSimilarTo
|
29
|
-
proxy_edm_isSuccessorOf proxy_edm_realizes proxy_edm_type
|
30
|
-
proxy_edm_unstored proxy_edm_wasPresentAt proxy_owl_sameAs
|
31
|
-
proxy_edm_rights proxy_edm_userTags proxy_edm_year proxy_ore_proxy
|
32
|
-
proxy_ore_proxyFor proxy_ore_proxyIn)
|
33
|
-
|
34
|
-
ORE_AGGREGATION = %w(provider_aggregation_ore_aggregation
|
35
|
-
provider_aggregation_ore_aggregates
|
36
|
-
provider_aggregation_edm_aggregatedCHO
|
37
|
-
provider_aggregation_edm_dataProvider
|
38
|
-
provider_aggregation_edm_hasView provider_aggregation_edm_isShownAt
|
39
|
-
provider_aggregation_edm_isShownBy provider_aggregation_edm_object
|
40
|
-
provider_aggregation_edm_provider provider_aggregation_dc_rights
|
41
|
-
provider_aggregation_edm_rights edm_UGC
|
42
|
-
provider_aggregation_edm_unstored edm_previewNoDistribute)
|
43
|
-
|
44
|
-
EDM_EUROPEANA_AGGREGATION = %w(edm_europeana_aggregation
|
45
|
-
europeana_aggregation_dc_creator europeana_aggregation_edm_country
|
46
|
-
europeana_aggregation_edm_hasView europeana_aggregation_edm_isShownBy
|
47
|
-
europeana_aggregation_edm_landingPage
|
48
|
-
europeana_aggregation_edm_language europeana_aggregation_edm_rights
|
49
|
-
europeana_aggregation_ore_aggregatedCHO
|
50
|
-
europeana_aggregation_ore_aggregates)
|
51
|
-
|
52
|
-
EDM_WEB_RESOURCE = %w(edm_webResource wr_dc_description wr_dc_format
|
53
|
-
wr_dc_rights wr_dc_source wr_dcterms_conformsTo wr_dcterms_created
|
54
|
-
wr_dcterms_extent wr_dcterms_hasPart wr_dcterms_isFormatOf
|
55
|
-
wr_dcterms_issued wr_edm_isNextInSequence wr_edm_rights)
|
56
|
-
|
57
|
-
EDM_AGENT = %w(edm_agent ag_skos_prefLabel ag_skos_altLabel
|
58
|
-
ag_skos_hiddenLabel ag_skos_note ag_dc_date ag_dc_identifier
|
59
|
-
ag_edm_begin ag_edm_end ag_edm_hasMet ag_edm_isRelatedTo
|
60
|
-
ag_edm_wasPresentAt ag_foaf_name ag_rdagr2_biographicalInformation
|
61
|
-
ag_rdagr2_dateOfBirth ag_rdagr2_dateOfDeath
|
62
|
-
ag_rdagr2_dateOfEstablishment ag_rdagr2_dateOfTermination
|
63
|
-
ag_rdagr2_gender ag_rdagr2_professionOrOccupation ag_owl_sameAs)
|
64
|
-
|
65
|
-
SKOS_CONCEPT = %w(skos_concept cc_skos_prefLabel cc_skos_altLabel
|
66
|
-
cc_skos_hiddenLabel cc_skos_broader cc_skos_broaderLabel
|
67
|
-
cc_skos_narrower cc_skos_related cc_skos_broadMatch
|
68
|
-
cc_skos_narrowMatch cc_skos_relatedMatch cc_skos_exactMatch
|
69
|
-
cc_skos_closeMatch cc_skos_note cc_skos_notation cc_skos_inScheme)
|
70
|
-
|
71
|
-
EDM_PLACE = %w(edm_place pl_wgs84_pos_lat pl_wgs84_pos_long
|
72
|
-
pl_wgs84_pos_alt pl_wgs84_pos_lat_long pl_skos_prefLabel
|
73
|
-
pl_skos_altLabel pl_skos_hiddenLabel pl_skos_note pl_dcterms_hasPart
|
74
|
-
pl_dcterms_isPartOf pl_owl_sameAs pl_dcterms_isPartOf_label)
|
75
|
-
|
76
|
-
EDM_TIME_SPAN = %w(edm_timespan ts_skos_prefLabel ts_skos_altLabel
|
77
|
-
ts_skos_hiddenLabel ts_skos_note ts_dcterms_hasPart
|
78
|
-
ts_dcterms_isPartOf ts_edm_begin ts_edm_end ts_owl_sameAs
|
79
|
-
ts_dcterms_isPartOf_label)
|
80
|
-
|
81
|
-
NON_EDM = %w(europeana_completeness europeana_previewNoDistribute
|
82
|
-
timestamp europeana_id identifier europeana_collectionName)
|
83
|
-
|
84
|
-
AGGREGATED = %w(country date description format language location
|
85
|
-
publisher relation rights source subject text title what when where
|
86
|
-
who)
|
87
|
-
|
88
|
-
FACETS = %w(COMPLETENESS CONTRIBUTOR COUNTRY DATA_PROVIDER LANGUAGE
|
89
|
-
LOCATION PROVIDER RIGHTS SUBJECT TYPE UGC USERTAGS YEAR)
|
90
|
-
|
91
|
-
AGGREGATED_FACETS = %w(DEFAULT)
|
92
|
-
|
93
|
-
MEDIA = %w(COLOURPALETTE IMAGE_ASPECTRATIO IMAGE_COLOR IMAGE_COLOUR
|
94
|
-
IMAGE_GRAYSCALE IMAGE_GREYSCALE IMAGE_SIZE MEDIA MIME_TYPE
|
95
|
-
SOUND_DURATION SOUND_HQ TEXT_FULLTEXT VIDEO_DURATION VIDEO_HD)
|
96
|
-
|
97
|
-
##
|
98
|
-
# Tests whether the given field name is valid in the API search context
|
99
|
-
#
|
100
|
-
# @param name [String] field name
|
101
|
-
# @return [Boolean]
|
102
|
-
def self.include?(name)
|
103
|
-
constants.any? do |scope|
|
104
|
-
const_get(scope).any? do |field|
|
105
|
-
field == name
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|