cocina-models 0.81.0 → 0.84.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +1 -1
- data/.gitignore +0 -2
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +6 -5
- data/Gemfile.lock +158 -0
- data/README.md +5 -14
- data/cocina-models.gemspec +3 -0
- data/lib/cocina/models/doi_exceptions.rb +9 -0
- data/lib/cocina/models/{doi.rb → doi_pattern.rb} +1 -1
- data/lib/cocina/models/identification.rb +1 -2
- data/lib/cocina/models/mapping/escape_html.rb +23 -0
- data/lib/cocina/models/mapping/from_mods/subject.rb +2 -1
- data/lib/cocina/models/mapping/to_mods/geographic.rb +3 -5
- data/lib/cocina/models/mapping/to_mods/note.rb +3 -1
- data/lib/cocina/models/mapping/to_mods/subject.rb +12 -2
- data/lib/cocina/models/validators/date_time_validator.rb +110 -0
- data/lib/cocina/models/validators/validator.rb +2 -1
- data/lib/cocina/models/version.rb +1 -1
- data/lib/cocina/models.rb +8 -1
- data/openapi.yml +12 -2
- metadata +50 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 326a7390c3ec14bfbc4ec9e4e79f26528b9dfa214f38aefd0646baddfbad96d5
|
4
|
+
data.tar.gz: cce43d9334fd16e47e790a082bbc999cf969b18ab78dc3d5e7015f757f6f4ea0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1a83488b4c5a07ae64073693c0a77424ce10fbfdada5e769fca6794d75cddc5c789d04ab92e35fa80a30eae82a0f8c284753df16ddab044bcd1ad11030b22f5
|
7
|
+
data.tar.gz: 6604a503733106b82f2e0155a2632a1eb8d00881dc762b52779179a96d962fd5e5143be6ebbe2738cb5d35eac88ee95d039c7f358d2664f10f6a82f71626b058
|
data/.circleci/config.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -184,7 +184,7 @@ Style/SoleNestedConditional: # (new in 0.89)
|
|
184
184
|
Style/StringConcatenation:
|
185
185
|
Enabled: true
|
186
186
|
|
187
|
-
Gemspec/
|
187
|
+
Gemspec/DeprecatedAttributeAssignment: # new in 1.10
|
188
188
|
Enabled: true
|
189
189
|
Gemspec/RequireMFA: # new in 1.23
|
190
190
|
Enabled: true
|
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/Gemfile.lock
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cocina-models (0.84.0)
|
5
|
+
activesupport
|
6
|
+
deprecation
|
7
|
+
dry-struct (~> 1.0)
|
8
|
+
dry-types (~> 1.1)
|
9
|
+
edtf
|
10
|
+
equivalent-xml
|
11
|
+
jsonpath
|
12
|
+
nokogiri
|
13
|
+
openapi3_parser
|
14
|
+
openapi_parser (>= 0.11.1, < 1.0)
|
15
|
+
rss
|
16
|
+
super_diff
|
17
|
+
thor
|
18
|
+
zeitwerk (~> 2.1)
|
19
|
+
|
20
|
+
GEM
|
21
|
+
remote: https://rubygems.org/
|
22
|
+
specs:
|
23
|
+
activesupport (7.0.3.1)
|
24
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
25
|
+
i18n (>= 1.6, < 2)
|
26
|
+
minitest (>= 5.1)
|
27
|
+
tzinfo (~> 2.0)
|
28
|
+
ast (2.4.2)
|
29
|
+
attr_extras (6.2.5)
|
30
|
+
byebug (11.1.3)
|
31
|
+
committee (4.4.0)
|
32
|
+
json_schema (~> 0.14, >= 0.14.3)
|
33
|
+
openapi_parser (>= 0.11.1, < 1.0)
|
34
|
+
rack (>= 1.5)
|
35
|
+
commonmarker (0.23.5)
|
36
|
+
concurrent-ruby (1.1.10)
|
37
|
+
deprecation (1.1.0)
|
38
|
+
activesupport
|
39
|
+
diff-lcs (1.5.0)
|
40
|
+
docile (1.4.0)
|
41
|
+
dry-container (0.10.1)
|
42
|
+
concurrent-ruby (~> 1.0)
|
43
|
+
dry-core (0.8.1)
|
44
|
+
concurrent-ruby (~> 1.0)
|
45
|
+
dry-inflector (0.3.0)
|
46
|
+
dry-logic (1.2.0)
|
47
|
+
concurrent-ruby (~> 1.0)
|
48
|
+
dry-core (~> 0.5, >= 0.5)
|
49
|
+
dry-struct (1.4.0)
|
50
|
+
dry-core (~> 0.5, >= 0.5)
|
51
|
+
dry-types (~> 1.5)
|
52
|
+
ice_nine (~> 0.11)
|
53
|
+
dry-types (1.5.1)
|
54
|
+
concurrent-ruby (~> 1.0)
|
55
|
+
dry-container (~> 0.3)
|
56
|
+
dry-core (~> 0.5, >= 0.5)
|
57
|
+
dry-inflector (~> 0.1, >= 0.1.2)
|
58
|
+
dry-logic (~> 1.0, >= 1.0.2)
|
59
|
+
edtf (3.1.0)
|
60
|
+
activesupport (>= 3.0, < 8.0)
|
61
|
+
equivalent-xml (0.6.0)
|
62
|
+
nokogiri (>= 1.4.3)
|
63
|
+
i18n (1.12.0)
|
64
|
+
concurrent-ruby (~> 1.0)
|
65
|
+
ice_nine (0.11.2)
|
66
|
+
json (2.6.2)
|
67
|
+
json_schema (0.21.0)
|
68
|
+
jsonpath (1.1.2)
|
69
|
+
multi_json
|
70
|
+
mini_portile2 (2.8.0)
|
71
|
+
minitest (5.16.3)
|
72
|
+
multi_json (1.15.0)
|
73
|
+
nokogiri (1.13.8)
|
74
|
+
mini_portile2 (~> 2.8.0)
|
75
|
+
racc (~> 1.4)
|
76
|
+
openapi3_parser (0.9.2)
|
77
|
+
commonmarker (~> 0.17)
|
78
|
+
openapi_parser (0.15.0)
|
79
|
+
optimist (3.0.1)
|
80
|
+
parallel (1.22.1)
|
81
|
+
parser (3.1.2.1)
|
82
|
+
ast (~> 2.4.1)
|
83
|
+
patience_diff (1.2.0)
|
84
|
+
optimist (~> 3.0)
|
85
|
+
racc (1.6.0)
|
86
|
+
rack (2.2.4)
|
87
|
+
rainbow (3.1.1)
|
88
|
+
rake (13.0.6)
|
89
|
+
regexp_parser (2.5.0)
|
90
|
+
rexml (3.2.5)
|
91
|
+
rspec (3.11.0)
|
92
|
+
rspec-core (~> 3.11.0)
|
93
|
+
rspec-expectations (~> 3.11.0)
|
94
|
+
rspec-mocks (~> 3.11.0)
|
95
|
+
rspec-core (3.11.0)
|
96
|
+
rspec-support (~> 3.11.0)
|
97
|
+
rspec-expectations (3.11.0)
|
98
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
99
|
+
rspec-support (~> 3.11.0)
|
100
|
+
rspec-mocks (3.11.1)
|
101
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
102
|
+
rspec-support (~> 3.11.0)
|
103
|
+
rspec-support (3.11.0)
|
104
|
+
rspec_junit_formatter (0.5.1)
|
105
|
+
rspec-core (>= 2, < 4, != 2.12.0)
|
106
|
+
rss (0.2.9)
|
107
|
+
rexml
|
108
|
+
rubocop (1.35.1)
|
109
|
+
json (~> 2.3)
|
110
|
+
parallel (~> 1.10)
|
111
|
+
parser (>= 3.1.2.1)
|
112
|
+
rainbow (>= 2.2.2, < 4.0)
|
113
|
+
regexp_parser (>= 1.8, < 3.0)
|
114
|
+
rexml (>= 3.2.5, < 4.0)
|
115
|
+
rubocop-ast (>= 1.20.1, < 2.0)
|
116
|
+
ruby-progressbar (~> 1.7)
|
117
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
118
|
+
rubocop-ast (1.21.0)
|
119
|
+
parser (>= 3.1.1.0)
|
120
|
+
rubocop-rake (0.6.0)
|
121
|
+
rubocop (~> 1.0)
|
122
|
+
rubocop-rspec (2.12.1)
|
123
|
+
rubocop (~> 1.31)
|
124
|
+
ruby-progressbar (1.11.0)
|
125
|
+
simplecov (0.21.2)
|
126
|
+
docile (~> 1.1)
|
127
|
+
simplecov-html (~> 0.11)
|
128
|
+
simplecov_json_formatter (~> 0.1)
|
129
|
+
simplecov-html (0.12.3)
|
130
|
+
simplecov_json_formatter (0.1.4)
|
131
|
+
super_diff (0.9.0)
|
132
|
+
attr_extras (>= 6.2.4)
|
133
|
+
diff-lcs
|
134
|
+
patience_diff
|
135
|
+
thor (1.2.1)
|
136
|
+
tzinfo (2.0.5)
|
137
|
+
concurrent-ruby (~> 1.0)
|
138
|
+
unicode-display_width (2.2.0)
|
139
|
+
zeitwerk (2.6.0)
|
140
|
+
|
141
|
+
PLATFORMS
|
142
|
+
ruby
|
143
|
+
|
144
|
+
DEPENDENCIES
|
145
|
+
bundler (~> 2.0)
|
146
|
+
byebug
|
147
|
+
cocina-models!
|
148
|
+
committee
|
149
|
+
rake (~> 13.0)
|
150
|
+
rspec (~> 3.0)
|
151
|
+
rspec_junit_formatter
|
152
|
+
rubocop (~> 1.24)
|
153
|
+
rubocop-rake
|
154
|
+
rubocop-rspec (~> 2.1)
|
155
|
+
simplecov
|
156
|
+
|
157
|
+
BUNDLED WITH
|
158
|
+
2.3.17
|
data/README.md
CHANGED
@@ -51,10 +51,10 @@ Beyond what is necessary to test the generator, the Cocina model classes are not
|
|
51
51
|
|
52
52
|
## Testing validation changes
|
53
53
|
|
54
|
-
If there is a possibility that a model or validation change will conflict with some existing objects then [validate-cocina](https://github.com/sul-dlss/dor-services-app/blob/main/bin/validate-cocina) should be used for testing. This must be run on sdr-
|
54
|
+
If there is a possibility that a model or validation change will conflict with some existing objects then [validate-cocina](https://github.com/sul-dlss/dor-services-app/blob/main/bin/validate-cocina) should be used for testing. This must be run on sdr-infra since it requires deploying a branch of cocina-models.
|
55
55
|
|
56
|
-
1. Create a cocina-models branch containing the proposed change and push to
|
57
|
-
2. On sdr-
|
56
|
+
1. Create a cocina-models branch containing the proposed change and push to GitHub.
|
57
|
+
2. On sdr-infra, check out `main`, update the `Gemfile` so that cocina-models references the branch, and `bundle install`.
|
58
58
|
3. Select the appropriate database.
|
59
59
|
For QA:
|
60
60
|
```
|
@@ -124,18 +124,9 @@ At the same, we have found it convenient to use these PRs to also bump the versi
|
|
124
124
|
|
125
125
|
### Step 4: Update other dependent applications
|
126
126
|
|
127
|
-
Once the above listed steps have been completed, all
|
127
|
+
Once the above listed steps have been completed, all applications that use cocina-models should be updated and released at the same time. "Cocina Level 2" describes this set of updates. The applications that use cocina-models are those in [this list](https://github.com/sul-dlss/access-update-scripts/blob/master/infrastructure/projects.yml) that are NOT marked with `cocina_level2: false`.
|
128
128
|
|
129
|
-
|
130
|
-
* [sul-dlss/common-accessioning](https://github.com/sul-dlss/common-accessioning/)
|
131
|
-
* [sul-dlss/dor_indexing_app](https://github.com/sul-dlss/dor_indexing_app/)
|
132
|
-
* [sul-dlss/google-books](https://github.com/sul-dlss/google-books/)
|
133
|
-
* [sul-dlss/happy-heron](https://github.com/sul-dlss/happy-heron/)
|
134
|
-
* [sul-dlss/hydra_etd](https://github.com/sul-dlss/hydra_etd/)
|
135
|
-
* [sul-dlss/infrastructure-integration-test](https://github.com/sul-dlss/infrastructure-integration-test/)
|
136
|
-
* [sul-dlss/pre-assembly](https://github.com/sul-dlss/pre-assembly/)
|
137
|
-
|
138
|
-
There are scripts to help with this:
|
129
|
+
There are scripts to help with updating other dependent applications:
|
139
130
|
|
140
131
|
#### Step 4A: Create the PRs
|
141
132
|
|
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'
|
@@ -7,8 +7,7 @@ module Cocina
|
|
7
7
|
attribute? :barcode, Types::Nominal::Any
|
8
8
|
attribute :catalogLinks, Types::Strict::Array.of(CatalogLink).default([].freeze)
|
9
9
|
# Digital Object Identifier (https://www.doi.org)
|
10
|
-
|
11
|
-
attribute? :doi, Types::Strict::String
|
10
|
+
attribute? :doi, Types::Nominal::Any
|
12
11
|
# Unique identifier in some other system. This is because a large proportion of what is deposited in SDR, historically and currently, are representations of objects that are also represented in other systems. For example, digitized paper and A/V collections have physical manifestations, and those physical objects are managed in systems that have their own identifiers. Similarly, books have barcodes, archival materials have collection numbers and physical locations, etc. The sourceId allows determining if an item has been deposited before and where to look for the original item if you're looking at its SDR representation. The format is: "namespace:identifier"
|
13
12
|
|
14
13
|
# example: sul:PC0170_s3_Fiesta_Bowl_2012-01-02_210609_2026
|
@@ -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
|
@@ -339,7 +339,8 @@ module Cocina
|
|
339
339
|
end
|
340
340
|
notes = name_notes_for(full_name[:role], node)
|
341
341
|
name_attrs[:note] = notes unless notes.empty?
|
342
|
-
name_attrs
|
342
|
+
name_attrs[:identifier] = full_name[:identifier]
|
343
|
+
name_attrs.compact.merge(attrs)
|
343
344
|
end
|
344
345
|
|
345
346
|
def name_notes_for(roles, name_node)
|
@@ -70,12 +70,10 @@ module Cocina
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def extract_type(geo)
|
73
|
-
type = geo
|
74
|
-
form
|
73
|
+
type = geo.form.find do |form|
|
74
|
+
form.type.match?(TYPE_REGEX) || (form.type.match?(MEDIA_REGEX) && form.value == 'Image')
|
75
75
|
end
|
76
|
-
|
77
|
-
|
78
|
-
nil
|
76
|
+
type&.value
|
79
77
|
end
|
80
78
|
|
81
79
|
def about(druid)
|
@@ -230,8 +230,11 @@ module Cocina
|
|
230
230
|
# Write nodes within MODS subject
|
231
231
|
def write_topic(subject, subject_value, is_parallel: false, type: nil, subject_values_have_same_authority: true)
|
232
232
|
type ||= subject_value.type
|
233
|
-
topic_attributes = topic_attributes_for(subject,
|
234
|
-
|
233
|
+
topic_attributes = topic_attributes_for(subject,
|
234
|
+
subject_value,
|
235
|
+
type,
|
236
|
+
is_parallel: is_parallel,
|
237
|
+
subject_values_have_same_authority: subject_values_have_same_authority)
|
235
238
|
case type
|
236
239
|
when 'person'
|
237
240
|
xml.name topic_attributes.merge(type: 'personal') do
|
@@ -395,6 +398,7 @@ module Cocina
|
|
395
398
|
write_name_part(subject_value)
|
396
399
|
write_display_form(display_values)
|
397
400
|
write_roles(subject_value.note)
|
401
|
+
write_identifier(subject.identifier)
|
398
402
|
write_other_notes(subject.note, 'description')
|
399
403
|
write_other_notes(subject.note, 'affiliation')
|
400
404
|
end
|
@@ -427,6 +431,12 @@ module Cocina
|
|
427
431
|
end.each { |role| RoleWriter.write(xml: xml, role: role) }
|
428
432
|
end
|
429
433
|
|
434
|
+
def write_identifier(identifiers)
|
435
|
+
identifiers.each do |identifier|
|
436
|
+
xml.nameIdentifier identifier.value, type: identifier.source.code
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
430
440
|
def write_other_notes(notes, type)
|
431
441
|
Array(notes).filter { |note| note.type == type }.each { |note| xml.public_send(type, note.value) }
|
432
442
|
end
|
@@ -0,0 +1,110 @@
|
|
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
|
+
# NOTE: the upstream EDTF implementation in the `edtf` gem does not
|
72
|
+
# allow a valid pattern that we use (possibly because only level
|
73
|
+
# 0 of the spec was implemented?):
|
74
|
+
#
|
75
|
+
# * Y-20555
|
76
|
+
#
|
77
|
+
# So we catch the false positives from the upstream gem and allow
|
78
|
+
# this pattern to validate
|
79
|
+
/\AY-?\d{5,}\Z/.match?(value)
|
80
|
+
end
|
81
|
+
|
82
|
+
def valid_iso8601?(value)
|
83
|
+
DateTime.iso8601(value)
|
84
|
+
true
|
85
|
+
rescue StandardError
|
86
|
+
false
|
87
|
+
end
|
88
|
+
|
89
|
+
def valid_w3cdtf?(value)
|
90
|
+
Time.w3cdtf(value)
|
91
|
+
true
|
92
|
+
rescue StandardError
|
93
|
+
# NOTE: the upstream W3CDTF implementation in the `rss` gem does not
|
94
|
+
# allow two patterns that should be valid per the specification:
|
95
|
+
#
|
96
|
+
# * YYYY
|
97
|
+
# * YYYY-MM
|
98
|
+
#
|
99
|
+
# So we catch the false positives from the upstream gem and allow
|
100
|
+
# these two patterns to validate
|
101
|
+
/\A\d{4}(-0[1-9]|-1[0-2])?\Z/.match?(value)
|
102
|
+
end
|
103
|
+
|
104
|
+
def druid
|
105
|
+
@druid ||= attributes[:externalIdentifier]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/lib/cocina/models.rb
CHANGED
@@ -40,7 +40,7 @@ loader = Zeitwerk::Loader.new
|
|
40
40
|
loader.inflector = CocinaModelsInflector.new
|
41
41
|
loader.push_dir(File.absolute_path("#{__FILE__}/../.."))
|
42
42
|
loader.ignore("#{__dir__}/rspec.rb")
|
43
|
-
loader.ignore("#{__dir__}/rspec
|
43
|
+
loader.ignore("#{__dir__}/rspec")
|
44
44
|
loader.setup
|
45
45
|
|
46
46
|
module Cocina
|
@@ -153,5 +153,12 @@ module Cocina
|
|
153
153
|
raise ValidationError, 'Type field not found'
|
154
154
|
end
|
155
155
|
private_class_method :type_for
|
156
|
+
|
157
|
+
def self.druid_regex
|
158
|
+
@druid_regex ||= begin
|
159
|
+
str = Openapi3Parser.load_file('openapi.yml').components.schemas['Druid'].pattern
|
160
|
+
Regexp.new(str)
|
161
|
+
end
|
162
|
+
end
|
156
163
|
end
|
157
164
|
end
|
data/openapi.yml
CHANGED
@@ -390,7 +390,7 @@ components:
|
|
390
390
|
- catalogRecordId
|
391
391
|
- refresh
|
392
392
|
CatkeyBarcode:
|
393
|
-
description: The barcode associated with a DRO object based on catkey, prefixed with
|
393
|
+
description: The barcode associated with a DRO object based on catkey, prefixed with a catkey followed by a hyphen
|
394
394
|
type: string
|
395
395
|
pattern: '^[0-9]+-[0-9]+$'
|
396
396
|
example: '6772719-1001'
|
@@ -896,8 +896,18 @@ components:
|
|
896
896
|
# description: An alphabet or other notation used to represent a
|
897
897
|
# language or other symbolic system of the descriptive element value.
|
898
898
|
DOI:
|
899
|
-
type: string
|
900
899
|
description: Digital Object Identifier (https://www.doi.org)
|
900
|
+
oneOf:
|
901
|
+
- $ref: '#/components/schemas/DoiPattern'
|
902
|
+
- $ref: '#/components/schemas/DoiExceptions'
|
903
|
+
DoiExceptions:
|
904
|
+
type: string
|
905
|
+
description: pre-existing Digital Object Identifiers (https://www.doi.org) not matching the pattern (case insensitive)
|
906
|
+
pattern: '^10\.(25740\/([vV][aA]90-[cC][tT]15|[sS][yY][xX][aA]-[mM]256|12[qQ][fF]-5243|65[jJ]8-6114)|25936\/629[tT]-[bB][xX]79)$'
|
907
|
+
example: '10.25740/12qF-5243'
|
908
|
+
DoiPattern:
|
909
|
+
type: string
|
910
|
+
description: Digital Object Identifier (https://www.doi.org) regex pattern
|
901
911
|
# The prod and test prefixes are permitted
|
902
912
|
pattern: '^10\.(25740|80343)\/[b-df-hjkmnp-tv-z]{2}[0-9]{3}[b-df-hjkmnp-tv-z]{2}[0-9]{4}$'
|
903
913
|
example: '10.25740/bc123df4567'
|
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.84.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-
|
11
|
+
date: 2022-08-23 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
|
@@ -297,6 +339,7 @@ files:
|
|
297
339
|
- ".rubocop.yml"
|
298
340
|
- ".rubocop_todo.yml"
|
299
341
|
- Gemfile
|
342
|
+
- Gemfile.lock
|
300
343
|
- README.md
|
301
344
|
- Rakefile
|
302
345
|
- bin/console
|
@@ -355,7 +398,8 @@ files:
|
|
355
398
|
- lib/cocina/models/descriptive_structured_value.rb
|
356
399
|
- lib/cocina/models/descriptive_value.rb
|
357
400
|
- lib/cocina/models/descriptive_value_language.rb
|
358
|
-
- lib/cocina/models/
|
401
|
+
- lib/cocina/models/doi_exceptions.rb
|
402
|
+
- lib/cocina/models/doi_pattern.rb
|
359
403
|
- lib/cocina/models/dro.rb
|
360
404
|
- lib/cocina/models/dro_access.rb
|
361
405
|
- lib/cocina/models/dro_structural.rb
|
@@ -377,6 +421,7 @@ files:
|
|
377
421
|
- lib/cocina/models/location_based_access.rb
|
378
422
|
- lib/cocina/models/location_based_download_access.rb
|
379
423
|
- lib/cocina/models/mapping/error_notifier.rb
|
424
|
+
- lib/cocina/models/mapping/escape_html.rb
|
380
425
|
- lib/cocina/models/mapping/from_mods/access.rb
|
381
426
|
- lib/cocina/models/mapping/from_mods/admin_metadata.rb
|
382
427
|
- lib/cocina/models/mapping/from_mods/alt_rep_group.rb
|
@@ -461,6 +506,7 @@ files:
|
|
461
506
|
- lib/cocina/models/validators/associated_name_validator.rb
|
462
507
|
- lib/cocina/models/validators/catalog_links_validator.rb
|
463
508
|
- lib/cocina/models/validators/dark_validator.rb
|
509
|
+
- lib/cocina/models/validators/date_time_validator.rb
|
464
510
|
- lib/cocina/models/validators/description_types_validator.rb
|
465
511
|
- lib/cocina/models/validators/description_values_validator.rb
|
466
512
|
- lib/cocina/models/validators/open_api_validator.rb
|
@@ -492,7 +538,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
492
538
|
- !ruby/object:Gem::Version
|
493
539
|
version: '0'
|
494
540
|
requirements: []
|
495
|
-
rubygems_version: 3.
|
541
|
+
rubygems_version: 3.2.32
|
496
542
|
signing_key:
|
497
543
|
specification_version: 4
|
498
544
|
summary: Data models for the SDR
|