cocina-models 0.91.4 → 0.93.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a36a4c39bf28e6e40d3adccaccf0c89cd14b24c3807c8bde9ac6ee58fcb1b42e
4
- data.tar.gz: 4898c15f9fbe3c6b1b3d3fb33edbe93b4fea5d2c1ca96edd25b91b9166967b29
3
+ metadata.gz: 9cf74f9126fce8106b3bfd5964c11731c460546a4f457da9ba7f35f895243c7a
4
+ data.tar.gz: 58467364ec8a96eac4f6d335dce400c0086833830f5704e263bae7365741d473
5
5
  SHA512:
6
- metadata.gz: 790a2ec388b08ace732d85d27b6544343f658b037791bff63d69527c3baa036073785ff52d4a67a5d483b7bda6da075068f3589db612130df9c15d76b29b7946
7
- data.tar.gz: 9b73c7b24751999006863cb8c181f715ff7ff844b83db3d74b5f94a2297860b2a18b4fc31f41fb2ff5af8654d6e7ef1c6701299142b1b232f8d96b55dcd8f2c4
6
+ metadata.gz: e2106344f663362d1609f5e40daf001c43f976abbbe7ae8a43c52295b2a2e280952c7d6d01b6f6b4cbdf124a07c26372bdfc17f9a903cce666edc39ba3b3db35
7
+ data.tar.gz: 5675d10901a099d8869baed51c4bdb82608305053d0e830130c28577644daddf73a650410cb20425e4e27ff4dfc60188f0e2cb0b77954c49bd3f00980ee56cf1
data/.rubocop.yml CHANGED
@@ -116,6 +116,9 @@ Naming/PredicateName:
116
116
 
117
117
  # ----- RSpec ------
118
118
 
119
+ RSpec/NestedGroups:
120
+ Max: 5
121
+
119
122
  RSpec/BeEq: # new in 2.9.0
120
123
  Enabled: true
121
124
 
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 2023-06-20 05:46:00 UTC using RuboCop version 1.52.1.
3
+ # on 2023-11-01 19:51:40 UTC using RuboCop version 1.57.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
@@ -57,18 +57,11 @@ Metrics/ParameterLists:
57
57
  RSpec/DescribeClass:
58
58
  Enabled: false
59
59
 
60
- # Offense count: 87
60
+ # Offense count: 224
61
61
  # Configuration parameters: CountAsOne.
62
62
  RSpec/ExampleLength:
63
63
  Max: 103
64
64
 
65
- # Offense count: 10
66
- # Configuration parameters: Max, AllowedGroups.
67
- RSpec/NestedGroups:
68
- Exclude:
69
- - 'spec/cocina/models/mapping/normalizers/mods/origin_info_normalizer_spec.rb'
70
- - 'spec/cocina/models/validators/date_time_validator_spec.rb'
71
-
72
65
  # Offense count: 19
73
66
  RSpec/PendingWithoutReason:
74
67
  Exclude:
@@ -99,7 +92,7 @@ Style/MultilineBlockChain:
99
92
  - 'lib/cocina/models/mapping/to_mods/form.rb'
100
93
  - 'lib/cocina/models/mapping/to_mods/subject.rb'
101
94
 
102
- # Offense count: 239
95
+ # Offense count: 249
103
96
  # This cop supports safe autocorrection (--autocorrect).
104
97
  # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
105
98
  # URISchemes: http, https
data/Gemfile.lock CHANGED
@@ -1,13 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cocina-models (0.91.4)
4
+ cocina-models (0.93.0)
5
5
  activesupport
6
6
  deprecation
7
7
  dry-struct (~> 1.0)
8
8
  dry-types (~> 1.1)
9
9
  edtf
10
10
  equivalent-xml
11
+ i18n
11
12
  jsonpath
12
13
  nokogiri
13
14
  openapi3_parser
@@ -79,7 +80,7 @@ GEM
79
80
  jsonpath (1.1.5)
80
81
  multi_json
81
82
  language_server-protocol (3.17.0.3)
82
- mini_portile2 (2.8.4)
83
+ mini_portile2 (2.8.5)
83
84
  minitest (5.20.0)
84
85
  multi_json (1.15.0)
85
86
  mutex_m (0.1.2)
@@ -96,10 +97,10 @@ GEM
96
97
  racc
97
98
  patience_diff (1.2.0)
98
99
  optimist (~> 3.0)
99
- racc (1.7.1)
100
+ racc (1.7.3)
100
101
  rack (3.0.8)
101
102
  rainbow (3.1.1)
102
- rake (13.0.6)
103
+ rake (13.1.0)
103
104
  regexp_parser (2.8.2)
104
105
  rexml (3.2.6)
105
106
  rspec (3.12.0)
@@ -119,8 +120,7 @@ GEM
119
120
  rspec-core (>= 2, < 4, != 2.12.0)
