lib/dry/swagger/struct_parser.rb in dry-swagger-0.3.0 vs lib/dry/swagger/struct_parser.rb in dry-swagger-0.4.0

- old
+ new

@@ -1,82 +1,133 @@ module Dry module Swagger class StructParser - SWAGGER_FIELD_TYPE_DEFINITIONS = { - "String" => { type: :string }, - "Integer" => { type: :integer }, - "TrueClass | FalseClass" => { type: :boolean }, - "BigDecimal" => { type: :decimal }, - "Float" => { type: :float }, - "DateTime" => { type: :string, format: :datetime }, - "Date" => { type: :string, format: :date }, - "Time" => { type: :string, format: :time } - } + PREDICATE_TYPES = { + String: 'string', + Integer: 'integer', + Bool: 'boolean', + Float: 'float', + Date: 'date', + DateTime: 'datetime', + Time: 'time' + }.freeze - def call(dto) - documentation = generate_fields_documentation(dto.schema) - { :type => :object, :properties => documentation[:properties], :required => documentation[:required]} + attr_reader :keys + + def initialize + @keys = {} + @config = Config::StructConfiguration end - def generate_fields_documentation(dto_schema) - documentation = { properties: {}, required: [] } - dto_schema.name_key_map.each do |name, schema_key_object| - documentation[:properties][name] = schema_key_object.type.optional? ? - generate_field_properties(schema_key_object.type.right, true) : - generate_field_properties(schema_key_object.type, false) + def to_h + { keys: keys } + end - documentation[:required] << name if ::Dry::Swagger.struct_enable_required_validation && schema_key_object.required? - end - documentation + def call(dto, &block) + @keys = {} + visit(dto.schema.to_ast) + instance_eval(&block) if block_given? + self end - def generate_field_properties(type, nullable) - field_type = type.name - if SWAGGER_FIELD_TYPE_DEFINITIONS[field_type] # IS PRIMITIVE FIELD? - definition = SWAGGER_FIELD_TYPE_DEFINITIONS[field_type] - definition = definition - if is_enum?(type.class.name) && ::Dry::Swagger.struct_enable_enums - enums = type.values - enums += [nil] if nullable - definition = definition.merge(enum: enums) - end - elsif is_array?(field_type) - definition = { type: :array } - if is_primitive?(type.type.member.name) - definition = definition - definition = definition.merge(items: SWAGGER_FIELD_TYPE_DEFINITIONS[type.type.member.name]) - else - schema = is_array_with_dynamic_schema?(type.type.member) ? type.type.member.left : type.type.member - definition = definition.merge(items: call(schema)) - end - else - schema = is_dynamic_schema?(type) ? type.left : type - definition = call(schema) + def visit(node, opts = {}) + meth, rest = node + public_send(:"visit_#{meth}", rest, opts) + end + + def visit_constructor(node, opts = {}) + visit(node[0], opts) + end + + def visit_schema(node, opts = {}) + target = (key = opts[:key]) ? self.class.new : self + + required = opts.fetch(:required, true) + nullable = opts.fetch(:nullable, false) + + node[0].each do |child| + target.visit(child) end - ::Dry::Swagger.struct_enable_nullable_validation ? definition.merge(::Dry::Swagger.nullable_type => nullable) : definition + return unless key + + target_info = target.to_h if opts[:member] + + type = opts[:array]? 'array' : 'hash' + + keys[key] = { + type: type, + required: required, + @config.nullable_type => nullable, + **target_info + } end - private + def visit_key(node, opts = {}) + name, required, rest = node + opts[:key] = name + opts[:required] = required + visit(rest, opts) + end - def is_enum?(class_name) - class_name == 'Dry::Types::Enum' + def visit_constrained(node, opts = {}) + node.each {|it| visit(it, opts) } end - def is_array?(type_name) - type_name == 'Array' + def visit_nominal(_node, _opts); end + + def visit_predicate(node, opts = {}) + name, rest = node + type = rest[0][1] + + if name.equal?(:type?) + type = type.to_s.to_sym + return unless PREDICATE_TYPES[type] + + type_definition = { + type: PREDICATE_TYPES[type], + required: opts.fetch(:required), + @config.nullable_type => opts.fetch(:nullable, false) + } + + type_definition[:array] = opts[:array] if opts[:array] + + keys[opts[:key]] = type_definition + elsif name.equal?(:included_in?) + type += [nil] if opts.fetch(:nullable, false) + keys[opts[:key]][:enum] = type + end end - def is_primitive?(type_name) - !SWAGGER_FIELD_TYPE_DEFINITIONS[type_name].nil? + def visit_and(node, opts = {}) + left, right = node + + visit(left, opts) + visit(right, opts) end - def is_array_with_dynamic_schema?(member) - member.respond_to?(:right) + def visit_enum(node, opts = {}) + visit(node[0], opts) end - def is_dynamic_schema?(type) - !type.respond_to?(:schema) + def visit_sum(node, opts = {}) + opts[:nullable] = true + visit(node[1], opts) + end + + def visit_struct(node, opts = {}) + opts[:member] = true + + visit(node[1], opts) + end + + def visit_array(node, opts = {}) + opts[:array] = true + visit(node[0], opts) + end + + def to_swagger + DocumentationGenerator.new(@config).generate_documentation(keys) end end end end \ No newline at end of file