valkyrie 2.0.0 → 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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +60 -56
- data/.lando.yml +40 -0
- data/.rubocop.yml +4 -1
- data/.tool-versions +1 -1
- data/Appraisals +4 -4
- data/CHANGELOG.md +136 -0
- data/README.md +21 -49
- data/Rakefile +21 -20
- data/db/config.yml +3 -10
- data/db/schema.rb +0 -40
- data/gemfiles/activerecord_5_2.gemfile +2 -0
- data/gemfiles/{activerecord_5_1.gemfile → activerecord_6_0.gemfile} +3 -1
- data/lib/generators/valkyrie/resource_generator.rb +3 -3
- data/lib/valkyrie.rb +33 -15
- data/lib/valkyrie/change_set.rb +3 -3
- data/lib/valkyrie/id.rb +26 -3
- data/lib/valkyrie/indexers/access_controls_indexer.rb +17 -17
- data/lib/valkyrie/logging.rb +72 -0
- data/lib/valkyrie/persistence/composite_persister.rb +1 -1
- data/lib/valkyrie/persistence/fedora.rb +2 -0
- data/lib/valkyrie/persistence/fedora/list_node.rb +46 -49
- data/lib/valkyrie/persistence/fedora/metadata_adapter.rb +2 -2
- data/lib/valkyrie/persistence/fedora/ordered_list.rb +90 -90
- data/lib/valkyrie/persistence/fedora/ordered_reader.rb +5 -5
- data/lib/valkyrie/persistence/fedora/permissive_schema.rb +3 -3
- data/lib/valkyrie/persistence/fedora/persister.rb +82 -83
- data/lib/valkyrie/persistence/fedora/persister/model_converter.rb +16 -17
- data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +38 -18
- data/lib/valkyrie/persistence/fedora/query_service.rb +54 -53
- data/lib/valkyrie/persistence/memory/persister.rb +33 -33
- data/lib/valkyrie/persistence/memory/query_service.rb +52 -34
- data/lib/valkyrie/persistence/postgres/orm_converter.rb +52 -52
- data/lib/valkyrie/persistence/postgres/query_service.rb +86 -33
- data/lib/valkyrie/persistence/postgres/resource_converter.rb +1 -1
- data/lib/valkyrie/persistence/shared/json_value_mapper.rb +4 -2
- data/lib/valkyrie/persistence/solr/model_converter.rb +337 -337
- data/lib/valkyrie/persistence/solr/orm_converter.rb +3 -3
- data/lib/valkyrie/persistence/solr/persister.rb +4 -17
- data/lib/valkyrie/persistence/solr/queries/find_all_query.rb +6 -0
- data/lib/valkyrie/persistence/solr/queries/find_members_query.rb +1 -1
- data/lib/valkyrie/persistence/solr/query_service.rb +42 -53
- data/lib/valkyrie/persistence/solr/repository.rb +2 -1
- data/lib/valkyrie/rdf_patches.rb +2 -2
- data/lib/valkyrie/resource.rb +36 -5
- data/lib/valkyrie/specs/shared_specs/change_set.rb +1 -1
- data/lib/valkyrie/specs/shared_specs/persister.rb +17 -6
- data/lib/valkyrie/specs/shared_specs/queries.rb +112 -9
- data/lib/valkyrie/storage/fedora.rb +17 -17
- data/lib/valkyrie/storage_adapter.rb +16 -13
- data/lib/valkyrie/types.rb +3 -1
- data/lib/valkyrie/version.rb +1 -1
- data/solr/config/solrconfig.xml +0 -10
- data/tasks/dev.rake +14 -51
- data/valkyrie.gemspec +4 -4
- metadata +40 -37
- data/.docker-stack/valkyrie-development/docker-compose.yml +0 -53
- data/.docker-stack/valkyrie-test/docker-compose.yml +0 -53
- data/db/seeds.rb +0 -8
- data/tasks/docker.rake +0 -31
@@ -48,7 +48,7 @@ module Valkyrie::Persistence::Solr
|
|
48
48
|
# Construct a Time object from the datestamp for the resource creation date indexed in Solr
|
49
49
|
# @return [Time]
|
50
50
|
def created_at
|
51
|
-
DateTime.parse(solr_document.fetch("created_at_dtsi").to_s).
|
51
|
+
DateTime.parse(solr_document.fetch("created_at_dtsi").to_s).new_offset(0)
|
52
52
|
end
|
53
53
|
|
54
54
|
# Construct a Time object from the datestamp for the date of the last resource update indexed in Solr
|
@@ -445,7 +445,7 @@ module Valkyrie::Persistence::Solr
|
|
445
445
|
# @return [Boolean]
|
446
446
|
def self.handles?(value)
|
447
447
|
return false unless value.to_s.start_with?("datetime-")
|
448
|
-
DateTime.iso8601(value.sub(/^datetime-/, '')).
|
448
|
+
DateTime.iso8601(value.sub(/^datetime-/, '')).new_offset(0)
|
449
449
|
rescue
|
450
450
|
false
|
451
451
|
end
|
@@ -453,7 +453,7 @@ module Valkyrie::Persistence::Solr
|
|
453
453
|
# Parses and casts the Solr field value into a UTC DateTime value
|
454
454
|
# @return [Time]
|
455
455
|
def result
|
456
|
-
DateTime.parse(value.sub(/^datetime-/, '')).
|
456
|
+
DateTime.parse(value.sub(/^datetime-/, '')).new_offset(0)
|
457
457
|
end
|
458
458
|
end
|
459
459
|
end
|
@@ -14,35 +14,22 @@ module Valkyrie::Persistence::Solr
|
|
14
14
|
@adapter = adapter
|
15
15
|
end
|
16
16
|
|
17
|
-
#
|
18
|
-
# @note Fields are saved using Solr's dynamic fields functionality.
|
19
|
-
# If the text has length > 1000, it is stored as *_tsim
|
20
|
-
# otherwise it's stored as *_tsim, *_ssim, and *_tesim
|
21
|
-
# e.g., a field called 'title' would be stored as 3 solr fields:
|
22
|
-
# 'title_tsim'
|
23
|
-
# 'title_ssim'
|
24
|
-
# 'title_tesim'
|
25
|
-
# @param [Valkyrie::Resource] resource
|
26
|
-
# @return [Valkyrie::Resource] the persisted resource
|
17
|
+
# (see Valkyrie::Persistence::Memory::Persister#save)
|
27
18
|
def save(resource:)
|
28
19
|
repository([resource]).persist.first
|
29
20
|
end
|
30
21
|
|
31
|
-
#
|
32
|
-
# @param [Array<Valkyrie::Resource>] resources
|
33
|
-
# @return [Valkyrie::Resource] the set of persisted resources
|
22
|
+
# (see Valkyrie::Persistence::Memory::Persister#save_all)
|
34
23
|
def save_all(resources:)
|
35
24
|
repository(resources).persist
|
36
25
|
end
|
37
26
|
|
38
|
-
#
|
39
|
-
# @param [Valkyrie::Resource] resource
|
40
|
-
# @return [Valkyrie::Resource] the deleted resource
|
27
|
+
# (see Valkyrie::Persistence::Memory::Persister#delete)
|
41
28
|
def delete(resource:)
|
42
29
|
repository([resource]).delete.first
|
43
30
|
end
|
44
31
|
|
45
|
-
#
|
32
|
+
# (see Valkyrie::Persistence::Memory::Persister#wipe!)
|
46
33
|
def wipe!
|
47
34
|
connection.delete_by_query("*:*")
|
48
35
|
connection.commit
|
@@ -33,6 +33,12 @@ module Valkyrie::Persistence::Solr::Queries
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
# Queries without making Resrouces and returns the RSolr page_total value
|
37
|
+
# @return [Integer]
|
38
|
+
def count
|
39
|
+
connection.get("select", params: { q: query })["response"]["numFound"].to_s.to_i
|
40
|
+
end
|
41
|
+
|
36
42
|
# Generates the Solr query for retrieving all Documents in the index
|
37
43
|
# If a model is specified for the query, it is scoped to that Valkyrie resource type
|
38
44
|
# @return [String]
|
@@ -27,7 +27,7 @@ module Valkyrie::Persistence::Solr::Queries
|
|
27
27
|
# Results are ordered by the member IDs specified in the Valkyrie Resource attribute
|
28
28
|
# @yield [Valkyrie::Resource]
|
29
29
|
def each
|
30
|
-
return []
|
30
|
+
return [] if resource.id.blank?
|
31
31
|
member_ids.map { |id| unordered_members.find { |member| member.id == id } }.reject(&:nil?).each do |member|
|
32
32
|
yield member
|
33
33
|
end
|
@@ -12,27 +12,21 @@ module Valkyrie::Persistence::Solr
|
|
12
12
|
@adapter = adapter
|
13
13
|
end
|
14
14
|
|
15
|
-
#
|
16
|
-
# @param [Valkyrie::ID] id
|
17
|
-
# @return [Valkyrie::Resource]
|
15
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_by)
|
18
16
|
def find_by(id:)
|
19
17
|
id = Valkyrie::ID.new(id.to_s) if id.is_a?(String)
|
20
18
|
validate_id(id)
|
21
19
|
Valkyrie::Persistence::Solr::Queries::FindByIdQuery.new(id, connection: connection, resource_factory: resource_factory).run
|
22
20
|
end
|
23
21
|
|
24
|
-
#
|
25
|
-
# @param [Valkyrie::ID] alternate_identifier
|
26
|
-
# @return [Valkyrie::Resource]
|
22
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_by_alternate_identifier)
|
27
23
|
def find_by_alternate_identifier(alternate_identifier:)
|
28
24
|
alternate_identifier = Valkyrie::ID.new(alternate_identifier.to_s) if alternate_identifier.is_a?(String)
|
29
25
|
validate_id(alternate_identifier)
|
30
26
|
Valkyrie::Persistence::Solr::Queries::FindByAlternateIdentifierQuery.new(alternate_identifier, connection: connection, resource_factory: resource_factory).run
|
31
27
|
end
|
32
28
|
|
33
|
-
#
|
34
|
-
# @param [Array<Valkyrie::ID>] ids
|
35
|
-
# @return [Array<Valkyrie::Resource>]
|
29
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_many_by_ids)
|
36
30
|
def find_many_by_ids(ids:)
|
37
31
|
ids.map! do |id|
|
38
32
|
id = Valkyrie::ID.new(id.to_s) if id.is_a?(String)
|
@@ -42,29 +36,29 @@ module Valkyrie::Persistence::Solr
|
|
42
36
|
Valkyrie::Persistence::Solr::Queries::FindManyByIdsQuery.new(ids, connection: connection, resource_factory: resource_factory).run
|
43
37
|
end
|
44
38
|
|
45
|
-
#
|
46
|
-
# @return [Array<Valkyrie::Resource>]
|
39
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_all)
|
47
40
|
def find_all
|
48
41
|
Valkyrie::Persistence::Solr::Queries::FindAllQuery.new(connection: connection, resource_factory: resource_factory).run
|
49
42
|
end
|
50
43
|
|
51
|
-
#
|
52
|
-
# @param [Class, String] model the Valkyrie::Resource Class
|
53
|
-
# @return [Array<Valkyrie::Resource>]
|
44
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_all_of_model)
|
54
45
|
def find_all_of_model(model:)
|
55
46
|
Valkyrie::Persistence::Solr::Queries::FindAllQuery.new(connection: connection, resource_factory: resource_factory, model: model).run
|
56
47
|
end
|
57
48
|
|
58
|
-
#
|
59
|
-
# @param [Valkyrie::Resource
|
60
|
-
# @return
|
49
|
+
# Count all of the Valkyrie Resources of a model persisted in the Solr index
|
50
|
+
# @param [Class, String] model the Valkyrie::Resource Class
|
51
|
+
# @return integer
|
52
|
+
def count_all_of_model(model:)
|
53
|
+
Valkyrie::Persistence::Solr::Queries::FindAllQuery.new(connection: connection, resource_factory: resource_factory, model: model).count
|
54
|
+
end
|
55
|
+
|
56
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_parents)
|
61
57
|
def find_parents(resource:)
|
62
58
|
find_inverse_references_by(resource: resource, property: :member_ids)
|
63
59
|
end
|
64
60
|
|
65
|
-
#
|
66
|
-
# @param [Valkyrie::Resource] parent resource
|
67
|
-
# @return [Array<Valkyrie::Resource>] member resources
|
61
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_members)
|
68
62
|
def find_members(resource:, model: nil)
|
69
63
|
Valkyrie::Persistence::Solr::Queries::FindMembersQuery.new(
|
70
64
|
resource: resource,
|
@@ -74,53 +68,48 @@ module Valkyrie::Persistence::Solr
|
|
74
68
|
).run
|
75
69
|
end
|
76
70
|
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
71
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_references_by)
|
72
|
+
def find_references_by(resource:, property:, model: nil)
|
73
|
+
result =
|
74
|
+
if ordered_property?(resource: resource, property: property)
|
75
|
+
Valkyrie::Persistence::Solr::Queries::FindOrderedReferencesQuery.new(resource: resource, property: property, connection: connection, resource_factory: resource_factory).run
|
76
|
+
else
|
77
|
+
Valkyrie::Persistence::Solr::Queries::FindReferencesQuery.new(resource: resource, property: property, connection: connection, resource_factory: resource_factory).run
|
78
|
+
end
|
79
|
+
return result unless model
|
80
|
+
result.select { |obj| obj.is_a?(model) }
|
87
81
|
end
|
88
82
|
|
89
|
-
#
|
90
|
-
|
91
|
-
# @param [Valkyrie::Resource] referenced resource
|
92
|
-
# @param [Symbol, String] property
|
93
|
-
# @return [Array<Valkyrie::Resource>] related resources
|
94
|
-
def find_inverse_references_by(resource: nil, id: nil, property:)
|
83
|
+
# (see Valkyrie::Persistence::Memory::QueryService#find_inverse_references_by)
|
84
|
+
def find_inverse_references_by(resource: nil, id: nil, property:, model: nil)
|
95
85
|
raise ArgumentError, "Provide resource or id" unless resource || id
|
96
86
|
ensure_persisted(resource) if resource
|
97
87
|
id ||= resource.id
|
98
|
-
Valkyrie::Persistence::Solr::Queries::FindInverseReferencesQuery.new(id: id, property: property, connection: connection, resource_factory: resource_factory).run
|
88
|
+
result = Valkyrie::Persistence::Solr::Queries::FindInverseReferencesQuery.new(id: id, property: property, connection: connection, resource_factory: resource_factory).run
|
89
|
+
return result unless model
|
90
|
+
result.select { |obj| obj.is_a?(model) }
|
99
91
|
end
|
100
92
|
|
101
|
-
#
|
102
|
-
# @return [Valkyrie::Persistence::CustomQueryContainer]
|
93
|
+
# (see Valkyrie::Persistence::Memory::QueryService#custom_queries)
|
103
94
|
def custom_queries
|
104
95
|
@custom_queries ||= ::Valkyrie::Persistence::CustomQueryContainer.new(query_service: self)
|
105
96
|
end
|
106
97
|
|
107
98
|
private
|
108
99
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
raise ArgumentError, 'id must be a Valkyrie::ID' unless id.is_a? Valkyrie::ID
|
114
|
-
end
|
100
|
+
# (see Valkyrie::Persistence::Memory::QueryService#validate_id)
|
101
|
+
def validate_id(id)
|
102
|
+
raise ArgumentError, 'id must be a Valkyrie::ID' unless id.is_a? Valkyrie::ID
|
103
|
+
end
|
115
104
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
end
|
105
|
+
# (see Valkyrie::Persistence::Memory::QueryService#ensure_persisted)
|
106
|
+
def ensure_persisted(resource)
|
107
|
+
raise ArgumentError, 'resource is not saved' unless resource.persisted?
|
108
|
+
end
|
121
109
|
|
122
|
-
|
123
|
-
|
124
|
-
|
110
|
+
# (see Valkyrie::Persistence::Memory::QueryService#ordered_property?)
|
111
|
+
def ordered_property?(resource:, property:)
|
112
|
+
resource.ordered_attribute?(property)
|
113
|
+
end
|
125
114
|
end
|
126
115
|
end
|
@@ -62,8 +62,9 @@ module Valkyrie::Persistence::Solr
|
|
62
62
|
# Given a new Valkyrie Resource, generate a random UUID and assign it to the Resource
|
63
63
|
# @param [Valkyrie::Resource] resource
|
64
64
|
# @param [String] the UUID for the new resource
|
65
|
+
# @see Valkyrie::Logging for details concerning log suppression.
|
65
66
|
def generate_id(resource)
|
66
|
-
Valkyrie.logger.warn
|
67
|
+
Valkyrie.logger.warn("The Solr adapter is not meant to persist new resources, but is now generating an ID.", logging_context: "Valkyrie::Persistence::Solr::Repository#generate_id")
|
67
68
|
resource.id = SecureRandom.uuid
|
68
69
|
end
|
69
70
|
|
data/lib/valkyrie/rdf_patches.rb
CHANGED
@@ -6,12 +6,12 @@
|
|
6
6
|
module RDF
|
7
7
|
class Literal
|
8
8
|
def as_json(*_args)
|
9
|
-
JSON::LD::API.fromRdf([RDF::Statement.new(RDF::URI(""), RDF::URI(""), self)])[0][""][0]
|
9
|
+
::JSON::LD::API.fromRdf([RDF::Statement.new(RDF::URI(""), RDF::URI(""), self)])[0][""][0]
|
10
10
|
end
|
11
11
|
end
|
12
12
|
class URI
|
13
13
|
def as_json(*_args)
|
14
|
-
JSON::LD::API.fromRdf([RDF::Statement.new(RDF::URI(""), RDF::URI(""), self)])[0][""][0]
|
14
|
+
::JSON::LD::API.fromRdf([RDF::Statement.new(RDF::URI(""), RDF::URI(""), self)])[0][""][0]
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
data/lib/valkyrie/resource.rb
CHANGED
@@ -13,6 +13,7 @@ module Valkyrie
|
|
13
13
|
# @see https://github.com/samvera-labs/valkyrie/wiki/ChangeSets-and-Dirty-Tracking Validation and change tracking is provided by change sets
|
14
14
|
#
|
15
15
|
# @see lib/valkyrie/specs/shared_specs/resource.rb
|
16
|
+
# rubocop:disable Metrics/ClassLength
|
16
17
|
class Resource < Dry::Struct
|
17
18
|
include Draper::Decoratable
|
18
19
|
# Allows a Valkyrie::Resource to be instantiated without providing every
|
@@ -48,13 +49,38 @@ module Valkyrie
|
|
48
49
|
raise ReservedAttributeError, "#{name} is a reserved attribute and defined by Valkyrie::Resource, do not redefine it." if reserved_attributes.include?(name.to_sym) &&
|
49
50
|
attribute_names.include?(name.to_sym) &&
|
50
51
|
!internal
|
51
|
-
|
52
|
-
set_value(name, value)
|
53
|
-
end
|
54
|
-
type = type.meta(ordered: true) if name == :member_ids
|
52
|
+
|
55
53
|
super(name, type)
|
56
54
|
end
|
57
55
|
|
56
|
+
# @param [Hash{Symbol => Dry::Types::Type}] new_schema
|
57
|
+
# @return [Dry::Struct]
|
58
|
+
# @raise [RepeatedAttributeError] when trying to define attribute with the
|
59
|
+
# same name as previously defined one
|
60
|
+
# @raise [ReservedAttributeError] when trying to define an attribute
|
61
|
+
# reserved by Valkyrie
|
62
|
+
# @see #attribute
|
63
|
+
# @note extends {Dry::Struct} by adding `#attr=` style setters
|
64
|
+
def self.attributes(new_schema)
|
65
|
+
new_schema[:member_ids] = new_schema[:member_ids].meta(ordered: true) if
|
66
|
+
new_schema.key?(:member_ids)
|
67
|
+
|
68
|
+
super
|
69
|
+
|
70
|
+
new_schema.each_key do |key|
|
71
|
+
key = key.to_s.chomp('?')
|
72
|
+
next if instance_methods.include?("#{key}=".to_sym)
|
73
|
+
|
74
|
+
class_eval(<<-RUBY)
|
75
|
+
def #{key}=(value)
|
76
|
+
set_value("#{key}".to_sym, value)
|
77
|
+
end
|
78
|
+
RUBY
|
79
|
+
end
|
80
|
+
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
58
84
|
def self.reserved_attributes
|
59
85
|
[:id, :internal_resource, :created_at, :updated_at, :new_record]
|
60
86
|
end
|
@@ -87,6 +113,10 @@ module Valkyrie
|
|
87
113
|
self.class.optimistic_locking_enabled?
|
88
114
|
end
|
89
115
|
|
116
|
+
def clear_optimistic_lock_token!
|
117
|
+
send("#{Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK}=", []) if optimistic_locking_enabled?
|
118
|
+
end
|
119
|
+
|
90
120
|
def attributes
|
91
121
|
Hash[self.class.attribute_names.map { |x| [x, nil] }].merge(super).freeze
|
92
122
|
end
|
@@ -161,9 +191,10 @@ module Valkyrie
|
|
161
191
|
|
162
192
|
# Returns if an attribute is set as ordered.
|
163
193
|
def ordered_attribute?(key)
|
164
|
-
self.class.schema.key(key).type.meta.try(:[], :ordered)
|
194
|
+
self.class.schema.key(key.to_sym).type.meta.try(:[], :ordered)
|
165
195
|
end
|
166
196
|
|
167
197
|
class ReservedAttributeError < StandardError; end
|
168
198
|
end
|
199
|
+
# rubocop:enable Metrics/ClassLength
|
169
200
|
end
|
@@ -75,7 +75,7 @@ RSpec.shared_examples 'a Valkyrie::ChangeSet' do |*_flags|
|
|
75
75
|
|
76
76
|
describe "#optimistic_locking_enabled?" do
|
77
77
|
it "delegates down to the resource" do
|
78
|
-
expect(change_set.optimistic_locking_enabled?).to eq
|
78
|
+
expect(change_set.optimistic_locking_enabled?).to eq change_set.resource.optimistic_locking_enabled?
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
@@ -7,6 +7,7 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
|
|
7
7
|
include Valkyrie::Resource::AccessControls
|
8
8
|
attribute :title
|
9
9
|
attribute :author
|
10
|
+
attribute :other_author
|
10
11
|
attribute :member_ids
|
11
12
|
attribute :nested_resource
|
12
13
|
attribute :single_value, Valkyrie::Types::String.optional
|
@@ -55,6 +56,14 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
|
|
55
56
|
output = persister.save(resource: resource)
|
56
57
|
|
57
58
|
expect(output.single_value).to eq "A single value"
|
59
|
+
|
60
|
+
reloaded = query_service.find_by(id: output.id)
|
61
|
+
|
62
|
+
reloaded.single_value = nil
|
63
|
+
persister.save(resource: reloaded)
|
64
|
+
reloaded = query_service.find_by(id: reloaded.id)
|
65
|
+
|
66
|
+
expect(reloaded.single_value).to eq nil
|
58
67
|
end
|
59
68
|
|
60
69
|
it "returns nil for an unset single value" do
|
@@ -75,14 +84,14 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
|
|
75
84
|
|
76
85
|
it "can support deep nesting of resources" do
|
77
86
|
pending "No support for deep nesting." if flags.include?(:no_deep_nesting)
|
78
|
-
book = resource_class.new(title: "Sub-nested", author: [Valkyrie::ID.new("test"), RDF::Literal.new("Test", language: :fr), RDF::URI("http://
|
87
|
+
book = resource_class.new(title: "Sub-nested", author: [Valkyrie::ID.new("test"), RDF::Literal.new("Test", language: :fr), RDF::URI("http://example.com")])
|
79
88
|
book2 = resource_class.new(title: "Nested", nested_resource: book)
|
80
89
|
book3 = persister.save(resource: resource_class.new(nested_resource: book2))
|
81
90
|
|
82
91
|
reloaded = query_service.find_by(id: book3.id)
|
83
92
|
expect(reloaded.nested_resource.first.title).to eq ["Nested"]
|
84
93
|
expect(reloaded.nested_resource.first.nested_resource.first.title).to eq ["Sub-nested"]
|
85
|
-
expect(reloaded.nested_resource.first.nested_resource.first.author).to contain_exactly Valkyrie::ID.new("test"), RDF::Literal.new("Test", language: :fr), RDF::URI("http://
|
94
|
+
expect(reloaded.nested_resource.first.nested_resource.first.author).to contain_exactly Valkyrie::ID.new("test"), RDF::Literal.new("Test", language: :fr), RDF::URI("http://example.com")
|
86
95
|
end
|
87
96
|
|
88
97
|
it "stores created_at/updated_at" do
|
@@ -179,14 +188,16 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
|
|
179
188
|
it "can store DateTimes" do
|
180
189
|
time1 = DateTime.current
|
181
190
|
time2 = Time.current.in_time_zone
|
182
|
-
|
191
|
+
time3 = "2019-01"
|
192
|
+
book = persister.save(resource: resource_class.new(title: [time1], author: [time2], other_author: [time3]))
|
183
193
|
|
184
194
|
reloaded = query_service.find_by(id: book.id)
|
185
195
|
|
186
196
|
expect(reloaded.title.first.to_i).to eq(time1.to_i)
|
187
|
-
expect(reloaded.title.first.zone).to eq('
|
197
|
+
expect(reloaded.title.first.zone).to eq('+00:00')
|
188
198
|
expect(reloaded.author.first.to_i).to eq(time2.to_i)
|
189
|
-
expect(reloaded.author.first.zone).to eq('
|
199
|
+
expect(reloaded.author.first.zone).to eq('+00:00')
|
200
|
+
expect(reloaded.other_author.first).to eq "2019-01"
|
190
201
|
end
|
191
202
|
|
192
203
|
it "can store Floats" do
|
@@ -349,7 +360,7 @@ RSpec.shared_examples 'a Valkyrie::Persister' do |*flags|
|
|
349
360
|
resource = MyLockingResource.new(title: ["My Locked Resource"])
|
350
361
|
initial_resource = persister.save(resource: resource)
|
351
362
|
initial_token = initial_resource[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK].first
|
352
|
-
initial_resource.
|
363
|
+
initial_resource.clear_optimistic_lock_token!
|
353
364
|
updated_resource = persister.save(resource: initial_resource)
|
354
365
|
expect(initial_token.serialize)
|
355
366
|
.not_to eq(updated_resource[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK].first.serialize)
|
@@ -12,10 +12,15 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
12
12
|
end
|
13
13
|
class Valkyrie::Specs::SecondResource < Valkyrie::Resource
|
14
14
|
end
|
15
|
+
class Valkyrie::Specs::ThirdResource < Valkyrie::Resource
|
16
|
+
attribute :a_member_of, Valkyrie::Types::Array
|
17
|
+
attribute :an_ordered_member_of, Valkyrie::Types::Array.meta(ordered: true)
|
18
|
+
end
|
15
19
|
end
|
16
20
|
after do
|
17
21
|
Valkyrie::Specs.send(:remove_const, :CustomResource)
|
18
22
|
Valkyrie::Specs.send(:remove_const, :SecondResource)
|
23
|
+
Valkyrie::Specs.send(:remove_const, :ThirdResource)
|
19
24
|
end
|
20
25
|
let(:resource_class) { Valkyrie::Specs::CustomResource }
|
21
26
|
let(:query_service) { adapter.query_service } unless defined? query_service
|
@@ -28,10 +33,11 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
28
33
|
it { is_expected.to respond_to(:find_by_alternate_identifier).with_keywords(:alternate_identifier) }
|
29
34
|
it { is_expected.to respond_to(:find_many_by_ids).with_keywords(:ids) }
|
30
35
|
it { is_expected.to respond_to(:find_members).with_keywords(:resource, :model) }
|
31
|
-
it { is_expected.to respond_to(:find_references_by).with_keywords(:resource, :property) }
|
32
|
-
it { is_expected.to respond_to(:find_inverse_references_by).with_keywords(:resource, :property) }
|
33
|
-
it { is_expected.to respond_to(:find_inverse_references_by).with_keywords(:id, :property) }
|
36
|
+
it { is_expected.to respond_to(:find_references_by).with_keywords(:resource, :property, :model) }
|
37
|
+
it { is_expected.to respond_to(:find_inverse_references_by).with_keywords(:resource, :property, :model) }
|
38
|
+
it { is_expected.to respond_to(:find_inverse_references_by).with_keywords(:id, :property, :model) }
|
34
39
|
it { is_expected.to respond_to(:find_parents).with_keywords(:resource) }
|
40
|
+
it { is_expected.to respond_to(:count_all_of_model).with_keywords(:model) }
|
35
41
|
|
36
42
|
describe ".find_all" do
|
37
43
|
it "returns all created resources" do
|
@@ -273,14 +279,50 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
273
279
|
expect(query_service.find_references_by(resource: child, property: :an_ordered_member_of).map(&:id).to_a).to eq []
|
274
280
|
end
|
275
281
|
end
|
282
|
+
|
283
|
+
context "filtering by model" do
|
284
|
+
context "when the object has related resources that match the filter" do
|
285
|
+
subject { query_service.find_references_by(resource: child1, property: :a_member_of, model: Valkyrie::Specs::SecondResource) }
|
286
|
+
let(:child1) { persister.save(resource: Valkyrie::Specs::ThirdResource.new(a_member_of: [parent3.id, parent2.id, parent.id])) }
|
287
|
+
let(:parent) { persister.save(resource: Valkyrie::Specs::SecondResource.new) }
|
288
|
+
let(:parent2) { persister.save(resource: Valkyrie::Specs::CustomResource.new) }
|
289
|
+
let(:parent3) { persister.save(resource: Valkyrie::Specs::SecondResource.new) }
|
290
|
+
|
291
|
+
it "returns only resources with the relationship filtered to the specified model" do
|
292
|
+
expect(subject.map(&:id).to_a).to match_array [parent3.id, parent.id]
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
context "when the object has ordered related resources that match the filter" do
|
297
|
+
subject { query_service.find_references_by(resource: child1, property: :an_ordered_member_of, model: Valkyrie::Specs::SecondResource) }
|
298
|
+
let(:child1) { persister.save(resource: Valkyrie::Specs::ThirdResource.new(an_ordered_member_of: [parent.id, parent3.id, parent2.id, parent.id])) }
|
299
|
+
let(:parent) { persister.save(resource: Valkyrie::Specs::SecondResource.new) }
|
300
|
+
let(:parent2) { persister.save(resource: Valkyrie::Specs::CustomResource.new) }
|
301
|
+
let(:parent3) { persister.save(resource: Valkyrie::Specs::SecondResource.new) }
|
302
|
+
|
303
|
+
it "returns only resources with the relationship filtered to the specified model" do
|
304
|
+
expect(subject.map(&:id).to_a).to match_array [parent.id, parent3.id, parent.id]
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
context "when there are no related resources that match the filter" do
|
309
|
+
subject { query_service.find_references_by(resource: child1, property: :a_member_of, model: Valkyrie::Specs::SecondResource) }
|
310
|
+
let(:child1) { persister.save(resource: Valkyrie::Specs::ThirdResource.new(a_member_of: [parent.id])) }
|
311
|
+
let(:parent) { persister.save(resource: Valkyrie::Specs::CustomResource.new) }
|
312
|
+
|
313
|
+
it "returns an empty array" do
|
314
|
+
expect(subject.to_a).to eq []
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
276
318
|
end
|
277
319
|
|
278
320
|
describe ".find_inverse_references_by" do
|
279
321
|
context "when the resource is saved" do
|
280
322
|
context "when the property is unordered" do
|
281
323
|
it "returns everything which references the given resource by the given property" do
|
282
|
-
parent = persister.save(resource:
|
283
|
-
parent2 = persister.save(resource:
|
324
|
+
parent = persister.save(resource: Valkyrie::Specs::SecondResource.new)
|
325
|
+
parent2 = persister.save(resource: Valkyrie::Specs::SecondResource.new)
|
284
326
|
child = persister.save(resource: resource_class.new(a_member_of: [parent.id]))
|
285
327
|
child2 = persister.save(resource: resource_class.new(a_member_of: [parent.id, parent2.id, parent.id]))
|
286
328
|
persister.save(resource: resource_class.new)
|
@@ -290,7 +332,7 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
290
332
|
end
|
291
333
|
|
292
334
|
it "returns an empty array if there are none" do
|
293
|
-
parent = persister.save(resource:
|
335
|
+
parent = persister.save(resource: Valkyrie::Specs::SecondResource.new)
|
294
336
|
|
295
337
|
expect(query_service.find_inverse_references_by(resource: parent, property: :a_member_of).to_a).to eq []
|
296
338
|
end
|
@@ -298,7 +340,7 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
298
340
|
|
299
341
|
context "when the property is ordered" do
|
300
342
|
it "returns everything which references the given resource by the given property" do
|
301
|
-
parent = persister.save(resource:
|
343
|
+
parent = persister.save(resource: Valkyrie::Specs::SecondResource.new)
|
302
344
|
child = persister.save(resource: resource_class.new(an_ordered_member_of: [parent.id]))
|
303
345
|
child2 = persister.save(resource: resource_class.new(an_ordered_member_of: [parent.id, parent.id]))
|
304
346
|
persister.save(resource: resource_class.new)
|
@@ -307,12 +349,38 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
307
349
|
expect(query_service.find_inverse_references_by(resource: parent, property: :an_ordered_member_of).map(&:id).to_a).to contain_exactly child.id, child2.id
|
308
350
|
end
|
309
351
|
end
|
352
|
+
|
353
|
+
context "when the property is ordered for one child but not the other" do
|
354
|
+
before do
|
355
|
+
class Valkyrie::Specs::Parent < Valkyrie::Resource; end
|
356
|
+
class Valkyrie::Specs::ChildWithUnorderedParents < Valkyrie::Resource
|
357
|
+
attribute :a_member_of, Valkyrie::Types::Array
|
358
|
+
end
|
359
|
+
class Valkyrie::Specs::ChildWithOrderedParents < Valkyrie::Resource
|
360
|
+
attribute :a_member_of, Valkyrie::Types::Array.meta(ordered: true)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
after do
|
364
|
+
Valkyrie::Specs.send(:remove_const, :Parent)
|
365
|
+
Valkyrie::Specs.send(:remove_const, :ChildWithUnorderedParents)
|
366
|
+
Valkyrie::Specs.send(:remove_const, :ChildWithOrderedParents)
|
367
|
+
end
|
368
|
+
it "returns" do
|
369
|
+
parent = persister.save(resource: Valkyrie::Specs::Parent.new)
|
370
|
+
child = persister.save(resource: Valkyrie::Specs::ChildWithUnorderedParents.new(a_member_of: [parent.id]))
|
371
|
+
child2 = persister.save(resource: Valkyrie::Specs::ChildWithOrderedParents.new(a_member_of: [parent.id, parent.id]))
|
372
|
+
persister.save(resource: Valkyrie::Specs::ChildWithUnorderedParents.new)
|
373
|
+
persister.save(resource: Valkyrie::Specs::Parent.new)
|
374
|
+
|
375
|
+
expect(query_service.find_inverse_references_by(resource: parent, property: :a_member_of).map(&:id).to_a).to contain_exactly child.id, child2.id
|
376
|
+
end
|
377
|
+
end
|
310
378
|
end
|
311
379
|
|
312
380
|
context "when id is passed instead of resource" do
|
313
381
|
it "returns everything which references the given resource by the given property" do
|
314
|
-
parent = persister.save(resource:
|
315
|
-
parent2 = persister.save(resource:
|
382
|
+
parent = persister.save(resource: Valkyrie::Specs::SecondResource.new)
|
383
|
+
parent2 = persister.save(resource: Valkyrie::Specs::SecondResource.new)
|
316
384
|
child = persister.save(resource: resource_class.new(a_member_of: [parent.id]))
|
317
385
|
child2 = persister.save(resource: resource_class.new(a_member_of: [parent.id, parent2.id, parent.id]))
|
318
386
|
persister.save(resource: resource_class.new)
|
@@ -335,6 +403,32 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
335
403
|
expect { query_service.find_inverse_references_by(resource: parent, property: :a_member_of).to_a }.to raise_error ArgumentError
|
336
404
|
end
|
337
405
|
end
|
406
|
+
|
407
|
+
context "filtering by model" do
|
408
|
+
subject { query_service.find_inverse_references_by(resource: parent, property: :a_member_of, model: Valkyrie::Specs::CustomResource) }
|
409
|
+
|
410
|
+
context "when the object has related resources that match the filter" do
|
411
|
+
let(:parent) { persister.save(resource: Valkyrie::Specs::SecondResource.new) }
|
412
|
+
|
413
|
+
it "returns only resources with the relationship filtered to the specified model" do
|
414
|
+
child1 = persister.save(resource: Valkyrie::Specs::CustomResource.new(a_member_of: [parent.id]))
|
415
|
+
persister.save(resource: Valkyrie::Specs::ThirdResource.new(a_member_of: [parent.id]))
|
416
|
+
child3 = persister.save(resource: Valkyrie::Specs::CustomResource.new(a_member_of: [parent.id]))
|
417
|
+
|
418
|
+
expect(subject.map(&:id).to_a).to match_array [child3.id, child1.id]
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
context "when there are no related resources that match the filter" do
|
423
|
+
let(:parent) { persister.save(resource: Valkyrie::Specs::SecondResource.new) }
|
424
|
+
|
425
|
+
it "returns an empty array" do
|
426
|
+
persister.save(resource: Valkyrie::Specs::ThirdResource.new(a_member_of: [parent.id]))
|
427
|
+
|
428
|
+
expect(subject.to_a).to eq []
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
338
432
|
end
|
339
433
|
|
340
434
|
describe ".find_parents" do
|
@@ -411,4 +505,13 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
411
505
|
expect(resource[Valkyrie::Persistence::Attributes::OPTIMISTIC_LOCK]).not_to be_empty
|
412
506
|
end
|
413
507
|
end
|
508
|
+
|
509
|
+
describe ".count_all_of_model" do
|
510
|
+
it "counts all of that model" do
|
511
|
+
persister.save(resource: resource_class.new)
|
512
|
+
persister.save(resource: Valkyrie::Specs::SecondResource.new)
|
513
|
+
persister.save(resource: Valkyrie::Specs::SecondResource.new)
|
514
|
+
expect(query_service.count_all_of_model(model: Valkyrie::Specs::SecondResource)).to eq(2)
|
515
|
+
end
|
516
|
+
end
|
414
517
|
end
|