valkyrie 2.1.1 → 2.1.2

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +33 -29
  3. data/.lando.yml +40 -0
  4. data/.rubocop.yml +4 -1
  5. data/.tool-versions +1 -1
  6. data/CHANGELOG.md +26 -0
  7. data/README.md +19 -47
  8. data/Rakefile +21 -20
  9. data/db/config.yml +3 -10
  10. data/lib/generators/valkyrie/resource_generator.rb +3 -3
  11. data/lib/valkyrie.rb +13 -13
  12. data/lib/valkyrie/change_set.rb +3 -3
  13. data/lib/valkyrie/id.rb +14 -14
  14. data/lib/valkyrie/indexers/access_controls_indexer.rb +17 -17
  15. data/lib/valkyrie/persistence/fedora/list_node.rb +43 -43
  16. data/lib/valkyrie/persistence/fedora/ordered_list.rb +90 -90
  17. data/lib/valkyrie/persistence/fedora/ordered_reader.rb +5 -5
  18. data/lib/valkyrie/persistence/fedora/permissive_schema.rb +1 -1
  19. data/lib/valkyrie/persistence/fedora/persister.rb +80 -82
  20. data/lib/valkyrie/persistence/fedora/persister/model_converter.rb +15 -15
  21. data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +38 -18
  22. data/lib/valkyrie/persistence/fedora/query_service.rb +38 -40
  23. data/lib/valkyrie/persistence/memory/persister.rb +33 -33
  24. data/lib/valkyrie/persistence/memory/query_service.rb +26 -30
  25. data/lib/valkyrie/persistence/postgres/orm_converter.rb +52 -52
  26. data/lib/valkyrie/persistence/postgres/query_service.rb +34 -34
  27. data/lib/valkyrie/persistence/shared/json_value_mapper.rb +1 -1
  28. data/lib/valkyrie/persistence/solr/model_converter.rb +337 -337
  29. data/lib/valkyrie/persistence/solr/orm_converter.rb +3 -3
  30. data/lib/valkyrie/persistence/solr/queries/find_members_query.rb +1 -1
  31. data/lib/valkyrie/persistence/solr/query_service.rb +12 -12
  32. data/lib/valkyrie/specs/shared_specs/persister.rb +2 -2
  33. data/lib/valkyrie/storage/fedora.rb +16 -16
  34. data/lib/valkyrie/storage_adapter.rb +11 -11
  35. data/lib/valkyrie/version.rb +1 -1
  36. data/tasks/dev.rake +14 -51
  37. data/valkyrie.gemspec +3 -4
  38. metadata +22 -33
  39. data/.docker-stack/valkyrie-development/docker-compose.yml +0 -53
  40. data/.docker-stack/valkyrie-test/docker-compose.yml +0 -53
  41. data/tasks/docker.rake +0 -31
@@ -40,24 +40,24 @@ module Valkyrie::Indexers
40
40
 
41
41
  private
42
42
 
43
- # rubocop:disable Metrics/MethodLength
44
- def default_config
45
- if defined?(Hydra) && Hydra.respond_to?(:config)
46
- {
47
- read_groups: Hydra.config[:permissions][:read].group,
48
- read_users: Hydra.config[:permissions][:read].individual,
49
- edit_groups: Hydra.config[:permissions][:edit].group,
50
- edit_users: Hydra.config[:permissions][:edit].individual
51
- }
52
- else
53
- {
54
- read_groups: 'read_access_group_ssim',
55
- read_users: 'read_access_person_ssim',
56
- edit_groups: 'edit_access_group_ssim',
57
- edit_users: 'edit_access_person_ssim'
58
- }
59
- end
43
+ # rubocop:disable Metrics/MethodLength
44
+ def default_config
45
+ if defined?(Hydra) && Hydra.respond_to?(:config)
46
+ {
47
+ read_groups: Hydra.config[:permissions][:read].group,
48
+ read_users: Hydra.config[:permissions][:read].individual,
49
+ edit_groups: Hydra.config[:permissions][:edit].group,
50
+ edit_users: Hydra.config[:permissions][:edit].individual
51
+ }
52
+ else
53
+ {
54
+ read_groups: 'read_access_group_ssim',
55
+ read_users: 'read_access_person_ssim',
56
+ edit_groups: 'edit_access_group_ssim',
57
+ edit_users: 'edit_access_person_ssim'
58
+ }
60
59
  end
