lacerda 1.1.0 → 2.0.1
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 +4 -4
- data/CHANGELOG.markdown +7 -0
- data/README.md +3 -3
- data/lib/lacerda/conversion.rb +8 -13
- data/lib/lacerda/conversion/apiary_to_json_schema.rb +2 -0
- data/lib/lacerda/conversion/data_structure.rb +4 -101
- data/lib/lacerda/conversion/data_structure/member.rb +53 -0
- data/lib/lacerda/conversion/data_structure/member/type.rb +100 -0
- data/lib/lacerda/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e13fe3981ad5e7dc33070f0343f43745d032269
|
4
|
+
data.tar.gz: 527ce7ff8b9e0a89a5d4e0d1f8c50a506b9b4cb5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ac94e6e2c6681138aa1dc83d6beb03d234ca0b8325e4c4aa5deb37967814ced69b38c866a77f98bbaed1051438a30a8ece9d59ae193d98740bc9a6b22ca515c
|
7
|
+
data.tar.gz: cc41f55260d38599b36e4403f567c9ba09483452d3890b99fa722f75211b638603631aa653ab9f328e0ea3c5f2e4ab937cc8f3a027ee2e136c8016a4ba512f57
|
data/CHANGELOG.markdown
CHANGED
data/README.md
CHANGED
@@ -62,10 +62,10 @@ conversion ourselves. These features from the MSON specification are currently s
|
|
62
62
|
- [x] primitive properties: `string`, `number`, `boolean`, `null`
|
63
63
|
- [x] `object` properties
|
64
64
|
- [x] `array` properties with items of one type
|
65
|
-
- [
|
65
|
+
- [x] `array` properties of mixed types
|
66
66
|
- [ ] `array` properties of arrays
|
67
|
-
- [
|
68
|
-
- [
|
67
|
+
- [x] `enum` properties
|
68
|
+
- [x] `One of` properties mutually exclusive properties
|
69
69
|
- [x] `Referencing`
|
70
70
|
- [ ] `Mixins`
|
71
71
|
- [ ] Variable property names
|
data/lib/lacerda/conversion.rb
CHANGED
@@ -99,8 +99,8 @@ module Lacerda
|
|
99
99
|
true
|
100
100
|
end
|
101
101
|
|
102
|
-
def self.raise_parsing_errors(mson_file,
|
103
|
-
parsing_errors = ast_parsing_errors(
|
102
|
+
def self.raise_parsing_errors(mson_file, elements)
|
103
|
+
parsing_errors = ast_parsing_errors(elements)
|
104
104
|
return if parsing_errors.empty?
|
105
105
|
raise Error, parsing_errors.prepend("The following errors were found in #{mson_file}:").join("\n")
|
106
106
|
end
|
@@ -122,6 +122,8 @@ module Lacerda
|
|
122
122
|
# The content of the ast parsing
|
123
123
|
elements = parse_result_contents_from_ast_file(filename)
|
124
124
|
|
125
|
+
raise_parsing_errors(filename, elements)
|
126
|
+
|
125
127
|
# We keep the content of the categories only, they could be annotations otherwise
|
126
128
|
result_categories = elements.select do |element|
|
127
129
|
element['element'] == 'category'
|
@@ -141,12 +143,10 @@ module Lacerda
|
|
141
143
|
end
|
142
144
|
end
|
143
145
|
|
144
|
-
def self.ast_parsing_annotation_messages(
|
145
|
-
|
146
|
-
annotation['meta']['classes'].include?(type)
|
147
|
-
end
|
148
|
-
return [] if annotations.empty?
|
149
|
-
annotations.map do |annotation|
|
146
|
+
def self.ast_parsing_annotation_messages(elements, type)
|
147
|
+
elements.select do |element|
|
148
|
+
element['element'] == 'annotation' && element['meta']['classes'].include?(type)
|
149
|
+
end.map do |annotation|
|
150
150
|
"#{type.capitalize} code #{annotation['attributes']['code']}: #{annotation['content']}"
|
151
151
|
end
|
152
152
|
end
|
@@ -176,10 +176,5 @@ module Lacerda
|
|
176
176
|
json = JSON.parse(open(filename).read)
|
177
177
|
json&.dig('content') || []
|
178
178
|
end
|
179
|
-
|
180
|
-
private_class_method def self.annotations_from_blueprint_ast(filename)
|
181
|
-
elements = parse_result_contents_from_ast_file(filename)
|
182
|
-
elements.select { |element| element['element'] == 'annotation' }
|
183
|
-
end
|
184
179
|
end
|
185
180
|
end
|
@@ -53,112 +53,16 @@ module Lacerda
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def add_properties_to_json_schema
|
56
|
-
|
57
|
-
return unless possible_members
|
58
|
-
# In the case that you create a nested data structure when type == 'object',
|
59
|
-
# the possible_members can be just a Hash, instead of an array
|
60
|
-
possible_members = [possible_members] if possible_members.is_a?(Hash)
|
61
|
-
members = possible_members.select { |d| d['element'] == 'member' }
|
56
|
+
members = Member.from_data_structure_content(@data&.first&.dig('content'), @scope)
|
62
57
|
# Iterate over each property
|
63
|
-
members.each do |
|
64
|
-
|
65
|
-
# Pluck some things out of the AST
|
66
|
-
content = s['content']
|
67
|
-
type_definition = content['value']
|
68
|
-
type = type_definition['element']
|
69
|
-
attributes = s.dig('attributes', 'typeAttributes') || []
|
70
|
-
is_required = attributes.include?('required')
|
71
|
-
|
72
|
-
# Prepare the json schema fragment
|
73
|
-
spec = {}
|
74
|
-
name = Lacerda.underscore(content['key']['content'])
|
75
|
-
|
76
|
-
# This is either type: primimtive or a oneOf { $ref: reference_name }
|
77
|
-
spec.merge!(primitive_or_oneOf(type, is_required))
|
78
|
-
|
79
|
-
# We might have a description
|
80
|
-
spec['description'] = s.dig('meta', 'description')
|
81
|
-
|
82
|
-
# If it's an array, we need to pluck out the item types
|
83
|
-
if type == 'array'
|
84
|
-
nestedTypes = type_definition['content'].map{|vc| vc['element'] }.uniq
|
85
|
-
spec['items'] = array_items(nestedTypes)
|
86
|
-
|
87
|
-
# If it's an object, we need recursion
|
88
|
-
elsif type == 'object'
|
89
|
-
spec['properties'] = {}
|
90
|
-
# The object has a value that will represent a data structure. the data
|
91
|
-
# passed to DataStructure normally is an array, but in this case if wouldn't
|
92
|
-
# So we have to wrap it if it's not an Array.
|
93
|
-
data = [content['value']] unless content['value'].is_a?(Array)
|
94
|
-
data_structure = DataStructure.new('tmp', [content['value']], @scope).to_json
|
95
|
-
spec['properties'].merge!(data_structure['properties'])
|
96
|
-
end
|
97
|
-
|
58
|
+
members.each do |member|
|
98
59
|
# Add the specification of this property to the schema
|
99
|
-
@schema['properties'][name] = spec
|
100
|
-
|
60
|
+
@schema['properties'][member.name] = member.spec
|
101
61
|
# Mark the property as required
|
102
|
-
@schema['required'] << name if
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# returns the type of an array, given its specified type(s). This will be
|
107
|
-
# either exactly one basic type, or a oneOf in case there are 1+ types
|
108
|
-
# or exactly 1 non-basic type.
|
109
|
-
# As there are specied types in the array, `nil` should not be a valid value
|
110
|
-
# and therefore required should be true.
|
111
|
-
def array_items(types)
|
112
|
-
if types.size == 1 && PRIMITIVES.include?(types.first)
|
113
|
-
primitive(types.first, true)
|
114
|
-
else
|
115
|
-
oneOf(types, true)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def primitive_or_oneOf(type, is_required)
|
120
|
-
return { 'type' => 'object' } if type.blank?
|
121
|
-
if PRIMITIVES.include?(type)
|
122
|
-
primitive(type, is_required)
|
123
|
-
else
|
124
|
-
oneOf([type], is_required)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# A basic type is either a primitive type with exactly 1 primitive type
|
129
|
-
# {'type' => [boolean] }
|
130
|
-
# a reference
|
131
|
-
# { '$ref' => "#/definitions/name" }
|
132
|
-
# or an object if the type in not there
|
133
|
-
# { 'type' => object }
|
134
|
-
# Basic types don't care about being required or not.
|
135
|
-
def basic_type(type)
|
136
|
-
return { 'type' => 'object' } if type.blank?
|
137
|
-
if PRIMITIVES.include?(type)
|
138
|
-
primitive(type, true)
|
139
|
-
else
|
140
|
-
reference(type)
|
62
|
+
@schema['required'] << member.name if member.required?
|
141
63
|
end
|
142
64
|
end
|
143
65
|
|
144
|
-
def oneOf(types, is_required)
|
145
|
-
types = types.map { |type| basic_type(type) }
|
146
|
-
types << { 'type' => 'null' } unless is_required
|
147
|
-
{
|
148
|
-
'oneOf' => types.uniq
|
149
|
-
}
|
150
|
-
end
|
151
|
-
|
152
|
-
def primitive(type, is_required)
|
153
|
-
types = [type]
|
154
|
-
types << 'null' unless is_required
|
155
|
-
{ 'type' => types }
|
156
|
-
end
|
157
|
-
|
158
|
-
def reference(type)
|
159
|
-
{'$ref' => "#/definitions/#{self.class.scope(@scope, type)}" }
|
160
|
-
end
|
161
|
-
|
162
66
|
def json_schema_blueprint
|
163
67
|
{
|
164
68
|
"type" => "object",
|
@@ -166,7 +70,6 @@ module Lacerda
|
|
166
70
|
"required" => []
|
167
71
|
}
|
168
72
|
end
|
169
|
-
|
170
73
|
end
|
171
74
|
end
|
172
75
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Represents a DataStructure AST member and
|
2
|
+
# helps transforming it into a json schema.
|
3
|
+
module Lacerda
|
4
|
+
module Conversion
|
5
|
+
class DataStructure
|
6
|
+
class Member
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
def self.from_data_structure_content(data_structure_content, scope)
|
10
|
+
return [] if data_structure_content.nil?
|
11
|
+
# In the case that you create a nested data structure when type == 'object',
|
12
|
+
# the possible_members can be just a Hash, instead of an array
|
13
|
+
if data_structure_content.is_a?(Hash)
|
14
|
+
data_structure_content = [data_structure_content]
|
15
|
+
end
|
16
|
+
members = data_structure_content.select do |d|
|
17
|
+
d['element'] == 'member'
|
18
|
+
end
|
19
|
+
members.map { |member| Member.new(member, scope) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(member, scope)
|
23
|
+
@description = member.dig('meta', 'description')
|
24
|
+
@content = member['content']
|
25
|
+
@name = Lacerda.underscore(@content['key']['content'])
|
26
|
+
@attributes = member.dig('attributes', 'typeAttributes') || []
|
27
|
+
@is_required = @attributes.include?('required')
|
28
|
+
@type = Type.new(@content['value'], @is_required, @scope)
|
29
|
+
@scope = scope
|
30
|
+
end
|
31
|
+
|
32
|
+
def required?
|
33
|
+
@is_required
|
34
|
+
end
|
35
|
+
|
36
|
+
def spec
|
37
|
+
spec = {}
|
38
|
+
# We might have a description
|
39
|
+
spec['description'] = @description
|
40
|
+
spec.merge!(@type.to_hash)
|
41
|
+
# Add the type of the array objects (if it is an array)
|
42
|
+
spec['items'] = @type.array_type
|
43
|
+
# If it's an object, we need recursion
|
44
|
+
if @type.object?
|
45
|
+
data_structure = DataStructure.new('tmp', [@content['value']], @scope).to_json
|
46
|
+
spec['properties'] = data_structure['properties']
|
47
|
+
end
|
48
|
+
spec
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Lacerda
|
2
|
+
module Conversion
|
3
|
+
class DataStructure
|
4
|
+
class Member
|
5
|
+
class Type
|
6
|
+
def initialize(type_definition, is_required, scope)
|
7
|
+
@type_definition = type_definition
|
8
|
+
@scope = scope
|
9
|
+
@type_name = type_definition['element']
|
10
|
+
@is_required = is_required
|
11
|
+
end
|
12
|
+
|
13
|
+
def object?
|
14
|
+
@type_name == 'object'
|
15
|
+
end
|
16
|
+
|
17
|
+
# A type is transformed to json schema either as a primitive:
|
18
|
+
# { "type" => ["string"] }
|
19
|
+
# { "type" => ["string", "null"] }
|
20
|
+
# Or as a oneOf. $ref are always within a oneOf
|
21
|
+
# {"oneOf"=>[{"$ref"=>"#/definitions/tag"}, {"type"=>"null"}]}
|
22
|
+
def to_hash
|
23
|
+
if PRIMITIVES.include?(@type_name)
|
24
|
+
primitive(@type_name, required?)
|
25
|
+
else
|
26
|
+
oneOf([@type_name], required?)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# bui
|
31
|
+
# As there are specied types in the array, `nil` should not be a valid value
|
32
|
+
# and therefore required should be true.
|
33
|
+
def array_type
|
34
|
+
return unless array?
|
35
|
+
if nested_types.size == 1 && PRIMITIVES.include?(nested_types.first)
|
36
|
+
primitive(nested_types.first, true)
|
37
|
+
else
|
38
|
+
oneOf(nested_types, true)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def array?
|
45
|
+
@type_name == 'array'
|
46
|
+
end
|
47
|
+
|
48
|
+
# A basic type is either a primitive type with exactly 1 primitive type
|
49
|
+
# {'type' => [boolean] }
|
50
|
+
# a reference
|
51
|
+
# { '$ref' => "#/definitions/name" }
|
52
|
+
# or an object if the type in not there
|
53
|
+
# { 'type' => object }
|
54
|
+
# Basic types don't care about being required or not.
|
55
|
+
def basic_type(type_name, is_required = required?)
|
56
|
+
if PRIMITIVES.include?(type_name)
|
57
|
+
primitive(type_name, is_required)
|
58
|
+
else
|
59
|
+
reference(type_name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def oneOf(types, is_required)
|
64
|
+
types = types.map { |type_name| basic_type(type_name,is_required) }
|
65
|
+
types << { 'type' => 'null' } unless is_required
|
66
|
+
{
|
67
|
+
'oneOf' => types.uniq
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def reference(type_name)
|
72
|
+
{'$ref' => "#/definitions/#{Lacerda::Conversion::DataStructure.scope(@scope, type_name)}" }
|
73
|
+
end
|
74
|
+
|
75
|
+
def primitive(type_name, is_required)
|
76
|
+
types = [type_name]
|
77
|
+
types << 'null' unless is_required
|
78
|
+
if type_name == 'enum'
|
79
|
+
enum_values = @type_definition['content'].map { |i| i['content'] }
|
80
|
+
{ 'type' => types, 'enum' => enum_values }
|
81
|
+
else
|
82
|
+
{ 'type' => types }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def required?
|
87
|
+
@is_required
|
88
|
+
end
|
89
|
+
|
90
|
+
def nested_types
|
91
|
+
error_msg = "This DataStructure::Member is a #{@type_name}, not "\
|
92
|
+
'an array, so it cannot have nested types'
|
93
|
+
raise error_msg unless array?
|
94
|
+
@type_definition['content'].map{|vc| vc['element'] }.uniq
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/lacerda/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lacerda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jannis Hermanns
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-08-
|
11
|
+
date: 2017-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -252,6 +252,8 @@ files:
|
|
252
252
|
- lib/lacerda/conversion.rb
|
253
253
|
- lib/lacerda/conversion/apiary_to_json_schema.rb
|
254
254
|
- lib/lacerda/conversion/data_structure.rb
|
255
|
+
- lib/lacerda/conversion/data_structure/member.rb
|
256
|
+
- lib/lacerda/conversion/data_structure/member/type.rb
|
255
257
|
- lib/lacerda/conversion/error.rb
|
256
258
|
- lib/lacerda/infrastructure.rb
|
257
259
|
- lib/lacerda/object_description.rb
|