120
121
  rss (0.3.0)
121
122
  rexml
122
- rubocop (1.57.1)
123
- base64 (~> 0.1.1)
123
+ rubocop (1.57.2)
124
124
  json (~> 2.3)
125
125
  language_server-protocol (>= 3.17.0)
126
126
  parallel (~> 1.10)
@@ -131,7 +131,7 @@ GEM
131
131
  rubocop-ast (>= 1.28.1, < 2.0)
132
132
  ruby-progressbar (~> 1.7)
133
133
  unicode-display_width (>= 2.4.0, < 3.0)
134
- rubocop-ast (1.29.0)
134
+ rubocop-ast (1.30.0)
135
135
  parser (>= 3.2.1.0)
136
136
  rubocop-capybara (2.19.0)
137
137
  rubocop (~> 1.41)
@@ -139,8 +139,8 @@ GEM
139
139
  rubocop (~> 1.33)
140
140
  rubocop-rake (0.6.0)
141
141
  rubocop (~> 1.0)
142
- rubocop-rspec (2.24.1)
143
- rubocop (~> 1.33)
142
+ rubocop-rspec (2.25.0)
143
+ rubocop (~> 1.40)
144
144
  rubocop-capybara (~> 2.17)
145
145
  rubocop-factory_bot (~> 2.22)
146
146
  ruby-progressbar (1.13.0)
@@ -155,7 +155,7 @@ GEM
155
155
  attr_extras (>= 6.2.4)
156
156
  diff-lcs
157
157
  patience_diff
158
- thor (1.2.2)
158
+ thor (1.3.0)
159
159
  tzinfo (2.0.6)
160
160
  concurrent-ruby (~> 1.0)
161
161
  unicode-display_width (2.5.0)
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency 'dry-types', '~> 1.1'
31
31
  spec.add_dependency 'edtf' # used for date/time validation
32
32
  spec.add_dependency 'equivalent-xml' # for diffing MODS
33
+ spec.add_dependency 'i18n' # for validating BCP 47 language tags, according to RFC 4646
33
34
  spec.add_dependency 'jsonpath' # used for date/time validation
34
35
  spec.add_dependency 'nokogiri'
35
36
  spec.add_dependency 'openapi3_parser' # Parsing openapi doc
@@ -363,6 +363,8 @@ _Path: identifier.type_
363
363
  * document number
364
364
  * DOI
365
365
  * druid
366
+ * FOLIO
367
+ * FOLIO HRID for the source record of the metadata.
366
368
  * GTIN-14 ID
367
369
  * Handle
368
370
  * inventory number
@@ -22,8 +22,10 @@ module Cocina
22
22
  attribute :version, Types::Strict::Integer
23
23
  # MIME Type of the File.
24
24
  attribute? :hasMimeType, Types::Strict::String
25
+ # BCP 47 language tag: https://www.rfc-editor.org/rfc/rfc4646.txt -- other applications (like media players) expect language codes of this format, see e.g. https://videojs.com/guides/text-tracks/#srclang
26
+ attribute? :languageTag, LanguageTag.optional
25
27
  # Use for the File.
26
- attribute? :use, Types::Strict::String
28
+ attribute? :use, FileUse.optional
27
29
  attribute :hasMessageDigests, Types::Strict::Array.of(MessageDigest).default([].freeze)
28
30
  attribute(:access, FileAccess.default { FileAccess.new })
29
31
  attribute(:administrative, FileAdministrative.default { FileAdministrative.new })
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ FileUse = Types::String
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ LanguageTag = Types::String
6
+ end
7
+ end
@@ -13,8 +13,11 @@ module Cocina
13
13
  attribute? :size, Types::Strict::Integer
14
14
  attribute :version, Types::Strict::Integer
15
15
  attribute? :hasMimeType, Types::Strict::String
16
+ # BCP 47 language tag: https://www.rfc-editor.org/rfc/rfc4646.txt -- other applications (like media players) expect language codes of this format, see e.g. https://videojs.com/guides/text-tracks/#srclang
17
+ attribute? :languageTag, LanguageTag.optional
16
18
  attribute? :externalIdentifier, Types::Strict::String
17
- attribute? :use, Types::Strict::String
19
+ # Use for the File.
20
+ attribute? :use, FileUse.optional
18
21
  attribute :hasMessageDigests, Types::Strict::Array.of(MessageDigest).default([].freeze)
19
22
  attribute(:access, FileAccess.default { FileAccess.new })
20
23
  attribute(:administrative, FileAdministrative.default { FileAdministrative.new })
@@ -39,12 +39,7 @@ module Cocina
39
39
  end
40
40
 
41
41
  def invalid_files