60
+ end
61
61
  # rubocop:enable Metrics/MethodLength
62
62
  end
63
63
  end
@@ -85,60 +85,60 @@ module Valkyrie::Persistence::Fedora
85
85
 
86
86
  private
87
87
 
88
- attr_reader :next_uri, :prev_uri, :node_cache
88
+ attr_reader :next_uri, :prev_uri, :node_cache
89
89
 
90
- # Class used to populate the RDF graph structure for the linked lists
91
- class Builder
92
- attr_reader :uri, :graph
90
+ # Class used to populate the RDF graph structure for the linked lists
91
+ class Builder
92
+ attr_reader :uri, :graph
93
93
 
94
- # @param uri [RDF::URI] the URI for the linked list in the graph store
95
- # @param graph [RDF::Repository] the RDF graph to be populated
96
- def initialize(uri, graph)
97
- @uri = uri
98
- @graph = graph
99
- end
94
+ # @param uri [RDF::URI] the URI for the linked list in the graph store
95
+ # @param graph [RDF::Repository] the RDF graph to be populated
96
+ def initialize(uri, graph)
97
+ @uri = uri
98
+ @graph = graph
99
+ end
100
100
 
101
- # Populates attributes for the LinkedNode
102
- # @param instance [ListNode]
103
- def populate(instance)
104
- instance.proxy_for = resource.proxy_for
105
- instance.proxy_in = resource.proxy_in
106
- instance.next_uri = resource.next
107
- instance.prev_uri = resource.prev
108
- end
101
+ # Populates attributes for the LinkedNode
102
+ # @param instance [ListNode]
103
+ def populate(instance)
104
+ instance.proxy_for = resource.proxy_for
105
+ instance.proxy_in = resource.proxy_in
106
+ instance.next_uri = resource.next
107
+ instance.prev_uri = resource.prev
108
+ end
109
109
 
110
- private
110
+ private
111
111
 
112
- # Constructs a set of triples using ActiveTriples as objects
113
- # @return [Valkyrie::Persistence::Fedora::ListNode::Resource]
114
- def resource
115
- @resource ||= Resource.new(uri, graph: graph)
116
- end
112
+ # Constructs a set of triples using ActiveTriples as objects
113
+ # @return [Valkyrie::Persistence::Fedora::ListNode::Resource]
114
+ def resource
115
+ @resource ||= Resource.new(uri, graph: graph)
117
116
  end
117
+ end
118
118
 
119
- # Class for providing a set of triples modeling linked list nodes
120
- class Resource
121
- def self.property(property, predicate:)
122
- define_method property do
123
- graph.query([uri, predicate, nil]).objects.first
124
- end
119
+ # Class for providing a set of triples modeling linked list nodes
120
+ class Resource
121
+ def self.property(property, predicate:)
122
+ define_method property do
123
+ graph.query([uri, predicate, nil]).objects.first
124
+ end
125
125
 
126
- define_method "#{property}=" do |val|
127
- return if val.nil?
128
- graph << [uri, predicate, val]
129
- end
126
+ define_method "#{property}=" do |val|
127
+ return if val.nil?
128
+ graph << [uri, predicate, val]
130
129
  end
130
+ end
131
131
 
132
- property :proxy_for, predicate: ::RDF::Vocab::ORE.proxyFor
133
- property :proxy_in, predicate: ::RDF::Vocab::ORE.proxyIn
134
- property :next, predicate: ::RDF::Vocab::IANA.next
135
- property :prev, predicate: ::RDF::Vocab::IANA.prev
132
+ property :proxy_for, predicate: ::RDF::Vocab::ORE.proxyFor
133
+ property :proxy_in, predicate: ::RDF::Vocab::ORE.proxyIn
134
+ property :next, predicate: ::RDF::Vocab::IANA.next
135
+ property :prev, predicate: ::RDF::Vocab::IANA.prev
136
136
 
137
- attr_reader :graph, :uri
138
- def initialize(uri, graph: RDF::Graph.new)
139
- @uri = uri
140
- @graph = graph
141
- end
137
+ attr_reader :graph, :uri
138
+ def initialize(uri, graph: RDF::Graph.new)
139
+ @uri = uri
140
+ @graph = graph
142
141
  end
142
+ end
143
143
  end
144
144
  end
@@ -67,111 +67,111 @@ module Valkyrie::Persistence::Fedora
67
67
 
68
68
  private
69
69
 
