Skip to content

Commit

Permalink
Handles nested body.
Browse files Browse the repository at this point in the history
- makes rubocop happy
  • Loading branch information
LeFnord committed Oct 22, 2023
1 parent 3fa31b3 commit 50b6d18
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 28 deletions.
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

0 comments on commit 50b6d18

Please sign in to comment.