diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/example/ExampleJsonGenerator.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/example/ExampleJsonGenerator.java index cf7250efa..e6a6f90c8 100644 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/example/ExampleJsonGenerator.java +++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/example/ExampleJsonGenerator.java @@ -2,6 +2,7 @@ package io.github.stavshamir.springwolf.schemas.example; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.util.RawValue; @@ -61,15 +62,17 @@ public Object fromSchema(Schema schema, Map definitions) { } static String buildSchema(Schema schema, Map definitions) { - return buildSchemaInternal(schema, definitions, new HashSet<>()); + return buildSchemaInternal(schema, definitions, new HashSet<>()).toString(); } - private static String buildSchemaInternal(Schema schema, Map definitions, Set visited) { - String exampleValue = ExampleJsonGenerator.getExampleValueFromSchemaAnnotation(schema); + private static ObjectNode buildSchemaInternal(Schema schema, Map definitions, Set visited) { + ObjectNode exampleValue = ExampleJsonGenerator.getExampleValueFromSchemaAnnotation(schema); if (exampleValue != null) { return exampleValue; } + ObjectNode schemaNode = objectMapper.createObjectNode(); + String ref = schema.get$ref(); if (ref != null) { String schemaName = StringUtils.substringAfterLast(ref, "/"); @@ -81,30 +84,54 @@ private static String buildSchemaInternal(Schema schema, Map def } String type = schema.getType(); - return switch (type) { - case "array" -> ExampleJsonGenerator.handleArraySchema(schema, definitions, visited); - case "boolean" -> DEFAULT_BOOLEAN_EXAMPLE; - case "integer" -> DEFAULT_INTEGER_EXAMPLE; - case "number" -> DEFAULT_NUMBER_EXAMPLE; - case "object" -> ExampleJsonGenerator.handleObject(schema, definitions, visited); - case "string" -> ExampleJsonGenerator.handleStringSchema(schema); - default -> DEFAULT_UNKNOWN_SCHEMA_EXAMPLE(type); - }; + switch (type) { + case "array": + // Handle array schema + ObjectNode itemsNode = ExampleJsonGenerator.handleArraySchema(schema, definitions, visited); + schemaNode.set("items", itemsNode); // Set the "items" property + break; + case "boolean": + schemaNode.textNode(DEFAULT_BOOLEAN_EXAMPLE); + break; + case "integer": + schemaNode.textNode(DEFAULT_INTEGER_EXAMPLE); + break; + case "number": + schemaNode.textNode(DEFAULT_NUMBER_EXAMPLE); + break; + case "object": + // Handle object schema + ObjectNode objectNode = ExampleJsonGenerator.handleObject(schema, definitions, visited); + schemaNode.set("object", objectNode); // Set the "properties" property + break; + case "string": + // Handle string schema + schemaNode.textNode(ExampleJsonGenerator.handleStringSchema(schema)); + break; + default: + schemaNode.put("example", DEFAULT_UNKNOWN_SCHEMA_EXAMPLE(type)); + } + + return schemaNode; } - private static String getExampleValueFromSchemaAnnotation(Schema schema) { + private static ObjectNode getExampleValueFromSchemaAnnotation(Schema schema) { Object exampleValue = schema.getExample(); if (exampleValue == null) { return null; } + // Create an ObjectNode to hold the example JSON + ObjectNode exampleNode = objectMapper.createObjectNode(); + // Handle special types (i.e. map) with custom @Schema annotation and specified example value Object additionalProperties = schema.getAdditionalProperties(); if (additionalProperties instanceof StringSchema) { StringSchema additionalPropertiesSchema = (StringSchema) additionalProperties; Object exampleValueString = additionalPropertiesSchema.getExample(); if (exampleValueString != null) { - return (String) exampleValueString; + exampleNode.set((String) exampleValueString, objectMapper.convertValue(exampleValue, JsonNode.class)); + return exampleNode; } } @@ -115,21 +142,35 @@ private static String getExampleValueFromSchemaAnnotation(Schema schema) { // exampleValue is represented in their native type if (exampleValue instanceof Boolean || exampleValue instanceof Number) { - return exampleValue.toString(); + exampleNode.set("example", objectMapper.convertValue(exampleValue, JsonNode.class)); + /* + JsonNode node = objectMapper.createObjectNode(); + node = objectMapper.valueToTree("false"); + return (ObjectNode) node; + */ + + return exampleNode; } try { // exampleValue (i.e. OffsetDateTime) is represented as string - return objectMapper.writeValueAsString(exampleValue.toString()); - } catch (JsonProcessingException ex) { - log.debug("Unable to convert example to string: %s".formatted(exampleValue.toString()), ex); + exampleNode.set("example", objectMapper.convertValue(exampleValue.toString(), JsonNode.class)); + } catch (IllegalArgumentException ex) { + log.debug("Unable to convert example to JSON: %s".formatted(exampleValue.toString()), ex); } - return "\"\""; + + return exampleNode; } - private static String handleArraySchema(Schema schema, Map definitions, Set visited) { - return Arrays.asList(buildSchemaInternal(schema.getItems(), definitions, visited)) - .toString(); + private static ObjectNode handleArraySchema(Schema schema, Map definitions, Set visited) { + + ObjectNode objectNode = objectMapper.createObjectNode(); + + List list = Arrays.asList(buildSchemaInternal(schema.getItems(), definitions, visited)); + + objectNode.set("array", objectMapper.valueToTree(list)); + + return objectNode; } private static String handleStringSchema(Schema schema) { @@ -167,13 +208,14 @@ private static String getFirstEnumValue(Schema schema) { return null; } - private static String handleObject(Schema schema, Map definitions, Set visited) { + private static ObjectNode handleObject(Schema schema, Map definitions, Set visited) { + Map properties = schema.getProperties(); if (properties != null) { if (!visited.contains(schema)) { visited.add(schema); - String example = handleObjectProperties(properties, definitions, visited); + ObjectNode example = handleObjectProperties(properties, definitions, visited); visited.remove(schema); return example; @@ -195,11 +237,12 @@ private static String handleObject(Schema schema, Map definition } // i.e. A MapSchema is type=object, but has properties=null - return "{}"; + return objectMapper.createObjectNode(); } - private static String handleObjectProperties( + private static ObjectNode handleObjectProperties( Map properties, Map definitions, Set visited) { + ObjectNode objectNode = objectMapper.createObjectNode(); properties.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(entry -> { @@ -208,6 +251,6 @@ private static String handleObjectProperties( objectNode.putRawValue(propertyKey, propertyRawValue); }); - return objectNode.toString(); + return objectNode; } }