Skip to content

Commit 170749d

Browse files
committed
refactor: simplify recursive embedded input type handling
Follow the same pattern as regular embedded types: - Remove unnecessary minimal schema creation for recursive types - Just return $ref for recursive types with schema names - Return empty Schema{} for recursive types without names - Let the non-recursive path create the actual schema This simplifies the code and removes unnecessary complexity that wasn't actually helping with recursion prevention.
1 parent e785e3a commit 170749d

File tree

1 file changed

+37
-82
lines changed

1 file changed

+37
-82
lines changed

lib/ash_json_api/json_schema/open_api.ex

Lines changed: 37 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,99 +1077,54 @@ if Code.ensure_loaded?(OpenApiSpex) do
10771077
type -> type
10781078
end
10791079

1080-
# Generate the schema name based on parent context when needed
1081-
parent_type = AshJsonApi.Resource.Info.type(parent_resource)
1082-
attribute_name = Map.get(attribute, :name)
1083-
1084-
# Check if this embedded resource has a JSON API type for input schema naming
1085-
json_api_type = AshJsonApi.Resource.Info.type(embedded_resource)
1086-
1087-
# Generate schema name - use parent context for embedded resources without their own type
10881080
input_schema_name =
1089-
if json_api_type do
1090-
"#{json_api_type}-input-#{action_type}"
1091-
else
1092-
# Use parent resource type and attribute name for schema naming
1093-
# This matches the pattern used in the generated refs
1094-
if parent_type && attribute_name do
1095-
"#{parent_type}_#{attribute_name}-input-#{action_type}"
1096-
else
1097-
nil
1098-
end
1099-
end
1081+
create_input_schema_name(attribute, parent_resource, action_type, embedded_resource)
11001082

1101-
if input_schema_name do
1102-
# Check if we already have this input schema
1103-
if Map.has_key?(acc.schemas, input_schema_name) do
1083+
# Check for recursion
1084+
type_key = {embedded_resource, action_type, attribute.constraints}
1085+
1086+
if type_key in acc.seen_input_types do
1087+
# We're in a recursive loop
1088+
if input_schema_name do
1089+
# Return $ref and unchanged accumulator (the schema will be created by the non-recursive path)
11041090
schema = %{"$ref" => "#/components/schemas/#{input_schema_name}"}
11051091
{schema, acc}
11061092
else
1107-
# Check for recursion
1108-
type_key = {embedded_resource, action_type, attribute.constraints}
1109-
1110-
if type_key in acc.seen_input_types do
1111-
# Recursive input type detected, using $ref instead of inline definition
1112-
1113-
# Create a minimal schema for the recursive type if it doesn't exist
1114-
# This ensures the $ref has something to point to
1115-
acc_with_schema =
1116-
if Map.has_key?(acc.schemas, input_schema_name) do
1117-
acc
1118-
else
1119-
# Create a minimal schema to break the recursion
1120-
minimal_schema = %Schema{
1121-
type: :object,
1122-
additionalProperties: false,
1123-
properties: %{},
1124-
description: "Recursive embedded type - see parent schema for structure"
1125-
}
1126-
1127-
%{acc | schemas: Map.put(acc.schemas, input_schema_name, minimal_schema)}
1128-
end
1129-
1130-
schema = %{"$ref" => "#/components/schemas/#{input_schema_name}"}
1131-
{schema, acc_with_schema}
1132-
else
1133-
# Mark this type as being processed
1134-
new_acc = %{acc | seen_input_types: [type_key | acc.seen_input_types]}
1135-
# Continue with normal processing
1136-
embedded_type_input_impl(
1137-
attribute,
1138-
embedded_resource,
1139-
action_type,
1140-
new_acc,
1141-
format,
1142-
input_schema_name
1143-
)
1144-
end
1093+
# No schema name, return empty schema to break recursion
1094+
{%Schema{}, acc}
11451095
end
11461096
else
1147-
# No input schema name could be generated (no JSON API type and no parent context)
1148-
type_key = {embedded_resource, action_type, attribute.constraints}
1097+
# Not recursive, mark as seen and process normally
1098+
new_acc = %{acc | seen_input_types: [type_key | acc.seen_input_types]}
1099+
1100+
# Build the schema
1101+
embedded_type_input_impl(
1102+
attribute,
1103+
embedded_resource,
1104+
action_type,
1105+
new_acc,
1106+
format,
1107+
input_schema_name
1108+
)
1109+
end
1110+
end
11491111

1150-
if type_key in acc.seen_input_types do
1151-
# Recursive input type detected (no schema name), returning minimal schema
1112+
defp create_input_schema_name(attribute, parent_resource, action_type, embedded_resource) do
1113+
# Check if this embedded resource has a JSON API type for input schema naming
1114+
json_api_type = AshJsonApi.Resource.Info.type(embedded_resource)
11521115

1153-
# Return a minimal schema for resources without proper naming
1154-
minimal_schema = %Schema{
1155-
type: :object,
1156-
additionalProperties: false,
1157-
properties: %{},
1158-
description: "Recursive embedded type"
1159-
}
1116+
if json_api_type do
1117+
"#{json_api_type}-input-#{action_type}"
1118+
else
1119+
# Use parent resource type and attribute name for schema naming
1120+
# This matches the pattern used in the generated refs
1121+
parent_type = AshJsonApi.Resource.Info.type(parent_resource)
1122+
attribute_name = Map.get(attribute, :name)
11601123

1161-
{minimal_schema, acc}
1124+
if parent_type && attribute_name do
1125+
"#{parent_type}_#{attribute_name}-input-#{action_type}"
11621126
else
1163-
new_acc = %{acc | seen_input_types: [type_key | acc.seen_input_types]}
1164-
1165-
embedded_type_input_impl(
1166-
attribute,
1167-
embedded_resource,
1168-
action_type,
1169-
new_acc,
1170-
format,
1171-
nil
1172-
)
1127+
nil
11731128
end
11741129
end
11751130
end

0 commit comments

Comments
 (0)