70
- attr_reader :node_cache
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
75
- def append_to(source, append_node)
76
- source.prev = append_node
77
- append_node.next.prev = source
78
- source.next = append_node.next
79
- append_node.next = source
80
- @changed = true
81
- end
70
+ attr_reader :node_cache
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
75
+ def append_to(source, append_node)
76
+ source.prev = append_node
77
+ append_node.next.prev = source
78
+ source.next = append_node.next
79
+ append_node.next = source
80
+ @changed = true
81
+ end
82
82
 
83
- # Constructs a new OrderedReader for this OrderedList
84
- # @return [OrderedReader]
85
- def ordered_reader
86
- OrderedReader.new(self)
87
- end
83
+ # Constructs a new OrderedReader for this OrderedList
84
+ # @return [OrderedReader]
85
+ def ordered_reader
86
+ OrderedReader.new(self)
87
+ end
88
88
 
89
- # Populates the list with constructed ListNode Objects
90
- # @param subject [RDF::URI]
91
- def build_node(subject = nil)
92
- return nil unless subject
93
- node_cache.fetch(subject) do
94
- ListNode.new(node_cache, subject, adapter, graph)
95
- end
89
+ # Populates the list with constructed ListNode Objects
90
+ # @param subject [RDF::URI]
91
+ def build_node(subject = nil)
92
+ return nil unless subject
93
+ node_cache.fetch(subject) do
94
+ ListNode.new(node_cache, subject, adapter, graph)
96
95
  end
96
+ end
97
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]
101
- def new_node_subject
102
- node = ::RDF::URI("##{::RDF::Node.new.id}")
103
- node = ::RDF::URI("##{::RDF::Node.new.id}") while node_cache.key?(node)
104
- node
105
- end
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]
101
+ def new_node_subject
102
+ node = ::RDF::URI("##{::RDF::Node.new.id}")
103
+ node = ::RDF::URI("##{::RDF::Node.new.id}") while node_cache.key?(node)
104
+ node
105
+ end
106
106
 
107
- # Class used for caching LinkedNode objects mapped to URIs
108
- class NodeCache
109
- def initialize
110
- @cache ||= {}
111
- end
107
+ # Class used for caching LinkedNode objects mapped to URIs
108
+ class NodeCache
109
+ def initialize
110
+ @cache ||= {}
111
+ end
112
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]
117
- def fetch(uri)
118
- @cache[uri] ||= yield if block_given?
119
- end
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]
117
+ def fetch(uri)
118
+ @cache[uri] ||= yield if block_given?
119
+ end
120
120
 
121
- # Determines whether or not the cache contains a key
122
- # @param key [Object]
123
- # @return [Boolean]
124
- def key?(key)
125
- @cache.key?(key)
126
- end
121
+ # Determines whether or not the cache contains a key
122
+ # @param key [Object]
123
+ # @return [Boolean]
124
+ def key?(key)
125
+ @cache.key?(key)
127
126
  end
127
+ end
128
128
 
129
- # Class modeling sentinels within the linked list
130
- # @see https://en.wikipedia.org/wiki/Sentinel_value
131
- class Sentinel
132
- attr_reader :parent, :next, :prev
133
- attr_writer :next, :prev
134
-
135
- # @param parent [Valkyrie::Persistence::Fedora::OrderedList]
136
- # @param next_node [ListNode]
137
- # @param prev_node [ListNode]
138
- def initialize(parent, next_node: nil, prev_node: nil)
139
- @parent = parent
140
- @next = next_node
141
- @prev = prev_node
142
- end
129
+ # Class modeling sentinels within the linked list
130
+ # @see https://en.wikipedia.org/wiki/Sentinel_value
131
+ class Sentinel
132
+ attr_reader :parent, :next, :prev
133
+ attr_writer :next, :prev
134
+
135
+ # @param parent [Valkyrie::Persistence::Fedora::OrderedList]
136
+ # @param next_node [ListNode]
137
+ # @param prev_node [ListNode]
138
+ def initialize(parent, next_node: nil, prev_node: nil)
139
+ @parent = parent
140
+ @next = next_node
141
+ @prev = prev_node
142
+ end
143
143
 
144
- # Ensure that this always behaves like a NilClass
145
- # @return [TrueClass]
146
- def nil?
147
- true
148
- end
144
+ # Ensure that this always behaves like a NilClass
145
+ # @return [TrueClass]
146
+ def nil?
147
+ true
148
+ end
149
149
 
