Skip to content
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
38 changes: 38 additions & 0 deletions checkstyle-project-fqn.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,22 @@
<property name="id" value="thinThingCommandService"/>
<property name="files" value=".*[/\\]src[/\\]test[/\\]java[/\\].*"/>
</module>
<module name="SuppressionSingleFilter">
<property name="id" value="snakeYamlOnlyInYamlAdapter"/>
<property name="files" value=".*[/\\]thingifier-yaml[/\\]src[/\\](main|test)[/\\]java[/\\].*"/>
</module>
<module name="SuppressionSingleFilter">
<property name="id" value="yamlAdapterTypesStayInYamlModule"/>
<property name="files" value=".*[/\\]thingifier-yaml[/\\]src[/\\](main|test)[/\\]java[/\\].*"/>
</module>
<module name="SuppressionSingleFilter">
<property name="id" value="schemaDefinitionSpecsNoMutableRuntimeSchema"/>
<property name="files" value=".*[/\\]src[/\\]test[/\\]java[/\\].*"/>
</module>
<module name="SuppressionSingleFilter">
<property name="id" value="schemaDefinitionSpecsNoMutableRuntimeSchema"/>
<property name="files" value=".*[/\\]thingifier[/\\]src[/\\]main[/\\]java[/\\]uk[/\\]co[/\\]compendiumdev[/\\]thingifier[/\\]application[/\\]schema[/\\]definition[/\\]ThingifierModel(Assembler|Exporter)\.java"/>
</module>
<module name="RegexpMultiline">
<property name="id" value="commandDtosNoMutableDomain"/>
<property name="format" value="(?s)package uk\.co\.compendiumdev\.thingifier\.application\.command;.*import uk\.co\.compendiumdev\.thingifier\.core\.domain\.(definitions\.EntityDefinition|instances\.(EntityInstance|EntityInstanceDraft));"/>
Expand Down Expand Up @@ -173,7 +189,29 @@
<property name="format" value="(?s)class ThingCommandService\b.*(private\s+ThingCommandResult\s+(create|amend|replace|delete|connectExistingRelationship|createAndConnect|relate|disconnectRelationship|connectRelationship|connectRelationshipReferences|connectRelationships)\s*\(|private\s+(EntityInstanceDraft|EntityInstance|RelationshipVectorDefinition|boolean|void)\s+(createDraft|resolveInstance|relationshipErrorIfInvalid|firstRelationshipVector|bodyReferencesExistingRelatedItem|relatedInstanceMatchingIdentifier|matchesQueryIdentifier|disconnectConnections|copyBaseDraftValues)\s*\(|private\s+static\s+final\s+class\s+(RelatedItemResolution|RelationshipSnapshot|RelationshipLink)\b)"/>
<property name="message" value="ThingCommandService should stay a thin transaction-wrapped dispatcher; moved use-case helpers belong in focused collaborators."/>
</module>
<module name="RegexpMultiline">
<property name="id" value="schemaDefinitionSpecsNoMutableRuntimeSchema"/>
<property name="format" value="(?s)package uk\.co\.compendiumdev\.thingifier\.application\.schema\.definition;.*import uk\.co\.compendiumdev\.thingifier\.core\.domain\.definitions\.(Cardinality|DefinedFields|EntityDefinition|field\.definition\.Field|relationship\.(RelationshipDefinition|RelationshipVectorDefinition));"/>
<property name="message" value="Schema definition specs must stay YAML-agnostic and runtime-schema neutral; only the assembler/exporter boundary may touch mutable schema objects."/>
</module>
<module name="RegexpMultiline">
<property name="id" value="coreNoYamlOrSchemaAdapter"/>
<property name="format" value="(?s)package uk\.co\.compendiumdev\.thingifier\.core(\.|;).*import uk\.co\.compendiumdev\.thingifier\.(yaml|application\.schema\.definition)\."/>
<property name="message" value="Core must not depend on YAML adapters or schema import-definition adapter types."/>
</module>
<module name="TreeWalker">
<module name="RegexpSinglelineJava">
<property name="id" value="snakeYamlOnlyInYamlAdapter"/>
<property name="format" value="^import org\.yaml\.snakeyaml\."/>
<property name="ignoreComments" value="true"/>
<property name="message" value="SnakeYAML imports belong only in the optional thingifier-yaml adapter module."/>
</module>
<module name="RegexpSinglelineJava">
<property name="id" value="yamlAdapterTypesStayInYamlModule"/>
<property name="format" value="^import uk\.co\.compendiumdev\.thingifier\.yaml\."/>
<property name="ignoreComments" value="true"/>
<property name="message" value="YAML adapter DTOs and API types must not leak into thingifier or ercoremodel."/>
</module>
<module name="RegexpSinglelineJava">
<property name="id" value="sparkImportsAtSparkEdge"/>
<property name="format" value="^import spark\."/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public final class Field {
private final String name;
private final FieldType type;
private final Set<String> fieldExamples;
private final Set<String> configuredExamples;
private boolean fieldIsOptional;

// default value for the field
Expand Down Expand Up @@ -50,6 +51,7 @@ public Field(final String name, final FieldType type) {
truncateStringIfTooLong = false;
truncatedStringLength = -1;
fieldExamples = new HashSet<>();
configuredExamples = new HashSet<>();

if (type == FieldType.INTEGER) {
typeValidationRule = new IntegerValidationRule();
Expand Down Expand Up @@ -270,6 +272,7 @@ public String getAsTruncatedString(FieldValue value) {
}

public Field withExample(final String anExample) {
configuredExamples.add(anExample);
fieldExamples.add(anExample);
if (type == FieldType.ENUM) {
typeValidationRule = new EnumValidationRule(getExamples());
Expand Down Expand Up @@ -336,6 +339,22 @@ public List<String> getExamples() {
return new ArrayList<>(buildExamples);
}

public List<String> getConfiguredExamples() {
return new ArrayList<>(configuredExamples);
}

public String getConfiguredDefaultValue() {
return defaultValue;
}

public int getTruncatedStringLength() {
return truncatedStringLength;
}

public ValidationRule getTypeValidationRule() {
return typeValidationRule;
}

public String getRandomExampleValue() {
final List<String> examples = getExamples();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package uk.co.compendiumdev.thingifier.core.domain.definitions.validation;

import java.util.ArrayList;
import java.util.List;
import uk.co.compendiumdev.thingifier.core.domain.definitions.field.definition.FieldType;
import uk.co.compendiumdev.thingifier.core.domain.definitions.field.instance.FieldValue;
Expand Down Expand Up @@ -36,4 +37,8 @@ public String getErrorMessage(FieldValue value) {
public String getExplanation() {
return String.format("Value must be an Enum (%s) value", valuesAsCsv());
}

public List<String> getValidValues() {
return new ArrayList<>(validValues);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,8 @@ public String getExplanation() {
return String.format(
"Value must contain text that matches the regex %s", this.regexToMatch);
}

public String getRegexToFind() {
return regexToMatch;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,8 @@ public String getErrorMessage(final FieldValue value) {
public String getExplanation() {
return String.format("Value must match the regex %s", this.regexToMatch);
}

public String getRegexToMatch() {
return regexToMatch;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ public String getErrorMessage(final FieldValue value) {
public String getExplanation() {
return "Maximum length allowed is %d".formatted(maxLength);
}

public int getMaximumLength() {
return maxLength;
}
}
2 changes: 2 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<swagger-models-version>2.2.27</swagger-models-version>
<gson-version>2.8.6</gson-version>
<sqlite-jdbc-version>3.53.2.0</sqlite-jdbc-version>
<snakeyaml-version>2.6</snakeyaml-version>
<junit.jupiter.version>5.6.2</junit.jupiter.version>
<java.version>16</java.version>
<rest-assured-version>5.5.0</rest-assured-version>
Expand Down Expand Up @@ -133,6 +134,7 @@
<modules>
<module>ercoremodel</module>
<module>thingifier</module>
<module>thingifier-yaml</module>
<module>examplemodels</module>
<module>todoManagerRestAuto</module>
<module>challenger</module>
Expand Down
87 changes: 87 additions & 0 deletions thingifier-yaml/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>uk.co.compendiumdev.thingifier</groupId>
<artifactId>thingifier-root</artifactId>
<version>1.5.6-SNAPSHOT</version>
</parent>

<groupId>uk.co.compendiumdev</groupId>
<artifactId>thingifier-yaml</artifactId>
<packaging>jar</packaging>

<name>thingifier yaml adapter</name>
<url>https://compendiumdev.co.uk</url>

<dependencies>
<dependency>
<groupId>uk.co.compendiumdev</groupId>
<artifactId>thingifier</artifactId>
<version>${thingifier.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml-version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>uk.co.compendiumdev.thingifier</groupId>
<artifactId>examplemodels</artifactId>
<version>${thingifier.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution>
<id>checkstyle-check</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package uk.co.compendiumdev.thingifier.yaml;

public class ThingifierYamlException extends RuntimeException {

public ThingifierYamlException(final String message) {
super(message);
}

public ThingifierYamlException(final String message, final Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package uk.co.compendiumdev.thingifier.yaml;

import java.util.Map;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import uk.co.compendiumdev.thingifier.Thingifier;
import uk.co.compendiumdev.thingifier.application.schema.definition.ThingifierModelDefinition;
import uk.co.compendiumdev.thingifier.application.schema.definition.ThingifierModelExporter;
import uk.co.compendiumdev.thingifier.yaml.internal.YamlThingifierDocumentMapper;

public final class ThingifierYamlExporter {

private final YamlThingifierDocumentMapper mapper;

public ThingifierYamlExporter() {
mapper = new YamlThingifierDocumentMapper();
}

public String export(final Thingifier thingifier) {
return export(new ThingifierModelExporter().export(thingifier));
}

public String export(final ThingifierModelDefinition definition) {
final Map<String, Object> document = mapper.toYamlMap(definition);
return yaml().dump(document);
}

private Yaml yaml() {
final DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
options.setPrettyFlow(true);
options.setSplitLines(false);
return new Yaml(options);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package uk.co.compendiumdev.thingifier.yaml;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.error.YAMLException;
import uk.co.compendiumdev.thingifier.Thingifier;
import uk.co.compendiumdev.thingifier.application.schema.definition.SchemaDefinitionValidationReport;
import uk.co.compendiumdev.thingifier.application.schema.definition.ThingifierModelAssembler;
import uk.co.compendiumdev.thingifier.application.schema.definition.ThingifierModelDefinition;
import uk.co.compendiumdev.thingifier.yaml.internal.YamlThingifierDocument;
import uk.co.compendiumdev.thingifier.yaml.internal.YamlThingifierDocumentMapper;

public final class ThingifierYamlLoader {

private final ThingifierModelAssembler assembler;
private final YamlThingifierDocumentMapper mapper;

public ThingifierYamlLoader() {
assembler = new ThingifierModelAssembler();
mapper = new YamlThingifierDocumentMapper();
}

public ThingifierModelDefinition loadDefinition(final Path path) throws IOException {
try (InputStream input = Files.newInputStream(path)) {
return loadDefinition(input);
}
}

public ThingifierModelDefinition loadDefinition(final InputStream input) {
return definitionFrom(loadDocument(input));
}

public ThingifierModelDefinition loadDefinition(final String yamlText) {
return definitionFrom(loadDocument(yamlText));
}

public Thingifier loadThingifier(final Path path) throws IOException {
return assemble(loadDefinition(path));
}

public Thingifier loadThingifier(final InputStream input) {
return assemble(loadDefinition(input));
}

public Thingifier loadThingifier(final String yamlText) {
return assemble(loadDefinition(yamlText));
}

private Thingifier assemble(final ThingifierModelDefinition definition) {
final SchemaDefinitionValidationReport report = assembler.validate(definition);
if (!report.isValid()) {
throw new ThingifierYamlException(report.combinedMessages());
}
return assembler.assemble(definition);
}

private YamlThingifierDocument loadDocument(final InputStream input) {
try {
return YamlThingifierDocument.fromObject(yaml().load(input));
} catch (YAMLException | ClassCastException | IllegalArgumentException e) {
throw new ThingifierYamlException("Could not parse YAML schema", e);
}
}

private YamlThingifierDocument loadDocument(final String yamlText) {
try {
return YamlThingifierDocument.fromObject(yaml().load(yamlText));
} catch (YAMLException | ClassCastException | IllegalArgumentException e) {
throw new ThingifierYamlException("Could not parse YAML schema", e);
}
}

private ThingifierModelDefinition definitionFrom(final YamlThingifierDocument document) {
try {
return mapper.toDefinition(document);
} catch (ClassCastException | IllegalArgumentException e) {
throw new ThingifierYamlException("Could not map YAML schema", e);
}
}

private Yaml yaml() {
final LoaderOptions options = new LoaderOptions();
options.setAllowDuplicateKeys(false);
options.setMaxAliasesForCollections(20);
return new Yaml(new SafeConstructor(options));
}
}
Loading
Loading