42
- @invalid_files ||=
43
- [].tap do |invalid_files|
44
- files.each do |file|
45
- invalid_files << file if invalid?(file)
46
- end
47
- end
42
+ @invalid_files ||= files.select { |file| invalid?(file) }
48
43
  end
49
44
 
50
45
  def invalid_filenames
@@ -12,7 +12,8 @@ module Cocina
12
12
  def initialize(clazz, attributes)
13
13
  @clazz = clazz
14
14
  @attributes = attributes
15
- @error_paths = []
15
+ @error_paths_multiple = []
16
+ @error_paths_blank = []
16
17
  end
17
18
 
18
19
  def validate
@@ -20,21 +21,21 @@ module Cocina
20
21
 
21
22
  validate_obj(attributes, [])
22
23
 
23
- return if error_paths.empty?
24
-
25
- raise ValidationError, "Multiple value, groupedValue, structuredValue, and parallelValue in description: #{error_paths.join(', ')}"
24
+ raise ValidationError, "Multiple value, groupedValue, structuredValue, and parallelValue in description: #{error_paths_multiple.join(', ')}" unless error_paths_multiple.empty?
25
+ raise ValidationError, "Blank value in description: #{error_paths_blank.join(', ')}" unless error_paths_blank.empty?
26
26
  end
27
27
 
28
28
  private
29
29
 
30
- attr_reader :clazz, :attributes, :error_paths
30
+ attr_reader :clazz, :attributes, :error_paths_blank, :error_paths_multiple
31
31
 
32
32
  def meets_preconditions?
33
33
  [Cocina::Models::Description, Cocina::Models::RequestDescription].include?(clazz)
34
34
  end
35
35
 
36
36
  def validate_hash(hash, path)
37
- validate_values(hash, path)
37
+ validate_values_for_blanks(hash, path)
38
+ validate_values_for_multiples(hash, path)
38
39
  hash.each do |key, obj|
39
40
  validate_obj(obj, path + [key])
40
41
  end
@@ -51,10 +52,16 @@ module Cocina
51
52
  validate_array(obj, path) if obj.is_a?(Array)
52
53
  end
53
54
 
54
- def validate_values(hash, path)
55
+ def validate_values_for_blanks(hash, path)
56
+ return unless hash[:value] && hash[:value].is_a?(String) && /\A\s+\z/.match?(hash[:value]) # rubocop:disable Style/SafeNavigation
57
+
58
+ error_paths_blank << path_to_s(path)
59
+ end
60
+
61
+ def validate_values_for_multiples(hash, path)
55
62
  return unless hash.count { |key, value| %i[value groupedValue structuredValue parallelValue].include?(key) && value.present? } > 1
56
63
 
57
- error_paths << path_to_s(path)
64
+ error_paths_multiple << path_to_s(path)
58
65
  end
59
66
 
60
67
  def path_to_s(path)
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cocina
4
+ module Models
5
+ module Validators
6
+ # Validates that a languageTag is valid according to RFC 4646, if one is present
7
+ class LanguageTagValidator
8
+ def self.validate(clazz, attributes)
9
+ new(clazz, attributes).validate
10
+ end
11
+
12
+ def initialize(clazz, attributes)
13
+ @clazz = clazz
14
+ @attributes = attributes
15
+ end
16
+
17
+ def validate
18
+ return unless meets_preconditions?
19
+
20
+ return if invalid_files.empty?
21
+
22
+ raise ValidationError, 'Some files have invalid language tags according to RFC 4646: ' \
23
+ "#{invalid_filenames_with_language_tags.join(', ')}"
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :clazz, :attributes
29
+
30
+ def meets_preconditions?
31
+ dro?
32
+ end
33
+
34
+ def dro?
35
+ (clazz::TYPES & DRO::TYPES).any?
36
+ rescue NameError
37
+ false
38
+ end
39
+
40
+ def valid_language_tag?(file)
41
+ # I18n::Locale::Tag::Rfc4646.tag will return an instance of I18n::Locale::Tag::Rfc4646 (with fields like language, script,
42
+ # region) for strings that can be parsed according to RFC 4646, and nil for strings that do not conform to the spec.
43
+ I18n::Locale::Tag::Rfc4646.tag(file[:languageTag]).present?
44
+ end
45
+
46
+ def invalid_files
47
+ @invalid_files ||= language_tag_files.reject { |file| valid_language_tag?(file) }
48
+ end
49
+
50
+ def invalid_filenames_with_language_tags
51
+ invalid_files.map { |invalid_file| "#{invalid_file[:filename] || invalid_file[:label]} (#{invalid_file[:languageTag]})" }
52
+ end
53
+
54
+ def language_tag_files
55
+ files.select { |file| file[:languageTag].present? }
56
+ end
57
+
58
+ def files
59
+ [].tap do |files|
60
+ next if attributes.dig(:structural, :contains).nil?
61
+
62
+ attributes[:structural][:contains].each do |fileset|
63
+ next if fileset.dig(:structural, :contains).nil?
64
+
65
+ fileset[:structural][:contains].each do |file|
66
+ files << file
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -15,7 +15,8 @@ module Cocina
15
15
  # See also spec/cocina/models/validatable_spec.rb:59
