cocina-models 0.30.0 → 0.34.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +51 -0
  3. data/.github/pull_request_template.md +8 -1
  4. data/.rubocop.yml +56 -1
  5. data/README.md +11 -14
  6. data/cocina-models.gemspec +3 -3
  7. data/docs/index.html +20 -0
  8. data/docs/maps/DRO.json +1 -1
  9. data/exe/generator +1 -1
  10. data/lib/cocina/generator.rb +0 -11
  11. data/lib/cocina/generator/generator.rb +1 -1
  12. data/lib/cocina/generator/schema.rb +29 -8
  13. data/lib/cocina/generator/schema_array.rb +5 -1
  14. data/lib/cocina/generator/schema_base.rb +22 -0
  15. data/lib/cocina/generator/schema_value.rb +3 -25
  16. data/lib/cocina/models.rb +7 -5
  17. data/lib/cocina/models/access.rb +4 -0
  18. data/lib/cocina/models/admin_policy.rb +1 -1
  19. data/lib/cocina/models/admin_policy_administrative.rb +1 -1
  20. data/lib/cocina/models/administrative.rb +1 -1
  21. data/lib/cocina/models/applies_to.rb +9 -0
  22. data/lib/cocina/models/collection.rb +1 -1
  23. data/lib/cocina/models/contributor.rb +14 -0
  24. data/lib/cocina/models/description.rb +17 -1
  25. data/lib/cocina/models/descriptive_admin_metadata.rb +12 -0
  26. data/lib/cocina/models/descriptive_basic_value.rb +21 -0
  27. data/lib/cocina/models/descriptive_structured_value.rb +9 -0
  28. data/lib/cocina/models/descriptive_value.rb +23 -0
  29. data/lib/cocina/models/descriptive_value_required.rb +23 -0
  30. data/lib/cocina/models/dro.rb +2 -2
  31. data/lib/cocina/models/dro_access.rb +7 -1
  32. data/lib/cocina/models/event.rb +15 -0
  33. data/lib/cocina/models/file.rb +1 -1
  34. data/lib/cocina/models/request_admin_policy.rb +1 -1
  35. data/lib/cocina/models/request_collection.rb +2 -2
  36. data/lib/cocina/models/request_dro.rb +4 -4
  37. data/lib/cocina/models/request_identification.rb +11 -0
  38. data/lib/cocina/models/sequence.rb +1 -0
  39. data/lib/cocina/models/source.rb +14 -0
  40. data/lib/cocina/models/validator.rb +12 -6
  41. data/lib/cocina/models/version.rb +1 -1
  42. data/openapi.yml +350 -20
  43. metadata +23 -18
  44. data/.travis.yml +0 -23
  45. data/docs/README.md +0 -9
  46. data/docs/_config.yml +0 -1
  47. data/docs/meta.json +0 -9
  48. data/docs/schema.json +0 -1654
  49. data/docs/schema.md +0 -268
  50. data/lib/cocina/models/title.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae254699614b4355dea34dab3e0ef324123b21d30d592c24ceffbad5fed3515e
4
- data.tar.gz: da8eecbff7bec360ed56445d61fc558b4a18684df9f0f92bc4a98668e2d16dac
3
+ metadata.gz: 258bc916aaaff62a84cd0bd264f8957e3c916310134ef3372cfdd3eca4c1ff4e
4
+ data.tar.gz: c26a6cc519230a25d19b218ddcec52fa552ea7780f647b6d449aee223be35eef
5
5
  SHA512:
