valkyrie 1.2.0.rc1 → 1.2.0.rc2
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/CHANGELOG.md +8 -0
- data/README.md +12 -4
- data/lib/valkyrie/persistence/composite_persister.rb +1 -1
- data/lib/valkyrie/persistence/fedora/list_node.rb +42 -3
- data/lib/valkyrie/persistence/fedora/metadata_adapter.rb +26 -0
- data/lib/valkyrie/persistence/fedora/ordered_list.rb +36 -5
- data/lib/valkyrie/persistence/fedora/ordered_reader.rb +6 -0
- data/lib/valkyrie/persistence/fedora/permissive_schema.rb +20 -1
- data/lib/valkyrie/persistence/fedora/persister.rb +33 -4
- data/lib/valkyrie/persistence/fedora/persister/alternate_identifier.rb +6 -0
- data/lib/valkyrie/persistence/fedora/persister/model_converter.rb +254 -4
- data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +250 -3
- data/lib/valkyrie/persistence/fedora/persister/resource_factory.rb +6 -0
- data/lib/valkyrie/persistence/fedora/query_service.rb +22 -4
- data/lib/valkyrie/persistence/memory/metadata_adapter.rb +2 -0
- data/lib/valkyrie/persistence/memory/persister.rb +11 -3
- data/lib/valkyrie/persistence/memory/query_service.rb +11 -0
- data/lib/valkyrie/persistence/postgres/metadata_adapter.rb +2 -0
- data/lib/valkyrie/persistence/postgres/orm.rb +4 -0
- data/lib/valkyrie/persistence/postgres/orm_converter.rb +62 -2
- data/lib/valkyrie/persistence/postgres/persister.rb +18 -7
- data/lib/valkyrie/persistence/postgres/query_service.rb +103 -11
- data/lib/valkyrie/persistence/postgres/resource_converter.rb +10 -0
- data/lib/valkyrie/persistence/postgres/resource_factory.rb +3 -0
- data/lib/valkyrie/persistence/solr/composite_indexer.rb +10 -0
- data/lib/valkyrie/persistence/solr/metadata_adapter.rb +7 -0
- data/lib/valkyrie/persistence/solr/model_converter.rb +137 -0
- data/lib/valkyrie/persistence/solr/orm_converter.rb +168 -0
- data/lib/valkyrie/persistence/solr/persister.rb +13 -5
- data/lib/valkyrie/persistence/solr/queries.rb +1 -0
- data/lib/valkyrie/persistence/solr/queries/default_paginator.rb +11 -1
- data/lib/valkyrie/persistence/solr/queries/find_all_query.rb +12 -0
- data/lib/valkyrie/persistence/solr/queries/find_by_alternate_identifier_query.rb +12 -0
- data/lib/valkyrie/persistence/solr/queries/find_by_id_query.rb +11 -0
- data/lib/valkyrie/persistence/solr/queries/find_inverse_references_query.rb +13 -0
- data/lib/valkyrie/persistence/solr/queries/find_many_by_ids_query.rb +9 -0
- data/lib/valkyrie/persistence/solr/queries/find_members_query.rb +23 -0
- data/lib/valkyrie/persistence/solr/queries/find_ordered_references_query.rb +50 -0
- data/lib/valkyrie/persistence/solr/queries/find_references_query.rb +15 -0
- data/lib/valkyrie/persistence/solr/query_service.rb +47 -14
- data/lib/valkyrie/persistence/solr/repository.rb +21 -4
- data/lib/valkyrie/persistence/solr/resource_factory.rb +2 -0
- data/lib/valkyrie/resource.rb +1 -0
- data/lib/valkyrie/specs/shared_specs.rb +1 -0
- data/lib/valkyrie/specs/shared_specs/persister.rb +92 -2
- data/lib/valkyrie/specs/shared_specs/queries.rb +12 -0
- data/lib/valkyrie/specs/shared_specs/solr_indexer.rb +40 -0
- data/lib/valkyrie/storage/fedora.rb +0 -2
- data/lib/valkyrie/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0ce0917b94c4102aec4a20c07ad0a2d9bb8abbec21c87c91ffe3100ee266e5ee
|
|
4
|
+
data.tar.gz: 11ab274a94c6510b0910a5cf16552244eb693daf1bc595c2f9db91480279ca8a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42c1aa0891d23c99092328e0b9ff7eb8e305c25b3210b9f0d64c0493b6f9ad0c55eb091a2cd481bb4212b3ab8eb148cd3ca949e52963549734dde508a11fa411
|
|
7
|
+
data.tar.gz: 30ed893f5b392f4c765950e003aec192039ce3d53147c8428997df492e64836cd47382414b2cd0141eb5f8f265583ad040823c8037144c498ac8e60307b36611
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# v1.2.0.RC2 2018-08-10
|
|
2
|
+
|
|
3
|
+
## Changes since last release
|
|
4
|
+
|
|
5
|
+
* Support for ordered properties.
|
|
6
|
+
[Documentation](https://github.com/samvera-labs/valkyrie/wiki/Using-Types#ordering-values)
|
|
7
|
+
* Shared specs for Solr indexers.
|
|
8
|
+
|
|
1
9
|
# v1.2.0.RC1 2018-08-09
|
|
2
10
|
|
|
3
11
|
## Changes since last release
|
data/README.md
CHANGED
|
@@ -123,8 +123,9 @@ these kinds of classes, you should use these shared specs to test your classes f
|
|
|
123
123
|
Valkyrie's API.
|
|
124
124
|
|
|
125
125
|
When breaking changes are introduced, necessitating a major version change, the shared specs will reflect
|
|
126
|
-
this.
|
|
127
|
-
|
|
126
|
+
this. When new features are added and a minor version is released there will be no change to the existing shared
|
|
127
|
+
specs, but there may be new ones. These new shared specs will fail in your
|
|
128
|
+
application if you have custom adapters, but your application will still work.
|
|
128
129
|
|
|
129
130
|
Using the shared specs in your own models is described in more [detail](https://github.com/samvera-labs/valkyrie/wiki/Shared-Specs).
|
|
130
131
|
|
|
@@ -136,11 +137,18 @@ Define a custom work class:
|
|
|
136
137
|
# frozen_string_literal: true
|
|
137
138
|
class MyModel < Valkyrie::Resource
|
|
138
139
|
include Valkyrie::Resource::AccessControls
|
|
139
|
-
attribute :title, Valkyrie::Types::Set
|
|
140
|
-
attribute :
|
|
140
|
+
attribute :title, Valkyrie::Types::Set # Sets deduplicate values
|
|
141
|
+
attribute :date, Valkyrie::Types::Array # Arrays can contain duplicate values
|
|
141
142
|
end
|
|
142
143
|
```
|
|
143
144
|
|
|
145
|
+
Attributes are unordered by default. Adding `ordered: true` to an attribute definition will preserve the
|
|
146
|
+
order of multiple values.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
attribute :authors, Valkyrie::Types::Array.meta(ordered: true)
|
|
150
|
+
```
|
|
151
|
+
|
|
144
152
|
Defining resource attributes is explained in greater detail within the [Wiki](https://github.com/samvera-labs/valkyrie/wiki/Using-Types).
|
|
145
153
|
|
|
146
154
|
#### Work Types Generator
|
|
@@ -37,7 +37,7 @@ module Valkyrie::Persistence
|
|
|
37
37
|
end
|
|
38
38
|
rescue Valkyrie::Persistence::StaleObjectError
|
|
39
39
|
# clear out any IDs returned to reduce potential confusion
|
|
40
|
-
raise Valkyrie::Persistence::StaleObjectError
|
|
40
|
+
raise Valkyrie::Persistence::StaleObjectError, "One or more resources have been updated by another process."
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# (see Valkyrie::Persistence::Memory::Persister#delete)
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module Valkyrie::Persistence::Fedora
|
|
3
3
|
# Represents a node in an ORE List. Used for persisting ordered members into
|
|
4
|
-
# an RDF Graph for Fedora, to keep order maintained.
|
|
4
|
+
# an Resource Description Framework (RDF) Graph for Fedora, to keep order maintained.
|
|
5
|
+
#
|
|
6
|
+
# RDF graph nodes are used to implement a linked list
|
|
7
|
+
# An RDF hash URI is referenced for the list itself
|
|
8
|
+
# <http://www.iana.org/assignments/relation/first> is the predicate which links to the first element in the list
|
|
9
|
+
# <http://www.iana.org/assignments/relation/last> is the predicate which links to the last element
|
|
10
|
+
# Each element is also referenced using a hash URI
|
|
11
|
+
# <http://www.iana.org/assignments/relation/next> is the predicate which links one element to the next element in the list
|
|
12
|
+
# (This permits unidirectional traversal)
|
|
13
|
+
# <http://www.openarchives.org/ore/terms/proxyFor> is the predicate which links any given element to its value
|
|
14
|
+
# (These can be IRIs or XML literals supported by a graph store)
|
|
15
|
+
#
|
|
16
|
+
# @see http://www.openarchives.org/ore/1.0/datamodel#Proxies
|
|
17
|
+
# @see https://www.iana.org/assignments/link-relations/link-relations.xhtml#link-relations-1
|
|
5
18
|
class ListNode
|
|
6
19
|
attr_reader :rdf_subject, :graph
|
|
7
20
|
attr_writer :next, :prev
|
|
@@ -9,6 +22,11 @@ module Valkyrie::Persistence::Fedora
|
|
|
9
22
|
attr_writer :next_uri, :prev_uri
|
|
10
23
|
attr_accessor :proxy_in, :proxy_for
|
|
11
24
|
attr_reader :adapter
|
|
25
|
+
|
|
26
|
+
# @param node_cache [Hash] structure used to cache the nodes of the graph
|
|
27
|
+
# @param rdf_subject [RDF::URI] the URI for the linked list in the graph store (usually a hash URI)
|
|
28
|
+
# @param adapter []
|
|
29
|
+
# @param graph [RDF::Repository] the RDF graph storing the structure of the RDF statements
|
|
12
30
|
def initialize(node_cache, rdf_subject, adapter, graph = RDF::Repository.new)
|
|
13
31
|
@rdf_subject = rdf_subject
|
|
14
32
|
@graph = graph
|
|
@@ -48,25 +66,43 @@ module Valkyrie::Persistence::Fedora
|
|
|
48
66
|
g
|
|
49
67
|
end
|
|
50
68
|
|
|
69
|
+
# Resolves the URI for the value of the list expression
|
|
70
|
+
# @return [RDF::URI]
|
|
51
71
|
def target_uri
|
|
52
|
-
|
|
72
|
+
if target_id.is_a?(Valkyrie::ID)
|
|
73
|
+
adapter.id_to_uri(target_id.to_s)
|
|
74
|
+
else
|
|
75
|
+
target_id
|
|
76
|
+
end
|
|
53
77
|
end
|
|
54
78
|
|
|
79
|
+
# Generates the string ID value for the value in the list expression
|
|
80
|
+
# @return [String]
|
|
55
81
|
def target_id
|
|
56
|
-
|
|
82
|
+
if proxy_for.to_s.include?("/") && proxy_for.to_s.start_with?(adapter.connection_prefix)
|
|
83
|
+
adapter.uri_to_id(proxy_for)
|
|
84
|
+
else
|
|
85
|
+
proxy_for
|
|
86
|
+
end
|
|
57
87
|
end
|
|
58
88
|
|
|
59
89
|
private
|
|
60
90
|
|
|
61
91
|
attr_reader :next_uri, :prev_uri, :node_cache
|
|
62
92
|
|
|
93
|
+
# Class used to populate the RDF graph structure for the linked lists
|
|
63
94
|
class Builder
|
|
64
95
|
attr_reader :uri, :graph
|
|
96
|
+
|
|
97
|
+
# @param uri [RDF::URI] the URI for the linked list in the graph store
|
|
98
|
+
# @param graph [RDF::Repository] the RDF graph to be populated
|
|
65
99
|
def initialize(uri, graph)
|
|
66
100
|
@uri = uri
|
|
67
101
|
@graph = graph
|
|
68
102
|
end
|
|
69
103
|
|
|
104
|
+
# Populates attributes for the LinkedNode
|
|
105
|
+
# @param instance [ListNode]
|
|
70
106
|
def populate(instance)
|
|
71
107
|
instance.proxy_for = resource.proxy_for.first
|
|
72
108
|
instance.proxy_in = resource.proxy_in.first
|
|
@@ -76,11 +112,14 @@ module Valkyrie::Persistence::Fedora
|
|
|
76
112
|
|
|
77
113
|
private
|
|
78
114
|
|
|
115
|
+
# Constructs a set of triples using ActiveTriples as objects
|
|
116
|
+
# @return [Valkyrie::Persistence::Fedora::ListNode::Resource]
|
|
79
117
|
def resource
|
|
80
118
|
@resource ||= Resource.new(uri, data: graph)
|
|
81
119
|
end
|
|
82
120
|
end
|
|
83
121
|
|
|
122
|
+
# Class for providing a set of triples modeling linked list nodes
|
|
84
123
|
class Resource < ActiveTriples::Resource
|
|
85
124
|
property :proxy_for, predicate: ::RDF::Vocab::ORE.proxyFor, cast: false
|
|
86
125
|
property :proxy_in, predicate: ::RDF::Vocab::ORE.proxyIn, cast: false
|
|
@@ -10,40 +10,66 @@ module Valkyrie::Persistence::Fedora
|
|
|
10
10
|
# )
|
|
11
11
|
class MetadataAdapter
|
|
12
12
|
attr_reader :connection, :base_path, :schema
|
|
13
|
+
|
|
14
|
+
# @param [Ldp::Client] connection
|
|
15
|
+
# @param [String] base_path
|
|
16
|
+
# @param [Valkyrie::Persistence::Fedora::PermissiveSchema] schema
|
|
13
17
|
def initialize(connection:, base_path: "/", schema: Valkyrie::Persistence::Fedora::PermissiveSchema.new)
|
|
14
18
|
@connection = connection
|
|
15
19
|
@base_path = base_path
|
|
16
20
|
@schema = schema
|
|
17
21
|
end
|
|
18
22
|
|
|
23
|
+
# Construct the query service object using this adapter
|
|
24
|
+
# @return [Valkyrie::Persistence::Fedora::QueryService]
|
|
19
25
|
def query_service
|
|
20
26
|
@query_service ||= Valkyrie::Persistence::Fedora::QueryService.new(adapter: self)
|
|
21
27
|
end
|
|
22
28
|
|
|
29
|
+
# Construct the persister object using this adapter
|
|
30
|
+
# @return [Valkyrie::Persistence::Fedora::Persister]
|
|
23
31
|
def persister
|
|
24
32
|
Valkyrie::Persistence::Fedora::Persister.new(adapter: self)
|
|
25
33
|
end
|
|
26
34
|
|
|
35
|
+
# Generate the Valkyrie ID for this unique metadata adapter
|
|
36
|
+
# This uses the URL of the Fedora endpoint to ensure that this is unique
|
|
37
|
+
# @return [Valkyrie::ID]
|
|
27
38
|
def id
|
|
28
39
|
@id ||= Valkyrie::ID.new(Digest::MD5.hexdigest(connection_prefix))
|
|
29
40
|
end
|
|
30
41
|
|
|
42
|
+
# Construct the factory object used to construct Valkyrie::Resource objects using this adapter
|
|
43
|
+
# @return [Valkyrie::Persistence::Fedora::Persister::ResourceFactory]
|
|
31
44
|
def resource_factory
|
|
32
45
|
Valkyrie::Persistence::Fedora::Persister::ResourceFactory.new(adapter: self)
|
|
33
46
|
end
|
|
34
47
|
|
|
48
|
+
# Generate a Valkyrie ID for a given URI
|
|
49
|
+
# @param [RDF::URI] uri the URI for a Fedora resource
|
|
50
|
+
# @return [Valkyrie::ID]
|
|
35
51
|
def uri_to_id(uri)
|
|
36
52
|
Valkyrie::ID.new(CGI.unescape(uri.to_s.gsub(/^.*\//, '')))
|
|
37
53
|
end
|
|
38
54
|
|
|
55
|
+
# Generate a URI for a given Valkyrie ID
|
|
56
|
+
# @param [RDF::URI] id the Valkyrie ID
|
|
57
|
+
# @return [RDF::URI]
|
|
39
58
|
def id_to_uri(id)
|
|
40
59
|
RDF::URI("#{connection_prefix}/#{pair_path(id)}/#{CGI.escape(id.to_s)}")
|
|
41
60
|
end
|
|
42
61
|
|
|
62
|
+
# Generate the pairtree path for a given Valkyrie ID
|
|
63
|
+
# @see https://confluence.ucop.edu/display/Curation/PairTree
|
|
64
|
+
# @see https://wiki.duraspace.org/display/FF/Design+-+Identifier+Generation
|
|
65
|
+
# @param [Valkyrie::ID] id the Valkyrie ID
|
|
66
|
+
# @return [Array<String>]
|
|
43
67
|
def pair_path(id)
|
|
44
68
|
id.to_s.split(/[-\/]/).first.split("").each_slice(2).map(&:join).join("/")
|
|
45
69
|
end
|
|
46
70
|
|
|
71
|
+
# Generate the prefix used in HTTP requests to the Fedora RESTful endpoint
|
|
72
|
+
# @return [String]
|
|
47
73
|
def connection_prefix
|
|
48
74
|
"#{connection.http.url_prefix}/#{base_path}"
|
|
49
75
|
end
|
|
@@ -8,6 +8,7 @@ module Valkyrie::Persistence::Fedora
|
|
|
8
8
|
attr_writer :head, :tail
|
|
9
9
|
delegate :each, to: :ordered_reader
|
|
10
10
|
delegate :length, to: :to_a
|
|
11
|
+
|
|
11
12
|
# @param [::RDF::Enumerable] graph Enumerable where ORE statements are
|
|
12
13
|
# stored.
|
|
13
14
|
# @param [::RDF::URI] head_subject URI of head node in list.
|
|
@@ -68,6 +69,9 @@ module Valkyrie::Persistence::Fedora
|
|
|
68
69
|
|
|
69
70
|
attr_reader :node_cache
|
|
70
71
|
|
|
72
|
+
# Append a node to a linked list
|
|
73
|
+
# @param source [ListNode] the node being appended
|
|
74
|
+
# @param append_node [ListNode] the node already in the existing list
|
|
71
75
|
def append_to(source, append_node)
|
|
72
76
|
source.prev = append_node
|
|
73
77
|
append_node.next.prev = source
|
|
@@ -76,10 +80,14 @@ module Valkyrie::Persistence::Fedora
|
|
|
76
80
|
@changed = true
|
|
77
81
|
end
|
|
78
82
|
|
|
83
|
+
# Constructs a new OrderedReader for this OrderedList
|
|
84
|
+
# @return [OrderedReader]
|
|
79
85
|
def ordered_reader
|
|
80
86
|
OrderedReader.new(self)
|
|
81
87
|
end
|
|
82
88
|
|
|
89
|
+
# Populates the list with constructed ListNode Objects
|
|
90
|
+
# @param subject [RDF::URI]
|
|
83
91
|
def build_node(subject = nil)
|
|
84
92
|
return nil unless subject
|
|
85
93
|
node_cache.fetch(subject) do
|
|
@@ -87,49 +95,69 @@ module Valkyrie::Persistence::Fedora
|
|
|
87
95
|
end
|
|
88
96
|
end
|
|
89
97
|
|
|
98
|
+
# Generates hash URIs for the subject of the LinkedList
|
|
99
|
+
# Should one of these URIs already be in use, a new URI will be generated
|
|
100
|
+
# @return [RDF::URI]
|
|
90
101
|
def new_node_subject
|
|
91
102
|
node = ::RDF::URI("##{::RDF::Node.new.id}")
|
|
92
103
|
node = ::RDF::URI("##{::RDF::Node.new.id}") while node_cache.key?(node)
|
|
93
104
|
node
|
|
94
105
|
end
|
|
95
106
|
|
|
107
|
+
# Class used for caching LinkedNode objects mapped to URIs
|
|
96
108
|
class NodeCache
|
|
97
109
|
def initialize
|
|
98
110
|
@cache ||= {}
|
|
99
111
|
end
|
|
100
112
|
|
|
113
|
+
# Retrieve the ListNode for a given URI
|
|
114
|
+
# If a block is passed, set its output to the cache
|
|
115
|
+
# @param uri [RDF::URI]
|
|
116
|
+
# @return [ListNode]
|
|
101
117
|
def fetch(uri)
|
|
102
118
|
@cache[uri] ||= yield if block_given?
|
|
103
119
|
end
|
|
104
120
|
|
|
121
|
+
# Determines whether or not the cache contains a key
|
|
122
|
+
# @param key [Object]
|
|
123
|
+
# @return [Boolean]
|
|
105
124
|
def key?(key)
|
|
106
125
|
@cache.key?(key)
|
|
107
126
|
end
|
|
108
127
|
end
|
|
109
128
|
|
|
129
|
+
# Class modeling sentinels within the linked list
|
|
130
|
+
# @see https://en.wikipedia.org/wiki/Sentinel_value
|
|
110
131
|
class Sentinel
|
|
111
|
-
attr_reader :parent
|
|
132
|
+
attr_reader :parent, :next, :prev
|
|
112
133
|
attr_writer :next, :prev
|
|
134
|
+
|
|
135
|
+
# @param parent [Valkyrie::Persistence::Fedora::OrderedList]
|
|
136
|
+
# @param next_node [ListNode]
|
|
137
|
+
# @param prev_node [ListNode]
|
|
113
138
|
def initialize(parent, next_node: nil, prev_node: nil)
|
|
114
139
|
@parent = parent
|
|
115
140
|
@next = next_node
|
|
116
141
|
@prev = prev_node
|
|
117
142
|
end
|
|
118
143
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
attr_reader :prev
|
|
122
|
-
|
|
144
|
+
# Ensure that this always behaves like a NilClass
|
|
145
|
+
# @return [TrueClass]
|
|
123
146
|
def nil?
|
|
124
147
|
true
|
|
125
148
|
end
|
|
126
149
|
|
|
150
|
+
# Ensure that this does not have a URI
|
|
151
|
+
# @return [NilClass]
|
|
127
152
|
def rdf_subject
|
|
128
153
|
nil
|
|
129
154
|
end
|
|
130
155
|
end
|
|
131
156
|
|
|
132
157
|
class HeadSentinel < Sentinel
|
|
158
|
+
# @param parent [Valkyrie::Persistence::Fedora::OrderedList]
|
|
159
|
+
# @param next_node [ListNode]
|
|
160
|
+
# @param prev_node [ListNode]
|
|
133
161
|
def initialize(*args)
|
|
134
162
|
super
|
|
135
163
|
@next ||= TailSentinel.new(parent, prev_node: self)
|
|
@@ -137,6 +165,9 @@ module Valkyrie::Persistence::Fedora
|
|
|
137
165
|
end
|
|
138
166
|
|
|
139
167
|
class TailSentinel < Sentinel
|
|
168
|
+
# @param parent [Valkyrie::Persistence::Fedora::OrderedList]
|
|
169
|
+
# @param next_node [ListNode]
|
|
170
|
+
# @param prev_node [ListNode]
|
|
140
171
|
def initialize(*args)
|
|
141
172
|
super
|
|
142
173
|
prev.next = self if prev&.next != self
|
|
@@ -6,10 +6,14 @@ module Valkyrie::Persistence::Fedora
|
|
|
6
6
|
class OrderedReader
|
|
7
7
|
include Enumerable
|
|
8
8
|
attr_reader :root
|
|
9
|
+
|
|
10
|
+
# @param root [Valkyrie::Persistence::Fedora::OrderedList]
|
|
9
11
|
def initialize(root)
|
|
10
12
|
@root = root
|
|
11
13
|
end
|
|
12
14
|
|
|
15
|
+
# Enumerates through each node in the RDF linked list
|
|
16
|
+
# @yield [Valkyrie::Persistence::Fedora::OrderedList::HeadSentinel, Valkyrie::Persistence::Fedora::ListNode]
|
|
13
17
|
def each
|
|
14
18
|
proxy = first_head
|
|
15
19
|
while proxy
|
|
@@ -22,6 +26,8 @@ module Valkyrie::Persistence::Fedora
|
|
|
22
26
|
|
|
23
27
|
private
|
|
24
28
|
|
|
29
|
+
# Access the "first" (head) node for the linked list
|
|
30
|
+
# @return [Valkyrie::Persistence::Fedora::OrderedList::HeadSentinel]
|
|
25
31
|
def first_head
|
|
26
32
|
root.head
|
|
27
33
|
end
|
|
@@ -53,6 +53,11 @@ module Valkyrie::Persistence::Fedora
|
|
|
53
53
|
uri_for(:valkyrie_datetime)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
+
# @return [RDF::URI]
|
|
57
|
+
def self.valkyrie_float
|
|
58
|
+
uri_for(:valkyrie_float)
|
|
59
|
+
end
|
|
60
|
+
|
|
56
61
|
# @return [RDF::URI]
|
|
57
62
|
def self.valkyrie_int
|
|
58
63
|
uri_for(:valkyrie_int)
|
|
@@ -76,10 +81,17 @@ module Valkyrie::Persistence::Fedora
|
|
|
76
81
|
end
|
|
77
82
|
|
|
78
83
|
attr_reader :schema
|
|
84
|
+
|
|
85
|
+
# @param schema [Hash] the structure used to store the mapping between property names and predicates
|
|
79
86
|
def initialize(schema = {})
|
|
80
87
|
@schema = schema
|
|
81
88
|
end
|
|
82
89
|
|
|
90
|
+
# Find the predicate in the schema for the Valkyrie property
|
|
91
|
+
# If this does not exist, a URI using the property name prefixed by URI_PREFIX generates it
|
|
92
|
+
# @param resource [Valkyrie::Resource]
|
|
93
|
+
# @param property [String]
|
|
94
|
+
# @return [RDF::URI]
|
|
83
95
|
def predicate_for(resource:, property:)
|
|
84
96
|
schema.fetch(property) { self.class.uri_for(property) }
|
|
85
97
|
end
|
|
@@ -89,8 +101,15 @@ module Valkyrie::Persistence::Fedora
|
|
|
89
101
|
# @example:
|
|
90
102
|
# property_for(resource: nil, predicate: "http://example.com/predicate/internal_resource")
|
|
91
103
|
# #=> 'internal_resource'
|
|
104
|
+
# @param resource [Valkyrie::Resource]
|
|
105
|
+
# @param predicate [RDF::URI, String]
|
|
106
|
+
# @return [String]
|
|
92
107
|
def property_for(resource:, predicate:)
|
|
93
|
-
|
|
108
|
+
existing_predicates = schema.find { |_k, v| v == RDF::URI(predicate.to_s) }
|
|
109
|
+
predicate_name = predicate.to_s.gsub(URI_PREFIX, '')
|
|
110
|
+
|
|
111
|
+
return predicate_name if existing_predicates.nil? || existing_predicates.empty?
|
|
112
|
+
existing_predicates.first
|
|
94
113
|
end
|
|
95
114
|
end
|
|
96
115
|
end
|
|
@@ -34,7 +34,7 @@ module Valkyrie::Persistence::Fedora
|
|
|
34
34
|
|
|
35
35
|
alternate_resources ? save_reference_to_resource(persisted_resource, alternate_resources) : persisted_resource
|
|
36
36
|
rescue Ldp::PreconditionFailed
|
|
37
|
-
raise Valkyrie::Persistence::StaleObjectError, internal_resource.id.
|
|
37
|
+
raise Valkyrie::Persistence::StaleObjectError, "The object #{internal_resource.id} has been updated by another process."
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# (see Valkyrie::Persistence::Memory::Persister#save_all)
|
|
@@ -44,7 +44,7 @@ module Valkyrie::Persistence::Fedora
|
|
|
44
44
|
end
|
|
45
45
|
rescue Valkyrie::Persistence::StaleObjectError
|
|
46
46
|
# blank out the message / id
|
|
47
|
-
raise Valkyrie::Persistence::StaleObjectError
|
|
47
|
+
raise Valkyrie::Persistence::StaleObjectError, "One or more resources have been updated by another process."
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# (see Valkyrie::Persistence::Memory::Persister#delete)
|
|
@@ -62,13 +62,18 @@ module Valkyrie::Persistence::Fedora
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
# (see Valkyrie::Persistence::Memory::Persister#wipe!)
|
|
65
|
+
# Deletes Fedora repository resource *and* the tombstone resources which remain
|
|
66
|
+
# @see https://wiki.duraspace.org/display/FEDORA4x/RESTful+HTTP+API#RESTfulHTTPAPI-RedDELETEDeletearesource
|
|
65
67
|
def wipe!
|
|
66
68
|
connection.delete(base_path)
|
|
67
69
|
connection.delete("#{base_path}/fcr:tombstone")
|
|
68
70
|
rescue => error
|
|
69
|
-
Valkyrie.logger.debug("Failed to wipe Fedora for some reason
|
|
71
|
+
Valkyrie.logger.debug("Failed to wipe Fedora for some reason: #{error}") unless error.is_a?(::Ldp::NotFound)
|
|
70
72
|
end
|
|
71
73
|
|
|
74
|
+
# Creates the root LDP Container for the connection with Fedora
|
|
75
|
+
# @see https://www.w3.org/TR/ldp/#ldpc
|
|
76
|
+
# @return [Ldp::Container::Basic]
|
|
72
77
|
def initialize_repository
|
|
73
78
|
@initialized ||=
|
|
74
79
|
begin
|
|
@@ -80,6 +85,9 @@ module Valkyrie::Persistence::Fedora
|
|
|
80
85
|
|
|
81
86
|
private
|
|
82
87
|
|
|
88
|
+
# Ensure that all alternate IDs for a given resource are persisted
|
|
89
|
+
# @param [Valkyrie::Resource] resource
|
|
90
|
+
# @return [Array<Valkyrie::Persistence::Fedora::AlternateIdentifier>]
|
|
83
91
|
def find_or_create_alternate_ids(resource)
|
|
84
92
|
return nil unless resource.try(:alternate_ids)
|
|
85
93
|
|
|
@@ -93,6 +101,8 @@ module Valkyrie::Persistence::Fedora
|
|
|
93
101
|
end
|
|
94
102
|
end
|
|
95
103
|
|
|
104
|
+
# Ensure that any Resources referenced by alternate IDs are deleted when a Resource has these IDs deleted
|
|
105
|
+
# @param [Valkyrie::Resource] updated_resource
|
|
96
106
|
def cleanup_alternate_resources(updated_resource)
|
|
97
107
|
persisted_resource = adapter.query_service.find_by(id: updated_resource.id)
|
|
98
108
|
removed_identifiers = persisted_resource.alternate_ids - updated_resource.alternate_ids
|
|
@@ -102,6 +112,10 @@ module Valkyrie::Persistence::Fedora
|
|
|
102
112
|
end
|
|
103
113
|
end
|
|
104
114
|
|
|
115
|
+
# Ensure that any Resources referenced by alternate IDs are persisted when a Resource has these IDs added
|
|
116
|
+
# @param [Valkyrie::Resource] resource
|
|
117
|
+
# @param [Array<Valkyrie::Persistence::Fedora::AlternateIdentifier>] alternate_resources
|
|
118
|
+
# @return [Valkyrie::Resource]
|
|
105
119
|
def save_reference_to_resource(resource, alternate_resources)
|
|
106
120
|
alternate_resources.each do |alternate_resource|
|
|
107
121
|
alternate_resource.references = resource.id
|
|
@@ -111,6 +125,10 @@ module Valkyrie::Persistence::Fedora
|
|
|
111
125
|
resource
|
|
112
126
|
end
|
|
113
127
|
|
|
128
|
+
# Generate the lock token for a Resource, and set it for attribute
|
|
129
|
+
# @param [Valkyrie::Resource] resource
|
|
130
|
+
# @return [Valkyrie::Persistence::OptimisticLockToken]
|
|
131
|
+
# @see https://github.com/samvera-labs/valkyrie/wiki/Optimistic-Locking
|
|
114
132
|
# @note Fedora's last modified response is not granular enough to produce an effective lock token
|
|
115
133
|
# therefore, we use the same implementation as the memory adapter. This could fail to lock a
|
|
116
134
|
# resource if Fedora updated this resource between the time it was saved and Valkyrie created
|
|
@@ -121,6 +139,13 @@ module Valkyrie::Persistence::Fedora
|
|
|
121
139
|
resource.send("#{Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK}=", token)
|
|
122
140
|
end
|
|
123
141
|
|
|
142
|
+
# Determine whether or not a lock token is still valid for a persisted Resource
|
|
143
|
+
# If the persisted Resource has been updated since it was last read into memory,
|
|
144
|
+
# then the resouce in memory has been invalidated and Valkyrie::Persistence::StaleObjectError
|
|
145
|
+
# is raised.
|
|
146
|
+
# @param [Valkyrie::Resource] resource
|
|
147
|
+
# @see https://github.com/samvera-labs/valkyrie/wiki/Optimistic-Locking
|
|
148
|
+
# @raise [Valkyrie::Persistence::StaleObjectError]
|
|
124
149
|
def validate_lock_token(resource)
|
|
125
150
|
return unless resource.optimistic_locking_enabled?
|
|
126
151
|
return if resource.id.blank?
|
|
@@ -132,10 +157,12 @@ module Valkyrie::Persistence::Fedora
|
|
|
132
157
|
retrieved_lock_token = retrieved_lock_tokens.find { |lock_token| lock_token.adapter_id == adapter.id }
|
|
133
158
|
return if retrieved_lock_token.blank?
|
|
134
159
|
|
|
135
|
-
raise Valkyrie::Persistence::StaleObjectError, resource.id.
|
|
160
|
+
raise Valkyrie::Persistence::StaleObjectError, "The object #{resource.id} has been updated by another process." unless current_lock_token == retrieved_lock_token
|
|
136
161
|
end
|
|
137
162
|
|
|
138
163
|
# Retrieve the lock token that holds Fedora's system-managed last-modified date
|
|
164
|
+
# @param [Valkyrie::Resource] resource
|
|
165
|
+
# @return [Valkyrie::Persistence::OptimisticLockToken]
|
|
139
166
|
def native_lock_token(resource)
|
|
140
167
|
return unless resource.optimistic_locking_enabled?
|
|
141
168
|
resource[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK].find { |lock_token| lock_token.adapter_id == "native-#{adapter.id}" }
|
|
@@ -144,6 +171,8 @@ module Valkyrie::Persistence::Fedora
|
|
|
144
171
|
# Set Fedora request headers:
|
|
145
172
|
# * `Prefer: handling=lenient; received="minimal"` allows us to avoid sending all server-managed triples
|
|
146
173
|
# * `If-Unmodified-Since` triggers Fedora's server-side optimistic locking
|
|
174
|
+
# @param request [Faraday::Request]
|
|
175
|
+
# @param lock_token [Valkyrie::Persistence::OptimisticLockToken]
|
|
147
176
|
def update_request_headers(request, lock_token)
|
|
148
177
|
request.headers["Prefer"] = "handling=lenient; received=\"minimal\""
|
|
149
178
|
request.headers["If-Unmodified-Since"] = lock_token.token if lock_token
|