moab-versioning 4.2.1 → 4.4.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 +5 -5
- data/lib/moab.rb +3 -2
- data/lib/moab/bagger.rb +25 -20
- data/lib/moab/config.rb +39 -8
- data/lib/moab/deposit_bag_validator.rb +22 -17
- data/lib/moab/exceptions.rb +10 -8
- data/lib/moab/file_group.rb +23 -22
- data/lib/moab/file_group_difference.rb +35 -35
- data/lib/moab/file_group_difference_subset.rb +5 -5
- data/lib/moab/file_instance.rb +4 -3
- data/lib/moab/file_instance_difference.rb +5 -5
- data/lib/moab/file_inventory.rb +25 -31
- data/lib/moab/file_inventory_difference.rb +11 -11
- data/lib/moab/file_manifestation.rb +8 -7
- data/lib/moab/file_signature.rb +35 -41
- data/lib/moab/signature_catalog.rb +19 -21
- data/lib/moab/signature_catalog_entry.rb +5 -5
- data/lib/moab/stanford.rb +2 -0
- data/lib/moab/storage_object.rb +23 -24
- data/lib/moab/storage_object_validator.rb +44 -16
- data/lib/moab/storage_object_version.rb +45 -40
- data/lib/moab/storage_repository.rb +59 -24
- data/lib/moab/storage_services.rb +17 -10
- data/lib/moab/utc_time.rb +3 -3
- data/lib/moab/verification_result.rb +3 -4
- data/lib/moab/version_metadata.rb +4 -4
- data/lib/moab/version_metadata_entry.rb +6 -6
- data/lib/moab/version_metadata_event.rb +1 -1
- data/lib/serializer.rb +2 -0
- data/lib/serializer/manifest.rb +9 -7
- data/lib/serializer/serializable.rb +41 -35
- data/lib/stanford/active_fedora_object.rb +1 -1
- data/lib/stanford/content_inventory.rb +40 -34
- data/lib/stanford/dor_metadata.rb +4 -3
- data/lib/stanford/moab_storage_directory.rb +4 -2
- data/lib/stanford/storage_object_validator.rb +1 -1
- data/lib/stanford/storage_repository.rb +9 -6
- data/lib/stanford/storage_services.rb +6 -6
- metadata +23 -38
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Moab
|
4
4
|
# An interface class to support access to SDR storage via a RESTful server
|
@@ -43,6 +43,13 @@ module Moab
|
|
43
43
|
@@repository.find_storage_object(object_id, include_deposit)
|
44
44
|
end
|
45
45
|
|
46
|
+
# @param object_id [String] The identifier of the digital object
|
47
|
+
# @param [Object] include_deposit
|
48
|
+
# @return [Array<StorageObject>] Representations of a digitial object's storage directories, or an empty array if none found.
|
49
|
+
def self.search_storage_objects(object_id, include_deposit = false)
|
50
|
+
@@repository.search_storage_objects(object_id, include_deposit)
|
51
|
+
end
|
52
|
+
|
46
53
|
# @param object_id [String] The identifier of the digital object whose size is desired
|
47
54
|
# @param include_deposit [Boolean] specifies whether to look in deposit areas for objects in process of initial ingest
|
48
55
|
# @return [Integer] the size occupied on disk by the storage object, in bytes. this is the entire moab (all versions).
|
@@ -79,7 +86,7 @@ module Moab
|
|
79
86
|
# @param [String] object_id The digital object identifier of the object
|
80
87
|
# @return [Pathname] Pathname object containing the full path for the specified file
|
81
88
|
def self.version_metadata(object_id)
|
82
|
-
|
89
|
+
retrieve_file('metadata', 'versionMetadata.xml', object_id)
|
83
90
|
end
|
84
91
|
|
85
92
|
# @param [String] object_id The digital object identifier of the object
|
@@ -87,11 +94,11 @@ module Moab
|
|
87
94
|
# @return [FileInventory] the file inventory for the specified object version
|
88
95
|
def self.retrieve_file_group(file_category, object_id, version_id = nil)
|
89
96
|
storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
|
90
|
-
if file_category =~ /manifest/
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
97
|
+
inventory_type = if file_category =~ /manifest/
|
98
|
+
file_category = 'manifests'
|
99
|
+
else
|
100
|
+
'version'
|
101
|
+
end
|
95
102
|
inventory = storage_object_version.file_inventory(inventory_type)
|
96
103
|
inventory.group(file_category)
|
97
104
|
end
|
@@ -103,7 +110,7 @@ module Moab
|
|
103
110
|
# @return [Pathname] Pathname object containing the full path for the specified file
|
104
111
|
def self.retrieve_file(file_category, file_id, object_id, version_id = nil)
|
105
112
|
storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
|
106
|
-
|
113
|
+
storage_object_version.find_filepath(file_category, file_id)
|
107
114
|
end
|
108
115
|
|
109
116
|
# @param [String] file_category The category of file ('content', 'metdata', or 'manifest')
|
@@ -113,7 +120,7 @@ module Moab
|
|
113
120
|
# @return [Pathname] Pathname object containing the full path for the specified file
|
114
121
|
def self.retrieve_file_using_signature(file_category, file_signature, object_id, version_id = nil)
|
115
122
|
storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
|
116
|
-
|
123
|
+
storage_object_version.find_filepath_using_signature(file_category, file_signature)
|
117
124
|
end
|
118
125
|
|
119
126
|
# @param [String] file_category The category of file ('content', 'metdata', or 'manifest')
|
@@ -123,7 +130,7 @@ module Moab
|
|
123
130
|
# @return [FileSignature] The signature of the file
|
124
131
|
def self.retrieve_file_signature(file_category, file_id, object_id, version_id = nil)
|
125
132
|
storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
|
126
|
-
|
133
|
+
storage_object_version.find_signature(file_category, file_id)
|
127
134
|
end
|
128
135
|
|
129
136
|
# @param [String] object_id The digital object identifier of the object
|
data/lib/moab/utc_time.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Moab
|
4
4
|
# Timestamp conversion methods.
|
@@ -16,7 +16,7 @@ module Moab
|
|
16
16
|
when Time
|
17
17
|
datetime
|
18
18
|
else
|
19
|
-
raise "unknown time format #{datetime.inspect}"
|
19
|
+
raise(MoabRuntimeError, "unknown time format #{datetime.inspect}")
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -31,7 +31,7 @@ module Moab
|
|
31
31
|
when Time
|
32
32
|
datetime.utc.iso8601
|
33
33
|
else
|
34
|
-
raise "unknown time format #{datetime.inspect}"
|
34
|
+
raise(MoabRuntimeError, "unknown time format #{datetime.inspect}")
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Moab
|
4
4
|
# A recursive "Tree" type object for verifications
|
@@ -54,10 +54,9 @@ module Moab
|
|
54
54
|
# @return [Hash] The verification result serialized to a hash
|
55
55
|
def to_hash(verbose = false, level = 0)
|
56
56
|
hash = { 'verified' => verified }
|
57
|
-
if verbose || !verified
|
58
|
-
hash['details'] = details || subentities_to_hash(verbose, level)
|
59
|
-
end
|
57
|
+
hash['details'] = details || subentities_to_hash(verbose, level) if verbose || !verified
|
60
58
|
return hash if level > 0
|
59
|
+
|
61
60
|
{ entity => hash }
|
62
61
|
end
|
63
62
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Moab
|
4
4
|
# The descriptive information about a digital object's collection of versions
|
@@ -19,16 +19,16 @@ module Moab
|
|
19
19
|
|
20
20
|
# (see Serializable#initialize)
|
21
21
|
def initialize(opts = {})
|
22
|
-
@versions =
|
22
|
+
@versions = []
|
23
23
|
super(opts)
|
24
24
|
end
|
25
25
|
|
26
26
|
# @attribute
|
27
27
|
# @return [String] The digital object identifier
|
28
|
-
attribute :digital_object_id, String, :
|
28
|
+
attribute :digital_object_id, String, tag: 'objectId'
|
29
29
|
|
30
30
|
# @attribute
|
31
31
|
# @return [Array<VersionMetadataEntry>] An array of version metadata entries, one per version
|
32
|
-
has_many :versions, VersionMetadataEntry, :
|
32
|
+
has_many :versions, VersionMetadataEntry, tag: 'version'
|
33
33
|
end
|
34
34
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Moab
|
4
4
|
# The descriptive attributes of a digital object version.
|
@@ -18,13 +18,13 @@ module Moab
|
|
18
18
|
|
19
19
|
# (see Serializable#initialize)
|
20
20
|
def initialize(opts = {})
|
21
|
-
@events =
|
21
|
+
@events = []
|
22
22
|
super(opts)
|
23
23
|
end
|
24
24
|
|
25
25
|
# @attribute
|
26
26
|
# @return [Integer] The object version number (A sequential integer)
|
27
|
-
attribute :version_id, Integer, :
|
27
|
+
attribute :version_id, Integer, tag: 'versionId', key: true, on_save: proc { |n| n.to_s }
|
28
28
|
|
29
29
|
# @attribute
|
30
30
|
# @return [String] "an external version label that increments the most significant digit for major revisions,
|
@@ -46,14 +46,14 @@ module Moab
|
|
46
46
|
|
47
47
|
# @attribute
|
48
48
|
# @return [FileGroupDifference] Summary of content file differences since previous version
|
49
|
-
element :content_changes, FileGroupDifference, :
|
49
|
+
element :content_changes, FileGroupDifference, tag: 'fileGroupDifference'
|
50
50
|
|
51
51
|
# @attribute
|
52
52
|
# @return [FileGroupDifference] Summary of metadata file differences since previous version
|
53
|
-
element :metadata_changes, FileGroupDifference, :
|
53
|
+
element :metadata_changes, FileGroupDifference, tag: 'fileGroupDifference'
|
54
54
|
|
55
55
|
# @attribute
|
56
56
|
# @return [Array<VersionMetadataEvent>] Array of events with timestamps that track lifecycle stages
|
57
|
-
has_many :events, VersionMetadataEvent, :
|
57
|
+
has_many :events, VersionMetadataEvent, tag: 'event'
|
58
58
|
end
|
59
59
|
end
|
data/lib/serializer.rb
CHANGED
data/lib/serializer/manifest.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Serializer
|
2
4
|
# Subclass of {Serializable} that adds methods for marshalling/unmarshalling data
|
3
5
|
# to a persistent XML file format.
|
@@ -19,8 +21,8 @@ module Serializer
|
|
19
21
|
if filename
|
20
22
|
filename
|
21
23
|
else
|
22
|
-
cname =
|
23
|
-
cname[0, 1].downcase
|
24
|
+
cname = name.split(/::/).last
|
25
|
+
"#{cname[0, 1].downcase}#{cname[1..]}.xml"
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
@@ -29,7 +31,7 @@ module Serializer
|
|
29
31
|
# @param filename [String] Optional filename if one wishes to override the default filename
|
30
32
|
# @return [Pathname] The location of the xml file
|
31
33
|
def self.xml_pathname(parent_dir, filename = nil)
|
32
|
-
Pathname.new(parent_dir).join(
|
34
|
+
Pathname.new(parent_dir).join(xml_filename(filename))
|
33
35
|
end
|
34
36
|
|
35
37
|
# @api external
|
@@ -37,7 +39,7 @@ module Serializer
|
|
37
39
|
# @param filename [String] Optional filename if one wishes to override the default filename
|
38
40
|
# @return [Boolean] Returns true if the xml file exists
|
39
41
|
def self.xml_pathname_exist?(parent_dir, filename = nil)
|
40
|
-
|
42
|
+
xml_pathname(parent_dir, filename).exist?
|
41
43
|
end
|
42
44
|
|
43
45
|
# @api external
|
@@ -46,7 +48,7 @@ module Serializer
|
|
46
48
|
# @return [Serializable] Read the xml file and return the parsed XML
|
47
49
|
# @example {include:file:spec/features/serializer/read_xml_spec.rb}
|
48
50
|
def self.read_xml_file(parent_dir, filename = nil)
|
49
|
-
|
51
|
+
parse(xml_pathname(parent_dir, filename).read)
|
50
52
|
end
|
51
53
|
|
52
54
|
# @api external
|
@@ -56,8 +58,8 @@ module Serializer
|
|
56
58
|
# @return [void] Serializize the in-memory object to a xml file instance
|
57
59
|
def self.write_xml_file(xml_object, parent_dir, filename = nil)
|
58
60
|
parent_dir.mkpath
|
59
|
-
|
60
|
-
xmlBuilder = Nokogiri::XML::Builder.new(:
|
61
|
+
xml_pathname(parent_dir, filename).open('w') do |f|
|
62
|
+
xmlBuilder = Nokogiri::XML::Builder.new(encoding: 'UTF-8')
|
61
63
|
xmlBuilder = xml_object.to_xml(xmlBuilder)
|
62
64
|
f << xmlBuilder.to_xml
|
63
65
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Serializer
|
2
4
|
# Some utility methods to faciliate serialization of data fields to Hash, JSON, or YAML shared by all subclasses.
|
3
5
|
# This class assumes that HappyMapper is used for declaration of fields to be serialized.
|
@@ -19,11 +21,10 @@ module Serializer
|
|
19
21
|
# The symbols should correspond to attributes declared using HappyMapper syntax
|
20
22
|
def initialize(opts = {})
|
21
23
|
opts.each do |key, value|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
24
|
+
errmsg = "#{key} is not a variable name in #{self.class.name}"
|
25
|
+
raise(Moab::MoabRuntimeError, errmsg) unless variable_names.include?(key.to_s) || key == :test
|
26
|
+
|
27
|
+
instance_variable_set("@#{key}", value)
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
@@ -45,7 +46,7 @@ module Serializer
|
|
45
46
|
# @api internal
|
46
47
|
# @return [Array] Extract the names of the variables
|
47
48
|
def variable_names
|
48
|
-
variables.collect
|
49
|
+
variables.collect(&:name)
|
49
50
|
end
|
50
51
|
|
51
52
|
# @api internal
|
@@ -54,7 +55,7 @@ module Serializer
|
|
54
55
|
# This follows the same convention as used by DataMapper
|
55
56
|
# @see http://datamapper.org/docs/properties.html
|
56
57
|
def key_name
|
57
|
-
|
58
|
+
unless defined?(@key_name)
|
58
59
|
@key_name = nil
|
59
60
|
self.class.attributes.each do |attribute|
|
60
61
|
if attribute.options[:key]
|
@@ -69,7 +70,8 @@ module Serializer
|
|
69
70
|
# @api internal
|
70
71
|
# @return [String] For the current object instance, return the string to use as a hash key
|
71
72
|
def key
|
72
|
-
return
|
73
|
+
return send(key_name) if key_name
|
74
|
+
|
73
75
|
nil
|
74
76
|
end
|
75
77
|
|
@@ -79,10 +81,10 @@ module Serializer
|
|
79
81
|
# If the array member has a field tagged as a key, that field will be used as the hash.key.
|
80
82
|
# Otherwise the index position of the array member will be used as the key
|
81
83
|
def array_to_hash(array, summary = false)
|
82
|
-
item_hash =
|
84
|
+
item_hash = {}
|
83
85
|
array.each_index do |index|
|
84
86
|
item = array[index]
|
85
|
-
ikey =
|
87
|
+
ikey = item.respond_to?(:key) && item.key ? item.key : index
|
86
88
|
item_hash[ikey] = item.respond_to?(:to_hash) ? item.to_hash(summary) : item
|
87
89
|
end
|
88
90
|
item_hash
|
@@ -92,41 +94,42 @@ module Serializer
|
|
92
94
|
# @return [Hash] Recursively generate an Hash containing the object's properties
|
93
95
|
# @param summary [Boolean] Controls the depth and detail of recursion
|
94
96
|
def to_hash(summary = false)
|
95
|
-
oh =
|
97
|
+
oh = {}
|
96
98
|
vars = summary ? variables.select { |v| summary_fields.include?(v.name) } : variables
|
97
99
|
vars.each do |variable|
|
98
100
|
key = variable.name.to_s
|
99
|
-
value =
|
100
|
-
case value
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
101
|
+
value = send(variable.name)
|
102
|
+
oh[key] = case value
|
103
|
+
when Array
|
104
|
+
array_to_hash(value, summary)
|
105
|
+
when Serializable
|
106
|
+
value.to_hash
|
107
|
+
else
|
108
|
+
value
|
109
|
+
end
|
108
110
|
end
|
109
111
|
oh
|
110
112
|
end
|
111
113
|
|
112
114
|
# @return [Hash] Calls to_hash(summary=true)
|
113
115
|
def summary
|
114
|
-
|
116
|
+
to_hash(true)
|
115
117
|
end
|
116
118
|
|
117
119
|
# @api internal
|
118
120
|
# @param other [Serializable] The other object being compared
|
119
121
|
# @return [Hash] Generate a hash containing the differences between two objects of the same type
|
120
122
|
def diff(other)
|
121
|
-
raise "Cannot compare different classes" if self.class != other.class
|
123
|
+
raise(Moab::MoabRuntimeError, "Cannot compare different classes") if self.class != other.class
|
124
|
+
|
122
125
|
left = other.to_hash
|
123
|
-
right =
|
124
|
-
if
|
126
|
+
right = to_hash
|
127
|
+
if key.nil? || other.key.nil?
|
125
128
|
ltag = :old
|
126
129
|
rtag = :new
|
127
130
|
else
|
128
131
|
ltag = other.key
|
129
|
-
rtag =
|
132
|
+
rtag = key
|
130
133
|
end
|
131
134
|
Serializable.deep_diff(ltag, left, rtag, right)
|
132
135
|
end
|
@@ -136,23 +139,26 @@ module Serializer
|
|
136
139
|
# @return [Hash] Generate a hash containing the differences between two hashes
|
137
140
|
# (recursively descend parallel trees of hashes)
|
138
141
|
# @see https://gist.github.com/146844
|
139
|
-
def
|
140
|
-
diff =
|
142
|
+
def self.deep_diff(*hashes)
|
143
|
+
diff = {}
|
141
144
|
case hashes.length
|
142
145
|
when 4
|
143
146
|
ltag, left, rtag, right = hashes
|
144
147
|
when 2
|
145
|
-
ltag
|
148
|
+
ltag = :left
|
149
|
+
left = hashes[0]
|
150
|
+
rtag = :right
|
151
|
+
right = hashes[1]
|
146
152
|
else
|
147
153
|
raise ArgumentError, "wrong number of arguments (#{hashes.length} for 2 or 4)"
|
148
154
|
end
|
149
155
|
(left.keys | right.keys).each do |k|
|
150
156
|
if left[k] != right[k]
|
151
|
-
if left[k].is_a?(Hash) && right[k].is_a?(Hash)
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
157
|
+
diff[k] = if left[k].is_a?(Hash) && right[k].is_a?(Hash)
|
158
|
+
deep_diff(ltag, left[k], rtag, right[k])
|
159
|
+
else
|
160
|
+
Hash.[](ltag, left[k], rtag, right[k])
|
161
|
+
end
|
156
162
|
end
|
157
163
|
end
|
158
164
|
diff
|
@@ -161,14 +167,14 @@ module Serializer
|
|
161
167
|
# @api internal
|
162
168
|
# @return [String] Generate JSON output from a hash of the object's variables
|
163
169
|
def to_json(summary = false)
|
164
|
-
hash =
|
170
|
+
hash = to_hash(summary)
|
165
171
|
JSON.pretty_generate(hash)
|
166
172
|
end
|
167
173
|
|
168
174
|
# @api internal
|
169
175
|
# @return [String] Generate YAML output from a hash of the object's variables
|
170
176
|
def to_yaml(summary = false)
|
171
|
-
|
177
|
+
to_hash(summary).to_yaml
|
172
178
|
end
|
173
179
|
end
|
174
180
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Stanford
|
4
4
|
# Stanford-specific utility methods for transforming contentMetadata to versionInventory and doing comparisons
|
@@ -23,7 +23,7 @@ module Stanford
|
|
23
23
|
# Many of these objects have contentMetadata with no child elements, such as this:
|
24
24
|
# <contentMetadata objectId="bd608mj3166" type="file"/>
|
25
25
|
# but there are also objects that have no datasteam of this name at all
|
26
|
-
cm_inventory = Moab::FileInventory.new(:
|
26
|
+
cm_inventory = Moab::FileInventory.new(type: "version", digital_object_id: object_id, version_id: version_id)
|
27
27
|
content_group = group_from_cm(content_metadata, subset)
|
28
28
|
cm_inventory.groups << content_group
|
29
29
|
cm_inventory
|
@@ -47,9 +47,9 @@ module Stanford
|
|
47
47
|
when 'all'
|
48
48
|
ng_doc.xpath("//file")
|
49
49
|
else
|
50
|
-
raise "Unknown disposition subset (#{subset})"
|
50
|
+
raise(Moab::MoabRuntimeError, "Unknown disposition subset (#{subset})")
|
51
51
|
end
|
52
|
-
content_group = Moab::FileGroup.new(:
|
52
|
+
content_group = Moab::FileGroup.new(group_id: 'content', data_source: "contentMetadata-#{subset}")
|
53
53
|
nodeset.each do |file_node|
|
54
54
|
signature = generate_signature(file_node)
|
55
55
|
instance = generate_instance(file_node)
|
@@ -62,7 +62,7 @@ module Stanford
|
|
62
62
|
# @param node [Nokogiri::XML::Node] The XML node containing file information
|
63
63
|
# @return [FileSignature] The {FileSignature} object generated from the XML data
|
64
64
|
def generate_signature(node)
|
65
|
-
signature = Moab::FileSignature.new
|
65
|
+
signature = Moab::FileSignature.new
|
66
66
|
signature.size = node.attributes['size'].content
|
67
67
|
checksum_nodes = node.xpath('checksum')
|
68
68
|
checksum_nodes.each do |checksum_node|
|
@@ -82,9 +82,13 @@ module Stanford
|
|
82
82
|
# @param node (see #generate_signature)
|
83
83
|
# @return [FileInstance] The {FileInstance} object generated from the XML data
|
84
84
|
def generate_instance(node)
|
85
|
-
instance = Moab::FileInstance.new
|
85
|
+
instance = Moab::FileInstance.new
|
86
86
|
instance.path = node.attributes['id'].content
|
87
|
-
instance.datetime =
|
87
|
+
instance.datetime = begin
|
88
|
+
node.attributes['datetime'].content
|
89
|
+
rescue
|
90
|
+
nil
|
91
|
+
end
|
88
92
|
instance
|
89
93
|
end
|
90
94
|
|
@@ -94,28 +98,28 @@ module Stanford
|
|
94
98
|
# @example {include:file:spec/features/stanford/content_metadata_write_spec.rb}
|
95
99
|
def generate_content_metadata(file_group, object_id, version_id)
|
96
100
|
cm = Nokogiri::XML::Builder.new do |xml|
|
97
|
-
xml.contentMetadata(:
|
98
|
-
xml.resource(:
|
101
|
+
xml.contentMetadata(type: "sample", objectId: object_id) do
|
102
|
+
xml.resource(type: "version", sequence: "1", id: "version-#{version_id}") do
|
99
103
|
file_group.files.each do |file_manifestation|
|
100
104
|
signature = file_manifestation.signature
|
101
105
|
file_manifestation.instances.each do |instance|
|
102
106
|
xml.file(
|
103
|
-
:
|
104
|
-
:
|
105
|
-
:
|
106
|
-
:
|
107
|
-
:
|
108
|
-
:
|
109
|
-
)
|
107
|
+
id: instance.path,
|
108
|
+
size: signature.size,
|
109
|
+
datetime: instance.datetime,
|
110
|
+
shelve: 'yes',
|
111
|
+
publish: 'yes',
|
112
|
+
preserve: 'yes'
|
113
|
+
) do
|
110
114
|
fixity = signature.fixity
|
111
|
-
xml.checksum(:
|
112
|
-
xml.checksum(:
|
113
|
-
xml.checksum(:
|
114
|
-
|
115
|
+
xml.checksum(type: "MD5") { xml.text signature.md5 } if fixity[:md5]
|
116
|
+
xml.checksum(type: "SHA-1") { xml.text signature.sha1 } if fixity[:sha1]
|
117
|
+
xml.checksum(type: "SHA-256") { xml.text signature.sha256 } if fixity[:sha256]
|
118
|
+
end
|
115
119
|
end
|
116
120
|
end
|
117
|
-
|
118
|
-
|
121
|
+
end
|
122
|
+
end
|
119
123
|
end
|
120
124
|
cm.to_xml
|
121
125
|
end
|
@@ -124,7 +128,8 @@ module Stanford
|
|
124
128
|
# @return [Boolean] True if contentMetadata has essential file attributes, else raise exception
|
125
129
|
def validate_content_metadata(content_metadata)
|
126
130
|
result = validate_content_metadata_details(content_metadata)
|
127
|
-
raise Moab::InvalidMetadataException, result[0]
|
131
|
+
raise Moab::InvalidMetadataException, "#{result[0]} ..." unless result.empty?
|
132
|
+
|
128
133
|
true
|
129
134
|
end
|
130
135
|
|
@@ -145,7 +150,7 @@ module Stanford
|
|
145
150
|
end
|
146
151
|
nodeset = content_metadata_doc.xpath("//file")
|
147
152
|
nodeset.each do |file_node|
|
148
|
-
missing = [
|
153
|
+
missing = %w[id size md5 sha1]
|
149
154
|
missing.delete('id') if file_node.has_attribute?('id')
|
150
155
|
missing.delete('size') if file_node.has_attribute?('size')
|
151
156
|
checksum_nodes = file_node.xpath('checksum')
|
@@ -159,7 +164,7 @@ module Stanford
|
|
159
164
|
end
|
160
165
|
if missing.include?('id')
|
161
166
|
result << "File node #{nodeset.index(file_node)} is missing #{missing.join(',')}"
|
162
|
-
elsif missing.
|
167
|
+
elsif !missing.empty?
|
163
168
|
id = file_node['id']
|
164
169
|
result << "File node having id='#{id}' is missing #{missing.join(',')}"
|
165
170
|
end
|
@@ -173,11 +178,12 @@ module Stanford
|
|
173
178
|
# @see http://blog.slashpoundbang.com/post/1454850669/how-to-pretty-print-xml-with-nokogiri
|
174
179
|
def remediate_content_metadata(content_metadata, content_group)
|
175
180
|
return nil if content_metadata.nil?
|
176
|
-
return content_metadata if content_group.nil?
|
181
|
+
return content_metadata if content_group.nil? || content_group.files.empty?
|
182
|
+
|
177
183
|
signature_for_path = content_group.path_hash
|
178
184
|
@type_for_name = Moab::FileSignature.checksum_type_for_name
|
179
185
|
@names_for_type = Moab::FileSignature.checksum_names_for_type
|
180
|
-
ng_doc = Nokogiri::XML(content_metadata
|
186
|
+
ng_doc = Nokogiri::XML(content_metadata, &:noblanks)
|
181
187
|
nodeset = ng_doc.xpath("//file")
|
182
188
|
nodeset.each do |file_node|
|
183
189
|
filepath = file_node['id']
|
@@ -185,7 +191,7 @@ module Stanford
|
|
185
191
|
remediate_file_size(file_node, signature)
|
186
192
|
remediate_checksum_nodes(file_node, signature)
|
187
193
|
end
|
188
|
-
ng_doc.to_xml(:
|
194
|
+
ng_doc.to_xml(indent: 2)
|
189
195
|
end
|
190
196
|
|
191
197
|
# @param [Nokogiri::XML::Element] file_node the File stanza being remediated
|
@@ -193,10 +199,10 @@ module Stanford
|
|
193
199
|
# @return [void] update the file size attribute if missing, raise exception if inconsistent
|
194
200
|
def remediate_file_size(file_node, signature)
|
195
201
|
file_size = file_node['size']
|
196
|
-
if file_size.nil?
|
202
|
+
if file_size.nil? || file_size.empty?
|
197
203
|
file_node['size'] = signature.size.to_s
|
198
204
|
elsif file_size != signature.size.to_s
|
199
|
-
raise "Inconsistent size for #{file_node['id']}: #{file_size} != #{signature.size}"
|
205
|
+
raise(Moab::MoabRuntimeError, "Inconsistent size for #{file_node['id']}: #{file_size} != #{signature.size}")
|
200
206
|
end
|
201
207
|
end
|
202
208
|
|
@@ -205,14 +211,14 @@ module Stanford
|
|
205
211
|
# @return [void] update the file's checksum elements if data missing, raise exception if inconsistent
|
206
212
|
def remediate_checksum_nodes(file_node, signature)
|
207
213
|
# collect <checksum> elements for checksum types that are already present
|
208
|
-
checksum_nodes =
|
214
|
+
checksum_nodes = {}
|
209
215
|
file_node.xpath('checksum').each do |checksum_node|
|
210
216
|
type = @type_for_name[checksum_node['type']]
|
211
217
|
checksum_nodes[type] = checksum_node
|
212
218
|
end
|
213
219
|
# add new <checksum> elements for the other checksum types that were missing
|
214
220
|
@names_for_type.each do |type, names|
|
215
|
-
unless checksum_nodes.
|
221
|
+
unless checksum_nodes.key?(type)
|
216
222
|
checksum_node = Nokogiri::XML::Element.new('checksum', file_node.document)
|
217
223
|
checksum_node['type'] = names[0]
|
218
224
|
file_node << checksum_node
|
@@ -223,10 +229,10 @@ module Stanford
|
|
223
229
|
checksum_nodes.each do |type, checksum_node|
|
224
230
|
cm_checksum = checksum_node.content
|
225
231
|
sig_checksum = signature.checksums[type]
|
226
|
-
if cm_checksum.nil?
|
232
|
+
if cm_checksum.nil? || cm_checksum.empty?
|
227
233
|
checksum_node.content = sig_checksum
|
228
234
|
elsif cm_checksum != sig_checksum
|
229
|
-
raise "Inconsistent #{type} for #{file_node['id']}: #{cm_checksum} != #{sig_checksum}"
|
235
|
+
raise(Moab::MoabRuntimeError, "Inconsistent #{type} for #{file_node['id']}: #{cm_checksum} != #{sig_checksum}")
|
230
236
|
end
|
231
237
|
end
|
232
238
|
end
|