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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +51 -0
- data/.github/pull_request_template.md +8 -1
- data/.rubocop.yml +56 -1
- data/README.md +11 -14
- data/cocina-models.gemspec +3 -3
- data/docs/index.html +20 -0
- data/docs/maps/DRO.json +1 -1
- data/exe/generator +1 -1
- data/lib/cocina/generator.rb +0 -11
- data/lib/cocina/generator/generator.rb +1 -1
- data/lib/cocina/generator/schema.rb +29 -8
- data/lib/cocina/generator/schema_array.rb +5 -1
- data/lib/cocina/generator/schema_base.rb +22 -0
- data/lib/cocina/generator/schema_value.rb +3 -25
- data/lib/cocina/models.rb +7 -5
- data/lib/cocina/models/access.rb +4 -0
- data/lib/cocina/models/admin_policy.rb +1 -1
- data/lib/cocina/models/admin_policy_administrative.rb +1 -1
- data/lib/cocina/models/administrative.rb +1 -1
- data/lib/cocina/models/applies_to.rb +9 -0
- data/lib/cocina/models/collection.rb +1 -1
- data/lib/cocina/models/contributor.rb +14 -0
- data/lib/cocina/models/description.rb +17 -1
- data/lib/cocina/models/descriptive_admin_metadata.rb +12 -0
- data/lib/cocina/models/descriptive_basic_value.rb +21 -0
- data/lib/cocina/models/descriptive_structured_value.rb +9 -0
- data/lib/cocina/models/descriptive_value.rb +23 -0
- data/lib/cocina/models/descriptive_value_required.rb +23 -0
- data/lib/cocina/models/dro.rb +2 -2
- data/lib/cocina/models/dro_access.rb +7 -1
- data/lib/cocina/models/event.rb +15 -0
- data/lib/cocina/models/file.rb +1 -1
- data/lib/cocina/models/request_admin_policy.rb +1 -1
- data/lib/cocina/models/request_collection.rb +2 -2
- data/lib/cocina/models/request_dro.rb +4 -4
- data/lib/cocina/models/request_identification.rb +11 -0
- data/lib/cocina/models/sequence.rb +1 -0
- data/lib/cocina/models/source.rb +14 -0
- data/lib/cocina/models/validator.rb +12 -6
- data/lib/cocina/models/version.rb +1 -1
- data/openapi.yml +350 -20
- metadata +23 -18
- data/.travis.yml +0 -23
- data/docs/README.md +0 -9
- data/docs/_config.yml +0 -1
- data/docs/meta.json +0 -9
- data/docs/schema.json +0 -1654
- data/docs/schema.md +0 -268
- data/lib/cocina/models/title.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 258bc916aaaff62a84cd0bd264f8957e3c916310134ef3372cfdd3eca4c1ff4e
|
4
|
+
data.tar.gz: c26a6cc519230a25d19b218ddcec52fa552ea7780f647b6d449aee223be35eef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/.rubocop.yml
CHANGED
@@ -4,7 +4,7 @@ inherit_from: .rubocop_todo.yml
|
|
4
4
|
require:
|
5
5
|
- rubocop-rspec
|
6
6
|
|
7
|
-
|
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
|
-
[![
|
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
|
-
|
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
|
-
##
|
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
|
-
|
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
|
-
|
36
|
-
prmd verify schema.json
|
35
|
+
## Using this gem
|
37
36
|
|
38
|
-
|
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
|
-
|
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.
|
data/cocina-models.gemspec
CHANGED
@@ -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', '~>
|
36
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
37
37
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
38
|
-
spec.add_development_dependency 'rubocop', '~> 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
|
data/docs/index.html
ADDED
@@ -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>
|
data/docs/maps/DRO.json
CHANGED
@@ -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", "
|
87
|
+
"enum": ["world", "stanford", "location-based", "none"]
|
88
88
|
},
|
89
89
|
"embargo": {
|
90
90
|
"description": "Embargo metadata",
|
data/exe/generator
CHANGED
data/lib/cocina/generator.rb
CHANGED
@@ -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
|
@@ -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 ||=
|
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(#{
|
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
|
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
|
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
|
|
data/lib/cocina/models.rb
CHANGED
@@ -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 '
|
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
|
-
|
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,
|
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,
|
103
|
+
clazz.new(dyn, false, validate)
|
102
104
|
end
|
103
105
|
end
|
104
106
|
end
|