150
- # Ensure that this does not have a URI
151
- # @return [NilClass]
152
- def rdf_subject
153
- nil
154
- end
150
+ # Ensure that this does not have a URI
151
+ # @return [NilClass]
152
+ def rdf_subject
153
+ nil
155
154
  end
155
+ end
156
156
 
157
- class HeadSentinel < Sentinel
158
- # @param parent [Valkyrie::Persistence::Fedora::OrderedList]
159
- # @param next_node [ListNode]
160
- # @param prev_node [ListNode]
161
- def initialize(*args)
162
- super
163
- @next ||= TailSentinel.new(parent, prev_node: self)
164
- end
157
+ class HeadSentinel < Sentinel
158
+ # @param parent [Valkyrie::Persistence::Fedora::OrderedList]
159
+ # @param next_node [ListNode]
160
+ # @param prev_node [ListNode]
161
+ def initialize(*args)
162
+ super
163
+ @next ||= TailSentinel.new(parent, prev_node: self)
165
164
  end
165
+ end
166
166
 
167
- class TailSentinel < Sentinel
168
- # @param parent [Valkyrie::Persistence::Fedora::OrderedList]
169
- # @param next_node [ListNode]
170
- # @param prev_node [ListNode]
171
- def initialize(*args)
172
- super
173
- prev.next = self if prev&.next != self
174
- end
167
+ class TailSentinel < Sentinel
168
+ # @param parent [Valkyrie::Persistence::Fedora::OrderedList]
169
+ # @param next_node [ListNode]
170
+ # @param prev_node [ListNode]
171
+ def initialize(*args)
172
+ super
173
+ prev.next = self if prev&.next != self
175
174
  end
175
+ end
176
176
  end
177
177
  end
@@ -26,10 +26,10 @@ module Valkyrie::Persistence::Fedora
26
26
 
27
27
  private
28
28
 
29
- # Access the "first" (head) node for the linked list
30
- # @return [Valkyrie::Persistence::Fedora::OrderedList::HeadSentinel]
31
- def first_head
32
- root.head
33
- end
29
+ # Access the "first" (head) node for the linked list
30
+ # @return [Valkyrie::Persistence::Fedora::OrderedList::HeadSentinel]
31
+ def first_head
32
+ root.head
33
+ end
34
34
  end
35
35
  end
@@ -92,7 +92,7 @@ module Valkyrie::Persistence::Fedora
92
92
  existing_predicates = schema.find { |_k, v| v == RDF::URI(predicate.to_s) }
93
93
  predicate_name = predicate.to_s.gsub(URI_PREFIX, '')
94
94
 
95
- return predicate_name if existing_predicates.nil? || existing_predicates.empty?
95
+ return predicate_name if existing_predicates.blank?
96
96
  existing_predicates.first
97
97
  end
98
98
  end
@@ -86,97 +86,95 @@ module Valkyrie::Persistence::Fedora
86
86
 
87
87
  private
88
88
 
89
- # Ensure that all alternate IDs for a given resource are persisted
90
- # @param [Valkyrie::Resource] resource
91
- # @return [Array<Valkyrie::Persistence::Fedora::AlternateIdentifier>]
92
- def find_or_create_alternate_ids(resource)
93
- return nil unless resource.try(:alternate_ids)
94
-
95
- resource.alternate_ids.map do |alternate_identifier|
96
- begin
97
- adapter.query_service.find_by(id: alternate_identifier)
98
- rescue ::Valkyrie::Persistence::ObjectNotFoundError
99
- alternate_resource = ::Valkyrie::Persistence::Fedora::AlternateIdentifier.new(id: alternate_identifier)
100
- adapter.persister.save(resource: alternate_resource)
101
- end
102
- end
89
+ # Ensure that all alternate IDs for a given resource are persisted
90
+ # @param [Valkyrie::Resource] resource
91
+ # @return [Array<Valkyrie::Persistence::Fedora::AlternateIdentifier>]
92
+ def find_or_create_alternate_ids(resource)
93
+ return nil unless resource.try(:alternate_ids)
94
+
95
+ resource.alternate_ids.map do |alternate_identifier|
96
+ adapter.query_service.find_by(id: alternate_identifier)
97
+ rescue ::Valkyrie::Persistence::ObjectNotFoundError
98
+ alternate_resource = ::Valkyrie::Persistence::Fedora::AlternateIdentifier.new(id: alternate_identifier)
99
+ adapter.persister.save(resource: alternate_resource)
103
100
  end
