Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handles nested body. #39

Merged
merged 1 commit into from
Oct 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ tmp
api/
.vscode
spec/fixtures/pmm.json
spec/fixtures/arena.json
spec/fixtures/nested.json
2 changes: 2 additions & 0 deletions lib/starter/importer/nested_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module Starter
module Importer
class NestedParams < Parameter
def initialize(name:, definition:)
@kind = :body
@nested = []
@name = name
@definition = definition
end
Expand Down
34 changes: 21 additions & 13 deletions lib/starter/importer/parameter.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/ClassLength
# frozen_string_literal: false

module Starter
Expand All @@ -24,8 +24,6 @@ def nested?
@nested.present?
end

private

# initialize helper
#
def validate_parameters(definition:, components:)
Expand All @@ -51,7 +49,7 @@ def prepare_attributes(definition:, components:) # rubocop:disable Metrics/Metho
end
when :body
definition['in'] = 'body'
schema = definition['content'].values.first['schema']
schema = definition['content'] ? definition['content'].values.first['schema'] : definition
if schema.key?('$ref')
path = schema['$ref'].split('/')[2..]

Expand All @@ -64,7 +62,7 @@ def prepare_attributes(definition:, components:) # rubocop:disable Metrics/Metho
end
end

def handle_body(definition:, properties:)
def handle_body(definition:, properties:) # rubocop:disable Metrics/MethodLength
if simple_object?(properties:)
name = properties['properties'].keys.first
type = properties.dig('properties', name, 'type') || 'array'
Expand All @@ -79,27 +77,37 @@ def handle_body(definition:, properties:)
definition['type'] = properties['type'].presence || 'JSON'
return [nil, definition] if properties.nil? || properties['properties'].nil?

properties['properties'].each do |nested_name, definition|
definition['required'] = properties['required']&.include?(nested_name) || false
@nested << NestedParams.new(name: nested_name, definition:)
properties['properties'].each do |nested_name, nested_definition|
nested_definition['required'] =
properties['required'] ? properties['required'].include?(nested_name) : false
nested = NestedParams.new(name: nested_name, definition: nested_definition)
nested.prepare_attributes(definition: nested.definition, components: {})
nested.name = nested_name
@nested << nested
end
[nil, definition]

[self.name, definition]
else # others
[nil, definition.merge(properties)]
[nil, properties ? definition.merge(properties) : definition]
end
end

# handle_body helper, check/find/define types
#
def object?(definition:)
definition['content'].keys.first.include?('application/json')
definition['type'] == 'object' ||
definition['content']&.keys&.first&.include?('application/json')
end

def simple_object?(properties:)
properties.key?('properties') &&
list_of_object?(properties:) &&
properties['properties'].length == 1
end

def list_of_object?(properties:)
properties&.key?('properties')
end

# to_s helper
#
def serialized_object
Expand Down Expand Up @@ -156,4 +164,4 @@ def documentation
end
end

# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/ClassLength
123 changes: 109 additions & 14 deletions spec/lib/importer/parameter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -291,46 +291,141 @@
"requires :postApiV1CalibrationsCreating, type: JSON, documentation: { in: 'body' } do"
)
expect(subject.to_s).to include(
"requires :crop, type: String, documentation: { desc: 'The Crop of it.' }"
"requires :crop, type: String, documentation: { desc: 'The Crop of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"requires :name, type: String, documentation: { desc: 'The Name of it.' }"
"requires :name, type: String, documentation: { desc: 'The Name of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :projects, type: Array[String], documentation: { desc: 'The project(s) of it.' }"
"optional :projects, type: Array[String], documentation: { desc: 'The project(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :pools, type: Array[String], documentation: { desc: 'The pool(s) of it.' }"
"optional :pools, type: Array[String], documentation: { desc: 'The pool(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :material_level, type: Array[String], documentation: { desc: 'The material level(s) of it.' }"
"optional :material_level, type: Array[String], documentation: { desc: 'The material level(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :material_groups, type: Array[String], documentation: { desc: 'The material group(s) of it.' }"
"optional :material_groups, type: Array[String], documentation: { desc: 'The material group(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :path, type: String, documentation: { desc: 'The path of it.' }"
"optional :path, type: String, documentation: { desc: 'The path of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :testers, type: Array[String], documentation: { desc: 'The tester(s) of it.' }"
"optional :testers, type: Array[String], documentation: { desc: 'The tester(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :locations, type: Array[String], documentation: { desc: 'The location(s) of it.' }"
"optional :locations, type: Array[String], documentation: { desc: 'The location(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :years, type: Array[Integer], documentation: { desc: 'The year(s) of it.' }"
"optional :years, type: Array[Integer], documentation: { desc: 'The year(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :tags, type: Array[String], documentation: { desc: 'The tag(s) of it.' }"
"optional :tags, type: Array[String], documentation: { desc: 'The tag(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :treatments, type: Array[String], documentation: { desc: 'The treatment(s) of it.' }"
"optional :treatments, type: Array[String], documentation: { desc: 'The treatment(s) of it.', in: 'body' }"
)
expect(subject.to_s).to include(
"optional :id, type: Integer, documentation: { desc: 'The ID of it (possible on back step, so editing is possible).', format: 'int32' }"
"optional :id, type: Integer, documentation: { desc: 'The ID of it (possible on back step, so editing is possible).', in: 'body', format: 'int32' }"
)
end
end

describe 'request body: nested object' do
let(:definition) do
{
'content' => {
'application/json' => {
'schema' => {
'$ref' => '#/components/schemas/ordered'
}
}
},
'required' => true
}
end

let(:components) do
{
'schemas' => {
'ordered' => {
'type' => 'object',
'properties' => {
'order' => {
'type' => 'object',
'properties' => {
'facet' => {
'type' => 'string',
'default' => 'score',
'enum' => %w[
created_at
updated_at
random
]
},
'dir' => {
'type' => 'string',
'default' => 'asc',
'enum' => %w[
asc
desc
]
}
},
'description' => 'Specify result order'
},
'per_page' => {
'type' => 'integer',
'format' => 'int32',
'default' => 24
},
'page' => {
'type' => 'integer',
'format' => 'int32',
'default' => 1
},
'choose' => {
'type' => 'array',
'items' => {
'type' => 'string',
'enum' => %w[a b]
}
}
},
'description' => 'something nested body request'
}
}
}
end

specify do
expect(subject.name).to eql 'ordered'
expect(subject.definition.keys).to match_array %w[
required content in type
]
expect(subject.definition['type']).to eql 'object'

expect(subject.to_s).to include(
"requires :ordered, type: JSON, documentation: { in: 'body' } do"
)
expect(subject.to_s).to include(
"optional :order, type: JSON, documentation: { desc: 'Specify result order', in: 'body' } do"
)
expect(subject.to_s).to include(
"optional :facet, type: String, documentation: { in: 'body' }"
)
expect(subject.to_s).to include(
"optional :dir, type: String, documentation: { in: 'body' }"
)
expect(subject.to_s).to include(
"optional :per_page, type: Integer, documentation: { in: 'body', format: 'int32' }"
)
expect(subject.to_s).to include(
"optional :page, type: Integer, documentation: { in: 'body', format: 'int32' }"
)
expect(subject.to_s).to include(
'end'
"optional :choose, type: Array[String], documentation: { in: 'body' }"
)
end
end
Expand Down