ngtk/open_api_annotator

View on GitHub
lib/open_api_annotator/components_builder.rb

Summary

Maintainability
A
2 hrs
Test Coverage
A
91%
module OpenApiAnnotator
  class ComponentsBuilder
    def build
      components = OpenApi::Components.new(schemas: {})

      serializers = fetch_all_serializers
      serializers.sort_by!(&:open_api_resource_name)
      serializers.each do |serializer|
        schema = build_schema(serializer)
        next unless schema
        components.schemas[serializer.open_api_resource_name] = schema
        puts "Schema component '#{serializer.open_api_resource_name}' has been created."
      end
      components
    end

    private

    def build_schema(serializer)
      schema = OpenApi::Schema.new(type: "object", properties: {})
      schema.properties.merge!(build_attribute_properties(serializer))
      schema.properties.merge!(build_has_many_association_properties(serializer))
      schema.properties.merge!(build_has_one_and_belongs_to_association_properties(serializer))
      required_fields = OpenApiAnnotator.config.always_required_fields
      if required_fields.present?
        schema.required = required_fields.select{|field| schema.properties[field] }
      end
      schema
    end

    def build_attribute_properties(serializer)
      properties = {}
      serializer.open_api_attributes.each do |attribute|
        next unless attribute.valid?
        properties[attribute.name.to_sym] = if attribute.type.is_a?(Array)
          OpenApi::Schema.new(
            type: "array",
            items: OpenApi::Schema.new(
              type: attribute.type.first,
              format: attribute.format,
              nullable: attribute.nullable,
            )
          )
        else
          OpenApi::Schema.new(
            type: attribute.type,
            format: attribute.format,
            nullable: attribute.nullable,
          )
        end
      end
      properties
    end

    def build_has_many_association_properties(serializer)
      properties = {}
      serializer.open_api_has_many_associations.each do |association|
        next unless association.valid?
        content = association.type.first
        return unless content
        content_name = content.try(:name) || content.to_s
        properties[association.name.to_sym] = OpenApi::Schema.new(
          type: "array",
          items: OpenApi::Reference.new(ref: "#/components/schemas/#{content_name}"),
          nullable: association.nullable,
        )
      end
      properties
    end

    def build_has_one_and_belongs_to_association_properties(serializer)
      properties = {}
      associations = serializer.open_api_has_one_associations + serializer.open_api_belongs_to_associations
      associations.each do |association|
        next unless association.valid?
        content_name = association.type.try(:name) || association.type.to_s
        reference = OpenApi::Reference.new(ref: "#/components/schemas/#{content_name}")
        properties[association.name.to_sym] = if association.nullable
          OpenApi::Schema.new(
            nullable: true,
            allOf: [reference]
          )
        else
          reference
        end
      end
      properties
    end

    def fetch_all_serializers
      require_all_serializers!

      OpenApiAnnotator.config.application_serializer_class.descendants
    end

    def require_all_serializers!
      all_serializer_features = Dir["#{Rails.root}/app/serializers/**/*_serializer.rb"]
      unloaded_features = all_serializer_features - $LOADED_FEATURES
      unloaded_features.each { |f| require f }
    end
  end
end