valkyrie 1.2.0.rc1 → 1.2.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|