6
- metadata.gz: 4c6c2e1beb7cd1b28d73bee2641be223497e18764bbffae9953fde54995336ae970be70e8d1ac31fa96f131ad9c17a02b2a5ef7b9e9beb098ecf9925899333ad
7
- data.tar.gz: 53246fecec8a7e4516aa3fef318dddfa644158678f4894d1eeb1285d0c247b6a2bdafd68a491ae18b19e41fa5a195bc2f078ba303adb9f83f37b26c4121585cf
6
+ metadata.gz: cd057f437502f2e54f6b2c689b6aef4c808ac4912c91d87755abfeb13c24100ef3380f262e7ec27f42592ecbd59b16d68669c878a31c55fc2402f069c5142f1e
7
+ data.tar.gz: 5a27ebb9108e9838117b02ec015bb025120d82e3f90749b12d23b523f3eea4efe865b6e405f649574f10f88ee0a70811334c339e9d522f5f1186b72f59df9240
@@ -0,0 +1,51 @@
1
+ version: 2.1
2
+ executors:
3
+ docker-publisher:
4
+ docker:
5
+ - image: circleci/buildpack-deps:stretch
6
+ jobs:
7
+ test:
8
+ docker:
9
+ - image: circleci/ruby:2.7.0-node
10
+ steps:
11
+ - checkout
12
+ - run:
13
+ name: install bundler
14
+ command: gem install bundler -v 2.1.4
15
+ - run:
16
+ name: Install gem dependencies
17
+ command: bundle check || bundle install
18
+ - run:
19
+ name: Lint using rubocop
20
+ command: bundle exec rubocop
21
+ - run:
22
+ name: Setup Code Climate test-reporter
23
+ command: |
24
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
25
+ chmod +x ./cc-test-reporter
26
+ ./cc-test-reporter before-build
27
+ - run:
28
+ name: Run RSpec test suite
29
+ command: bundle exec rspec
30
+ - run:
31
+ name: upload test coverage report to Code Climate
32
+ command: ./cc-test-reporter after-build --coverage-input-type simplecov --exit-code $?
33
+ - run:
34
+ name: Validate API specification
35
+ command: |
36
+ sudo npm install -g openapi-enforcer-cli
37
+ result=$(openapi-enforcer validate openapi.yml)
38
+ [[ $result =~ "Document is valid" ]] && {
39
+ echo "Validation good"
40
+ exit 0
41
+ } || {
42
+ echo $result
43
+ exit 1
44
+ }
45
+
46
+ workflows:
47
+ version: 2
48
+
49
+ test:
50
+ jobs:
51
+ - test
@@ -2,4 +2,11 @@
2
2
 
3
3
 
4
4
 
5
- ## Was the documentation (README, wiki) updated?
5
+ ## How was this change tested?
6
+
7
+
8
+
9
+ ## Which documentation and/or configurations were updated?
10
+
11
+
12
+
@@ -4,7 +4,7 @@ inherit_from: .rubocop_todo.yml
4
4
  require:
5
5
  - rubocop-rspec
6
6
 
7
- Metrics/LineLength:
7
+ Layout/LineLength:
8
8
  Max: 114
9
9
  Exclude:
