Skip to content

Add $dynamicRef #4891

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2025
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,15 @@
@OpenAPI31
String $dynamicAnchor() default "";

/**
* Provides the $dynamicRef related to schema
*
* @since 2.2.32 / OpenAPI 3.1
* @return $dynamicRef schema
*/
@OpenAPI31
String $dynamicRef() default "";

/**
* Provides the content encoding related to this schema
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2962,6 +2962,13 @@ protected String resolveId(Annotated a, Annotation[] annotations, io.swagger.v3.
return null;
}

protected String resolve$dynamicRef(Annotated a, Annotation[] annotations, io.swagger.v3.oas.annotations.media.Schema schema) {
if (schema != null && StringUtils.isNotBlank(schema.$dynamicRef())) {
return schema.$dynamicRef();
}
return null;
}

protected String resolveContentEncoding(Annotated a, Annotation[] annotations, io.swagger.v3.oas.annotations.media.Schema schema) {
if (schema != null && StringUtils.isNotBlank(schema.contentEncoding())) {
return schema.contentEncoding();
Expand Down Expand Up @@ -3332,6 +3339,10 @@ protected void resolveSchemaMembers(Schema schema, Annotated a, Annotation[] ann
if ($dynamicAnchor != null) {
schema.$dynamicAnchor($dynamicAnchor);
}
String $dynamicRef = resolve$dynamicRef(a, annotations, schemaAnnotation);
if ($dynamicRef != null) {
schema.$dynamicRef($dynamicRef);
}
String contentEncoding = resolveContentEncoding(a, annotations, schemaAnnotation);
if (contentEncoding != null) {
schema.setContentEncoding(contentEncoding);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,9 @@ public static Optional<Schema> getSchemaFromAnnotation(
if (StringUtils.isNotBlank(schema.$dynamicAnchor())) {
schemaObject.set$dynamicAnchor(schema.$dynamicAnchor());
}
if (StringUtils.isNotBlank(schema.$dynamicRef())) {
schemaObject.set$dynamicRef(schema.$dynamicRef());
}
if (StringUtils.isNotBlank(schema.contentEncoding())) {
schemaObject.setContentEncoding(schema.contentEncoding());
}
Expand Down Expand Up @@ -2484,6 +2487,14 @@ public Class<?> contains() {
return patch.$dynamicAnchor();
}

@Override
public String $dynamicRef() {
if (StringUtils.isNotBlank(master.$dynamicRef()) || StringUtils.isBlank(patch.$dynamicRef())) {
return master.$dynamicRef();
}
return patch.$dynamicRef();
}

@Override
public String contentEncoding() {
if (StringUtils.isNotBlank(master.contentEncoding()) || StringUtils.isBlank(patch.contentEncoding())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import static org.testng.Assert.assertTrue;

public class SwaggerSerializerTest {
ObjectMapper m = Json.mapper();

@Test(description = "it should convert a spec")
public void convertSpec() throws IOException {
Expand All @@ -63,15 +62,10 @@ public void convertSpec() throws IOException {
.info(info)
.addServersItem(new Server()
.url("http://petstore.swagger.io"))

// .securityDefinition("api-key", new ApiKeyAuthDefinition("key", In.HEADER))
// .consumes("application/json")
// .produces("application/json")
.schema("Person", personModel)
.schema("Error", errorModel);

final Operation get = new Operation()
// .produces("application/json")
.summary("finds pets in the system")
.description("a longer description")
.addTagsItem("Pet Operations")
Expand Down Expand Up @@ -173,7 +167,7 @@ public void writeSpecWithParameterReferences() throws IOException {
.addParametersItem(new Parameter().$ref("#/parameters/Foo"));

swagger
.components(new Components().addParameters("Foo", parameter))
.components(swagger.getComponents().addParameters("Foo", parameter))
.path("/pets", new PathItem().get(get));

final String swaggerJson = Json.mapper().writeValueAsString(swagger);
Expand Down Expand Up @@ -225,4 +219,17 @@ public void setValue(String value) {

}
}

@Test
public void testDynamicRefSerialization() throws IOException {
Schema<?> dynamicRefSchema = new Schema<>();
dynamicRefSchema.set$dynamicRef("#node");

Components components = new Components().addSchemas("DynamicRefSchema", dynamicRefSchema);
OpenAPI openAPI = new OpenAPI().components(components);
String json = Json.mapper().writeValueAsString(openAPI);

assertTrue(json.contains("\"$dynamicRef\":\"#node\""));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.swagger.v3.core.util.Yaml31;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.JsonSchema;
import io.swagger.v3.oas.models.media.Schema;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -251,4 +252,18 @@ public void testBooleanSchemaDeserialization() throws Exception{
oas = Yaml.mapper().readValue(yaml, OpenAPI.class);
assertNull(oas.getComponents().getSchemas().get("test").getBooleanSchemaValue());
}

@Test
public void testDynamicRefDeserializationOnOAS31() throws IOException {
final String jsonString = ResourceUtils.loadClassResource(getClass(), "specFiles/3.1.0/specWithDynamicRef.yaml");
OpenAPI openAPI = Yaml31.mapper().readValue(jsonString, OpenAPI.class);
assertNotNull(openAPI);

Schema baseNodeSchema = openAPI.getComponents().getSchemas().get("BaseNode");
assertNotNull(baseNodeSchema);
assertNotNull(baseNodeSchema.getProperties().get("children"));
JsonSchema childrenSchema = (JsonSchema) baseNodeSchema.getProperties().get("children");
assertEquals(childrenSchema.getItems().get$dynamicRef(), "#node");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
openapi: 3.1.0
info:
title: Tree API
version: 1.0.0

paths:
/tree:
post:
summary: Create a tree structure
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ExtendedNode'
responses:
'200':
description: Tree accepted
content:
application/json:
schema:
type: string

components:
schemas:
BaseNode:
$id: "https://example.com/schemas/base-node"
$schema: "https://json-schema.org/draft/2020-12/schema"
$dynamicAnchor: "node"
type: object
properties:
name:
type: string
children:
type: array
items:
$dynamicRef: "#node"
required: [name]

ExtendedProperties:
$id: "https://example.com/schemas/extended-properties"
$schema: "https://json-schema.org/draft/2020-12/schema"
$dynamicAnchor: "node"
type: object
properties:
type:
type: string
enum: [folder, file]
required: [type]

ExtendedNode:
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://example.com/schemas/extended-node"
allOf:
- $ref: "#/components/schemas/BaseNode"
- $ref: "#/components/schemas/ExtendedProperties"

Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ public Schema specVersion(SpecVersion specVersion) {
@OpenAPI31
private String $dynamicAnchor;

/**
* @since 2.2.32 (OpenAPI 3.1.0)
*/
@OpenAPI31
private String $dynamicRef;

