cocina-models 0.81.0 → 0.82.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +6 -5
- data/cocina-models.gemspec +3 -0
- data/lib/cocina/models/mapping/escape_html.rb +23 -0
- data/lib/cocina/models/mapping/to_mods/note.rb +3 -1
- data/lib/cocina/models/validators/date_time_validator.rb +102 -0
- data/lib/cocina/models/version.rb +1 -1
- metadata +47 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebc24ff8dbe8eb1fe740eb977abb73abc504409aa510905395e6660adcd05b3b
|
4
|
+
data.tar.gz: 62ad4e7cafce859250323e92cc2173257957b16775e767224a4de2e9fd639bae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a3b0ed8ab383f945450d42343dbaed6e709854ff0f3deca1f62b93c79883d3157b42f14cdaafd9392ef858620a0c58ae6867c16037c627499efddf3cb33880b
|
7
|
+
data.tar.gz: 9e1084ea73c6f547eab7b8a076d9e2a61a46361c9da1541effca8529d48d032621b6cd5868bd2886308aea1f834b4c586ae1058ca6f67f0315393e4eb632bbb9
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config --auto-gen-only-exclude`
|
3
|
-
# on 2022-
|
3
|
+
# on 2022-06-08 23:20:02 UTC using RuboCop version 1.28.2.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -35,7 +35,7 @@ Lint/UnusedMethodArgument:
|
|
35
35
|
- 'lib/cocina/models/mapping/from_mods/subject.rb'
|
36
36
|
- 'lib/cocina/models/mapping/to_mods/event.rb'
|
37
37
|
|
38
|
-
# Offense count:
|
38
|
+
# Offense count: 96
|
39
39
|
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
|
40
40
|
Metrics/AbcSize:
|
41
41
|
Max: 40
|
@@ -56,16 +56,17 @@ Metrics/ParameterLists:
|
|
56
56
|
RSpec/DescribeClass:
|
57
57
|
Enabled: false
|
58
58
|
|
59
|
-
# Offense count:
|
59
|
+
# Offense count: 87
|
60
60
|
# Configuration parameters: CountAsOne.
|
61
61
|
RSpec/ExampleLength:
|
62
62
|
Max: 128
|
63
63
|
|
64
|
-
# Offense count:
|
64
|
+
# Offense count: 10
|
65
65
|
# Configuration parameters: Max.
|
66
66
|
RSpec/NestedGroups:
|
67
67
|
Exclude:
|
68
68
|
- 'spec/cocina/models/mapping/normalizers/mods/origin_info_normalizer_spec.rb'
|
69
|
+
- 'spec/cocina/models/validators/date_time_validator_spec.rb'
|
69
70
|
|
70
71
|
# Offense count: 1
|
71
72
|
# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
|
@@ -87,7 +88,7 @@ Style/MultilineBlockChain:
|
|
87
88
|
- 'lib/cocina/models/mapping/to_mods/form.rb'
|
88
89
|
- 'lib/cocina/models/mapping/to_mods/subject.rb'
|
89
90
|
|
90
|
-
# Offense count:
|
91
|
+
# Offense count: 223
|
91
92
|
# This cop supports safe auto-correction (--auto-correct).
|
92
93
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns.
|
93
94
|
# URISchemes: http, https
|
data/cocina-models.gemspec
CHANGED
@@ -28,12 +28,15 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_dependency 'deprecation'
|
29
29
|
spec.add_dependency 'dry-struct', '~> 1.0'
|
30
30
|
spec.add_dependency 'dry-types', '~> 1.1'
|
31
|
+
spec.add_dependency 'edtf' # used for date/time validation
|
31
32
|
spec.add_dependency 'equivalent-xml' # for diffing MODS
|
33
|
+
spec.add_dependency 'jsonpath' # used for date/time validation
|
32
34
|
spec.add_dependency 'nokogiri'
|
33
35
|
spec.add_dependency 'openapi3_parser' # Parsing openapi doc
|
34
36
|
# Match these version requirements to what committee wants,
|
35
37
|
# so that our client (non-committee) users have the same dependencies.
|
36
38
|
spec.add_dependency 'openapi_parser', '>= 0.11.1', '< 1.0'
|
39
|
+
spec.add_dependency 'rss' # used for date/time validation
|
37
40
|
spec.add_dependency 'super_diff'
|
38
41
|
spec.add_dependency 'thor'
|
39
42
|
spec.add_dependency 'zeitwerk', '~> 2.1'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cocina
|
4
|
+
module Models
|
5
|
+
module Mapping
|
6
|
+
# Escaps HTML entities as CDATA for MODS since HTML is not permitted in MODS
|
7
|
+
class EscapeHtml
|
8
|
+
# @param [String] data
|
9
|
+
# @param [Nokogiri::XML::Builder]
|
10
|
+
def self.with_cdata(data, builder)
|
11
|
+
tokens = data.split(%r{(</?(?:i|cite)>)})
|
12
|
+
tokens.map do |token|
|
13
|
+
if /\A<.+>\z/.match? token
|
14
|
+
builder.cdata(token)
|
15
|
+
else
|
16
|
+
builder.text(token)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'edtf'
|
4
|
+
require 'jsonpath'
|
5
|
+
require 'rss'
|
6
|
+
|
7
|
+
module Cocina
|
8
|
+
module Models
|
9
|
+
module Validators
|
10
|
+
# Validates that dates of known types are type-valid
|
11
|
+
class DateTimeValidator
|
12
|
+
VALIDATABLE_TYPES = %w[edtf iso8601 w3cdtf].freeze
|
13
|
+
|
14
|
+
def self.validate(_clazz, attributes)
|
15
|
+
new(attributes).validate
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(attributes)
|
19
|
+
@attributes = attributes
|
20
|
+
end
|
21
|
+
|
22
|
+
def validate
|
23
|
+
return unless meets_preconditions?
|
24
|
+
|
25
|
+
return if invalid_dates.empty?
|
26
|
+
|
27
|
+
raise ValidationError, "Invalid date(s) for #{druid}: #{invalid_dates}"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :attributes
|
33
|
+
|
34
|
+
def meets_preconditions?
|
35
|
+
attributes.key?(:description)
|
36
|
+
end
|
37
|
+
|
38
|
+
def invalid_dates
|
39
|
+
@invalid_dates ||= validatable_dates.filter_map do |date_hash|
|
40
|
+
code = date_hash.dig('encoding', 'code')
|
41
|
+
bad_values = JsonPath.new('$..value').on(date_hash.to_json).reject do |value|
|
42
|
+
send("valid_#{code}?", value)
|
43
|
+
end
|
44
|
+
|
45
|
+
next if bad_values.empty?
|
46
|
+
|
47
|
+
[*bad_values, code]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def validatable_dates
|
52
|
+
# Why is the `uniq` needed below? Odd behavior when handling highly nested use cases:
|
53
|
+
#
|
54
|
+
# > JsonPath.new("$..date..[?(@.encoding.code =~ /#{VALIDATABLE_TYPES.join('|')}/)]").on(attributes[:description].to_json)
|
55
|
+
# > [
|
56
|
+
# {"structuredValue"=>[{"value"=>"1996", "type"=>"start"}, {"value"=>"1998", "type"=>"end"}], "encoding"=>{"code"=>"iso8601"}},
|
57
|
+
# {"structuredValue"=>[{"value"=>"1996", "type"=>"start"}, {"value"=>"1998", "type"=>"end"}], "encoding"=>{"code"=>"iso8601"}}
|
58
|
+
# ]
|
59
|
+
#
|
60
|
+
# Notice how the JSONPath expression returns the *same exact* structure twice despite only being present once in the data.
|
61
|
+
JsonPath
|
62
|
+
.new("$..date..[?(@.encoding.code =~ /#{VALIDATABLE_TYPES.join('|')}/)]")
|
63
|
+
.on(attributes[:description].to_json)
|
64
|
+
.uniq
|
65
|
+
end
|
66
|
+
|
67
|
+
def valid_edtf?(value)
|
68
|
+
Date.edtf!(value)
|
69
|
+
true
|
70
|
+
rescue StandardError
|
71
|
+
false
|
72
|
+
end
|
73
|
+
|
74
|
+
def valid_iso8601?(value)
|
75
|
+
DateTime.iso8601(value)
|
76
|
+
true
|
77
|
+
rescue StandardError
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
81
|
+
def valid_w3cdtf?(value)
|
82
|
+
Time.w3cdtf(value)
|
83
|
+
true
|
84
|
+
rescue StandardError
|
85
|
+
# NOTE: the upstream W3CDTF implementation in the `rss` gem does not
|
86
|
+
# allow two patterns that should be valid per the specification:
|
87
|
+
#
|
88
|
+
# * YYYY
|
89
|
+
# * YYYY-MM
|
90
|
+
#
|
91
|
+
# So we catch the false positives from the upstream gem and allow
|
92
|
+
# these two patterns to validate
|
93
|
+
/\A\d{4}(-0[1-9]|1[0-2])?\Z/.match?(value)
|
94
|
+
end
|
95
|
+
|
96
|
+
def druid
|
97
|
+
@druid ||= attributes[:externalIdentifier]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cocina-models
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.82.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Coyne
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: edtf
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: equivalent-xml
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,20 @@ dependencies:
|
|
80
94
|
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: jsonpath
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: nokogiri
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,6 +156,20 @@ dependencies:
|
|
128
156
|
- - "<"
|
129
157
|
- !ruby/object:Gem::Version
|
130
158
|
version: '1.0'
|
159
|
+
- !ruby/object:Gem::Dependency
|
160
|
+
name: rss
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :runtime
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0'
|
131
173
|
- !ruby/object:Gem::Dependency
|
132
174
|
name: super_diff
|
133
175
|
requirement: !ruby/object:Gem::Requirement
|
@@ -377,6 +419,7 @@ files:
|
|
377
419
|
- lib/cocina/models/location_based_access.rb
|
378
420
|
- lib/cocina/models/location_based_download_access.rb
|
379
421
|
- lib/cocina/models/mapping/error_notifier.rb
|
422
|
+
- lib/cocina/models/mapping/escape_html.rb
|
380
423
|
- lib/cocina/models/mapping/from_mods/access.rb
|
381
424
|
- lib/cocina/models/mapping/from_mods/admin_metadata.rb
|
382
425
|
- lib/cocina/models/mapping/from_mods/alt_rep_group.rb
|
@@ -461,6 +504,7 @@ files:
|
|
461
504
|
- lib/cocina/models/validators/associated_name_validator.rb
|
462
505
|
- lib/cocina/models/validators/catalog_links_validator.rb
|
463
506
|
- lib/cocina/models/validators/dark_validator.rb
|
507
|
+
- lib/cocina/models/validators/date_time_validator.rb
|
464
508
|
- lib/cocina/models/validators/description_types_validator.rb
|
465
509
|
- lib/cocina/models/validators/description_values_validator.rb
|
466
510
|
- lib/cocina/models/validators/open_api_validator.rb
|
@@ -492,7 +536,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
492
536
|
- !ruby/object:Gem::Version
|
493
537
|
version: '0'
|
494
538
|
requirements: []
|
495
|
-
rubygems_version: 3.3.
|
539
|
+
rubygems_version: 3.3.7
|
496
540
|
signing_key:
|
497
541
|
specification_version: 4
|
498
542
|
summary: Data models for the SDR
|