10
10
  - lib/cocina/models/*
@@ -22,7 +22,62 @@ RSpec/MultipleExpectations:
22
22
 
23
23
  RSpec/ExampleLength:
24
24
  Max: 18
25
+ Exclude:
26
+ - spec/cocina/models/description_spec.rb
27
+ - spec/cocina/models/dro_shared_examples.rb
25
28
 
26
29
  Style/Documentation:
27
30
  Exclude:
28
31
  - lib/cocina/models/*
32
+
33
+ Layout/EmptyLinesAroundAttributeAccessor:
34
+ Enabled: true
35
+
36
+ Layout/SpaceAroundMethodCallOperator:
37
+ Enabled: true
38
+
39
+ Lint/DeprecatedOpenSSLConstant:
40
+ Enabled: true
41
+
42
+ Lint/MixedRegexpCaptureTypes:
43
+ Enabled: true
44
+
45
+ Lint/RaiseException:
46
+ Enabled: true
47
+
48
+ Lint/StructNewOverride:
49
+ Enabled: true
50
+
51
+ Style/AccessorGrouping:
52
+ Enabled: true
53
+
54
+ Style/BisectedAttrAccessor:
55
+ Enabled: true
56
+
57
+ Style/ExponentialNotation:
58
+ Enabled: true
59
+
60
+ Style/HashEachMethods:
61
+ Enabled: true
62
+
63
+ Style/HashTransformKeys:
64
+ Enabled: true
65
+
66
+ Style/HashTransformValues:
67
+ Enabled: true
68
+
69
+ Style/RedundantAssignment:
70
+ Enabled: true
71
+
72
+ Style/RedundantFetchBlock:
73
+ Enabled: true
74
+
75
+ Style/RedundantRegexpCharacterClass:
76
+ Enabled: true
77
+
78
+ Style/RedundantRegexpEscape:
79
+ Enabled: true
80
+
81
+ Style/SlicingWithRange:
82
+ Enabled: true
83
+
data/README.md CHANGED
@@ -1,18 +1,21 @@
1
- [![Build Status](https://travis-ci.com/sul-dlss/cocina-models.svg?branch=master)](https://travis-ci.com/sul-dlss/cocina-models)
1
+ [![CircleCI](https://circleci.com/gh/sul-dlss/cocina-models.svg?style=svg)](https://circleci.com/gh/sul-dlss/cocina-models)
2
2
  [![Test Coverage](https://api.codeclimate.com/v1/badges/472273351516ac01dce1/test_coverage)](https://codeclimate.com/github/sul-dlss/cocina-models/test_coverage)
3
3
  [![Maintainability](https://api.codeclimate.com/v1/badges/472273351516ac01dce1/maintainability)](https://codeclimate.com/github/sul-dlss/cocina-models/maintainability)
4
4
  [![Gem Version](https://badge.fury.io/rb/cocina-models.svg)](https://badge.fury.io/rb/cocina-models)
5
+ [![OpenAPI Validator](http://validator.swagger.io/validator?url=https://raw.githubusercontent.com/sul-dlss/cocina-models/master/openapi.yml)](http://validator.swagger.io/validator/debug?url=https://raw.githubusercontent.com/sul-dlss/cocina-models/master/openapi.yml)
5
6
 
6
7
  # Cocina::Models
7
8
 
8
9
  This is a Ruby implementation of the SDR data model (named "COCINA"). The data being modeled includes digital repository objects.
9
10
 
10
- It provides a way for consumers to validate objects against models using dry-struct and dry-types gems.
11
+ Validation is performed by openapi (using OpenAPIParser). Modeling is provided by dry-struct and dry-types. Together, these provide a way for consumers to validate objects against models and to manipulate thos objects.
11
12
 
12
13
  This is a work in progress that will ultimately implement the full [COCINA data model](http://sul-dlss.github.io/cocina-models/). See also [architecture documentation](https://sul-dlss.github.io/taco-truck/COCINA.html#cocina-data-models--shapes).
13
14
 
14
15
  ## Generate models from openapi.yml
15
16
 
17
+ Note that only a small subset of openapi is supported. If you are using a new openapi feature or pattern, verify that the model will be generated as expected.
18
+
16
19
  ### All
17
20
  ```
18
21
  exe/generator generate
@@ -23,20 +26,14 @@ exe/generator generate
23
26
  exe/generator generate_schema DRO
24
27
  ```
25
28
 
26
- ## Generate Documentation
29
+ ## Testing
27
30
 
28
- ```
29
- gem install prmd
30
- cd docs
31
+ The generator is tested via its output when run against `openapi.yml`, viz., the Cocina model classes. Thus, `generate` should be run after any changes to `openapi.yml`.
31
32
 
32
- # Combine into a single schema
33
- prmd combine --meta meta.json maps/ > schema.json
33
+ Beyond what is necessary to test the generator, the Cocina model classes are not tested, i.e., they are assumed to be as specified in `openapi.yml`.
34
34
 
35
- # Check it’s all good
36
- prmd verify schema.json
35
+ ## Using this gem
37
36
 
38
- # Build docs
39
- prmd doc schema.json > schema.md
40
- ```
37
+ If you are using this gem in an application that has an API that accepts Cocina models (e.g., SDR API, Dor-Services-App), make sure that the `openapi.yml` for the application includes the schemas that match the schemas in this `openapi.yml`.
41
38
 
42
- Then check in the resulting changes to `docs/schema.json` and `docs/schema.md`
39
+ This can be accomplished by cutting and pasting these schemas. By convention, these schemas are listed first in the `openapi.yml` of the associated projects, followed by the application-specific schemas.
@@ -33,9 +33,9 @@ Gem::Specification.new do |spec|
33
33
 
34
34
  spec.add_development_dependency 'bundler', '~> 2.0'
35
35
  spec.add_development_dependency 'committee'
36
- spec.add_development_dependency 'rake', '~> 12.0'
36
+ spec.add_development_dependency 'rake', '~> 13.0'
37
37
  spec.add_development_dependency 'rspec', '~> 3.0'
38
- spec.add_development_dependency 'rubocop', '~> 0.74.0'
38
+ spec.add_development_dependency 'rubocop', '~> 0.87'
39
39
  spec.add_development_dependency 'rubocop-rspec'
40
- spec.add_development_dependency 'simplecov'
40
+ spec.add_development_dependency 'simplecov', '~> 0.17.0'
41
41
  end
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>API documentation</title>
5
+
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+
8
+
9
+ <style>
10
+ body {
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <redoc spec-url='https://raw.githubusercontent.com/sul-dlss/cocina-models/master/openapi.yml'></redoc>
18
+ <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
19
+ </body>
20
+ </html>
@@ -84,7 +84,7 @@
84
84
  "download": {
85
85
  "description": "Download level for the DRO metadata.",
86
86
  "type": "string",
87
- "enum": ["world", "stanford", "location-based", "citation-only", "dark"]
87
+ "enum": ["world", "stanford", "location-based", "none"]
88
88
  },
89
89
  "embargo": {
90
90
  "description": "Embargo metadata",
@@ -4,6 +4,6 @@
4
4
  require 'bundler/setup'
5
5
  $LOAD_PATH.unshift 'lib'
6
6
 
7
- require 'cocina/generator'
7
+ require 'cocina/models'
8
8
 
9
9
  Cocina::Generator::Generator.start(ARGV)
@@ -1,16 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'zeitwerk'
4
- require 'yaml'
5
- require 'active_support/core_ext/string'
6
- require 'thor'
7
- require 'openapi3_parser'
8
- require 'byebug'
9
-
10
- loader = Zeitwerk::Loader.new
11
- loader.push_dir(File.absolute_path("#{__FILE__}/../.."))
12
- loader.setup
13
-
14
3
  module Cocina
15
4
  # Module for generating Cocina models from openapi.
16
5
  module Generator
@@ -17,7 +17,7 @@ module Cocina
17
17
  def generate
18
18
  clean_output
19
19
 
20
- schemas.keys.each do |schema_name|
20
+ schemas.each_key do |schema_name|
21
21
  schema = schema_for(schema_name)
22
22
  generate_for(schema) if schema
23
23
  end
@@ -5,12 +5,7 @@ module Cocina
5
5
  # Class for generating from an openapi schema
6
6
  class Schema < SchemaBase
7
7
  def schema_properties
8
- @schema_properties ||= schema_doc.properties.map do |key, properties_doc|
9
- property_class_for(properties_doc).new(properties_doc,
10
- key: key,
11
- required: schema_doc.requires?(properties_doc),
12
- parent: self)
13
- end
8
+ @schema_properties ||= (properties + all_of_properties).uniq(&:key)
14
9
  end
15
10
 
16
11
  def generate
@@ -54,7 +49,7 @@ module Cocina
54
49
 
55
50
  def types
56
51
  type_properties_doc = schema_doc.properties['type']
57
- return '' if type_properties_doc.nil?
52
+ return '' if type_properties_doc.nil? || type_properties_doc.enum.nil?
58
53
 
59
54
  types_list = type_properties_doc.enum.map { |item| "'#{item}'" }.join(",\n ")
60
55
 
@@ -70,7 +65,7 @@ module Cocina
70
65
 
71
66
  <<~RUBY
72
67
  def self.new(attributes = default_attributes, safe = false, validate = true, &block)
73
- Validator.validate(self, attributes.with_indifferent_access) if validate
68
+ Validator.validate(self, attributes.with_indifferent_access) if validate && name
74
69
  super(attributes, safe, &block)
75
70
  end
76
71
  RUBY
@@ -79,6 +74,32 @@ module Cocina
79
74
  def validatable?
80
75
  !schema_doc.node_context.document.paths["/validate/#{schema_doc.name}"].nil?
81
76
  end
77
+
78
+ def properties
79
+ schema_properties_for(schema_doc)
80
+ end
81
+
82
+ def all_of_properties
83
+ all_of_properties_for(schema_doc)
84
+ end
85
+
86
+ def all_of_properties_for(doc)
87
+ return [] if doc.all_of.nil?
88
+
89
+ doc.all_of.map do |all_of_schema|
90
+ # All of for this + recurse
91
+ schema_properties_for(all_of_schema) + all_of_properties_for(all_of_schema)
92
+ end.flatten
93
+ end
94
+
95
+ def schema_properties_for(doc)
96
+ doc.properties.map do |key, properties_doc|
97
+ property_class_for(properties_doc).new(properties_doc,
98
+ key: key,
99
+ required: doc.requires?(properties_doc),
100
+ parent: self)
101
+ end
102
+ end
82
103
  end
83
104
  end
84
105
  end
@@ -5,7 +5,7 @@ module Cocina
5
5
  # Class for generating from an openapi array
6
6
  class SchemaArray < SchemaBase
7
7
  def generate
8
- "attribute :#{name.camelize(:lower)}, Types::Strict::Array.of(#{schema_doc.items.name})#{omittable}"
8
+ "attribute :#{name.camelize(:lower)}, Types::Strict::Array.of(#{array_of_type})#{omittable}"
9
9
  end
10
10
 
11
11
  def omittable
@@ -15,6 +15,10 @@ module Cocina
15
15
  '.meta(omittable: true)'
16
16
  end
17
17
  end
18
+
19
+ def array_of_type
20
+ schema_doc.items.name || "Types::#{dry_datatype(schema_doc.items)}"
21
+ end
18
22
  end
19
23
  end
20
24
  end
@@ -44,6 +44,28 @@ module Cocina
44
44
 
45
45
  "# example: #{schema_doc.example}\n"
46
46
  end
47
+
48
+ def dry_datatype(doc)
49
+ case doc.type
50
+ when 'integer'
51
+ 'Strict::Integer'
52
+ when 'string'
53
+ string_dry_datatype(doc)
54
+ when 'boolean'
55
+ 'Strict::Bool'
56
+ else
57
+ raise "#{schema_doc.type} not supported"
58
+ end
59
+ end
60
+
61
+ def string_dry_datatype(doc)
62
+ case doc.format
63
+ when 'date-time'
64
+ 'Params::DateTime'
65
+ else
66
+ 'Strict::String'
67
+ end
68
+ end
47
69
  end
48
70
  end
49
71
  end
@@ -4,36 +4,14 @@ module Cocina
4
4
  module Generator
5
5
  # Class for generating from an openapi value
6
6
  class SchemaValue < SchemaBase
7
- # rubocop:disable Metrics/LineLength
7
+ # rubocop:disable Layout/LineLength
8
8
  def generate
9
- "#{description}#{example}attribute :#{name.camelize(:lower)}, Types::#{dry_datatype}#{default}#{enum}#{omittable}"
9
+ "#{description}#{example}attribute :#{name.camelize(:lower)}, Types::#{dry_datatype(schema_doc)}#{default}#{enum}#{omittable}"
10
10
  end
11
- # rubocop:enable Metrics/LineLength
11
+ # rubocop:enable Layout/LineLength
12
12
 
13
13
  private
14
14
 
15
- def dry_datatype
16
- case schema_doc.type
17
- when 'integer'
18
- 'Strict::Integer'
19
- when 'string'
20
- string_dry_datatype
21
- when 'boolean'
22
- 'Strict::Bool'
23
- else
24
- raise "#{schema_doc.type} not supported"
25
- end
26
- end
27
-
28
- def string_dry_datatype
29
- case schema_doc.format
30
- when 'date-time'
31
- 'Params::DateTime'
32
- else
33
- 'Strict::String'
34
- end
35
- end
36
-
37
15
  def enum
38
16
  return '' unless schema_doc.enum
39
17
 
@@ -5,13 +5,15 @@ require 'zeitwerk'
5
5
  require 'dry-struct'
6
6
  require 'dry-types'
7
7
  require 'json'
8
+ require 'yaml'
8
9
  require 'openapi_parser'
10
+ require 'openapi3_parser'
9
11
  require 'active_support/core_ext/hash/indifferent_access'
10
- require 'yaml'
12
+ require 'active_support/core_ext/string'
13
+ require 'thor'
11
14
 
12
15
  # Help Zeitwerk find some of our classes
13
16
  class CocinaModelsInflector < Zeitwerk::Inflector
14
- # rubocop:disable Metrics/CyclomaticComplexity
15
17
  # rubocop:disable Metrics/MethodLength
16
18
  def camelize(basename, _abspath)
17
19
  case basename
@@ -31,7 +33,7 @@ class CocinaModelsInflector < Zeitwerk::Inflector
31
33
  super
32
34
  end
33
35
  end
34
- # rubocop:enable Metrics/CyclomaticComplexity
36
+
35
37
  # rubocop:enable Metrics/MethodLength
36
38
  end
37
39
 
@@ -78,7 +80,7 @@ module Cocina
78
80
  else
79
81
  raise UnknownTypeError, "Unknown type: '#{dyn.fetch('type')}'"
80
82
  end
81
- clazz.new(dyn, validate: validate)
83
+ clazz.new(dyn, false, validate)
82
84
  end
83
85
 
84
86
  # @param [Hash] dyn a ruby hash representation of the JSON serialization of a request for a Collection or DRO
@@ -98,7 +100,7 @@ module Cocina
98
100
  else
99
101
  raise UnknownTypeError, "Unknown type: '#{dyn.fetch('type')}'"
100
102
  end
101
- clazz.new(dyn, validate: validate)
103
+ clazz.new(dyn, false, validate)
102
104
  end
103
105
  end
104
106
  end