101
+ end
104
102
 
105
- # Ensure that any Resources referenced by alternate IDs are deleted when a Resource has these IDs deleted
106
- # @param [Valkyrie::Resource] updated_resource
107
- def cleanup_alternate_resources(updated_resource)
108
- persisted_resource = adapter.query_service.find_by(id: updated_resource.id)
109
- removed_identifiers = persisted_resource.alternate_ids - updated_resource.alternate_ids
103
+ # Ensure that any Resources referenced by alternate IDs are deleted when a Resource has these IDs deleted
104
+ # @param [Valkyrie::Resource] updated_resource
105
+ def cleanup_alternate_resources(updated_resource)
106
+ persisted_resource = adapter.query_service.find_by(id: updated_resource.id)
107
+ removed_identifiers = persisted_resource.alternate_ids - updated_resource.alternate_ids
110
108
 
111
- removed_identifiers.each do |removed_id|
112
- adapter.persister.delete(resource: adapter.query_service.find_by(id: removed_id))
113
- end
109
+ removed_identifiers.each do |removed_id|
110
+ adapter.persister.delete(resource: adapter.query_service.find_by(id: removed_id))
114
111
  end
112
+ end
115
113
 
116
- # Ensure that any Resources referenced by alternate IDs are persisted when a Resource has these IDs added
117
- # @param [Valkyrie::Resource] resource
118
- # @param [Array<Valkyrie::Persistence::Fedora::AlternateIdentifier>] alternate_resources
119
- # @return [Valkyrie::Resource]
120
- def save_reference_to_resource(resource, alternate_resources)
121
- alternate_resources.each do |alternate_resource|
122
- alternate_resource.references = resource.id
123
- adapter.persister.save(resource: alternate_resource)
124
- end
125
-
126
- resource
114
+ # Ensure that any Resources referenced by alternate IDs are persisted when a Resource has these IDs added
115
+ # @param [Valkyrie::Resource] resource
116
+ # @param [Array<Valkyrie::Persistence::Fedora::AlternateIdentifier>] alternate_resources
117
+ # @return [Valkyrie::Resource]
118
+ def save_reference_to_resource(resource, alternate_resources)
119
+ alternate_resources.each do |alternate_resource|
120
+ alternate_resource.references = resource.id
121
+ adapter.persister.save(resource: alternate_resource)
127
122
  end
128
123
 
129
- # Generate the lock token for a Resource, and set it for attribute
130
- # @param [Valkyrie::Resource] resource
131
- # @return [Valkyrie::Persistence::OptimisticLockToken]
132
- # @see https://github.com/samvera-labs/valkyrie/wiki/Optimistic-Locking
133
- # @note Fedora's last modified response is not granular enough to produce an effective lock token
134
- # therefore, we use the same implementation as the memory adapter. This could fail to lock a
135
- # resource if Fedora updated this resource between the time it was saved and Valkyrie created
136
- # the token.
137
- def generate_lock_token(resource)
138
- return unless resource.optimistic_locking_enabled?
139
- token = Valkyrie::Persistence::OptimisticLockToken.new(adapter_id: adapter.id, token: Time.now.to_r)
140
- resource.send("#{Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK}=", token)
141
- end
124
+ resource
125
+ end
142
126
 
143
- # Determine whether or not a lock token is still valid for a persisted Resource
144
- # If the persisted Resource has been updated since it was last read into memory,
145
- # then the resouce in memory has been invalidated and Valkyrie::Persistence::StaleObjectError
146
- # is raised.
147
- # @param [Valkyrie::Resource] resource
148
- # @see https://github.com/samvera-labs/valkyrie/wiki/Optimistic-Locking
149
- # @raise [Valkyrie::Persistence::StaleObjectError]
150
- def validate_lock_token(resource)
151
- return unless resource.optimistic_locking_enabled?
152
- return if resource.id.blank?
153
-
154
- current_lock_token = resource[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK].find { |lock_token| lock_token.adapter_id == adapter.id }
155
- return if current_lock_token.blank?
156
-
157
- retrieved_lock_tokens = adapter.query_service.find_by(id: resource.id)[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK]
158
- retrieved_lock_token = retrieved_lock_tokens.find { |lock_token| lock_token.adapter_id == adapter.id }
159
- return if retrieved_lock_token.blank?
160
-
161
- raise Valkyrie::Persistence::StaleObjectError, "The object #{resource.id} has been updated by another process." unless current_lock_token == retrieved_lock_token
162
- end
127
+ # Generate the lock token for a Resource, and set it for attribute
128
+ # @param [Valkyrie::Resource] resource
129
+ # @return [Valkyrie::Persistence::OptimisticLockToken]
130
+ # @see https://github.com/samvera-labs/valkyrie/wiki/Optimistic-Locking
131
+ # @note Fedora's last modified response is not granular enough to produce an effective lock token
132
+ # therefore, we use the same implementation as the memory adapter. This could fail to lock a
133
+ # resource if Fedora updated this resource between the time it was saved and Valkyrie created
134
+ # the token.
135
+ def generate_lock_token(resource)
136
+ return unless resource.optimistic_locking_enabled?
137
+ token = Valkyrie::Persistence::OptimisticLockToken.new(adapter_id: adapter.id, token: Time.now.to_r)
138
+ resource.send("#{Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK}=", token)
139
+ end
163
140
 
