use of com.github.victools.jsonschema.generator.impl.DefinitionKey in project jsonschema-generator by victools.
the class SchemaBuilder method buildDefinitionsAndResolveReferences.
/**
* Finalisation Step: collect the entries for the generated schema's "definitions" and ensure that all references are either pointing to the
* appropriate definition or contain the respective (sub) schema directly inline.
*
* @param designatedDefinitionPath designated path to the returned definitions node (to be incorporated in {@link SchemaKeyword#TAG_REF} values)
* @param mainSchemaKey definition key identifying the main type for which createSchemaReference() was invoked
* @param generationContext context containing all definitions of (sub) schemas and the list of references to them
* @return node representing the main schema's "definitions" (may be empty)
*/
private ObjectNode buildDefinitionsAndResolveReferences(String designatedDefinitionPath, DefinitionKey mainSchemaKey, SchemaGenerationContextImpl generationContext) {
final ObjectNode definitionsNode = this.config.createObjectNode();
final boolean createDefinitionsForAll = this.config.shouldCreateDefinitionsForAllObjects();
final boolean inlineAllSchemas = this.config.shouldInlineAllSchemas();
final AtomicBoolean considerOnlyDirectReferences = new AtomicBoolean(false);
Predicate<DefinitionKey> shouldProduceDefinition = definitionKey -> {
if (inlineAllSchemas) {
return false;
}
if (definitionKey.equals(mainSchemaKey)) {
return true;
}
List<ObjectNode> references = generationContext.getReferences(definitionKey);
if (considerOnlyDirectReferences.get() && references.isEmpty()) {
return false;
}
List<ObjectNode> nullableReferences = generationContext.getNullableReferences(definitionKey);
return createDefinitionsForAll || (references.size() + nullableReferences.size()) > 1;
};
Map<DefinitionKey, String> baseReferenceKeys = this.getReferenceKeys(mainSchemaKey, shouldProduceDefinition, generationContext);
considerOnlyDirectReferences.set(true);
final boolean createDefinitionForMainSchema = this.config.shouldCreateDefinitionForMainSchema();
for (Map.Entry<DefinitionKey, String> entry : baseReferenceKeys.entrySet()) {
String definitionName = entry.getValue();
DefinitionKey definitionKey = entry.getKey();
List<ObjectNode> references = generationContext.getReferences(definitionKey);
List<ObjectNode> nullableReferences = generationContext.getNullableReferences(definitionKey);
final String referenceKey;
boolean referenceInline = !shouldProduceDefinition.test(definitionKey);
if (referenceInline) {
// it is a simple type, just in-line the sub-schema everywhere
ObjectNode definition = generationContext.getDefinition(definitionKey);
references.forEach(node -> AttributeCollector.mergeMissingAttributes(node, definition));
referenceKey = null;
} else {
// the same sub-schema is referenced in multiple places
if (createDefinitionForMainSchema || !definitionKey.equals(mainSchemaKey)) {
// add it to the definitions (unless it is the main schema that is not explicitly moved there via an Option)
definitionsNode.set(definitionName, generationContext.getDefinition(definitionKey));
referenceKey = this.config.getKeyword(SchemaKeyword.TAG_REF_MAIN) + '/' + designatedDefinitionPath + '/' + definitionName;
} else {
referenceKey = this.config.getKeyword(SchemaKeyword.TAG_REF_MAIN);
}
references.forEach(node -> node.put(this.config.getKeyword(SchemaKeyword.TAG_REF), referenceKey));
}
if (!nullableReferences.isEmpty()) {
ObjectNode definition;
if (referenceInline) {
definition = generationContext.getDefinition(definitionKey);
} else {
definition = this.config.createObjectNode().put(this.config.getKeyword(SchemaKeyword.TAG_REF), referenceKey);
}
generationContext.makeNullable(definition);
if (!inlineAllSchemas && (createDefinitionsForAll || nullableReferences.size() > 1)) {
String nullableDefinitionName = this.definitionNamingStrategy.adjustNullableName(definitionKey, definitionName, generationContext);
definitionsNode.set(nullableDefinitionName, definition);
nullableReferences.forEach(node -> node.put(this.config.getKeyword(SchemaKeyword.TAG_REF), this.config.getKeyword(SchemaKeyword.TAG_REF_MAIN) + '/' + designatedDefinitionPath + '/' + nullableDefinitionName));
} else {
nullableReferences.forEach(node -> AttributeCollector.mergeMissingAttributes(node, definition));
}
}
}
definitionsNode.forEach(node -> this.schemaNodes.add((ObjectNode) node));
return definitionsNode;
}
use of com.github.victools.jsonschema.generator.impl.DefinitionKey in project jreleaser by jreleaser.
the class JsonSchema method execute.
protected void execute() {
Map<String, String> mappings = new LinkedHashMap<>();
mappings.put("Map<String, Object>", "Properties");
mappings.put("Map<String, String>", "StringProperties");
mappings.put("Map<String, Webhook>", "WebhookMap");
mappings.put("Map<String, Archive>", "ArchiveMap");
mappings.put("Map<String, Jlink>", "JlinkMap");
mappings.put("Map<String, Jpackage>", "JpackageMap");
mappings.put("Map<String, NativeImage>", "NativeImageMap");
mappings.put("Map<String, Distribution>", "DistributionMap");
mappings.put("Map<String, DockerSpec>", "DockerSpecMap");
mappings.put("Map<String, Artifactory>", "ArtifactoryMap");
mappings.put("Map<String, Http>", "HttpMap");
mappings.put("Map<String, S3>", "S3Map");
try {
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2019_09, OptionPreset.PLAIN_JSON);
configBuilder.getObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
configBuilder.with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT);
configBuilder.with(Option.DEFINITION_FOR_MAIN_SCHEMA);
configBuilder.with(Option.DEFINITIONS_FOR_ALL_OBJECTS);
JacksonModule jacksonModule = new JacksonModule();
configBuilder.with(jacksonModule);
configBuilder.forTypesInGeneral().withDescriptionResolver(scope -> scope.getType().getErasedType() == JReleaserModel.class ? String.format("JReleaser %s", JReleaserVersion.getPlainVersion()) : null).withPatternPropertiesResolver(scope -> {
if (scope.getType().isInstanceOf(Map.class)) {
ResolvedType type = scope.getTypeParameterFor(Map.class, 1);
if (type.getErasedType() != String.class && type.getErasedType() != Object.class) {
return singletonMap("^[a-zA-Z-]+$", type);
}
}
return null;
}).withAdditionalPropertiesResolver(scope -> {
if (scope.getType().isInstanceOf(Map.class)) {
ResolvedType type = scope.getTypeParameterFor(Map.class, 1);
if (type.getErasedType() == String.class || type.getErasedType() == Object.class) {
return scope.getTypeParameterFor(Map.class, 0);
}
}
return null;
}).withDefinitionNamingStrategy(new DefaultSchemaDefinitionNamingStrategy() {
@Override
public String getDefinitionNameForKey(DefinitionKey key, SchemaGenerationContext context) {
String definitionNameForKey = super.getDefinitionNameForKey(key, context);
return mappings.getOrDefault(definitionNameForKey, definitionNameForKey);
}
});
SchemaGeneratorConfig config = configBuilder.build();
SchemaGenerator generator = new SchemaGenerator(config);
JsonNode jsonSchema = generator.generateSchema(JReleaserModel.class);
String fileName = String.format("jreleaser-%s-schema.json", JReleaserVersion.getPlainVersion());
Path schemaPath = Paths.get(fileName);
String json = configBuilder.getObjectMapper().writeValueAsString(jsonSchema);
Files.write(schemaPath, json.getBytes(), CREATE, WRITE, TRUNCATE_EXISTING);
parent().out.println("Schema written to " + schemaPath.toAbsolutePath());
} catch (Exception e) {
throw new JReleaserException($("ERROR_unexpected_error"), e);
}
}
use of com.github.victools.jsonschema.generator.impl.DefinitionKey in project kestra by kestra-io.
the class JsonSchemaGenerator method build.
protected <T> void build(SchemaGeneratorConfigBuilder builder, Class<? extends T> cls) {
builder.with(new JacksonModule()).with(new JavaxValidationModule(JavaxValidationOption.NOT_NULLABLE_FIELD_IS_REQUIRED, JavaxValidationOption.INCLUDE_PATTERN_EXPRESSIONS)).with(new Swagger2Module()).with(Option.DEFINITIONS_FOR_ALL_OBJECTS).with(Option.DEFINITION_FOR_MAIN_SCHEMA).with(Option.PLAIN_DEFINITION_KEYS).with(Option.ALLOF_CLEANUP_AT_THE_END);
// default value
builder.forFields().withDefaultResolver(this::defaults);
// def name
builder.forTypesInGeneral().withDefinitionNamingStrategy(new DefaultSchemaDefinitionNamingStrategy() {
@Override
public String getDefinitionNameForKey(DefinitionKey key, SchemaGenerationContext context) {
TypeContext typeContext = context.getTypeContext();
ResolvedType type = key.getType();
return typeContext.getFullTypeDescription(type);
}
@Override
public String adjustNullableName(DefinitionKey key, String definitionName, SchemaGenerationContext context) {
return definitionName;
}
});
// inline some type
builder.forTypesInGeneral().withCustomDefinitionProvider(new CustomDefinitionProviderV2() {
@Override
public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
if (javaType.isInstanceOf(Map.class) || javaType.isInstanceOf(Enum.class)) {
ObjectNode definition = context.createStandardDefinition(javaType, this);
return new CustomDefinition(definition, true);
} else if (javaType.isInstanceOf(Duration.class)) {
ObjectNode definitionReference = context.createDefinitionReference(context.getTypeContext().resolve(String.class)).put("format", "duration");
return new CustomDefinition(definitionReference, true);
} else {
return null;
}
}
});
// PluginProperty $dynamic && deprecated swagger properties
builder.forFields().withInstanceAttributeOverride((memberAttributes, member, context) -> {
PluginProperty pluginPropertyAnnotation = member.getAnnotation(PluginProperty.class);
if (pluginPropertyAnnotation != null) {
memberAttributes.put("$dynamic", pluginPropertyAnnotation.dynamic());
}
Schema schema = member.getAnnotation(Schema.class);
if (schema != null && schema.deprecated()) {
memberAttributes.put("$deprecated", true);
}
});
// Add Plugin annotation special docs
builder.forTypesInGeneral().withTypeAttributeOverride((collectedTypeAttributes, scope, context) -> {
Plugin pluginAnnotation = scope.getType().getErasedType().getAnnotation(Plugin.class);
if (pluginAnnotation != null) {
List<ObjectNode> examples = Arrays.stream(pluginAnnotation.examples()).map(example -> context.getGeneratorConfig().createObjectNode().put("full", example.full()).put("code", String.join("\n", example.code())).put("lang", example.lang()).put("title", example.title())).collect(Collectors.toList());
if (examples.size() > 0) {
collectedTypeAttributes.set("$examples", context.getGeneratorConfig().createArrayNode().addAll(examples));
}
}
});
// PluginProperty additionalProperties
builder.forFields().withAdditionalPropertiesResolver(target -> {
PluginProperty pluginPropertyAnnotation = target.getAnnotation(PluginProperty.class);
if (pluginPropertyAnnotation != null) {
return pluginPropertyAnnotation.additionalProperties();
}
return Object.class;
});
}
use of com.github.victools.jsonschema.generator.impl.DefinitionKey in project jsonschema-generator by victools.
the class SchemaGeneratorCustomDefinitionsTest method testGenerateSchema_CustomStandardDefinition.
@Test
@Parameters(source = SchemaVersion.class)
public void testGenerateSchema_CustomStandardDefinition(SchemaVersion schemaVersion) throws Exception {
CustomDefinitionProviderV2 customDefinitionProviderOne = new CustomDefinitionProviderV2() {
@Override
public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
ObjectNode customDefinition = context.getGeneratorConfig().createObjectNode().put(context.getKeyword(SchemaKeyword.TAG_TITLE), "Custom Definition #1 for " + context.getTypeContext().getSimpleTypeDescription(javaType));
// using SchemaGenerationContext.createStandardDefinitionReference() to avoid endless loop with this custom definition
customDefinition.withArray(context.getKeyword(SchemaKeyword.TAG_ANYOF)).add(context.createStandardDefinitionReference(javaType, this)).addObject().put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(SchemaKeyword.TAG_TYPE_NULL));
return new CustomDefinition(customDefinition);
}
};
CustomDefinitionProviderV2 customDefinitionProviderTwo = new CustomDefinitionProviderV2() {
@Override
public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
if (javaType.getErasedType() == String.class) {
return null;
}
ObjectNode customDefinition = context.getGeneratorConfig().createObjectNode().put(context.getKeyword(SchemaKeyword.TAG_TITLE), "Custom Definition #2 for " + context.getTypeContext().getFullTypeDescription(javaType));
// using SchemaGenerationContext.createStandardDefinitionReference() to avoid endless loop with this custom definition
customDefinition.withArray(context.getKeyword(SchemaKeyword.TAG_ANYOF)).add(context.createStandardDefinitionReference(javaType, this)).addObject().put(context.getKeyword(SchemaKeyword.TAG_TYPE), context.getKeyword(SchemaKeyword.TAG_TYPE_NULL));
return new CustomDefinition(customDefinition);
}
};
SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(schemaVersion, OptionPreset.PLAIN_JSON).with(Option.DEFINITIONS_FOR_ALL_OBJECTS);
configBuilder.forTypesInGeneral().withCustomDefinitionProvider(customDefinitionProviderOne).withCustomDefinitionProvider(customDefinitionProviderTwo).withDefinitionNamingStrategy(new SchemaDefinitionNamingStrategy() {
@Override
public String getDefinitionNameForKey(DefinitionKey key, SchemaGenerationContext generationContext) {
return key.getType().getErasedType().getSimpleName().toLowerCase();
}
@Override
public void adjustDuplicateNames(Map<DefinitionKey, String> duplicateNames, SchemaGenerationContext context) {
char suffix = 'a';
for (Map.Entry<DefinitionKey, String> singleEntry : duplicateNames.entrySet()) {
singleEntry.setValue(singleEntry.getValue() + " (" + suffix + ")");
suffix++;
}
}
});
SchemaGenerator generator = new SchemaGenerator(configBuilder.build());
JsonNode result = generator.generateSchema(TestDirectCircularClass.class);
JSONAssert.assertEquals('\n' + result.toString() + '\n', TestUtils.loadResource(this.getClass(), "multiple-definitions-one-type-" + schemaVersion.name() + ".json"), result.toString(), JSONCompareMode.STRICT);
}
use of com.github.victools.jsonschema.generator.impl.DefinitionKey in project jsonschema-generator by victools.
the class SchemaDefinitionNamingStrategyTest method parametersForTestExampleStrategy.
public Object[] parametersForTestExampleStrategy() {
NamingBase jacksonSnakeCase = (NamingBase) PropertyNamingStrategies.SNAKE_CASE;
SchemaDefinitionNamingStrategy snakeCase = new DefaultSchemaDefinitionNamingStrategy() {
@Override
public String getDefinitionNameForKey(DefinitionKey key, SchemaGenerationContext generationContext) {
return jacksonSnakeCase.translate(super.getDefinitionNameForKey(key, generationContext)).replaceAll("<_", "<").replaceAll(", _", ",");
}
};
NamingBase jacksonDotCase = (NamingBase) PropertyNamingStrategies.LOWER_DOT_CASE;
SchemaDefinitionNamingStrategy dotCase = new DefaultSchemaDefinitionNamingStrategy() {
@Override
public String getDefinitionNameForKey(DefinitionKey key, SchemaGenerationContext generationContext) {
return jacksonDotCase.translate(super.getDefinitionNameForKey(key, generationContext)).replaceAll("<.", "<").replaceAll(", .", "-");
}
};
SchemaDefinitionNamingStrategy inclPackage = (definitionKey, context) -> context.getTypeContext().getFullTypeDescription(definitionKey.getType());
return new Object[][] { { "Snake Case", snakeCase, typeContext.resolve(BigDecimal.class), "big_decimal", "big_decimal" }, { "Snake Case", snakeCase, typeContext.resolve(Map.class, String.class, BigDecimal.class), "map(string,big_decimal)", "map_string.big_decimal_" }, { "Dot Case", dotCase, typeContext.resolve(BigDecimal.class), "big.decimal", "big.decimal" }, { "Dot Case", dotCase, typeContext.resolve(Map.class, String.class, BigDecimal.class), "map(string-big.decimal)", "map_string-big.decimal_" }, { "Incl. Package", inclPackage, typeContext.resolve(BigDecimal.class), "java.math.BigDecimal", "java.math.BigDecimal" }, { "Incl. Package", inclPackage, typeContext.resolve(Map.class, String.class, BigDecimal.class), "java.util.Map(java.lang.String,java.math.BigDecimal)", "java.util.Map_java.lang.String.java.math.BigDecimal_" } };
}
Aggregations