valkyrie 3.4.0 → 3.5.1
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 +7 -27
- data/.gitignore +1 -0
- data/.lando.yml +1 -19
- data/Appraisals +5 -5
- data/CHANGELOG.md +13 -0
- data/README.md +2 -2
- data/gemfiles/{activerecord_7_0.gemfile → activerecord_8_0.gemfile} +1 -1
- data/gemfiles/faraday_1.gemfile +1 -1
- data/lib/valkyrie/persistence/fedora/persister/orm_converter.rb +3 -2
- data/lib/valkyrie/persistence/fedora/query_service.rb +1 -0
- data/lib/valkyrie/persistence/fedora.rb +1 -1
- data/lib/valkyrie/persistence/postgres/metadata_adapter.rb +3 -3
- data/lib/valkyrie/persistence/shared/json_value_mapper.rb +1 -0
- data/lib/valkyrie/specs/shared_specs/queries.rb +10 -0
- data/lib/valkyrie/specs/shared_specs/storage_adapter.rb +23 -6
- data/lib/valkyrie/storage/disk.rb +10 -3
- data/lib/valkyrie/storage/fedora.rb +17 -14
- data/lib/valkyrie/storage/memory.rb +9 -2
- data/lib/valkyrie/storage/versioned_disk.rb +18 -7
- data/lib/valkyrie/version.rb +1 -1
- data/lib/valkyrie.rb +6 -1
- data/valkyrie.gemspec +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '019ff2935ad45b095aa1a480fa630b3acd45486ba398fcc33b881c76f94856c9'
|
|
4
|
+
data.tar.gz: 6d5be6b5d178daaef17769f561fd2d3bc2d6795ea8d33bd76ccf038772b290c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '08ca3c0f78259f19bbf327363177c0560926d791e832cff00a6707540f00051ee02b1e2aade681104aa90140e1aa5c268a43686df6d49cc6d2545381362c9088'
|
|
7
|
+
data.tar.gz: 7edd93eb4e765b35f458cf1c6d372103249d191bd580f78e96f4a7e5c2f7012071371eb5bfc4315121d448fa17ecb7b09a76c58cf0658a0859a4ca654b6d2c3a
|
data/.circleci/config.yml
CHANGED
|
@@ -24,11 +24,7 @@ jobs:
|
|
|
24
24
|
environment:
|
|
25
25
|
CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
|
|
26
26
|
JAVA_OPTIONS: "-Djetty.http.port=8988"
|
|
27
|
-
- image:
|
|
28
|
-
environment:
|
|
29
|
-
CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
|
|
30
|
-
JAVA_OPTIONS: "-Djetty.http.port=8998 -Dfcrepo.dynamic.jms.port=61618 -Dfcrepo.dynamic.stomp.port=61614"
|
|
31
|
-
- image: fcrepo/fcrepo:6.4.0
|
|
27
|
+
- image: fcrepo/fcrepo:6.5.1-RC3-tomcat9
|
|
32
28
|
environment:
|
|
33
29
|
CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
|
|
34
30
|
JAVA_OPTS: "-Djetty.http.port=8978 -Dfcrepo.dynamic.jms.port=61619 -Dfcrepo.dynamic.stomp.port=61615 -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
|
|
@@ -65,7 +61,7 @@ jobs:
|
|
|
65
61
|
- run: gem install bundler -v '~> 2.0'
|
|
66
62
|
- restore_cache:
|
|
67
63
|
keys:
|
|
68
|
-
- bundle-{{ checksum "<< parameters.gemfile >>" }}-{{ checksum "valkyrie.gemspec" }}-<< parameters.ruby >>-
|
|
64
|
+
- bundle-{{ checksum "<< parameters.gemfile >>" }}-{{ checksum "valkyrie.gemspec" }}-<< parameters.ruby >>-7
|
|
69
65
|
- run: sudo apt update -y && sudo apt-get install -y libpq-dev lsof
|
|
70
66
|
- run:
|
|
71
67
|
name: Set BUNDLE_GEMFILE
|
|
@@ -75,7 +71,7 @@ jobs:
|
|
|
75
71
|
name: Install dependencies
|
|
76
72
|
command: bundle install --path=vendor/bundle --jobs 4 --retry 3
|
|
77
73
|
- save_cache:
|
|
78
|
-
key: bundle-{{ checksum "<< parameters.gemfile >>" }}-{{ checksum "valkyrie.gemspec" }}-<< parameters.ruby >>-
|
|
74
|
+
key: bundle-{{ checksum "<< parameters.gemfile >>" }}-{{ checksum "valkyrie.gemspec" }}-<< parameters.ruby >>-7
|
|
79
75
|
paths:
|
|
80
76
|
- "vendor/bundle"
|
|
81
77
|
- "gemfiles/vendor/bundle"
|
|
@@ -103,10 +99,6 @@ workflows:
|
|
|
103
99
|
gemfile: "gemfiles/activerecord_7_1.gemfile"
|
|
104
100
|
ruby: 3.3.5
|
|
105
101
|
name: "Ruby3-3_rails7-1"
|
|
106
|
-
- build:
|
|
107
|
-
gemfile: "gemfiles/activerecord_7_0.gemfile"
|
|
108
|
-
ruby: 3.3.5
|
|
109
|
-
name: "Ruby3-3_rails7-0"
|
|
110
102
|
- build:
|
|
111
103
|
gemfile: "gemfiles/activerecord_7_2.gemfile"
|
|
112
104
|
ruby: 3.2.5
|
|
@@ -115,10 +107,6 @@ workflows:
|
|
|
115
107
|
gemfile: "gemfiles/activerecord_7_1.gemfile"
|
|
116
108
|
ruby: 3.2.5
|
|
117
109
|
name: "Ruby3-2_rails7-1"
|
|
118
|
-
- build:
|
|
119
|
-
gemfile: "gemfiles/activerecord_7_0.gemfile"
|
|
120
|
-
ruby: 3.2.5
|
|
121
|
-
name: "Ruby3-2_rails7-0"
|
|
122
110
|
- build:
|
|
123
111
|
gemfile: "gemfiles/activerecord_7_2.gemfile"
|
|
124
112
|
ruby: 3.1.6
|
|
@@ -127,10 +115,6 @@ workflows:
|
|
|
127
115
|
gemfile: "gemfiles/activerecord_7_1.gemfile"
|
|
128
116
|
ruby: 3.1.6
|
|
129
117
|
name: "Ruby3-1_rails7-1"
|
|
130
|
-
- build:
|
|
131
|
-
gemfile: "gemfiles/activerecord_7_0.gemfile"
|
|
132
|
-
ruby: 3.1.6
|
|
133
|
-
name: "Ruby3-1_rails7-0"
|
|
134
118
|
- build:
|
|
135
119
|
gemfile: "gemfiles/faraday_1.gemfile"
|
|
136
120
|
ruby: 3.1.6
|
|
@@ -145,6 +129,10 @@ workflows:
|
|
|
145
129
|
only:
|
|
146
130
|
- master
|
|
147
131
|
jobs:
|
|
132
|
+
- build:
|
|
133
|
+
gemfile: "gemfiles/activerecord_8_0.gemfile"
|
|
134
|
+
ruby: 3.3.5
|
|
135
|
+
name: "Ruby3-3_rails8-0"
|
|
148
136
|
- build:
|
|
149
137
|
gemfile: "gemfiles/activerecord_7_2.gemfile"
|
|
150
138
|
ruby: 3.3.5
|
|
@@ -153,10 +141,6 @@ workflows:
|
|
|
153
141
|
gemfile: "gemfiles/activerecord_7_1.gemfile"
|
|
154
142
|
ruby: 3.3.5
|
|
155
143
|
name: "Ruby3-3_rails7-1"
|
|
156
|
-
- build:
|
|
157
|
-
gemfile: "gemfiles/activerecord_7_0.gemfile"
|
|
158
|
-
ruby: 3.3.5
|
|
159
|
-
name: "Ruby3-3_rails7-0"
|
|
160
144
|
- build:
|
|
161
145
|
gemfile: "gemfiles/activerecord_7_2.gemfile"
|
|
162
146
|
ruby: 3.2.5
|
|
@@ -177,10 +161,6 @@ workflows:
|
|
|
177
161
|
gemfile: "gemfiles/activerecord_7_1.gemfile"
|
|
178
162
|
ruby: 3.1.6
|
|
179
163
|
name: "Ruby3-1_rails7-1"
|
|
180
|
-
- build:
|
|
181
|
-
gemfile: "gemfiles/activerecord_7_0.gemfile"
|
|
182
|
-
ruby: 3.1.6
|
|
183
|
-
name: "Ruby3-1_rails7-0"
|
|
184
164
|
- build:
|
|
185
165
|
gemfile: "gemfiles/faraday_1.gemfile"
|
|
186
166
|
ruby: 3.1.6
|
data/.gitignore
CHANGED
data/.lando.yml
CHANGED
|
@@ -27,31 +27,13 @@ services:
|
|
|
27
27
|
environment:
|
|
28
28
|
CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
|
|
29
29
|
portforward: 8988
|
|
30
|
-
valkyrie_fedora_5:
|
|
31
|
-
type: compose
|
|
32
|
-
app_mount: false
|
|
33
|
-
volumes:
|
|
34
|
-
fedora5:
|
|
35
|
-
services:
|
|
36
|
-
image: fcrepo/fcrepo:5.1.1-multiplatform
|
|
37
|
-
command:
|
|
38
|
-
- "catalina.sh"
|
|
39
|
-
- "run"
|
|
40
|
-
volumes:
|
|
41
|
-
- fedora5:/data
|
|
42
|
-
ports:
|
|
43
|
-
- 8998:8080
|
|
44
|
-
environment:
|
|
45
|
-
CATALINA_OPTS: "-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms512m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
|
|
46
|
-
JAVA_OPTS: "-Dfcrepo.dynamic.jms.port=61620 -Dfcrepo.dynamic.stomp.port=61617 -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true"
|
|
47
|
-
portforward: true
|
|
48
30
|
valkyrie_fedora_6:
|
|
49
31
|
type: compose
|
|
50
32
|
app_mount: false
|
|
51
33
|
volumes:
|
|
52
34
|
fedora6:
|
|
53
35
|
services:
|
|
54
|
-
image: fcrepo/fcrepo:6.
|
|
36
|
+
image: fcrepo/fcrepo:6.5.1-RC3-tomcat9
|
|
55
37
|
command:
|
|
56
38
|
- "catalina.sh"
|
|
57
39
|
- "run"
|
data/Appraisals
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
appraise "activerecord-8-0" do
|
|
4
|
+
gem "activerecord", "~> 8.0.0"
|
|
5
|
+
end
|
|
6
|
+
|
|
3
7
|
appraise "activerecord-7-2" do
|
|
4
8
|
gem "activerecord", "~> 7.2.0"
|
|
5
9
|
end
|
|
@@ -8,11 +12,7 @@ appraise "activerecord-7-1" do
|
|
|
8
12
|
gem "activerecord", "~> 7.1.0"
|
|
9
13
|
end
|
|
10
14
|
|
|
11
|
-
appraise "activerecord-7-0" do
|
|
12
|
-
gem "activerecord", "~> 7.0.0"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
15
|
appraise "faraday-1" do
|
|
16
16
|
gem "faraday", "~> 1.0"
|
|
17
|
-
gem "activerecord", "~> 7.
|
|
17
|
+
gem "activerecord", "~> 7.1.0"
|
|
18
18
|
end
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# v3.5.1 2026-01-16
|
|
2
|
+
|
|
3
|
+
* Remove Fedora 5. ([tpendragon](https://github.com/tpendragon))
|
|
4
|
+
* Limit LSOF command to the current process ID ([hackartisan](https://github.com/hackartisan))
|
|
5
|
+
|
|
6
|
+
# v3.5.0 2024-12-10
|
|
7
|
+
|
|
8
|
+
* Provides new boolean configuration setting `auto_cast_iso8601_as_datetime` ([dchandekstark](https://github.com/dchandekstark))
|
|
9
|
+
* Align behavior of server managed properties with Fedora. ([dlpierce](https://github.com/dlpierce))
|
|
10
|
+
* Check for nil id to avoid InvalidURIError ([dlpierce](https://github.com/dlpierce))
|
|
11
|
+
* Support Fedora 6.5 ([dlpierce](https://github.com/dlpierce))
|
|
12
|
+
* Make storage adapters report their protocol string ([dlpierce](https://github.com/dlpierce))
|
|
13
|
+
|
|
1
14
|
# v3.4.0 2024-10-11
|
|
2
15
|
|
|
3
16
|
## Changes since last release
|
data/README.md
CHANGED
|
@@ -224,14 +224,14 @@ custom adapters, but your application will still work.
|
|
|
224
224
|
Using the shared specs in your own models is described in more detail on the [Shared Specs Wiki
|
|
225
225
|
page](https://github.com/samvera/valkyrie/wiki/Shared-Specs).
|
|
226
226
|
|
|
227
|
-
### Fedora
|
|
227
|
+
### Fedora 6 Compatibility
|
|
228
228
|
When configuring your adapter, include the `fedora_version` parameter in your metadata or storage adapter
|
|
229
229
|
config. If Fedora requires auth, you can also include that in the URL, e.g.:
|
|
230
230
|
|
|
231
231
|
```
|
|
232
232
|
Valkyrie::Storage::Fedora.new(
|
|
233
233
|
connection: Ldp::Client.new("http://fedoraAdmin:fedoraAdmin@localhost:8988/rest"),
|
|
234
|
-
fedora_version:
|
|
234
|
+
fedora_version: 6
|
|
235
235
|
)
|
|
236
236
|
```
|
|
237
237
|
#### Pairtree paths in Fedora 4/6
|
data/gemfiles/faraday_1.gemfile
CHANGED
|
@@ -105,7 +105,8 @@ module Valkyrie::Persistence::Fedora
|
|
|
105
105
|
# @param [Property] value
|
|
106
106
|
# @return [Boolean]
|
|
107
107
|
def self.handles?(value)
|
|
108
|
-
value.statement.
|
|
108
|
+
value.statement.predicate == RDF.type &&
|
|
109
|
+
value.statement.object.to_s.start_with?('http://www.w3.org/ns/ldp#', 'http://fedora.info/definitions/v4/repository#')
|
|
109
110
|
end
|
|
110
111
|
|
|
111
112
|
# Provide the NullApplicator Class for any Property in a deny listed namespace
|
|
@@ -562,7 +563,7 @@ module Valkyrie::Persistence::Fedora
|
|
|
562
563
|
# @return [Array<String>]
|
|
563
564
|
def denylist
|
|
564
565
|
[
|
|
565
|
-
"http://fedora.info/definitions",
|
|
566
|
+
"http://fedora.info/definitions/v4/repository#",
|
|
566
567
|
"http://www.iana.org/assignments/relation/last"
|
|
567
568
|
]
|
|
568
569
|
end
|
|
@@ -162,6 +162,7 @@ module Valkyrie::Persistence::Fedora
|
|
|
162
162
|
# @return [Valkyrie::Resource]
|
|
163
163
|
# @raise [Valkyrie::Persistence::ObjectNotFoundError]
|
|
164
164
|
def resource_from_uri(uri)
|
|
165
|
+
raise ::Valkyrie::Persistence::ObjectNotFoundError if uri.nil?
|
|
165
166
|
resource = Ldp::Resource.for(connection, uri, connection.get(uri))
|
|
166
167
|
resource_factory.to_resource(object: resource)
|
|
167
168
|
rescue ::Ldp::Gone, ::Ldp::NotFound
|
|
@@ -9,12 +9,12 @@ module Valkyrie::Persistence::Postgres
|
|
|
9
9
|
#
|
|
10
10
|
# @see https://github.com/samvera-labs/valkyrie/wiki/Set-up-Valkyrie-database-in-a-Rails-Application
|
|
11
11
|
class MetadataAdapter
|
|
12
|
-
# @return [
|
|
12
|
+
# @return [Valkyrie::Persistence::Postgres::Persister]
|
|
13
13
|
def persister
|
|
14
14
|
Valkyrie::Persistence::Postgres::Persister.new(adapter: self)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
# @return [
|
|
17
|
+
# @return [Valkyrie::Persistence::Postgres::QueryService]
|
|
18
18
|
def query_service
|
|
19
19
|
@query_service ||= Valkyrie::Persistence::Postgres::QueryService.new(
|
|
20
20
|
resource_factory: resource_factory,
|
|
@@ -22,7 +22,7 @@ module Valkyrie::Persistence::Postgres
|
|
|
22
22
|
)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# @return [
|
|
25
|
+
# @return [Valkyrie::Persistence::Postgres::ResourceFactory]
|
|
26
26
|
def resource_factory
|
|
27
27
|
@resource_factory ||= Valkyrie::Persistence::Postgres::ResourceFactory.new(adapter: self)
|
|
28
28
|
end
|
|
@@ -139,6 +139,7 @@ module Valkyrie::Persistence::Shared
|
|
|
139
139
|
# @return [Boolean]
|
|
140
140
|
# rubocop:disable Metrics/CyclomaticComplexity
|
|
141
141
|
def self.handles?(value)
|
|
142
|
+
return false unless ::Valkyrie.config.auto_cast_iso8601_as_datetime
|
|
142
143
|
return false unless value.is_a?(String)
|
|
143
144
|
return false unless value[4] == "-" && value[10] == "T"
|
|
144
145
|
year = value.to_s[0..3]
|
|
@@ -86,8 +86,13 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
|
86
86
|
expect { query_service.find_by(id: Valkyrie::ID.new("123123123")) }.to raise_error ::Valkyrie::Persistence::ObjectNotFoundError
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
it 'returns a Valkyrie::Persistence::ObjectNotFoundError for an empty string' do
|
|
90
|
+
expect { query_service.find_by(id: '') }.to raise_error ::Valkyrie::Persistence::ObjectNotFoundError
|
|
91
|
+
end
|
|
92
|
+
|
|
89
93
|
it 'raises an error if the id is not a Valkyrie::ID or a string' do
|
|
90
94
|
expect { query_service.find_by(id: 123) }.to raise_error ArgumentError
|
|
95
|
+
expect { query_service.find_by(id: nil) }.to raise_error ArgumentError
|
|
91
96
|
end
|
|
92
97
|
end
|
|
93
98
|
|
|
@@ -117,8 +122,13 @@ RSpec.shared_examples 'a Valkyrie query provider' do
|
|
|
117
122
|
expect { query_service.find_by_alternate_identifier(alternate_identifier: Valkyrie::ID.new("123123123")) }.to raise_error ::Valkyrie::Persistence::ObjectNotFoundError
|
|
118
123
|
end
|
|
119
124
|
|
|
125
|
+
it "raises a Valkyrie::Persistence::ObjectNotFoundError for a non-found alternate identifier" do
|
|
126
|
+
expect { query_service.find_by_alternate_identifier(alternate_identifier: '') }.to raise_error ::Valkyrie::Persistence::ObjectNotFoundError
|
|
127
|
+
end
|
|
128
|
+
|
|
120
129
|
it 'raises an error if the alternate identifier is not a Valkyrie::ID or a string' do
|
|
121
130
|
expect { query_service.find_by_alternate_identifier(alternate_identifier: 123) }.to raise_error ArgumentError
|
|
131
|
+
expect { query_service.find_by_alternate_identifier(alternate_identifier: nil) }.to raise_error ArgumentError
|
|
122
132
|
end
|
|
123
133
|
|
|
124
134
|
it 'can have multiple alternate identifiers' do
|
|
@@ -12,6 +12,7 @@ RSpec.shared_examples 'a Valkyrie::StorageAdapter' do
|
|
|
12
12
|
Valkyrie::Specs.send(:remove_const, :CustomResource)
|
|
13
13
|
end
|
|
14
14
|
subject { storage_adapter }
|
|
15
|
+
it { is_expected.to respond_to(:protocol) }
|
|
15
16
|
it { is_expected.to respond_to(:handles?).with_keywords(:id) }
|
|
16
17
|
it { is_expected.to respond_to(:find_by).with_keywords(:id) }
|
|
17
18
|
it { is_expected.to respond_to(:delete).with_keywords(:id) }
|
|
@@ -63,19 +64,21 @@ RSpec.shared_examples 'a Valkyrie::StorageAdapter' do
|
|
|
63
64
|
pre_open_files = open_files
|
|
64
65
|
the_file = storage_adapter.find_by(id: uploaded_file.id)
|
|
65
66
|
expect(the_file).to be_kind_of Valkyrie::StorageAdapter::File
|
|
66
|
-
expect(pre_open_files.size).to
|
|
67
|
+
expect(pre_open_files.size).to be <= open_files.size
|
|
67
68
|
end
|
|
68
69
|
|
|
69
70
|
def open_files
|
|
70
|
-
|
|
71
|
+
GC.start
|
|
72
|
+
`lsof -p #{Process.pid}`.split("\n").map { |r| r.split(/\s+/).last }
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
it "can upload, validate, re-fetch, and delete a file" do
|
|
74
76
|
resource = Valkyrie::Specs::CustomResource.new(id: "test#{SecureRandom.uuid}")
|
|
75
77
|
sha1 = Digest::SHA1.file(file).to_s
|
|
76
78
|
size = file.size
|
|
77
|
-
|
|
79
|
+
uploaded_file = storage_adapter.upload(file: file, original_filename: 'foo.jpg', resource: resource, fake_upload_argument: true)
|
|
78
80
|
|
|
81
|
+
expect(uploaded_file).to be_kind_of Valkyrie::StorageAdapter::File
|
|
79
82
|
expect(uploaded_file).to respond_to(:checksum).with_keywords(:digests)
|
|
80
83
|
expect(uploaded_file).to respond_to(:valid?).with_keywords(:size, :digests)
|
|
81
84
|
expect(uploaded_file.checksum(digests: [Digest::SHA1.new])).to eq([sha1])
|
|
@@ -133,28 +136,42 @@ RSpec.shared_examples 'a Valkyrie::StorageAdapter' do
|
|
|
133
136
|
# Deleting a version should leave the current versions
|
|
134
137
|
if storage_adapter.supports?(:version_deletion)
|
|
135
138
|
storage_adapter.delete(id: uploaded_file.version_id)
|
|
136
|
-
|
|
139
|
+
remnants = storage_adapter.find_versions(id: uploaded_file.id)
|
|
140
|
+
expect(remnants.length).to eq 1
|
|
141
|
+
expect(remnants.first.version_id).to eq new_version.version_id
|
|
137
142
|
expect { storage_adapter.find_by(id: uploaded_file.version_id) }.to raise_error Valkyrie::StorageAdapter::FileNotFound
|
|
138
143
|
end
|
|
139
144
|
current_length = storage_adapter.find_versions(id: new_version.id).length
|
|
140
145
|
|
|
141
146
|
# Restoring a previous version is just pumping its file into upload_version
|
|
142
147
|
newest_version = storage_adapter.upload_version(file: new_version, id: new_version.id)
|
|
148
|
+
current_length += 1
|
|
143
149
|
expect(newest_version.version_id).not_to eq new_version.id
|
|
144
150
|
expect(storage_adapter.find_by(id: newest_version.id).version_id).to eq newest_version.version_id
|
|
145
151
|
|
|
146
152
|
# I can restore a version twice
|
|
147
153
|
newest_version = storage_adapter.upload_version(file: new_version, id: new_version.id)
|
|
154
|
+
current_length += 1
|
|
148
155
|
expect(newest_version.version_id).not_to eq new_version.id
|
|
149
156
|
expect(storage_adapter.find_by(id: newest_version.id).version_id).to eq newest_version.version_id
|
|
150
|
-
expect(storage_adapter.find_versions(id: newest_version.id).length).to eq current_length
|
|
157
|
+
expect(storage_adapter.find_versions(id: newest_version.id).length).to eq current_length
|
|
158
|
+
|
|
159
|
+
# Fedora 6.5 may not create versions when the timestamp is the same?
|
|
160
|
+
# See: https://fedora-repository.atlassian.net/browse/FCREPO-3958
|
|
161
|
+
sleep 1 if storage_adapter.supports?(:list_deleted_versions)
|
|
151
162
|
|
|
152
163
|
# NOTE: We originally wanted deleting the current record to push it into the
|
|
153
164
|
# versions history, but FCRepo 4/5/6 doesn't work that way, so we changed to
|
|
154
165
|
# instead make deleting delete everything.
|
|
155
166
|
storage_adapter.delete(id: new_version.id)
|
|
156
167
|
expect { storage_adapter.find_by(id: new_version.id) }.to raise_error Valkyrie::StorageAdapter::FileNotFound
|
|
157
|
-
|
|
168
|
+
|
|
169
|
+
if storage_adapter.supports?(:list_deleted_versions)
|
|
170
|
+
expect(storage_adapter.find_versions(id: new_version.id).length).to eq current_length
|
|
171
|
+
else
|
|
172
|
+
expect(storage_adapter.find_versions(id: new_version.id).length).to eq 0
|
|
173
|
+
end
|
|
174
|
+
|
|
158
175
|
ensure
|
|
159
176
|
f&.close
|
|
160
177
|
end
|
|
@@ -3,6 +3,8 @@ module Valkyrie::Storage
|
|
|
3
3
|
# Implements the DataMapper Pattern to store binary data on disk
|
|
4
4
|
class Disk
|
|
5
5
|
attr_reader :base_path, :path_generator, :file_mover
|
|
6
|
+
PROTOCOL = 'disk://'
|
|
7
|
+
|
|
6
8
|
def initialize(base_path:, path_generator: BucketedStorage, file_mover: FileUtils.method(:mv))
|
|
7
9
|
@base_path = Pathname.new(base_path.to_s)
|
|
8
10
|
@path_generator = path_generator.new(base_path: base_path)
|
|
@@ -18,13 +20,13 @@ module Valkyrie::Storage
|
|
|
18
20
|
new_path = path_generator.generate(resource: resource, file: file, original_filename: original_filename)
|
|
19
21
|
FileUtils.mkdir_p(new_path.parent)
|
|
20
22
|
file_mover.call(file.path, new_path)
|
|
21
|
-
find_by(id: Valkyrie::ID.new("
|
|
23
|
+
find_by(id: Valkyrie::ID.new("#{protocol}#{new_path}"))
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
# @param id [Valkyrie::ID]
|
|
25
27
|
# @return [Boolean] true if this adapter can handle this type of identifer
|
|
26
28
|
def handles?(id:)
|
|
27
|
-
id.to_s.start_with?("
|
|
29
|
+
id.to_s.start_with?("#{protocol}#{base_path}")
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
# @param feature [Symbol] Feature to test for.
|
|
@@ -33,8 +35,13 @@ module Valkyrie::Storage
|
|
|
33
35
|
false
|
|
34
36
|
end
|
|
35
37
|
|
|
38
|
+
# @return [String] identifier prefix
|
|
39
|
+
def protocol
|
|
40
|
+
PROTOCOL
|
|
41
|
+
end
|
|
42
|
+
|
|
36
43
|
def file_path(id)
|
|
37
|
-
id.to_s.
|
|
44
|
+
id.to_s.delete_prefix(protocol)
|
|
38
45
|
end
|
|
39
46
|
|
|
40
47
|
# Return the file associated with the given identifier
|
|
@@ -27,7 +27,7 @@ module Valkyrie::Storage
|
|
|
27
27
|
# @param id [Valkyrie::ID]
|
|
28
28
|
# @return [Boolean] true if this adapter can handle this type of identifer
|
|
29
29
|
def handles?(id:)
|
|
30
|
-
id.to_s.start_with?(
|
|
30
|
+
id.to_s.start_with?(protocol)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# @param feature [Symbol] Feature to test for.
|
|
@@ -36,9 +36,16 @@ module Valkyrie::Storage
|
|
|
36
36
|
return true if feature == :versions
|
|
37
37
|
# Fedora 6 auto versions and you can't delete versions.
|
|
38
38
|
return true if feature == :version_deletion && fedora_version < 6
|
|
39
|
+
# Fedora 6.5+ lists versions for deleted objects
|
|
40
|
+
return true if feature == :list_deleted_versions && fedora_version >= 6.5
|
|
39
41
|
false
|
|
40
42
|
end
|
|
41
43
|
|
|
44
|
+
# @return [String] identifier prefix
|
|
45
|
+
def protocol
|
|
46
|
+
PROTOCOL
|
|
47
|
+
end
|
|
48
|
+
|
|
42
49
|
# Return the file associated with the given identifier
|
|
43
50
|
# @param id [Valkyrie::ID]
|
|
44
51
|
# @return [Valkyrie::StorageAdapter::StreamFile]
|
|
@@ -61,7 +68,7 @@ module Valkyrie::Storage
|
|
|
61
68
|
# Fedora 6 auto versions, so check to see if there's a version for this
|
|
62
69
|
# initial upload. If not, then mint one (fedora 4/5)
|
|
63
70
|
version_id = current_version_id(id: valkyrie_identifier(uri: identifier)) || mint_version(identifier, latest_version(identifier))
|
|
64
|
-
perform_find(id: Valkyrie::ID.new(identifier.to_s.sub(/^.+\/\//,
|
|
71
|
+
perform_find(id: Valkyrie::ID.new(identifier.to_s.sub(/^.+\/\//, protocol)), version_id: version_id)
|
|
65
72
|
end
|
|
66
73
|
|
|
67
74
|
# @param id [Valkyrie::ID] ID of the Valkyrie::StorageAdapter::StreamFile to
|
|
@@ -77,7 +84,7 @@ module Valkyrie::Storage
|
|
|
77
84
|
end
|
|
78
85
|
upload_file(fedora_uri: uri, io: file)
|
|
79
86
|
version_id = mint_version(uri, latest_version(uri))
|
|
80
|
-
perform_find(id: Valkyrie::ID.new(uri.to_s.sub(/^.+\/\//,
|
|
87
|
+
perform_find(id: Valkyrie::ID.new(uri.to_s.sub(/^.+\/\//, protocol)), version_id: version_id)
|
|
81
88
|
end
|
|
82
89
|
|
|
83
90
|
# @param id [Valkyrie::ID]
|
|
@@ -88,7 +95,9 @@ module Valkyrie::Storage
|
|
|
88
95
|
version_list.map do |version|
|
|
89
96
|
id = valkyrie_identifier(uri: version["@id"])
|
|
90
97
|
perform_find(id: id, version_id: id)
|
|
91
|
-
|
|
98
|
+
rescue Valkyrie::StorageAdapter::FileNotFound
|
|
99
|
+
nil
|
|
100
|
+
end.compact
|
|
92
101
|
end
|
|
93
102
|
|
|
94
103
|
# Delete the file in Fedora associated with the given identifier.
|
|
@@ -107,7 +116,7 @@ module Valkyrie::Storage
|
|
|
107
116
|
if fedora_version == 4
|
|
108
117
|
version_graph&.fetch("http://fedora.info/definitions/v4/repository#hasVersion", [])
|
|
109
118
|
else
|
|
110
|
-
# Fedora
|
|
119
|
+
# Fedora 6 uses Memento.
|
|
111
120
|
version_graph&.fetch("http://www.w3.org/ns/ldp#contains", [])&.sort_by { |x| x["@id"] }&.reverse
|
|
112
121
|
end
|
|
113
122
|
end
|
|
@@ -167,12 +176,6 @@ module Valkyrie::Storage
|
|
|
167
176
|
end
|
|
168
177
|
# If there's a deletion marker, don't return anything. (Fedora 4)
|
|
169
178
|
return nil if response.status == 410
|
|
170
|
-
# This is awful, but versioning is locked to per-second increments,
|
|
171
|
-
# returns a 409 in Fedora 5 if there's a conflict.
|
|
172
|
-
if response.status == 409
|
|
173
|
-
sleep(0.5)
|
|
174
|
-
return mint_version(identifier, version_name)
|
|
175
|
-
end
|
|
176
179
|
raise "Version unable to be created" unless response.status == 201
|
|
177
180
|
valkyrie_identifier(uri: response.headers["location"].gsub("/fcr:metadata", ""))
|
|
178
181
|
end
|
|
@@ -197,12 +200,12 @@ module Valkyrie::Storage
|
|
|
197
200
|
# Translate the Valkrie ID into a URL for the fedora file
|
|
198
201
|
# @return [RDF::URI]
|
|
199
202
|
def fedora_identifier(id:)
|
|
200
|
-
identifier = id.to_s.sub(
|
|
203
|
+
identifier = id.to_s.sub(protocol, "#{connection.http.scheme}://")
|
|
201
204
|
RDF::URI(identifier)
|
|
202
205
|
end
|
|
203
206
|
|
|
204
207
|
def valkyrie_identifier(uri:)
|
|
205
|
-
id = uri.to_s.sub("http://",
|
|
208
|
+
id = uri.to_s.sub("http://", protocol)
|
|
206
209
|
Valkyrie::ID.new(id)
|
|
207
210
|
end
|
|
208
211
|
|
|
@@ -211,7 +214,7 @@ module Valkyrie::Storage
|
|
|
211
214
|
# @return [IOProxy]
|
|
212
215
|
def response(id:)
|
|
213
216
|
response = connection.http.get(fedora_identifier(id: id))
|
|
214
|
-
raise Valkyrie::StorageAdapter::FileNotFound unless response.success?
|
|
217
|
+
raise Valkyrie::StorageAdapter::FileNotFound, "HTTP #{response.status} #{response.body}" unless response.success?
|
|
215
218
|
IOProxy.new(response.body)
|
|
216
219
|
end
|
|
217
220
|
|
|
@@ -6,6 +6,8 @@ module Valkyrie::Storage
|
|
|
6
6
|
# in cases where you want to preserve real data
|
|
7
7
|
class Memory
|
|
8
8
|
attr_reader :cache
|
|
9
|
+
PROTOCOL = 'memory://'
|
|
10
|
+
|
|
9
11
|
def initialize
|
|
10
12
|
@cache = {}
|
|
11
13
|
end
|
|
@@ -16,7 +18,7 @@ module Valkyrie::Storage
|
|
|
16
18
|
# @param _extra_arguments [Hash] additional arguments which may be passed to other adapters
|
|
17
19
|
# @return [Valkyrie::StorageAdapter::StreamFile]
|
|
18
20
|
def upload(file:, original_filename:, resource: nil, **_extra_arguments)
|
|
19
|
-
identifier = Valkyrie::ID.new("
|
|
21
|
+
identifier = Valkyrie::ID.new("#{protocol}#{resource.id}")
|
|
20
22
|
version_id = Valkyrie::ID.new("#{identifier}##{SecureRandom.uuid}")
|
|
21
23
|
cache[identifier] ||= {}
|
|
22
24
|
cache[identifier][:current] = Valkyrie::StorageAdapter::StreamFile.new(id: identifier, io: file, version_id: version_id)
|
|
@@ -67,7 +69,7 @@ module Valkyrie::Storage
|
|
|
67
69
|
# @param id [Valkyrie::ID]
|
|
68
70
|
# @return [Boolean] true if this adapter can handle this type of identifer
|
|
69
71
|
def handles?(id:)
|
|
70
|
-
id.to_s.start_with?(
|
|
72
|
+
id.to_s.start_with?(protocol)
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
# @param feature [Symbol] Feature to test for.
|
|
@@ -83,6 +85,11 @@ module Valkyrie::Storage
|
|
|
83
85
|
end
|
|
84
86
|
end
|
|
85
87
|
|
|
88
|
+
# @return [String] identifier prefix
|
|
89
|
+
def protocol
|
|
90
|
+
PROTOCOL
|
|
91
|
+
end
|
|
92
|
+
|
|
86
93
|
def id_and_version(id)
|
|
87
94
|
id, version = id.to_s.split("#")
|
|
88
95
|
[Valkyrie::ID.new(id), version]
|
|
@@ -7,6 +7,8 @@ module Valkyrie::Storage
|
|
|
7
7
|
# with "deletionmarker" in the name of the file.
|
|
8
8
|
class VersionedDisk
|
|
9
9
|
attr_reader :base_path, :path_generator, :file_mover
|
|
10
|
+
PROTOCOL = 'versiondisk://'
|
|
11
|
+
|
|
10
12
|
def initialize(base_path:, path_generator: ::Valkyrie::Storage::Disk::BucketedStorage, file_mover: FileUtils.method(:cp))
|
|
11
13
|
@base_path = Pathname.new(base_path.to_s)
|
|
12
14
|
@path_generator = path_generator.new(base_path: base_path)
|
|
@@ -26,7 +28,7 @@ module Valkyrie::Storage
|
|
|
26
28
|
return sleep(0.001) && upload(file: file, original_filename: original_filename, resource: resource, paused: true, **extra_arguments) if !paused && File.exist?(new_path)
|
|
27
29
|
FileUtils.mkdir_p(new_path.parent)
|
|
28
30
|
file_mover.call(file.try(:path) || file.try(:disk_path), new_path)
|
|
29
|
-
find_by(id: Valkyrie::ID.new("
|
|
31
|
+
find_by(id: Valkyrie::ID.new("#{protocol}#{new_path}"))
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def current_timestamp
|
|
@@ -50,13 +52,13 @@ module Valkyrie::Storage
|
|
|
50
52
|
return sleep(0.001) && upload_version(id: id, file: file, paused: true) if !paused && File.exist?(new_path)
|
|
51
53
|
FileUtils.mkdir_p(new_path.parent)
|
|
52
54
|
file_mover.call(file.try(:path) || file.try(:disk_path), new_path)
|
|
53
|
-
find_by(id: Valkyrie::ID.new("
|
|
55
|
+
find_by(id: Valkyrie::ID.new("#{protocol}#{new_path}"))
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
# @param id [Valkyrie::ID]
|
|
57
59
|
# @return [Boolean] true if this adapter can handle this type of identifer
|
|
58
60
|
def handles?(id:)
|
|
59
|
-
id.to_s.start_with?("
|
|
61
|
+
id.to_s.start_with?("#{protocol}#{base_path}")
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
# @param feature [Symbol] Feature to test for.
|
|
@@ -66,6 +68,11 @@ module Valkyrie::Storage
|
|
|
66
68
|
false
|
|
67
69
|
end
|
|
68
70
|
|
|
71
|
+
# @return [String] identifier prefix
|
|
72
|
+
def protocol
|
|
73
|
+
PROTOCOL
|
|
74
|
+
end
|
|
75
|
+
|
|
69
76
|
# Return the file associated with the given identifier
|
|
70
77
|
# @param id [Valkyrie::ID]
|
|
71
78
|
# @return [Valkyrie::StorageAdapter::File]
|
|
@@ -95,7 +102,7 @@ module Valkyrie::Storage
|
|
|
95
102
|
# @return [Array<Valkyrie::StorageAdapter::File>]
|
|
96
103
|
def find_versions(id:)
|
|
97
104
|
version_files(id: id).select { |x| !x.to_s.include?("deletionmarker") }.map do |file|
|
|
98
|
-
find_by(id: Valkyrie::ID.new("
|
|
105
|
+
find_by(id: Valkyrie::ID.new("#{protocol}#{file}"))
|
|
99
106
|
end
|
|
100
107
|
end
|
|
101
108
|
|
|
@@ -106,7 +113,7 @@ module Valkyrie::Storage
|
|
|
106
113
|
end
|
|
107
114
|
|
|
108
115
|
def file_path(version_id)
|
|
109
|
-
version_id.to_s.gsub(
|
|
116
|
+
version_id.to_s.gsub(/^#{Regexp.escape(protocol)}/, '')
|
|
110
117
|
end
|
|
111
118
|
|
|
112
119
|
# @return VersionId A VersionId value that's resolved a current reference,
|
|
@@ -128,6 +135,10 @@ module Valkyrie::Storage
|
|
|
128
135
|
@id = id
|
|
129
136
|
end
|
|
130
137
|
|
|
138
|
+
def protocol
|
|
139
|
+
PROTOCOL
|
|
140
|
+
end
|
|
141
|
+
|
|
131
142
|
def current_reference_id
|
|
132
143
|
self.class.new(Valkyrie::ID.new(string_id.gsub(version, "current")))
|
|
133
144
|
end
|
|
@@ -139,13 +150,13 @@ module Valkyrie::Storage
|
|
|
139
150
|
end
|
|
140
151
|
|
|
141
152
|
def file_path
|
|
142
|
-
@file_path ||= string_id.gsub(
|
|
153
|
+
@file_path ||= string_id.gsub(/^#{Regexp.escape(protocol)}/, '')
|
|
143
154
|
end
|
|
144
155
|
|
|
145
156
|
def version_files
|
|
146
157
|
root = Pathname.new(file_path)
|
|
147
158
|
root.parent.children.select { |file| file.basename.to_s.end_with?(filename) }.sort.reverse.map do |file|
|
|
148
|
-
VersionId.new(Valkyrie::ID.new("
|
|
159
|
+
VersionId.new(Valkyrie::ID.new("#{protocol}#{file}"))
|
|
149
160
|
end
|
|
150
161
|
end
|
|
151
162
|
|
data/lib/valkyrie/version.rb
CHANGED
data/lib/valkyrie.rb
CHANGED
|
@@ -101,6 +101,10 @@ module Valkyrie
|
|
|
101
101
|
self[:index_tsim_only_threshold].to_i
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
def auto_cast_iso8601_as_datetime
|
|
105
|
+
self[:auto_cast_iso8601_as_datetime]
|
|
106
|
+
end
|
|
107
|
+
|
|
104
108
|
# @api public
|
|
105
109
|
#
|
|
106
110
|
# The returned anonymous method (e.g. responds to #call) has a signature of
|
|
@@ -123,7 +127,8 @@ module Valkyrie
|
|
|
123
127
|
def defaults
|
|
124
128
|
{
|
|
125
129
|
resource_class_resolver: method(:default_resource_class_resolver),
|
|
126
|
-
index_tsim_only_threshold: 1000
|
|
130
|
+
index_tsim_only_threshold: 1000,
|
|
131
|
+
auto_cast_iso8601_as_datetime: true
|
|
127
132
|
}
|
|
128
133
|
end
|
|
129
134
|
|
data/valkyrie.gemspec
CHANGED
|
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.add_dependency 'dry-struct'
|
|
26
26
|
spec.add_dependency 'activemodel'
|
|
27
27
|
spec.add_dependency 'dry-types', '~> 1.0'
|
|
28
|
-
spec.add_dependency 'rdf', '~> 3.0', '>= 3.
|
|
28
|
+
spec.add_dependency 'rdf', '~> 3.0', '>= 3.3.2'
|
|
29
29
|
spec.add_dependency 'activesupport'
|
|
30
30
|
spec.add_dependency 'railties' # To use generators and engines
|
|
31
31
|
spec.add_dependency 'reform', '~> 2.2'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: valkyrie
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.5.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Trey Pendragon
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: dry-struct
|
|
@@ -61,7 +61,7 @@ dependencies:
|
|
|
61
61
|
version: '3.0'
|
|
62
62
|
- - ">="
|
|
63
63
|
- !ruby/object:Gem::Version
|
|
64
|
-
version: 3.
|
|
64
|
+
version: 3.3.2
|
|
65
65
|
type: :runtime
|
|
66
66
|
prerelease: false
|
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -71,7 +71,7 @@ dependencies:
|
|
|
71
71
|
version: '3.0'
|
|
72
72
|
- - ">="
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 3.
|
|
74
|
+
version: 3.3.2
|
|
75
75
|
- !ruby/object:Gem::Dependency
|
|
76
76
|
name: activesupport
|
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -452,9 +452,9 @@ files:
|
|
|
452
452
|
- db/migrate/20180212092225_create_updated_at_index.rb
|
|
453
453
|
- db/migrate/20180802220739_add_optimistic_locking_to_orm_resources.rb
|
|
454
454
|
- db/schema.rb
|
|
455
|
-
- gemfiles/activerecord_7_0.gemfile
|
|
456
455
|
- gemfiles/activerecord_7_1.gemfile
|
|
457
456
|
- gemfiles/activerecord_7_2.gemfile
|
|
457
|
+
- gemfiles/activerecord_8_0.gemfile
|
|
458
458
|
- gemfiles/faraday_1.gemfile
|
|
459
459
|
- lib/valkyrie.rb
|
|
460
460
|
- lib/valkyrie/adapter_container.rb
|