Updating schema examples using in the custom AsyncApiCustomizer does not change examples of surrounding schemas
Describe the bug
I wrote a custom AsyncApiCustomizer that updates the examples of some schema objects.
Dependencies and versions used
- [x]
springwolf-uiversion1.12.0 - [x]
springwolf-stompversion1.12.0
Code example
@Component
public class CustomAsyncApiCustomizer implements AsyncApiCustomizer {
@Override
public void customize(AsyncAPI asyncAPI) {
Map<String, SchemaObject> schemas = asyncAPI.getComponents().getSchemas();
flattenSchema(schemas, Ipv4Address.class, "10.0.4.5");
}
private void flattenSchema(Map<String, SchemaObject> schemas, Class<?> schemaClass, String example) {
SchemaObject schemaObject = schemas.get(schemaClass.getName());
schemaObject.setType("string");
schemaObject.setProperties(null);
schemaObject.setExamples(List.of(example));
}
}
But I have other schema's, e.g. Ipv4AddressConfiguration, that have Ipv4Address as a property. In the generated Springwolf UI, I can see that the example of Ipv4Address is changed but not the example of Ipv4AddressConfiguration.
{
"ipv4Address": {
"bytes": "1sdasdasd="
},
"subnetMask": 8
}
How can I make sure that the example of the Ipv4AddressConfiguration is regenerated AFTER updating the "leaf" schema examples? Is there a way to regenerate ALL examples from the AsyncApiCustomizer?
Stack trace and error logs No stack traces
Welcome to Springwolf. Thanks a lot for reporting your first issue. Please check out our contributors guide and feel free to join us on discord.
Hi @limburgie, thanks for reaching out. AsyncApiCustomizer is the last extension point to intercept generated document before serialization (see springwolf docs). There are no Springwolf processes working on the specification afterwards. It is therefore considered a low-level api where it its the implementors responsibility to take care of creating a consistent specification. Therefore I would not consider it a bug at the moment.
However, the general approach for customizing examples is using the @Schema annotation. Would this approach work for you? If not, what is your use case that prevents you from using it? I'd hope we find a better solution than going through AsyncApiCustomizer...
I have a similar need, I was able to update all my examples by simply iterating over all schemas.
override fun customize(asyncAPI: AsyncAPI?) {
asyncAPI?.components?.schemas?.forEach { schema ->
val jsonNode: JsonNode = // your custom code here
schema.value.examples = listOf(jsonNode)
}
}
Hi @sam0r040,
I agree that using @Schema would be the best option, however I don't have access to the core domain (which is also used as DTO's for my API)... So that's why I was looking for a way to edit the examples in another way.
To give you some more information as to why this is needed. I have implemented custom JsonSerializers and JsonDeserializers for some of my domain objects. The reason I am using these (de)serializers is because, again, I have no access to the domain so I cannot use @JsonValue and @JsonCreator on the domain classes. But this also causes Springwolf to have no clue that these domain classes are serialized/deserialized in a custom way.
I am using a custom ObjectMapper to register these custom (de)serializers in my application code. I noticed that Springwolf has its own ObjectMapper that it uses for e.g. generating examples. So another way might be if I could inject my custom ObjectMapper (or at least my custom (de)serializers) in Springwolf so they are taken into account?
I would love to hear your take on this! Thanks for your help.
If you want to change how examples are treated, then you should have a look at the ExampleValueGenerators (ie ExampleJsonValueGenerator). They also use their own ObjectMapper instance. Internally Springwolf is not just deserializing objects to create examples, but reconstructing them from the schema provided by swagger core. Therefore I'm not sure if changing the ObjectMapper would really help.
There are multiple possibilities to change the schema or examples along the way:
- ExampleValueGenerator.create*Example() -> Change example based on the Schema and Type
- ExampleValueGenerator.prepareForSerialization() -> Change full Example as JsonNode based on Schema
- Use a custom io.swagger.v3.core.converter.ModelConverter like MonetaryAmountConverter where could for example replace domain classes with your own classes.
Your use case is not clear enough for me to point you in a more specific direction, can you provide more information?
@limburgie Are the contributions still active for this project ??