/**
* @since 2.2.0 (OpenAPI 3.1.0)
Expand Down Expand Up @@ -616,6 +621,34 @@ public Schema typesItem(String type) {
return this;
}

/**
*
* @since 2.2.32 (OpenAPI 3.1.0)
*/
@OpenAPI31
public String get$dynamicRef() {
return $dynamicRef;
}

/**
*
* @since 2.2.32 (OpenAPI 3.1.0)
*/
@OpenAPI31
public void set$dynamicRef(String $dynamicRef) {
this.$dynamicRef = $dynamicRef;
}

/**
*
* @since 2.2.32 (OpenAPI 3.1.0)
*/
@OpenAPI31
public Schema $dynamicRef(String $dynamicRef) {
this.$dynamicRef = $dynamicRef;
return this;
}

/**
*
* @since 2.2.0 (OpenAPI 3.1.0)
Expand Down Expand Up @@ -2098,6 +2131,7 @@ public boolean equals(java.lang.Object o) {
Objects.equals(this.$schema, schema.$schema) &&
Objects.equals(this.$vocabulary, schema.$vocabulary) &&
Objects.equals(this.$dynamicAnchor, schema.$dynamicAnchor) &&
Objects.equals(this.$dynamicRef, schema.$dynamicRef) &&
Objects.equals(this.types, schema.types) &&
Objects.equals(this.allOf, schema.allOf) &&
Objects.equals(this.anyOf, schema.anyOf) &&
Expand Down Expand Up @@ -2132,10 +2166,10 @@ public int hashCode() {
exclusiveMinimum, exclusiveMinimumValue, maxLength, minLength, pattern, maxItems, minItems, uniqueItems,
maxProperties, minProperties, required, type, not, properties, additionalProperties, description,
format, $ref, nullable, readOnly, writeOnly, example, externalDocs, deprecated, xml, extensions,
discriminator, _enum, _default, patternProperties, $id, $anchor, $schema, $vocabulary, $dynamicAnchor, types, allOf, anyOf, oneOf, _const,
contentEncoding, contentMediaType, contentSchema, propertyNames, unevaluatedProperties, maxContains,
minContains, additionalItems, unevaluatedItems, _if, _else, then, dependentRequired, dependentSchemas,
$comment, examples, prefixItems, items);
discriminator, _enum, _default, patternProperties, $id, $anchor, $schema, $vocabulary, $dynamicAnchor,
$dynamicRef, types, allOf, anyOf, oneOf, _const, contentEncoding, contentMediaType, contentSchema,
propertyNames, unevaluatedProperties, maxContains, minContains, additionalItems, unevaluatedItems,
_if, _else, then, dependentRequired, dependentSchemas, $comment, examples, prefixItems, items);
}

public java.util.Map<String, Object> getExtensions() {
Expand Down Expand Up @@ -2206,6 +2240,7 @@ public String toString() {
sb.append(" $schema: ").append(toIndentedString($schema)).append("\n");
sb.append(" $vocabulary: ").append(toIndentedString($vocabulary)).append("\n");
sb.append(" $dynamicAnchor: ").append(toIndentedString($dynamicAnchor)).append("\n");
sb.append(" $dynamicRef: ").append(toIndentedString($dynamicRef)).append("\n");
sb.append(" const: ").append(toIndentedString(_const)).append("\n");
sb.append(" contentEncoding: ").append(toIndentedString(contentEncoding)).append("\n");
sb.append(" contentMediaType: ").append(toIndentedString(contentMediaType)).append("\n");
Expand Down
Loading