class Praxis::Docs::OpenApi::SchemaObject
Attributes
Public Class Methods
new(info:)
click to toggle source
# File lib/praxis/docs/open_api/schema_object.rb, line 10 def initialize(info:) # info could be an attribute ... or a type # We will always try to work with the attribute if it is there, otherwise, we'll use type underneath if info.is_a?(Attributor::Attribute) @type = info.type @attribute = info else @type = info end @collection = type.respond_to?(:member_type) end
Public Instance Methods
_slice_options(object, names, prefix: nil)
click to toggle source
# File lib/praxis/docs/open_api/schema_object.rb, line 139 def _slice_options(object, names, prefix: nil) subset = object.options.slice(*names) return subset if prefix.nil? subset.transform_keys do |key| "#{prefix}-#{key}".to_sym end subset end
convert_family_to_json_type(praxis_type)
click to toggle source
# File lib/praxis/docs/open_api/schema_object.rb, line 115 def convert_family_to_json_type(praxis_type) case praxis_type[:family].to_sym when :string :string when :hash :object when :array # Warning! Multipart types are arrays! :array when :numeric jtypes = { 'Attributor-Integer' => :integer, 'Attributor-BigDecimal' => :integer, 'Attributor-Float' => :number } jtypes[praxis_type[:id]] when :temporal :string when :boolean :boolean else raise "Unknown praxis family type: #{praxis_type[:family]}" end end
dump_example()
click to toggle source
# File lib/praxis/docs/open_api/schema_object.rb, line 23 def dump_example ex = (attribute || type).example ex.respond_to?(:dump) ? ex.dump : ex end
dump_schema(shallow: false, allow_ref: false)
click to toggle source
# File lib/praxis/docs/open_api/schema_object.rb, line 28 def dump_schema(shallow: false, allow_ref: false) important_options = %i[description null] # Compile all options from the underlying tye and attribute (if any), translating them # to OpenAPI schema options with the x- prefix for our custom ones base_options = _slice_options(type, important_options) base_options.merge! _slice_options(type, Attributor::Attribute.custom_options, prefix: 'x') if attribute base_options.merge! _slice_options(attribute, important_options, prefix: 'x') base_options.merge! _slice_options(attribute, Attributor::Attribute.custom_options, prefix: 'x') end # Tag on OpenAPI specific requirements that aren't already added in the underlying JSON schema model # Nullable: (it seems we need to ensure there is a null option to the enum, if there is one) if base_options.key?(:null) base_options[:nullable] = Attributor::Attribute::nullable_attribute?(base_options) base_options.delete(:null) else # Even though no :null option is passed in, still see if the default is 'nullable', and if so, pass it in. # This is to handle the case where a type is defined as nullable by default, but the user didn't explicitly # pass in the null: true option base_options[:nullable] = true if Attributor::Attribute.default_for_null end # We will dump schemas for mediatypes by simply creating a reference to the components' section if type < Attributor::Container && !(type < Praxis::Types::MultipartArray) if (type < Praxis::Blueprint || type < Attributor::Model) && allow_ref && !type.anonymous? # NOTE: Technically OpenAPI/JSON schema support passing a description when pointing to a $ref (to override it) # However, it seems that UI browsers like redoc or elements have bugs where if that's done, they get into a loop and crash # so for now, we're gonna avoid overriding the description until that is solved base_options.delete(:description) Praxis::Docs::OpenApiGenerator.instance.register_seen_component(type) base_options.merge!('$ref' => "#/components/schemas/#{type.id}") elsif @collection items = OpenApi::SchemaObject.new(info: type.member_type).dump_schema(allow_ref: allow_ref, shallow: false) base_options.merge!(type: 'array', items: items) else # Attributor::Struct, etc # Requirements are reported at the outter schema layer, we we need to gather them from the description here reqs = type < Praxis::Blueprint ? type.attribute.type.requirements : type.requirements # Full requirements specified at the struct level that apply to all are considered required attributes required_attributes = (reqs || []).filter { |r| r.type == :all }.map(&:attr_names).flatten.compact # Also, if any inner attribute has the required: true option, that, obviously means required as well sub_attributes = (attribute || type).attributes direct_required = sub_attributes ? sub_attributes.select { |_, a| a.options[:required] == true }.keys : [] required_attributes.concat(direct_required) required_attributes.uniq! props = sub_attributes.transform_values do |definition| OpenApi::SchemaObject.new(info: definition).dump_schema(allow_ref: true, shallow: shallow) end base_options.merge!(type: :object) base_options[:properties] = props if props.presence base_options[:required] = required_attributes unless required_attributes.empty? end else desc = (attribute || type).as_json_schema(shallow: shallow, example: nil) # OpenApi::SchemaObject.new(info:target).dump_schema(allow_ref: allow_ref, shallow: shallow) # TODO...we need to make sure we can use refs in the underlying components after the first level... # ... maybe we need to loop over the attributes if it's an object/struct?... base_options.merge!(desc) end base_options # # TODO: FIXME: return a generic object type if the passed info was weird. # return { type: :object } unless info # h = { # #type: convert_family_to_json_type( info[:type] ) # type: info[:type] # #TODO: format? # } # # required prop!!!?? # h[:default] = info[:default] if info[:default] # h[:pattern] = info[:regexp] if info[:regexp] # # TODO: there are other possible things we can do..maximum, minimum...etc # if h[:type] == :array # # FIXME: ... hack it for MultiPart arrays...where there's no member attr # member_type = info[:type][:member_attribute] # unless member_type # member_type = { family: :hash} # end # h[:items] = SchemaObject.new(info: member_type ).dump_schema # end # h rescue StandardError => e puts "Error dumping schema #{e}" end