apia-open_api 0.1.5 → 0.1.6
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/Gemfile +1 -0
- data/lib/apia/open_api/helpers.rb +11 -2
- data/lib/apia/open_api/objects/response.rb +19 -10
- data/lib/apia/open_api/objects/schema.rb +29 -9
- data/lib/apia/open_api/specification.rb +6 -1
- data/lib/apia/open_api/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6621197f4a9535afae13e2a83de1fc5993290f379660e347afe989e94f3179ba
|
4
|
+
data.tar.gz: a932546d15d8302931a4c55d1fa4fa1e088b4f74ee7b227d0817de5f7b3f6dbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5bb084664b9a2729f8b0fb8408c8cd66eb6b8a637255582aba4c01cf5867eb2b88b5de5d1baba1eb4ebbece5f8d7353a7bedcbe68abe75036c7be9578a1a5c3
|
7
|
+
data.tar.gz: b99e2cc62ea871c657cbe523e1680d78f3341919b0a726d0fd6548635173a2064027c5c927d1a246be3120a508366e4627e4351c0040951d30a84694316fda3f
|
data/Gemfile
CHANGED
@@ -52,17 +52,26 @@ module Apia
|
|
52
52
|
schema
|
53
53
|
end
|
54
54
|
|
55
|
-
def generate_schema_ref(definition, id: nil, **schema_opts)
|
55
|
+
def generate_schema_ref(definition, id: nil, sibling_props: false, **schema_opts)
|
56
56
|
id ||= generate_id_from_definition(definition.type.klass.definition)
|
57
57
|
success = add_to_components_schemas(definition, id, **schema_opts)
|
58
58
|
|
59
|
-
|
59
|
+
# sibling_props indicates we want to allow sibling properties (typically setting nullable: true)
|
60
|
+
# In OpenAPI 3.0 sibling properties are not allowed for $refs (but are allowed in 3.1)
|
61
|
+
# Using allOf is a workaround to allow us to set a ref as `nullable` in OpenAPI 3.0
|
62
|
+
if success && sibling_props
|
63
|
+
{
|
64
|
+
allOf: [{ "$ref": "#/components/schemas/#{id}" }]
|
65
|
+
}
|
66
|
+
elsif success
|
60
67
|
{ "$ref": "#/components/schemas/#{id}" }
|
61
68
|
else # no properties were defined, so just declare an object with unknown properties
|
62
69
|
{ type: "object" }
|
63
70
|
end
|
64
71
|
end
|
65
72
|
|
73
|
+
# Converts the definition id to a short version:
|
74
|
+
# e.g. CoreAPI/Objects/TimeZone => TimeZone
|
66
75
|
def generate_id_from_definition(definition)
|
67
76
|
definition.id.split("/").last
|
68
77
|
end
|
@@ -98,15 +98,15 @@ module Apia
|
|
98
98
|
else
|
99
99
|
# We assume the partially selected attributes must be present in all of the polymorph options
|
100
100
|
# and that each option returns the same data type for that attribute.
|
101
|
-
# The same 'allOf workaround' is used here as for objects and enums below.
|
102
101
|
ref = generate_schema_ref(
|
103
102
|
field.type.klass.definition.options.values.first,
|
104
103
|
id: generate_field_id(field_name),
|
104
|
+
sibling_props: field.description.present? || field.null?,
|
105
105
|
endpoint: @endpoint,
|
106
106
|
path: [field]
|
107
107
|
)
|
108
108
|
|
109
|
-
properties[field_name] =
|
109
|
+
properties[field_name] = ref
|
110
110
|
end
|
111
111
|
properties[field_name][:description] = field.description if field.description.present?
|
112
112
|
end
|
@@ -135,28 +135,32 @@ module Apia
|
|
135
135
|
properties[field_name][:description] = field.description if field.description.present?
|
136
136
|
end
|
137
137
|
|
138
|
-
# Using allOf is a 'workaround' so that we can include a description for the field
|
139
|
-
# In OpenAPI 3.0 sibling properties are not allowed for $refs (but are allowed in 3.1)
|
140
138
|
# We don't want to put the description on the $ref itself because the description is
|
141
139
|
# specific to the endpoint and not necessarily applicable to all uses of the $ref.
|
142
140
|
def build_properties_for_object_or_enum(field_name, field, properties)
|
143
|
-
|
144
|
-
properties[field_name][:description] = field.description if field.description.present?
|
141
|
+
sibling_props = field.description.present? || field.null?
|
145
142
|
if field_includes_all_properties?(field)
|
146
|
-
ref = generate_schema_ref(field)
|
143
|
+
ref = generate_schema_ref(field, sibling_props: sibling_props)
|
147
144
|
else
|
148
145
|
ref = generate_schema_ref(
|
149
146
|
field,
|
150
147
|
id: generate_field_id(field_name),
|
148
|
+
sibling_props: sibling_props,
|
151
149
|
endpoint: @endpoint,
|
152
150
|
path: [field]
|
153
151
|
)
|
154
152
|
end
|
155
|
-
|
153
|
+
|
154
|
+
properties[field_name] = {
|
155
|
+
description: (field.description if field.description.present?),
|
156
|
+
**ref
|
157
|
+
}.compact
|
156
158
|
end
|
157
159
|
|
160
|
+
# We used to check if field.include was nil, but explicitly checking for a string is more robust.
|
161
|
+
# As some apia definitions declare `include: true`, which is not the correct way to use the option.
|
158
162
|
def field_includes_all_properties?(field)
|
159
|
-
field.include.
|
163
|
+
!field.include.is_a?(String)
|
160
164
|
end
|
161
165
|
|
162
166
|
def generate_field_id(field_name)
|
@@ -173,7 +177,11 @@ module Apia
|
|
173
177
|
d.http_status_code.to_s.to_sym
|
174
178
|
end
|
175
179
|
|
176
|
-
|
180
|
+
deduplicated_potential_errors = grouped_potential_errors.map do |http_status_code, potential_errors|
|
181
|
+
[http_status_code, potential_errors.uniq { |error| error.code }]
|
182
|
+
end
|
183
|
+
|
184
|
+
sorted_grouped_potential_errors = deduplicated_potential_errors.sort_by do |http_status_code, _|
|
177
185
|
http_status_code.to_s.to_i
|
178
186
|
end
|
179
187
|
|
@@ -270,6 +278,7 @@ module Apia
|
|
270
278
|
oneOf: definitions.map { |d| generate_ref("schemas", http_status_code, [d]) }
|
271
279
|
}
|
272
280
|
|
281
|
+
# we don't need the ref allOf workaround here because these responses are not nullable
|
273
282
|
schema = { "$ref": "#/components/schemas/#{one_of_id}" }
|
274
283
|
end
|
275
284
|
|
@@ -40,7 +40,7 @@ module Apia
|
|
40
40
|
@definition = definition
|
41
41
|
@schema = schema
|
42
42
|
@id = id
|
43
|
-
@endpoint = endpoint # endpoint gets specified when we are dealing with a partial response
|
43
|
+
@endpoint = endpoint # endpoint gets specified when we are dealing with a partial response (see below)
|
44
44
|
@path = path
|
45
45
|
@children = []
|
46
46
|
end
|
@@ -90,10 +90,10 @@ module Apia
|
|
90
90
|
|
91
91
|
return if @children.empty?
|
92
92
|
|
93
|
-
all_properties_included = error_definition? || enum_definition? || @endpoint.nil?
|
94
93
|
@children.each do |child|
|
95
94
|
next unless @endpoint.nil? || (!enum_definition? && @endpoint.include_field?(@path + [child]))
|
96
95
|
|
96
|
+
all_properties_included = all_properties_included_for_child?(child)
|
97
97
|
if child.respond_to?(:array?) && child.array?
|
98
98
|
generate_schema_for_child_array(@schema, child, all_properties_included)
|
99
99
|
else
|
@@ -102,6 +102,25 @@ module Apia
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
# We need to check if all properties are included, because if they aren't we cannot use
|
106
|
+
# an existing schema $ref. We need to generate a new schema only for the endpoint.
|
107
|
+
# A field is considered 'partial' when declared in Apia with the `include` option, e.g.:
|
108
|
+
# ```
|
109
|
+
# field :zones,
|
110
|
+
# type: [Objects::Zone],
|
111
|
+
# include: 'id,name,permalink,data_center[reference]'
|
112
|
+
# ```
|
113
|
+
# the above example means we only want to include the id, name and permalink fields for the zone
|
114
|
+
# and data_center is a nested object in zone, which should only include reference.
|
115
|
+
# so the parent zone is a partial object and the child data_center is also a partial object.
|
116
|
+
def all_properties_included_for_child?(child)
|
117
|
+
return true if error_definition? || enum_definition? || @endpoint.nil? || !child.type.object?
|
118
|
+
|
119
|
+
child.type.klass.definition.fields.values.all? do |child_field|
|
120
|
+
@endpoint.include_field?(@path + [child, child_field])
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
105
124
|
def generate_schema_for_child_array(schema, child, all_properties_included)
|
106
125
|
child_schema = generate_schema_for_child({}, child, all_properties_included)
|
107
126
|
items = child_schema.dig(:properties, child.name.to_s)
|
@@ -115,22 +134,23 @@ module Apia
|
|
115
134
|
end
|
116
135
|
|
117
136
|
def generate_schema_for_child(schema, child, all_properties_included)
|
137
|
+
nullable = child.try(:null?)
|
118
138
|
if enum_definition?
|
119
139
|
schema[:type] = "string"
|
120
140
|
schema[:enum] = @children.map { |c| c[:name] }
|
121
141
|
elsif child.type.argument_set? || child.type.enum? || child.type.polymorph?
|
122
142
|
schema[:type] = "object"
|
123
143
|
schema[:properties] ||= {}
|
124
|
-
schema[:properties][child.name.to_s] = generate_schema_ref(child)
|
144
|
+
schema[:properties][child.name.to_s] = generate_schema_ref(child, sibling_props: nullable)
|
125
145
|
elsif child.type.object?
|
126
|
-
generate_properties_for_object(schema, child, all_properties_included)
|
146
|
+
generate_properties_for_object(schema, child, all_properties_included, nullable)
|
127
147
|
else # scalar
|
128
148
|
schema[:type] = "object"
|
129
149
|
schema[:properties] ||= {}
|
130
150
|
schema[:properties][child.name.to_s] = generate_scalar_schema(child)
|
131
151
|
end
|
132
152
|
|
133
|
-
if
|
153
|
+
if nullable
|
134
154
|
schema[:properties][child.name.to_s][:nullable] = true
|
135
155
|
end
|
136
156
|
|
@@ -141,11 +161,11 @@ module Apia
|
|
141
161
|
schema
|
142
162
|
end
|
143
163
|
|
144
|
-
def generate_properties_for_object(schema, child, all_properties_included)
|
164
|
+
def generate_properties_for_object(schema, child, all_properties_included, nullable)
|
145
165
|
schema[:type] = "object"
|
146
166
|
schema[:properties] ||= {}
|
147
167
|
if all_properties_included
|
148
|
-
ref = generate_schema_ref(child)
|
168
|
+
ref = generate_schema_ref(child, sibling_props: nullable)
|
149
169
|
else
|
150
170
|
child_path = @path.nil? ? nil : @path + [child]
|
151
171
|
|
@@ -156,13 +176,13 @@ module Apia
|
|
156
176
|
ref = generate_schema_ref(
|
157
177
|
child,
|
158
178
|
id: "#{truncated_id}Part_#{child.name}".camelize,
|
179
|
+
sibling_props: nullable,
|
159
180
|
endpoint: @endpoint,
|
160
181
|
path: child_path
|
161
182
|
)
|
162
183
|
end
|
163
184
|
|
164
|
-
|
165
|
-
schema[:properties][child.name.to_s] = { allOf: [ref] }
|
185
|
+
schema[:properties][child.name.to_s] = ref
|
166
186
|
end
|
167
187
|
|
168
188
|
end
|
@@ -28,6 +28,9 @@ module Apia
|
|
28
28
|
},
|
29
29
|
security: []
|
30
30
|
}
|
31
|
+
|
32
|
+
# path_ids is used to keep track of all the IDs of all the paths we've generated, to avoid duplicates
|
33
|
+
# refer to the Path object for more info
|
31
34
|
@path_ids = []
|
32
35
|
build_spec
|
33
36
|
end
|
@@ -60,7 +63,9 @@ module Apia
|
|
60
63
|
|
61
64
|
def add_paths
|
62
65
|
@api.definition.route_set.routes.each do |route|
|
63
|
-
|
66
|
+
# not all routes should be documented
|
67
|
+
next unless route.group.nil? || route.group.schema?
|
68
|
+
next unless route.endpoint.definition.schema?
|
64
69
|
|
65
70
|
Objects::Path.new(
|
66
71
|
spec: @spec,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apia-open_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Sturgess
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|