dor-services 5.19.0 → 5.20.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/dor-services.rb +2 -0
- data/lib/dor/datastreams/content_metadata_ds.rb +0 -39
- data/lib/dor/datastreams/embargo_metadata_ds.rb +1 -0
- data/lib/dor/datastreams/identity_metadata_ds.rb +3 -0
- data/lib/dor/datastreams/rights_metadata_ds.rb +1 -3
- data/lib/dor/datastreams/workflow_definition_ds.rb +1 -20
- data/lib/dor/models/concerns/describable.rb +0 -22
- data/lib/dor/models/concerns/editable.rb +5 -0
- data/lib/dor/models/concerns/geoable.rb +0 -17
- data/lib/dor/models/concerns/identifiable.rb +4 -0
- data/lib/dor/services/public_xml_service.rb +75 -23
- data/lib/dor/utils/hydrus_shims.rb +11 -0
- data/lib/dor/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39d428d9a931ec01cf66aa31d6b57b8799790d25
|
4
|
+
data.tar.gz: cf2c1b5e154ec386b91c25acd0a3fad3e972c721
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24ae862bf0f70a297e2cb19d52f1589664fe20ed0c5cc289e954e1efb3906098f11d8ba22f2daed4b6a131c7478ef2c9a39299aaf0e5c7b425db00b59f3e9b17
|
7
|
+
data.tar.gz: f8977fc6407cd2054f787423e3ea21579ed3efa359a5560d4f006b29f02908191b086efccdec5435f99155c51d7563872f194132c4886e6ad36edf7bbe936d45
|
data/lib/dor-services.rb
CHANGED
@@ -33,45 +33,6 @@ module Dor
|
|
33
33
|
|
34
34
|
### READ ONLY METHODS
|
35
35
|
|
36
|
-
# @return [Nokogiri::XML::Document] sanitized for public consumption
|
37
|
-
def public_xml
|
38
|
-
result = ng_xml.clone
|
39
|
-
|
40
|
-
# remove any resources or attributes that are not destined for the public XML
|
41
|
-
result.xpath('/contentMetadata/resource[not(file[(@deliver="yes" or @publish="yes")]|externalFile)]').each(&:remove)
|
42
|
-
result.xpath('/contentMetadata/resource/file[not(@deliver="yes" or @publish="yes")]' ).each(&:remove)
|
43
|
-
result.xpath('/contentMetadata/resource/file').xpath('@preserve|@shelve|@publish|@deliver' ).each(&:remove)
|
44
|
-
result.xpath('/contentMetadata/resource/file/checksum' ).each(&:remove)
|
45
|
-
|
46
|
-
# support for dereferencing links via externalFile element(s) to the source (child) item - see JUMBO-19
|
47
|
-
result.xpath('/contentMetadata/resource/externalFile').each do |externalFile|
|
48
|
-
# enforce pre-conditions that resourceId, objectId, fileId are required
|
49
|
-
src_resource_id = externalFile['resourceId']
|
50
|
-
src_druid = externalFile['objectId']
|
51
|
-
src_file_id = externalFile['fileId']
|
52
|
-
fail ArgumentError, "Malformed externalFile data: #{externalFile.inspect}" if [src_resource_id, src_file_id, src_druid].map(&:blank?).any?
|
53
|
-
|
54
|
-
# grab source item
|
55
|
-
src_item = Dor.find(src_druid)
|
56
|
-
|
57
|
-
# locate and extract the resourceId/fileId elements
|
58
|
-
doc = src_item.datastreams['contentMetadata'].ng_xml
|
59
|
-
src_resource = doc.at_xpath("//resource[@id=\"#{src_resource_id}\"]")
|
60
|
-
src_file = src_resource.at_xpath("file[@id=\"#{src_file_id}\"]")
|
61
|
-
src_image_data = src_file.at_xpath('imageData')
|
62
|
-
|
63
|
-
# always use title regardless of whether a child label is present
|
64
|
-
src_label = doc.create_element('label')
|
65
|
-
src_label.content = src_item.full_title
|
66
|
-
|
67
|
-
# add the extracted label and imageData
|
68
|
-
externalFile.add_previous_sibling(src_label)
|
69
|
-
externalFile << src_image_data unless src_image_data.nil?
|
70
|
-
end
|
71
|
-
|
72
|
-
result
|
73
|
-
end
|
74
|
-
|
75
36
|
# Only use this when you want the behavior of raising an exception if anything besides exactly one matching node
|
76
37
|
# is found. Otherwise just use .xpath, .at_xpath or .search.
|
77
38
|
# @param xpath [String] accessor invocation for Nokogiri xpath
|
@@ -28,6 +28,7 @@ class IdentityMetadataDS < ActiveFedora::OmDatastream
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def add_value(name, value, attrs = {})
|
31
|
+
ng_xml_will_change!
|
31
32
|
add_child_node(ng_xml.root, :value, name, value, attrs)
|
32
33
|
end
|
33
34
|
|
@@ -44,6 +45,7 @@ class IdentityMetadataDS < ActiveFedora::OmDatastream
|
|
44
45
|
# @return [String, Nil] The same value, as per Ruby convention for assignment operators
|
45
46
|
# @note The actual values assigned will have leading/trailing whitespace stripped.
|
46
47
|
def sourceId=(value)
|
48
|
+
ng_xml_will_change!
|
47
49
|
node = find_by_terms(:sourceId).first
|
48
50
|
unless value.present? # so setting it to '' is the same as removal: worth documenting maybe?
|
49
51
|
node.remove unless node.nil?
|
@@ -72,6 +74,7 @@ class IdentityMetadataDS < ActiveFedora::OmDatastream
|
|
72
74
|
end
|
73
75
|
|
74
76
|
def add_otherId(other_id)
|
77
|
+
ng_xml_will_change!
|
75
78
|
(name, val) = other_id.split(/:/, 2)
|
76
79
|
node = ng_xml.root.add_child('<otherId/>').first
|
77
80
|
node['name'] = name
|
@@ -2,8 +2,6 @@ module Dor
|
|
2
2
|
class RightsMetadataDS < ActiveFedora::OmDatastream
|
3
3
|
require 'dor/rights_auth'
|
4
4
|
|
5
|
-
attr_writer :dra_object
|
6
|
-
|
7
5
|
# This is separate from default_object_rights because
|
8
6
|
# (1) we cannot default to such a permissive state
|
9
7
|
# (2) this is real, not default
|
@@ -131,7 +129,7 @@ module Dor
|
|
131
129
|
ng_xml_will_change!
|
132
130
|
RightsMetadataDS.upd_rights_xml_for_rights_type(rights_xml, rights)
|
133
131
|
|
134
|
-
|
132
|
+
@dra_object = nil # until TODO complete, we'll expect to have to reparse after modification
|
135
133
|
end
|
136
134
|
|
137
135
|
def to_solr(solr_doc = {}, *args)
|
@@ -33,6 +33,7 @@ class WorkflowDefinitionDs < ActiveFedora::OmDatastream
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def add_process(attributes)
|
36
|
+
ng_xml_will_change!
|
36
37
|
add_child_node(ng_xml.at_xpath('/workflow-def'), :process, self, attributes)
|
37
38
|
end
|
38
39
|
|
@@ -54,22 +55,6 @@ class WorkflowDefinitionDs < ActiveFedora::OmDatastream
|
|
54
55
|
ng_xml.at_xpath('/workflow-def/@repository').to_s
|
55
56
|
end
|
56
57
|
|
57
|
-
def configuration
|
58
|
-
result = ActiveSupport::OrderedHash.new
|
59
|
-
result['repository'] = repo
|
60
|
-
result['name'] = name
|
61
|
-
processes.each { |process| result[process.name] = process.to_hash }
|
62
|
-
result
|
63
|
-
end
|
64
|
-
|
65
|
-
def configuration=(hash)
|
66
|
-
self.ng_xml = Nokogiri::XML(%(<workflow-def id="#{hash['name']}" repository="#{hash['repository']}"/>))
|
67
|
-
i = 0
|
68
|
-
hash.each_pair do |k, v|
|
69
|
-
add_process(v.merge({:name => k, :sequence => i += 1})) if v.is_a?(Hash)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
58
|
# Creates the xml used by Dor::WorkflowService#create_workflow
|
74
59
|
# @return [String] An object's initial workflow as defined by the <workflow-def> in content
|
75
60
|
def initial_workflow
|
@@ -101,10 +86,6 @@ class WorkflowDefinitionDs < ActiveFedora::OmDatastream
|
|
101
86
|
solr_doc
|
102
87
|
end
|
103
88
|
|
104
|
-
def to_yaml
|
105
|
-
YAML.dump(configuration)
|
106
|
-
end
|
107
|
-
|
108
89
|
# maintain AF < 8 indexing behavior
|
109
90
|
def prefix
|
110
91
|
''
|
@@ -67,7 +67,6 @@ module Dor
|
|
67
67
|
def to_solr(solr_doc = {}, *args)
|
68
68
|
solr_doc = super solr_doc, *args
|
69
69
|
add_metadata_format_to_solr_doc(solr_doc)
|
70
|
-
add_dc_to_solr_doc(solr_doc)
|
71
70
|
add_mods_to_solr_doc(solr_doc)
|
72
71
|
end
|
73
72
|
|
@@ -76,27 +75,6 @@ module Dor
|
|
76
75
|
solr_doc['metadata_format_ssim'] += ['mods']
|
77
76
|
end
|
78
77
|
|
79
|
-
def add_dc_to_solr_doc(solr_doc)
|
80
|
-
dc_doc = generate_dublin_core(include_collection_as_related_item: false)
|
81
|
-
# we excluding the generated collection relation here; we instead get the collection
|
82
|
-
# title from Dor::Identifiable.
|
83
|
-
dc_doc.xpath('/oai_dc:dc/*', oai_dc: XMLNS_OAI_DC).each do |node|
|
84
|
-
add_solr_value(solr_doc, "public_dc_#{node.name}", node.text, :string, [:stored_searchable])
|
85
|
-
end
|
86
|
-
creator = ''
|
87
|
-
dc_doc.xpath('//dc:creator', dc: XMLNS_DC).each do |node|
|
88
|
-
creator = node.text
|
89
|
-
end
|
90
|
-
title = ''
|
91
|
-
dc_doc.xpath('//dc:title', dc: XMLNS_DC).each do |node|
|
92
|
-
title = node.text
|
93
|
-
end
|
94
|
-
creator_title = creator + title
|
95
|
-
add_solr_value(solr_doc, 'creator_title', creator_title, :string, [:stored_sortable])
|
96
|
-
rescue CrosswalkError => e
|
97
|
-
Dor.logger.warn "Cannot index #{pid}.descMetadata: #{e.message}"
|
98
|
-
end
|
99
|
-
|
100
78
|
def add_mods_to_solr_doc(solr_doc)
|
101
79
|
mods_sources = {
|
102
80
|
sw_title_display: %w(sw_display_title_tesim),
|
@@ -140,6 +140,7 @@ module Dor
|
|
140
140
|
end
|
141
141
|
def metadata_source=(val)
|
142
142
|
if administrativeMetadata.descMetadata.nil?
|
143
|
+
administrativeMetadata.ng_xml_will_change!
|
143
144
|
administrativeMetadata.add_child_node(administrativeMetadata, :descMetadata)
|
144
145
|
end
|
145
146
|
administrativeMetadata.update_values({[:descMetadata, :source] => val})
|
@@ -279,6 +280,7 @@ module Dor
|
|
279
280
|
raise(ArgumentError, "Unrecognized rights value '#{rights}'") unless RightsMetadataDS.valid_rights_type? rights
|
280
281
|
|
281
282
|
rights_xml = defaultObjectRights.ng_xml
|
283
|
+
defaultObjectRights.ng_xml_will_change!
|
282
284
|
RightsMetadataDS.upd_rights_xml_for_rights_type(rights_xml, rights)
|
283
285
|
end
|
284
286
|
|
@@ -288,6 +290,7 @@ module Dor
|
|
288
290
|
def desc_metadata_format=(format)
|
289
291
|
# create the node if it isnt there already
|
290
292
|
unless administrativeMetadata.metadata_format.first
|
293
|
+
administrativeMetadata.ng_xml_will_change!
|
291
294
|
administrativeMetadata.add_child_node(administrativeMetadata.ng_xml.root, :metadata_format)
|
292
295
|
end
|
293
296
|
administrativeMetadata.update_values({[:metadata_format] => format})
|
@@ -299,6 +302,7 @@ module Dor
|
|
299
302
|
def desc_metadata_source=(source)
|
300
303
|
# create the node if it isnt there already
|
301
304
|
unless administrativeMetadata.metadata_source.first
|
305
|
+
administrativeMetadata.ng_xml_will_change!
|
302
306
|
administrativeMetadata.add_child_node(administrativeMetadata.ng_xml.root, :metadata_source)
|
303
307
|
end
|
304
308
|
administrativeMetadata.update_values({[:metadata_source] => format})
|
@@ -314,6 +318,7 @@ module Dor
|
|
314
318
|
def default_workflow=(wf)
|
315
319
|
fail ArgumentError, 'Must have a valid workflow for default' if wf.blank?
|
316
320
|
xml = administrativeMetadata.ng_xml
|
321
|
+
administrativeMetadata.ng_xml_will_change!
|
317
322
|
nodes = xml.search('//registration/workflow')
|
318
323
|
if nodes.first
|
319
324
|
nodes.first['id'] = wf
|
@@ -8,22 +8,5 @@ module Dor
|
|
8
8
|
:label => 'Geographic Information Metadata in ISO 19139',
|
9
9
|
:control_group => 'M'
|
10
10
|
end
|
11
|
-
|
12
|
-
# @return [String, nil] XML
|
13
|
-
def fetch_geoMetadata_datastream
|
14
|
-
candidates = datastreams['identityMetadata'].otherId.collect { |oid| oid.to_s }
|
15
|
-
metadata_id = Dor::MetadataService.resolvable(candidates).first
|
16
|
-
return nil if metadata_id.nil?
|
17
|
-
Dor::MetadataService.fetch(metadata_id.to_s)
|
18
|
-
end
|
19
|
-
|
20
|
-
def build_geoMetadata_datastream(ds)
|
21
|
-
content = fetch_geoMetadata_datastream
|
22
|
-
return nil if content.nil?
|
23
|
-
ds.dsLabel = label
|
24
|
-
ds.ng_xml = Nokogiri::XML(content)
|
25
|
-
ds.ng_xml.normalize_text!
|
26
|
-
ds.content = ds.ng_xml.to_xml
|
27
|
-
end
|
28
11
|
end
|
29
12
|
end
|
@@ -97,6 +97,7 @@ module Dor
|
|
97
97
|
def update_other_Id(type, new_val, val = nil)
|
98
98
|
identityMetadata.ng_xml.search('//otherId[@name=\'' + type + '\']')
|
99
99
|
.select { |node| val.nil? || node.content == val }
|
100
|
+
.each { identityMetadata.ng_xml_will_change! }
|
100
101
|
.each { |node| node.content = new_val }
|
101
102
|
.any?
|
102
103
|
end
|
@@ -104,6 +105,7 @@ module Dor
|
|
104
105
|
def remove_other_Id(type, val = nil)
|
105
106
|
identityMetadata.ng_xml.search('//otherId[@name=\'' + type + '\']')
|
106
107
|
.select { |node| val.nil? || node.content == val }
|
108
|
+
.each { identityMetadata.ng_xml_will_change! }
|
107
109
|
.each(&:remove)
|
108
110
|
.any?
|
109
111
|
end
|
@@ -166,6 +168,7 @@ module Dor
|
|
166
168
|
normtag = normalize_tag(tag)
|
167
169
|
identityMetadata.ng_xml.search('//tag')
|
168
170
|
.select { |node| normalize_tag(node.content) == normtag }
|
171
|
+
.each { identityMetadata.ng_xml_will_change! }
|
169
172
|
.each(&:remove)
|
170
173
|
.any?
|
171
174
|
end
|
@@ -174,6 +177,7 @@ module Dor
|
|
174
177
|
normtag = normalize_tag(old_tag)
|
175
178
|
identityMetadata.ng_xml.search('//tag')
|
176
179
|
.select { |node| normalize_tag(node.content) == normtag }
|
180
|
+
.each { identityMetadata.ng_xml_will_change! }
|
177
181
|
.each { |node| node.content = normalize_tag(new_tag) }
|
178
182
|
.any?
|
179
183
|
end
|
@@ -11,42 +11,94 @@ module Dor
|
|
11
11
|
pub['id'] = object.pid
|
12
12
|
pub['published'] = Time.now.utc.xmlschema
|
13
13
|
pub['publishVersion'] = 'dor-services/' + Dor::VERSION
|
14
|
-
release_xml = Nokogiri(generate_release_xml)
|
15
14
|
|
16
|
-
|
17
|
-
|
15
|
+
pub.add_child(public_identity_metadata.root) # add in modified identityMetadata datastream
|
16
|
+
pub.add_child(public_content_metadata.root) if public_content_metadata.xpath('//resource').any?
|
17
|
+
pub.add_child(public_rights_metadata.root)
|
18
18
|
|
19
|
-
pub.add_child(
|
20
|
-
|
21
|
-
pub.add_child(
|
22
|
-
pub.add_child(
|
23
|
-
|
24
|
-
rels = object.public_relationships.root
|
25
|
-
pub.add_child(rels.clone) unless rels.nil? # TODO: Should never be nil in practice; working around an ActiveFedora quirk for testing
|
26
|
-
pub.add_child(object.generate_dublin_core.root.clone)
|
27
|
-
pub.add_child(Nokogiri::XML(object.generate_public_desc_md).root.clone)
|
28
|
-
pub.add_child(release_xml.root.clone) unless release_xml.xpath('//release').children.size == 0 # If there are no release_tags, this prevents an empty <releaseData/> from being added
|
19
|
+
pub.add_child(public_relationships.root) unless public_relationships.nil? # TODO: Should never be nil in practice; working around an ActiveFedora quirk for testing
|
20
|
+
pub.add_child(object.generate_dublin_core.root)
|
21
|
+
pub.add_child(Nokogiri::XML(object.generate_public_desc_md).root)
|
22
|
+
pub.add_child(release_xml.root) unless release_xml.xpath('//release').children.size == 0 # If there are no release_tags, this prevents an empty <releaseData/> from being added
|
29
23
|
# Note we cannot base this on if an individual object has release tags or not, because the collection may cause one to be generated for an item,
|
30
24
|
# so we need to calculate it and then look at the final result.s
|
31
|
-
pub.add_child(Nokogiri("<thumb>#{object.thumb}</thumb>").root
|
25
|
+
pub.add_child(Nokogiri("<thumb>#{object.thumb}</thumb>").root) unless object.thumb.nil?
|
32
26
|
|
33
27
|
new_pub = Nokogiri::XML(pub.to_xml) { |x| x.noblanks }
|
34
28
|
new_pub.encoding = 'UTF-8'
|
35
29
|
new_pub.to_xml
|
36
30
|
end
|
37
|
-
|
38
31
|
|
39
32
|
# Generate XML structure for inclusion to Purl
|
40
33
|
# @return [String] The XML release node as a string, with ReleaseDigest as the root document
|
41
|
-
def
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
34
|
+
def release_xml
|
35
|
+
@release_xml ||= begin
|
36
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
37
|
+
xml.releaseData {
|
38
|
+
object.released_for.each do |project, released_value|
|
39
|
+
xml.release(released_value['release'], :to => project)
|
40
|
+
end
|
41
|
+
}
|
42
|
+
end
|
43
|
+
Nokogiri::XML(builder.to_xml)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def public_relationships
|
48
|
+
@public_relationships ||= object.public_relationships.clone
|
49
|
+
end
|
50
|
+
|
51
|
+
def public_rights_metadata
|
52
|
+
@public_rights_metadata ||= object.datastreams['rightsMetadata'].ng_xml.clone
|
53
|
+
end
|
54
|
+
|
55
|
+
def public_identity_metadata
|
56
|
+
@public_identity_metadata ||= begin
|
57
|
+
im = object.datastreams['identityMetadata'].ng_xml.clone
|
58
|
+
im.search('//release').each(&:remove) # remove any <release> tags from public xml which have full history
|
59
|
+
im
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Nokogiri::XML::Document] sanitized for public consumption
|
64
|
+
def public_content_metadata
|
65
|
+
@public_content_metadata ||= begin
|
66
|
+
result = object.datastreams['contentMetadata'].ng_xml.clone
|
67
|
+
|
68
|
+
# remove any resources or attributes that are not destined for the public XML
|
69
|
+
result.xpath('/contentMetadata/resource[not(file[(@deliver="yes" or @publish="yes")]|externalFile)]').each(&:remove)
|
70
|
+
result.xpath('/contentMetadata/resource/file[not(@deliver="yes" or @publish="yes")]' ).each(&:remove)
|
71
|
+
result.xpath('/contentMetadata/resource/file').xpath('@preserve|@shelve|@publish|@deliver' ).each(&:remove)
|
72
|
+
result.xpath('/contentMetadata/resource/file/checksum' ).each(&:remove)
|
73
|
+
|
74
|
+
# support for dereferencing links via externalFile element(s) to the source (child) item - see JUMBO-19
|
75
|
+
result.xpath('/contentMetadata/resource/externalFile').each do |externalFile|
|
76
|
+
# enforce pre-conditions that resourceId, objectId, fileId are required
|
77
|
+
src_resource_id = externalFile['resourceId']
|
78
|
+
src_druid = externalFile['objectId']
|
79
|
+
src_file_id = externalFile['fileId']
|
80
|
+
fail ArgumentError, "Malformed externalFile data: #{externalFile.inspect}" if [src_resource_id, src_file_id, src_druid].map(&:blank?).any?
|
81
|
+
|
82
|
+
# grab source item
|
83
|
+
src_item = Dor.find(src_druid)
|
84
|
+
|
85
|
+
# locate and extract the resourceId/fileId elements
|
86
|
+
doc = src_item.datastreams['contentMetadata'].ng_xml
|
87
|
+
src_resource = doc.at_xpath("//resource[@id=\"#{src_resource_id}\"]")
|
88
|
+
src_file = src_resource.at_xpath("file[@id=\"#{src_file_id}\"]")
|
89
|
+
src_image_data = src_file.at_xpath('imageData')
|
90
|
+
|
91
|
+
# always use title regardless of whether a child label is present
|
92
|
+
src_label = doc.create_element('label')
|
93
|
+
src_label.content = src_item.full_title
|
94
|
+
|
95
|
+
# add the extracted label and imageData
|
96
|
+
externalFile.add_previous_sibling(src_label)
|
97
|
+
externalFile << src_image_data unless src_image_data.nil?
|
98
|
+
end
|
99
|
+
|
100
|
+
result
|
48
101
|
end
|
49
|
-
builder.to_xml
|
50
102
|
end
|
51
103
|
end
|
52
104
|
end
|
data/lib/dor/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dor-services
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.20.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Klein
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date:
|
17
|
+
date: 2017-01-18 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: active-fedora
|
@@ -651,6 +651,7 @@ files:
|
|
651
651
|
- lib/dor/services/search_service.rb
|
652
652
|
- lib/dor/services/suri_service.rb
|
653
653
|
- lib/dor/services/technical_metadata_service.rb
|
654
|
+
- lib/dor/utils/hydrus_shims.rb
|
654
655
|
- lib/dor/utils/ng_tidy.rb
|
655
656
|
- lib/dor/utils/predicate_patch.rb
|
656
657
|
- lib/dor/utils/sdr_client.rb
|