ocfl-tools 0.9.14 → 0.9.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/ocfl_tools/ocfl_errors.rb +59 -9
- data/lib/ocfl_tools/ocfl_inventory.rb +22 -9
- data/lib/ocfl_tools/ocfl_object.rb +17 -13
- data/lib/ocfl_tools/ocfl_validator.rb +75 -59
- data/lib/ocfl_tools/utils_file.rb +29 -12
- data/lib/ocfl_tools/utils_inventory.rb +1 -1
- data/ocfl-tools.gemspec +1 -1
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 817dfd488b3cb6f05522175c68cba6385c76007950dc03af3162579517a1a653
|
4
|
+
data.tar.gz: 402eb44681f151b91e1403ed5fa289c632f62480c1f3199f7e8f468ca6f4e567
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8a98f2d7bfab21babf2103857d970ad8d8e6e2cb1b6131b39f654568f161055ab4b4c567c2360d0d772bf709bb791d8125df0eff161de07eafec04d1c2fdffa
|
7
|
+
data.tar.gz: a8e692758b075775d124939bb533761b56f03b5b5d79a2ed3f916105f61b2361c1cd024f49886d554fd2578ac7e768c155c703ff85238b399a6b204047782476
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.9.
|
1
|
+
0.9.15
|
@@ -1,21 +1,71 @@
|
|
1
1
|
module OcflTools
|
2
2
|
module Errors
|
3
|
+
# See this for a nice model.
|
4
|
+
# https://github.com/ryanb/cancan/blob/master/lib/cancan/exceptions.rb
|
5
|
+
class ValidationError < StandardError
|
6
|
+
attr_accessor :details
|
7
|
+
attr_accessor :msg
|
8
|
+
def initialize(msg: "A validation error has occured.", details: {} )
|
9
|
+
@msg = msg
|
10
|
+
@details = details
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# For bad client requests
|
15
|
+
class ClientError < StandardError; end
|
16
|
+
|
17
|
+
class SyntaxError < StandardError
|
18
|
+
def initialize(msg="Generic syntax error.")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class UnableToLoadInventoryFile < StandardError
|
23
|
+
def initialize(msg="Requested inventory file failed to load. See downstream errors for details.")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
### Client errors (you asked for the wrong stuff)
|
28
|
+
class RequestedKeyNotFound < ClientError
|
29
|
+
# You ask for key 'foo', but you are dumb and key 'foo' is not in the spec.
|
30
|
+
def initialize(msg="Requested key not found in provided inventory.json.")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class RequestedFileNotFound < ClientError
|
35
|
+
def initialize(msg="Requested file does not exist.")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class RequestedDirectoryNotFound < ClientError
|
40
|
+
def initialize(msg="Requested directory does not exist.")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class FileMissingFromVersionState < ClientError
|
45
|
+
def initialize(msg="The requested file cannot be found in the provided version state block.")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class FileDigestMismatch < ClientError
|
50
|
+
def initialize(msg="The requested file already exists in inventory with different digest.")
|
51
|
+
end
|
52
|
+
end
|
3
53
|
|
4
|
-
|
5
|
-
|
6
|
-
|
54
|
+
# You asked for version -1, or version 44c.
|
55
|
+
class NonCompliantValue < ClientError
|
56
|
+
def initialize(msg="Value provided is outside of specification bounds.")
|
7
57
|
end
|
8
58
|
end
|
9
59
|
|
10
|
-
|
11
|
-
|
12
|
-
|
60
|
+
### Validation errors (stuff that MUST be true, per spec, is not)
|
61
|
+
class RequiredKeyNotFound < ValidationError
|
62
|
+
# key 'foo' is in the spec and should be in the inventory. Fail if not.
|
63
|
+
def initialize(msg="Required key not found in provided inventory.json.")
|
13
64
|
end
|
14
65
|
end
|
15
66
|
|
16
|
-
class
|
17
|
-
def initialize(msg="
|
18
|
-
super
|
67
|
+
class CannotEditPreviousVersion < ValidationError
|
68
|
+
def initialize(msg="Previous version state blocks are considered read-only.")
|
19
69
|
end
|
20
70
|
end
|
21
71
|
|
@@ -54,9 +54,13 @@ module OcflTools
|
|
54
54
|
begin
|
55
55
|
JSON.parse(File.read(file))
|
56
56
|
rescue JSON::ParserError
|
57
|
-
raise OcflTools::Errors::
|
58
|
-
rescue
|
59
|
-
raise "
|
57
|
+
raise OcflTools::Errors::ValidationError, details: { "E211" => ["#{file} is not valid JSON."] }
|
58
|
+
rescue Errno::ENOENT
|
59
|
+
# raise OcflTools::Errors::Error215, "expected inventory file #{file} not found!"
|
60
|
+
raise OcflTools::Errors::ValidationError, details: { "E215" => ["expected inventory file #{file} not found!"] }
|
61
|
+
# rescue Errno::EACCES Don't think we need to explicitly raise file permissions; let StdErr take it.
|
62
|
+
rescue StandardError => e
|
63
|
+
raise "An unknown error occured reading file #{file}: #{e}" # catch/encapsulate any FileIO issues
|
60
64
|
end
|
61
65
|
end
|
62
66
|
|
@@ -67,22 +71,31 @@ module OcflTools
|
|
67
71
|
import_hash = read_json(file)
|
68
72
|
|
69
73
|
# REQUIRED keys; raise exception if not found.
|
74
|
+
e216_errors = []
|
75
|
+
e217_errors = []
|
76
|
+
error_hash = {}
|
70
77
|
[ 'id', 'head', 'type', 'digestAlgorithm', 'manifest', 'versions' ].each do | key |
|
71
78
|
unless import_hash.key?(key)
|
72
|
-
|
79
|
+
e216_errors << "Required key #{key} not found in #{file}"
|
80
|
+
error_hash["E216"] = e216_errors # we'll keep updating this value as new errors are recorded.
|
73
81
|
end
|
74
|
-
if import_hash[key].empty?
|
75
|
-
|
82
|
+
if import_hash.key?(key) && import_hash[key].empty?
|
83
|
+
# If the key exists but it's empty, that's also a problem!
|
84
|
+
e217_errors << "Required key #{key} in #{file} must contain a value"
|
85
|
+
error_hash["E217"] = e217_errors
|
76
86
|
end
|
77
87
|
end
|
88
|
+
# Raise a problem if we have anything in error_hash.
|
89
|
+
if error_hash.size > 0
|
90
|
+
raise OcflTools::Errors::ValidationError, details: error_hash
|
91
|
+
end
|
92
|
+
|
93
|
+
# The actual values of these keys may be hot garbage, but it's not ocfl_inventory's place to check.
|
78
94
|
|
79
95
|
@id = import_hash['id']
|
80
96
|
@head = import_hash['head']
|
81
97
|
@type = import_hash['type']
|
82
98
|
@digestAlgorithm = import_hash['digestAlgorithm']
|
83
|
-
# if import_hash.key?('contentDirectory')
|
84
|
-
# @contentDirectory = import_hash['contentDirectory']
|
85
|
-
# end
|
86
99
|
@manifest = import_hash['manifest']
|
87
100
|
@versions = import_hash['versions']
|
88
101
|
# Optional keys - contentDirectory and fixity block.
|
@@ -52,7 +52,7 @@ module OcflTools
|
|
52
52
|
# @note will raise an exception if you attempt to query a non-existent version.
|
53
53
|
def set_version_message(version, message)
|
54
54
|
unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
|
55
|
-
raise "Version #{version} does not yet exist!"
|
55
|
+
raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
|
56
56
|
end
|
57
57
|
|
58
58
|
@versions[OcflTools::Utils.version_int_to_string(version)]['message'] = message
|
@@ -64,7 +64,7 @@ module OcflTools
|
|
64
64
|
# @note will raise an exception if you attempt to query a non-existent version.
|
65
65
|
def get_version_message(version)
|
66
66
|
unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
|
67
|
-
raise "Version #{version} does not yet exist!"
|
67
|
+
raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
|
68
68
|
end
|
69
69
|
|
70
70
|
@versions[OcflTools::Utils.version_int_to_string(version)]['message']
|
@@ -76,7 +76,7 @@ module OcflTools
|
|
76
76
|
# @note will raise an exception if you attempt to query a non-existent version.
|
77
77
|
def set_version_created(version, created)
|
78
78
|
unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
|
79
|
-
raise "Version #{version} does not yet exist!"
|
79
|
+
raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
|
80
80
|
end
|
81
81
|
|
82
82
|
@versions[OcflTools::Utils.version_int_to_string(version)]['created'] = created
|
@@ -88,7 +88,7 @@ module OcflTools
|
|
88
88
|
# @note will raise an exception if you attempt to query a non-existent version.
|
89
89
|
def get_version_created(version)
|
90
90
|
unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
|
91
|
-
raise "Version #{version} does not yet exist!"
|
91
|
+
raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
|
92
92
|
end
|
93
93
|
|
94
94
|
@versions[OcflTools::Utils.version_int_to_string(version)]['created']
|
@@ -100,7 +100,7 @@ module OcflTools
|
|
100
100
|
# @note will raise an exception if you attempt to query a nonexistent version.
|
101
101
|
def set_version_user(version, user)
|
102
102
|
unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
|
103
|
-
raise "Version #{version} does not yet exist!"
|
103
|
+
raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
|
104
104
|
end
|
105
105
|
|
106
106
|
@versions[OcflTools::Utils.version_int_to_string(version)]['user'] = user
|
@@ -112,7 +112,7 @@ module OcflTools
|
|
112
112
|
# @note will raise an exception if you attempt to query a nonexistent version.
|
113
113
|
def get_version_user(version)
|
114
114
|
unless @versions.key?(OcflTools::Utils.version_int_to_string(version))
|
115
|
-
raise "Version #{version} does not yet exist!"
|
115
|
+
raise OcflTools::Errors::RequestedKeyNotFound, "Version #{version} does not yet exist!"
|
116
116
|
end
|
117
117
|
|
118
118
|
@versions[OcflTools::Utils.version_int_to_string(version)]['user']
|
@@ -184,7 +184,7 @@ module OcflTools
|
|
184
184
|
my_state = get_state(version)
|
185
185
|
|
186
186
|
unless version == version_id_list.max
|
187
|
-
raise "Can't edit prior versions! Only version #{version_id_list.max} can be modified now."
|
187
|
+
raise OcflTools::Errors::CannotEditPreviousVersion, "Can't edit prior versions! Only version #{version_id_list.max} can be modified now."
|
188
188
|
end
|
189
189
|
|
190
190
|
# if the key is not in the manifest, assume that we meant to add it.
|
@@ -205,7 +205,7 @@ module OcflTools
|
|
205
205
|
# If so; fail. We don't do implicit / soft adds. You want that, be explict: do an update_file instead.
|
206
206
|
existing_files = get_files(version)
|
207
207
|
if existing_files.key?(file)
|
208
|
-
raise
|
208
|
+
raise OcflTools::Errors::FileDigestMismatch, "#{file} already exists with different digest in version #{version}. Consider update instead."
|
209
209
|
end
|
210
210
|
|
211
211
|
# if it's not in State already, just add it.
|
@@ -256,7 +256,7 @@ module OcflTools
|
|
256
256
|
# Does Digest exist in @manifest? Fail if not.
|
257
257
|
# Doe fixityAlgorithm exist as a key in @fixity? Add if not.
|
258
258
|
unless @manifest.key?(digest) == true
|
259
|
-
raise "Unable to find digest #{digest} in manifest!"
|
259
|
+
raise OcflTools::Errors::RequestedKeyNotFound, "Unable to find digest #{digest} in manifest!"
|
260
260
|
end
|
261
261
|
|
262
262
|
filepaths = @manifest[digest]
|
@@ -289,7 +289,7 @@ module OcflTools
|
|
289
289
|
my_state = get_state(version) # Creates version & copies state from prior version if doesn't exist.
|
290
290
|
|
291
291
|
unless version == version_id_list.max
|
292
|
-
raise "Can't edit prior versions! Only version #{
|
292
|
+
raise OcflTools::Errors::CannotEditPreviousVersion, "Can't edit prior versions! Only version #{version_id_list.max} can be modified now."
|
293
293
|
end
|
294
294
|
|
295
295
|
my_digest = get_digest(file, version)
|
@@ -364,7 +364,7 @@ module OcflTools
|
|
364
364
|
end
|
365
365
|
# Now see if the requested file is actually here.
|
366
366
|
unless my_files.key?(file)
|
367
|
-
raise "Get_digest can't find requested file in
|
367
|
+
raise OcflTools::Errors::FileMissingFromVersionState, "Get_digest can't find requested file #{file} in version #{version}."
|
368
368
|
end
|
369
369
|
|
370
370
|
my_files[file]
|
@@ -377,7 +377,7 @@ module OcflTools
|
|
377
377
|
# @note If a (n-1) version exists in the object, and the requested version does not yet exist, this method will copy that version's state block into the new version.
|
378
378
|
def get_version(version)
|
379
379
|
unless version > 0
|
380
|
-
raise "
|
380
|
+
raise OcflTools::Errors::NonCompliantValue, "Requested value '#{version}' for object version does not comply with specification."
|
381
381
|
end
|
382
382
|
if @versions.key?(OcflTools::Utils.version_int_to_string(version))
|
383
383
|
@versions[OcflTools::Utils.version_int_to_string(version)]
|
@@ -414,11 +414,15 @@ module OcflTools
|
|
414
414
|
# @param [Hash] hash use this hash for the content of the new OCFL version block.
|
415
415
|
def set_version(version, hash)
|
416
416
|
# SAN Check to make sure passed Hash has all expected keys.
|
417
|
+
e216_errors = []
|
417
418
|
%w[created message user state].each do |key|
|
418
419
|
if hash.key?(key) == false
|
419
|
-
|
420
|
+
e216_errors << "version #{version} hash block is missing required #{key} key."
|
420
421
|
end
|
421
422
|
end
|
423
|
+
if e216_errors.size > 0
|
424
|
+
raise OcflTools::Errors::ValidationError, details: { "E216" => e216_errors }
|
425
|
+
end
|
422
426
|
@versions[OcflTools::Utils.version_int_to_string(version)] = hash
|
423
427
|
end
|
424
428
|
end
|
@@ -63,7 +63,14 @@ module OcflTools
|
|
63
63
|
# @return {OcflTools::OcflResults} of event results
|
64
64
|
def verify_fixity(inventory_file: "#{@ocfl_object_root}/inventory.json", digest: 'md5')
|
65
65
|
# Gets the appropriate fixity block, calls compare_hash_checksums
|
66
|
-
|
66
|
+
|
67
|
+
begin
|
68
|
+
@inventory = load_inventory(inventory_file)
|
69
|
+
rescue OcflTools::Errors::ValidationError
|
70
|
+
@my_results.error('E210', 'verify_fixity', "Unable to process inventory file #{inventory_file}.")
|
71
|
+
return @my_results
|
72
|
+
end
|
73
|
+
|
67
74
|
# Since fixity blocks are not required to be complete, we just validate what's there.
|
68
75
|
# So get the fixity block, flip it, expand it, checksum it against the same files on disk.
|
69
76
|
|
@@ -121,9 +128,9 @@ module OcflTools
|
|
121
128
|
return @my_results
|
122
129
|
end
|
123
130
|
|
124
|
-
|
125
|
-
@inventory
|
126
|
-
|
131
|
+
begin
|
132
|
+
@inventory = load_inventory(inventory_file)
|
133
|
+
rescue OcflTools::Errors::ValidationError
|
127
134
|
@my_results.error('E210', 'verify_fixity', "Unable to process inventory file #{inventory_file}.")
|
128
135
|
return @my_results
|
129
136
|
end
|
@@ -260,9 +267,9 @@ module OcflTools
|
|
260
267
|
return @my_results
|
261
268
|
end
|
262
269
|
|
263
|
-
|
264
|
-
@inventory =
|
265
|
-
|
270
|
+
begin
|
271
|
+
@inventory = load_inventory(inventory_file)
|
272
|
+
rescue OcflTools::Errors::ValidationError
|
266
273
|
@my_results.error('E210', 'verify_checksums', "Unable to process inventory file #{inventory_file}.")
|
267
274
|
return @my_results
|
268
275
|
end
|
@@ -312,7 +319,8 @@ module OcflTools
|
|
312
319
|
# 1. use get_version_format to determine the format used for version directories.
|
313
320
|
# If we can't deduce it by inspection of the object_root, ERROR and try and process using site-wide defaults.
|
314
321
|
if get_version_format == false
|
315
|
-
error
|
322
|
+
@my_results.error('E111', 'verify_structure', 'OCFL unable to determine version format by inspection of directories.')
|
323
|
+
@error = true
|
316
324
|
end
|
317
325
|
|
318
326
|
object_root_dirs = []
|
@@ -339,12 +347,13 @@ module OcflTools
|
|
339
347
|
# 2b. What's the highest version we should find here?
|
340
348
|
# 2c. What should our contentDirectory value be?
|
341
349
|
if File.exist? "#{@ocfl_object_root}/inventory.json"
|
342
|
-
|
350
|
+
begin
|
351
|
+
@inventory = load_inventory("#{@ocfl_object_root}/inventory.json")
|
343
352
|
json_digest = OcflTools::Utils::Inventory.get_digestAlgorithm("#{@ocfl_object_root}/inventory.json")
|
344
353
|
contentDirectory = OcflTools::Utils::Inventory.get_contentDirectory("#{@ocfl_object_root}/inventory.json")
|
345
354
|
expect_head = OcflTools::Utils::Inventory.get_value("#{@ocfl_object_root}/inventory.json", 'head')
|
346
355
|
file_checks << "inventory.json.#{json_digest}"
|
347
|
-
|
356
|
+
rescue OcflTools::Errors::ValidationError
|
348
357
|
# We couldn't load up the inventory; use site defaults.
|
349
358
|
contentDirectory = OcflTools.config.content_directory
|
350
359
|
json_digest = OcflTools.config.digest_algorithm
|
@@ -444,7 +453,17 @@ module OcflTools
|
|
444
453
|
object_root_dirs.delete('extensions')
|
445
454
|
end
|
446
455
|
|
447
|
-
|
456
|
+
begin
|
457
|
+
version_directories = OcflTools::Utils::Files.get_version_directories(@ocfl_object_root)
|
458
|
+
rescue OcflTools::Errors::ValidationError => e
|
459
|
+
e.details.each do | code, messages |
|
460
|
+
messages.each do | msg |
|
461
|
+
@my_results.error(code, 'verify_structure', msg)
|
462
|
+
end
|
463
|
+
end
|
464
|
+
# If we actually throw a validation error, we can't proceed: no version directories found!
|
465
|
+
return @my_results
|
466
|
+
end
|
448
467
|
|
449
468
|
remaining_dirs = object_root_dirs - version_directories
|
450
469
|
|
@@ -466,7 +485,7 @@ module OcflTools
|
|
466
485
|
# just that they're valid version dir names, sorted in ascending order, and they exist.
|
467
486
|
if version_directories.include? expected_directory
|
468
487
|
# Could verbose log this here.
|
469
|
-
# @my_results.
|
488
|
+
# @my_results.info('I200', 'verify_sructure', "Expected version directory #{expected_directory} found.")
|
470
489
|
else
|
471
490
|
@my_results.error('E013', 'verify_structure', "Expected version directory #{expected_directory} missing from directory list #{version_directories} ")
|
472
491
|
error = true
|
@@ -499,7 +518,8 @@ module OcflTools
|
|
499
518
|
# 9. Warn if inventory.json and sidecar are not present in version directory.
|
500
519
|
file_checks = []
|
501
520
|
if File.exist? "#{@ocfl_object_root}/#{ver}/inventory.json"
|
502
|
-
|
521
|
+
begin
|
522
|
+
@inventory = load_inventory("#{@ocfl_object_root}/#{ver}/inventory.json")
|
503
523
|
json_digest = OcflTools::Utils::Inventory.get_digestAlgorithm("#{@ocfl_object_root}/#{ver}/inventory.json")
|
504
524
|
file_checks << 'inventory.json'
|
505
525
|
file_checks << "inventory.json.#{json_digest}"
|
@@ -508,12 +528,14 @@ module OcflTools
|
|
508
528
|
@my_results.error('E111', 'verify_structure', "contentDirectory value #{versionContentDirectory} in version #{ver} does not match expected contentDirectory value #{contentDirectory}.")
|
509
529
|
error = true
|
510
530
|
end
|
511
|
-
|
531
|
+
rescue OcflTools::Errors::ValidationError
|
532
|
+
# We couldn't load up the inventory; use site defaults.
|
533
|
+
# We should also record the error in @my_results?
|
512
534
|
json_digest = OcflTools.config.digest_algorithm
|
513
535
|
file_checks << 'inventory.json'
|
514
536
|
file_checks << "inventory.json.#{json_digest}"
|
537
|
+
error = true
|
515
538
|
end
|
516
|
-
|
517
539
|
else
|
518
540
|
file_checks << 'inventory.json' # We look for it, even though we know we won't find it, so we can log the omission.
|
519
541
|
file_checks << 'inventory.json.sha512' # We look for it, even though we know we won't find it, so we can log the omission.
|
@@ -557,12 +579,6 @@ module OcflTools
|
|
557
579
|
# 11. WARN if a contentDirectory exists, but is empty.
|
558
580
|
if version_dirs.include? contentDirectory
|
559
581
|
version_dirs.delete(contentDirectory)
|
560
|
-
# if Dir.empty?(contentDirectory)
|
561
|
-
# @my_results.warn('W102', 'verify_structure', "OCFL 3.3.1 version #{ver} contentDirectory should not be empty.")
|
562
|
-
# end
|
563
|
-
# else
|
564
|
-
# # Informational message that contentDir does not exist. Not necssarily a problem!
|
565
|
-
# @my_results.info('I101', 'verify_structure', "OCFL 3.3.1 version #{ver} does not contain a contentDirectory.")
|
566
582
|
end
|
567
583
|
|
568
584
|
# 12. Warn if any directories other than the expected 'content' directory are found in the version directory.
|
@@ -587,11 +603,8 @@ module OcflTools
|
|
587
603
|
# @return {OcflTools::OcflResults} of verify events
|
588
604
|
def verify_directory(version)
|
589
605
|
# start by getting version format and directories.
|
590
|
-
|
591
|
-
@version_format = OcflTools::Utils::Files.get_version_format(@ocfl_object_root)
|
592
|
-
end
|
606
|
+
get_version_format # sets @version_format, one way or another.
|
593
607
|
|
594
|
-
# result = OcflTools.config.version_format % version.to_i
|
595
608
|
version_name = @version_format % version.to_i
|
596
609
|
# Make sure this directory actually exists.
|
597
610
|
unless File.directory?("#{@ocfl_object_root}/#{version_name}")
|
@@ -653,44 +666,38 @@ module OcflTools
|
|
653
666
|
def verify_inventory(inventory_file = "#{@ocfl_object_root}/inventory.json")
|
654
667
|
# Load up the object with ocfl_inventory, push it through ocfl_verify.
|
655
668
|
@my_results ||= OcflTools::OcflResults.new
|
656
|
-
#
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
end
|
667
|
-
else
|
668
|
-
@my_results.error('E215', 'verify_inventory', "Expected inventory file #{inventory_file} not found.")
|
669
|
+
# If inventory_file does not exist, load_inventory will throw and log an E215.
|
670
|
+
begin
|
671
|
+
@inventory = load_inventory(inventory_file)
|
672
|
+
@inventory = OcflTools::OcflInventory.new.from_file(inventory_file)
|
673
|
+
@verify = OcflTools::OcflVerify.new(@inventory)
|
674
|
+
@verify.check_all # creates & returns @results object from OcflVerify
|
675
|
+
# This could be OcflTools::Errors::ValidationError now.
|
676
|
+
rescue OcflTools::Errors::ValidationError
|
677
|
+
# I don't think we need to throw this E210 any more.
|
678
|
+
@my_results.error('E210', 'verify_inventory', "Unable to process inventory file #{inventory_file}.")
|
669
679
|
return @my_results
|
670
680
|
end
|
671
681
|
end
|
672
682
|
|
673
683
|
private
|
674
684
|
# load up an inventory file and handle any errors.
|
675
|
-
# Returns
|
685
|
+
# Returns an inventory file is syntatically correct; false if otherwise.
|
676
686
|
def load_inventory(inventory_file)
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
@my_results.error('E217', 'load_inventory', "#{e} in #{inventory_file}")
|
692
|
-
return false
|
693
|
-
end
|
687
|
+
@my_results ||= OcflTools::OcflResults.new
|
688
|
+
OcflTools::OcflInventory.new.from_file(inventory_file)
|
689
|
+
# The generic 'something went wrong but I don't know what'; not sure if we should keep this.
|
690
|
+
rescue RuntimeError => e
|
691
|
+
@my_results.error('E210', 'load_inventory', "#{e}")
|
692
|
+
raise
|
693
|
+
rescue OcflTools::Errors::ValidationError => e
|
694
|
+
e.details.each do | code, messages |
|
695
|
+
# code is a string, messages is an array.
|
696
|
+
messages.each do | msg |
|
697
|
+
@my_results.error(code, 'load_inventory', msg)
|
698
|
+
end
|
699
|
+
end
|
700
|
+
raise # re-raise the error.
|
694
701
|
end
|
695
702
|
|
696
703
|
# Compares the state blocks for all versions across all inventories in the object,
|
@@ -784,13 +791,22 @@ module OcflTools
|
|
784
791
|
|
785
792
|
def get_version_format
|
786
793
|
begin
|
794
|
+
@my_results ||= OcflTools::OcflResults.new
|
787
795
|
@version_format ||= OcflTools::Utils::Files.get_version_format(@ocfl_object_root)
|
788
796
|
@my_results.ok('O111', 'version_format', 'OCFL conforming first version directory found.')
|
789
|
-
return
|
790
|
-
rescue
|
797
|
+
return @version_format
|
798
|
+
rescue OcflTools::Errors::ValidationError => e
|
799
|
+
# OcflTools::Utils::Files.get_version_format doesn't set errors, so capture them here.
|
800
|
+
e.details.each do | code, messages |
|
801
|
+
# code is a string, messages is an array.
|
802
|
+
messages.each do | msg |
|
803
|
+
@my_results.error(code, 'load_inventory', msg)
|
804
|
+
end
|
805
|
+
end
|
806
|
+
# Add on another error explaining how we got here.
|
791
807
|
@my_results.error('E111', 'version_format', 'OCFL unable to determine version format by inspection of directories.')
|
792
|
-
@version_format = OcflTools.config.version_format
|
793
808
|
@my_results.warn('W111', 'version_format', "Attempting to process using default value: #{OcflTools.config.version_format}")
|
809
|
+
@version_format = OcflTools.config.version_format
|
794
810
|
return false
|
795
811
|
end
|
796
812
|
end
|
@@ -52,7 +52,8 @@ module OcflTools
|
|
52
52
|
# Given an object root directory, deduce and return the version directories by inspecting disk.
|
53
53
|
def self.get_version_directories(object_root_dir)
|
54
54
|
unless Dir.exist?(object_root_dir) == true
|
55
|
-
|
55
|
+
# This is a client error.
|
56
|
+
raise OcflTools::Errors::RequestedDirectoryNotFound, "#{object_root_dir} does not exist!"
|
56
57
|
end
|
57
58
|
|
58
59
|
object_root_dirs = []
|
@@ -62,7 +63,8 @@ module OcflTools
|
|
62
63
|
object_root_dirs << file if File.directory? file
|
63
64
|
end
|
64
65
|
if object_root_dirs.empty?
|
65
|
-
|
66
|
+
# This is a validation error.
|
67
|
+
raise OcflTools::Errors::ValidationError, details: { "E100" => ["#{object_root_dir} is empty!"] }
|
66
68
|
end
|
67
69
|
|
68
70
|
# Needs to call get version_format method here.
|
@@ -71,15 +73,18 @@ module OcflTools
|
|
71
73
|
version_directories << i
|
72
74
|
end
|
73
75
|
end
|
74
|
-
|
75
|
-
|
76
|
+
# This is a validation error; we expect to find matching version directories.
|
77
|
+
if version_directories.empty?
|
78
|
+
raise OcflTools::Errors::ValidationError, details: { "E013" => ["#{object_root_dir} must contain at least one identifiable version directory."] }
|
79
|
+
end
|
76
80
|
version_directories.sort! # sort it, to be nice.
|
77
81
|
end
|
78
82
|
|
79
83
|
# Given an object_root_directory, deduce the format used to describe version directories.
|
80
84
|
def self.get_version_format(object_root_dir)
|
81
85
|
unless Dir.exist?(object_root_dir) == true
|
82
|
-
|
86
|
+
# This is a client error.
|
87
|
+
raise OcflTools::Errors::RequestedDirectoryNotFound, "#{object_root_dir} does not exist!"
|
83
88
|
end
|
84
89
|
|
85
90
|
# Get all directories starting with 'v', sort them.
|
@@ -98,14 +103,16 @@ module OcflTools
|
|
98
103
|
first_version.slice!(0, 1) # cut the leading 'v' from the string.
|
99
104
|
if first_version.length == 1 # A length of 1 for the first version implies 'v1'
|
100
105
|
unless first_version.to_i == 1
|
101
|
-
|
106
|
+
# This is a validation error; there must be a v1 directory.
|
107
|
+
# E015 "OCFL 3.5.3 Expected version sequence not found. Expected version #{count}, found version #{my_versions[count]}."
|
108
|
+
raise OcflTools::Errors::ValidationError, details: { "E015" => ["Expected version 1 not found. Found version #{first_version.to_i} instead."] }
|
102
109
|
end
|
103
110
|
|
104
111
|
version_format = 'v%d'
|
105
112
|
else
|
106
113
|
# Make sure this is Integer 1.
|
107
114
|
unless first_version.to_i == 1
|
108
|
-
raise
|
115
|
+
raise OcflTools::Errors::ValidationError, details: { "E015" => ["Expected version 1 not found. Found version #{first_version.to_i} instead."] }
|
109
116
|
end
|
110
117
|
|
111
118
|
version_format = "v%0#{first_version.length}d"
|
@@ -170,24 +177,34 @@ module OcflTools
|
|
170
177
|
# g_v_d returns a sorted array already. Reverse it, so we start with highest version.
|
171
178
|
my_versions = OcflTools::Utils::Files.get_version_directories(object_root_dir).reverse
|
172
179
|
case
|
180
|
+
# Return the inventory file in the highest version dir, if it exists.
|
173
181
|
when File.exist?("#{object_root_dir}/#{my_versions[0]}/inventory.json")
|
174
182
|
return "#{object_root_dir}/#{my_versions[0]}/inventory.json"
|
183
|
+
# Otherwise, return the inventory file in the root, if it exists.
|
175
184
|
when File.exist?("#{object_root_dir}/inventory.json")
|
176
185
|
return "#{object_root_dir}/inventory.json"
|
177
186
|
else
|
178
|
-
#
|
187
|
+
# We don't have a highest-version inventory, and we don't have a root inventory.
|
188
|
+
# This is a problem! But there might be an inventory file in a non-highest-version dir.
|
189
|
+
# Quit out here if there was only 1 version directory (We've already checked this)
|
179
190
|
unless my_versions.size > 1
|
180
|
-
|
191
|
+
# This is a validation error; no inventory files found.
|
192
|
+
raise OcflTools::Errors::ValidationError, details: { "E215" => ["OCFL 3.1 Expected inventory file not found in #{object_root_dir} or discovered version directories."] }
|
181
193
|
end
|
182
194
|
|
183
|
-
my_versions.delete_at(0) # drop the first element.
|
195
|
+
my_versions.delete_at(0) # drop the first element (we've already checked it).
|
184
196
|
my_versions.each do |v|
|
197
|
+
# Return the highest version inventory file we find.
|
198
|
+
# Note, this is technically a non-compliant object root, but we
|
199
|
+
# want to return a result if we have one - we might be attempting object
|
200
|
+
# recovery and something is better than nothing.
|
185
201
|
if File.exist?("#{object_root_dir}/#{v}/inventory.json")
|
186
202
|
return "#{object_root_dir}/#{v}/inventory.json"
|
187
203
|
end
|
188
204
|
end
|
189
|
-
# If we get here, no inventory file found
|
190
|
-
|
205
|
+
# If we get here, no inventory file found in any version dirs or the object root.
|
206
|
+
# This is a validation error.
|
207
|
+
raise OcflTools::Errors::ValidationError, details: { "E215" => ["OCFL 3.1 Expected inventory file not found in #{object_root_dir} or discovered version directories."] }
|
191
208
|
end
|
192
209
|
end
|
193
210
|
end
|
@@ -12,7 +12,7 @@ module OcflTools
|
|
12
12
|
# @return [String or nil] the value of the requested key, or nil if not found.
|
13
13
|
def self.get_value(inventory_file, key)
|
14
14
|
unless %w[contentDirectory digestAlgorithm head type id].include?(key)
|
15
|
-
raise "#{key} is not a valid OCFL inventory header key"
|
15
|
+
raise OcflTools::Errors::RequestedKeyNotFound, "#{key} is not a valid OCFL inventory header key"
|
16
16
|
end
|
17
17
|
|
18
18
|
inventory = OcflTools::OcflInventory.new.from_file(inventory_file)
|
data/ocfl-tools.gemspec
CHANGED
@@ -8,10 +8,10 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.homepage = 'https://github.com/sul-dlss-labs/OCFL-Tools'
|
9
9
|
gem.licenses = ['Apache-2.0']
|
10
10
|
|
11
|
+
# Don't include fixtures in the gem.
|
11
12
|
gem.files = `git ls-files -z`.split("\x0").reject do |f|
|
12
13
|
f.match(%r{^(test|spec|features)/})
|
13
14
|
end
|
14
|
-
# gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
|
15
15
|
|
16
16
|
gem.add_runtime_dependency 'anyway_config', '~> 1.0'
|
17
17
|
gem.add_runtime_dependency 'fileutils', '~> 1.3'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ocfl-tools
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian M. Morley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: anyway_config
|
@@ -42,22 +42,22 @@ dependencies:
|
|
42
42
|
name: json
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 2.2.0
|
48
45
|
- - "~>"
|
49
46
|
- !ruby/object:Gem::Version
|
50
47
|
version: '2.2'
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.2.0
|
51
51
|
type: :runtime
|
52
52
|
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
54
54
|
requirements:
|
55
|
-
- - ">="
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: 2.2.0
|
58
55
|
- - "~>"
|
59
56
|
- !ruby/object:Gem::Version
|
60
57
|
version: '2.2'
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 2.2.0
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: pry-byebug
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -183,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
183
|
- !ruby/object:Gem::Version
|
184
184
|
version: '0'
|
185
185
|
requirements: []
|
186
|
-
rubygems_version: 3.
|
186
|
+
rubygems_version: 3.1.2
|
187
187
|
signing_key:
|
188
188
|
specification_version: 4
|
189
189
|
summary: Tools to create, manipulate and write Oxford Common File Layout (OCFL) preservation
|