Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- [OpenAPI] Add support for blueprint inheritance. Child blueprints now inherit fields from parent blueprints.
- [OpenAPI] Refactor `Rage::OpenAPI::Parsers::Ext::Blueprinter` using live reflection instead of Prism AST traversal to simplify schema extraction
- [OpenAPI] Add Blueprinter association parsing with cycle detection (#291)

### Fixed

Expand Down
39 changes: 37 additions & 2 deletions lib/rage/openapi/parsers/ext/blueprinter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class Rage::OpenAPI::Parsers::Ext::Blueprinter
def initialize(namespace: Object, root: Rage::OpenAPI::Nodes::Root.new, **)
@namespace = namespace
@root = root
@parsing_stack = Set.new
end

def known_definition?(str)
Expand All @@ -16,17 +17,29 @@ def known_definition?(str)
def parse(klass_str)
is_collection, raw_klass_str, _ = Rage::OpenAPI.__parse_serializer_args(klass_str)
klass = @namespace.const_get(raw_klass_str)
build_schema(klass, is_collection)
schema = build_schema(klass, is_collection)

if @root.schema_registry.key?(raw_klass_str)
clean = is_collection ? schema["items"] : schema
@root.schema_registry[raw_klass_str] = clean
end

schema
end

private

def build_schema(klass, is_collection)
@parsing_stack.add(klass.name) if klass.name

reflections = klass.reflections
identifier_fields = extract_fields(reflections, :identifier)
default_fields = extract_fields(reflections, :default)
association_fields = extract_associations(reflections, :default)

@parsing_stack.delete(klass.name) if klass.name

schema = identifier_fields.merge(default_fields.sort.to_h)
schema = identifier_fields.merge(default_fields.sort.to_h).merge(association_fields)

result = { "type" => "object" }
result["properties"] = schema if schema.any?
Expand All @@ -41,4 +54,26 @@ def extract_fields(reflections, view_name)
hash[field.display_name.to_s] = { "type" => "string" }
end
end

def extract_associations(reflections, view_name)
return {} unless (view = reflections[view_name])

view.associations.each_with_object({}) do |(_, assoc), hash|
blueprint = assoc.blueprint
name = blueprint&.name

if name && @parsing_stack.include?(name)
@root.schema_registry[name] ||= nil
hash[assoc.display_name.to_s] = {
"type" => "array",
"items" => { "$ref" => "#/components/schemas/#{name}" }
}
else
hash[assoc.display_name.to_s] = {
"type" => "array",
"items" => build_schema(blueprint, false)
}
end
end
end
end
Loading