Skip to content

Commit

Permalink
Handle parameters from request body.
Browse files Browse the repository at this point in the history
- simple Arrays
  • Loading branch information
LeFnord committed Sep 21, 2023
1 parent 94141e1 commit bf5270f
Show file tree
Hide file tree
Showing 9 changed files with 4,454 additions and 81 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ template/Gemfile.lock
grape-starter.md
tmp
api/
.vscode
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,19 @@ Layout/IndentationWidth:
Layout/LineLength:
Max: 120

Lint/MissingSuper:
Enabled: false

Metrics/BlockLength:
Exclude:
- 'spec/**/*'

Metrics/AbcSize:
Max: 20

Metrics/ClassLength:
Max: 120

Metrics/MethodLength:
Max: 20

Expand Down
2 changes: 1 addition & 1 deletion lib/starter/importer/namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class #{@naming.klass_name} < Grape::API
private

def namespace
naming.version_klass ? "'#{naming.origin}'" : ":#{naming.resource.downcase}"
@namespace ||= naming.version_klass ? "'#{naming.origin}'" : ":#{naming.resource.downcase}"
end

def endpoints
Expand Down
12 changes: 12 additions & 0 deletions lib/starter/importer/nested_params.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: false

module Starter
module Importer
class NestedParams < Parameter
def initialize(name:, definition:)
@name = name
@definition = definition
end
end
end
end
88 changes: 84 additions & 4 deletions lib/starter/importer/parameter.rb
Original file line number Diff line number Diff line change
@@ -1,38 +1,55 @@
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# frozen_string_literal: false

module Starter
module Importer
class Parameter
class Error < StandardError; end

attr_accessor :kind, :name, :definition
attr_accessor :kind, :name, :definition, :nested

def initialize(definition:, components: {})
@nested = []
@kind = validate_parameters(definition:, components:)
prepare_attributes(definition:, components:)
end

def to_s
# TODO: handle object/arrays and its properties
return serialized_object if nested.present?

type = definition['type'] || definition['schema']['type']
type.scan(/\w+/).each { |x| type.match?('JSON') ? type : type.sub!(x, x.capitalize) }

if type == 'Array' && definition.key?('items')
sub = definition.dig('items', 'type').to_s.capitalize
type = "#{type}[#{sub}]"
end

entry = definition['required'] ? 'requires' : 'optional'
entry << " :#{name}"
entry << ", type: #{definition['schema']['type'].capitalize}"

entry << ", type: #{type}"
doc = documentation
entry << ", #{doc}" if doc

entry
rescue StandardError => e
print e
end

private

# initialize helper
#
def validate_parameters(definition:, components:)
return :direct if definition.key?('name')
return :ref if definition.key?('$ref') && components.key?('parameters')
return :body if definition.key?('content')

raise Error, 'no valid combination given'
end

def prepare_attributes(definition:, components:)
def prepare_attributes(definition:, components:) # rubocop:disable Metrics/MethodLength
case kind
when :direct
@name = definition['name']
Expand All @@ -45,7 +62,68 @@ def prepare_attributes(definition:, components:)
if (value = @definition.dig('schema', '$ref').presence)
@definition['schema'] = components.dig(*value.split('/')[2..])
end
when :body
definition['in'] = 'body'
schema = definition['content'].values.first['schema']
if schema.key?('$ref')
path = schema['$ref'].split('/')[2..]

@name, @definition = handle_body(definition:, properties: components.dig(*path))
@name ||= path.last
else
@name, @definition = handle_body(definition:, properties: schema)
end
end
end

def handle_body(definition:, properties:)
if object?(definition:, properties:) # a nested object -> JSON
definition['type'] = properties['type'].presence || 'JSON'
return [nil, definition] if properties.nil? || properties['properties'].nil?

properties['properties'].each do |name, definition|
definition['required'] = properties['required']&.include?(name) || false
@nested << NestedParams.new(name:, definition:)
end

[nil, definition]
elsif simple_array?(properties:)
name = properties['properties'].keys.first
type = properties.dig('properties', name, 'type') || 'array'
subtype = properties.dig('properties', name, 'items', 'type')
definition['type'] = subtype.nil? ? type : "#{type}[#{subtype}]"

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

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

def simple_array?(properties:)
properties.key?('properties') &&
properties['properties'].length == 1 &&
properties['properties'].values.first['type'] == 'array'
end

# to_s helper
#
def serialized_object
definition.tap do |foo|
foo['type'] = foo['type'] == 'object' ? 'JSON' : foo['type']
end

parent = NestedParams.new(name: name, definition: definition)

entry = "#{parent} do\n"
nested.each { |n| entry << " #{n}\n" }
entry << ' end'
end

def documentation
Expand All @@ -68,3 +146,5 @@ def documentation
end
end
end

# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
6 changes: 3 additions & 3 deletions lib/starter/importer/specification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def segmentize(path)
[rest.shift, rest.empty? ? '/' : "/#{rest.join('/')}"]
end

def prepare_verbs(spec)
def prepare_verbs(spec) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
path_params = nil
spec.each_with_object({}) do |(verb, content), memo|
if verb == 'parameters'
Expand All @@ -56,9 +56,9 @@ def prepare_verbs(spec)
end

memo[verb] = content
next unless content.key?('parameters') || path_params
next unless content.key?('parameters') || content.key?('requestBody') || path_params

parameters = content['parameters'] || path_params
parameters = ((content['parameters'] || path_params || []) + [content['requestBody']]).compact

memo[verb]['parameters'] = parameters.each_with_object({}) do |definition, para|
parameter = Parameter.new(definition:, components:)
Expand Down
Loading

0 comments on commit bf5270f

Please # to comment.