16
16
  # DescriptionTypesValidator,
17
17
  DescriptionValuesValidator,
18
- DateTimeValidator
18
+ DateTimeValidator,
19
+ LanguageTagValidator
19
20
  ].freeze
20
21
 
21
22
  def self.validate(clazz, attributes)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Cocina
4
4
  module Models
5
- VERSION = '0.91.4'
5
+ VERSION = '0.93.0'
6
6
  end
7
7
  end
data/openapi.yml CHANGED
@@ -1044,9 +1044,10 @@ components:
1044
1044
  hasMimeType:
1045
1045
  description: MIME Type of the File.
1046
1046
  type: string
1047
+ languageTag:
1048
+ $ref: '#/components/schemas/LanguageTag'
1047
1049
  use:
1048
- description: Use for the File.
1049
- type: string
1050
+ $ref: '#/components/schemas/FileUse'
1050
1051
  hasMessageDigests:
1051
1052
  type: array
1052
1053
  items:
@@ -1141,6 +1142,10 @@ components:
1141
1142
  type: array
1142
1143
  items:
1143
1144
  $ref: '#/components/schemas/File'
1145
+ FileUse:
1146
+ description: Use for the File.
1147
+ type: string
1148
+ nullable: true
1144
1149
  FolioCatalogLink:
1145
1150
  description: A linkage between an object and a Folio catalog record
1146
1151
  type: object
@@ -1269,6 +1274,10 @@ components:
1269
1274
  valueLanguage:
1270
1275
  # description: present for mapping to additional schemas in the future and for consistency but not otherwise used
1271
1276
  $ref: "#/components/schemas/DescriptiveValueLanguage"
1277
+ LanguageTag:
1278
+ description: "BCP 47 language tag: https://www.rfc-editor.org/rfc/rfc4646.txt -- other applications (like media players) expect language codes of this format, see e.g. https://videojs.com/guides/text-tracks/#srclang"
1279
+ type: string
1280
+ nullable: true
1272
1281
  License:
1273
1282
  description: The license governing reuse of the DRO. Should be an IRI for known licenses (i.e. CC, RightsStatement.org URI, etc.).
1274
1283
  type: string
@@ -1775,10 +1784,12 @@ components:
1775
1784
  type: integer
1776
1785
  hasMimeType:
1777
1786
  type: string
1787
+ languageTag:
1788
+ $ref: '#/components/schemas/LanguageTag'
1778
1789
  externalIdentifier:
1779
1790
  type: string
1780
1791
  use:
1781
- type: string
1792
+ $ref: '#/components/schemas/FileUse'
1782
1793
  hasMessageDigests:
1783
1794
  type: array
1784
1795
  items:
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.91.4
4
+ version: 0.93.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: 2023-10-18 00:00:00.000000000 Z
11
+ date: 2023-11-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: i18n
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'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: jsonpath
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -415,11 +429,13 @@ files:
415
429
  - lib/cocina/models/file_set.rb
416
430
  - lib/cocina/models/file_set_structural.rb
417
431
  - lib/cocina/models/file_set_type.rb
432
+ - lib/cocina/models/file_use.rb
418
433
  - lib/cocina/models/folio_catalog_link.rb
419
434
  - lib/cocina/models/geographic.rb
420
435
  - lib/cocina/models/identification.rb
421
436
  - lib/cocina/models/lane_medical_barcode.rb
422
437
  - lib/cocina/models/language.rb
438
+ - lib/cocina/models/language_tag.rb
423
439
  - lib/cocina/models/license.rb
424
440
  - lib/cocina/models/location_based_access.rb
425
441
  - lib/cocina/models/location_based_download_access.rb
@@ -516,6 +532,7 @@ files:
516
532
  - lib/cocina/models/validators/date_time_validator.rb
517
533
  - lib/cocina/models/validators/description_types_validator.rb
518
534
  - lib/cocina/models/validators/description_values_validator.rb
535
+ - lib/cocina/models/validators/language_tag_validator.rb
519
536
  - lib/cocina/models/validators/open_api_validator.rb
520
537
  - lib/cocina/models/validators/purl_validator.rb
521
538
  - lib/cocina/models/validators/validator.rb
@@ -545,7 +562,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
545
562
  - !ruby/object:Gem::Version
546
563
  version: '0'
547
564
  requirements: []
548
- rubygems_version: 3.3.7
565
+ rubygems_version: 3.4.19
549
566
  signing_key:
550
567
  specification_version: 4
551
568
  summary: Data models for the SDR