164
- # Retrieve the lock token that holds Fedora's system-managed last-modified date
165
- # @param [Valkyrie::Resource] resource
166
- # @return [Valkyrie::Persistence::OptimisticLockToken]
167
- def native_lock_token(resource)
168
- return unless resource.optimistic_locking_enabled?
169
- resource[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK].find { |lock_token| lock_token.adapter_id.to_s == "native-#{adapter.id}" }
170
- end
141
+ # Determine whether or not a lock token is still valid for a persisted Resource
142
+ # If the persisted Resource has been updated since it was last read into memory,
143
+ # then the resouce in memory has been invalidated and Valkyrie::Persistence::StaleObjectError
144
+ # is raised.
145
+ # @param [Valkyrie::Resource] resource
146
+ # @see https://github.com/samvera-labs/valkyrie/wiki/Optimistic-Locking
147
+ # @raise [Valkyrie::Persistence::StaleObjectError]
148
+ def validate_lock_token(resource)
149
+ return unless resource.optimistic_locking_enabled?
150
+ return if resource.id.blank?
151
+
152
+ current_lock_token = resource[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK].find { |lock_token| lock_token.adapter_id == adapter.id }
153
+ return if current_lock_token.blank?
154
+
155
+ retrieved_lock_tokens = adapter.query_service.find_by(id: resource.id)[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK]
156
+ retrieved_lock_token = retrieved_lock_tokens.find { |lock_token| lock_token.adapter_id == adapter.id }
157
+ return if retrieved_lock_token.blank?
158
+
159
+ raise Valkyrie::Persistence::StaleObjectError, "The object #{resource.id} has been updated by another process." unless current_lock_token == retrieved_lock_token
160
+ end
171
161
 
172
- # Set Fedora request headers:
173
- # * `Prefer: handling=lenient; received="minimal"` allows us to avoid sending all server-managed triples
174
- # * `If-Unmodified-Since` triggers Fedora's server-side optimistic locking
175
- # @param request [Faraday::Request]
176
- # @param lock_token [Valkyrie::Persistence::OptimisticLockToken]
177
- def update_request_headers(request, lock_token)
178
- request.headers["Prefer"] = "handling=lenient; received=\"minimal\""
179
- request.headers["If-Unmodified-Since"] = lock_token.token if lock_token
180
- end
162
+ # Retrieve the lock token that holds Fedora's system-managed last-modified date
163
+ # @param [Valkyrie::Resource] resource
164
+ # @return [Valkyrie::Persistence::OptimisticLockToken]
165
+ def native_lock_token(resource)
166
+ return unless resource.optimistic_locking_enabled?
167
+ resource[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK].find { |lock_token| lock_token.adapter_id.to_s == "native-#{adapter.id}" }
168
+ end
169
+
170
+ # Set Fedora request headers:
171
+ # * `Prefer: handling=lenient; received="minimal"` allows us to avoid sending all server-managed triples
172
+ # * `If-Unmodified-Since` triggers Fedora's server-side optimistic locking
173
+ # @param request [Faraday::Request]
174
+ # @param lock_token [Valkyrie::Persistence::OptimisticLockToken]
175
+ def update_request_headers(request, lock_token)
176
+ request.headers["Prefer"] = "handling=lenient; received=\"minimal\""
177
+ request.headers["If-Unmodified-Since"] = lock_token.token if lock_token
178
+ end
181
179
  end
182
180
  end