diff --git a/pom.xml b/pom.xml index 1de8600..9b81fef 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,7 @@ 1.21.0 generator-maven-plugin - 2.8.7 + 2.8.7 diff --git a/subscription-descriptor-parser/pom.xml b/subscription-descriptor-parser/pom.xml index 680cbc8..5d76f17 100644 --- a/subscription-descriptor-parser/pom.xml +++ b/subscription-descriptor-parser/pom.xml @@ -34,7 +34,7 @@ com.fasterxml.jackson.dataformat jackson-dataformat-yaml - ${jackson-dataformat-yaml-version} + ${jackson-dataformat-yaml.version} com.fasterxml.jackson.datatype @@ -44,6 +44,10 @@ com.fasterxml.jackson.module jackson-module-parameter-names + + com.github.everit-org.json-schema + org.everit.json.schema + junit junit diff --git a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorException.java b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorException.java new file mode 100644 index 0000000..d611e6f --- /dev/null +++ b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorException.java @@ -0,0 +1,7 @@ +package uk.gov.justice.maven.generator.io.files.parser; + +public class SubscriptionDescriptorException extends RuntimeException { + public SubscriptionDescriptorException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParser.java b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParser.java index f2e8c32..ee3fdcf 100644 --- a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParser.java +++ b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParser.java @@ -1,12 +1,13 @@ package uk.gov.justice.maven.generator.io.files.parser; import static com.fasterxml.jackson.annotation.JsonCreator.Mode.PROPERTIES; +import static java.lang.String.format; import uk.gov.justice.domain.subscriptiondescriptor.SubscriptionDescriptor; import uk.gov.justice.domain.subscriptiondescriptor.SubscriptionDescriptorDef; -import java.io.File; import java.io.IOException; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Collection; import java.util.stream.Collectors; @@ -16,27 +17,40 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; +import org.everit.json.schema.ValidationException; public class SubscriptionDescriptorFileParser implements FileParser { + + private SubscriptionDescriptorFileValidator subscriptionDescriptorFileValidator; + + public SubscriptionDescriptorFileParser(final SubscriptionDescriptorFileValidator subscriptionDescriptorFileValidator) { + this.subscriptionDescriptorFileValidator = subscriptionDescriptorFileValidator; + } @Override public Collection parse(final Path baseDir, final Collection paths) { return paths.stream() - .map(path -> read(baseDir.resolve(path).toAbsolutePath().toString())) + .map(path -> read(baseDir.resolve(path).toAbsolutePath())) .collect(Collectors.toList()); } - private SubscriptionDescriptor read(final String filePath) { - final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()) - .registerModule(new Jdk8Module()) - .registerModule(new ParameterNamesModule(PROPERTIES)); + private SubscriptionDescriptor read(final Path filePath) { + try { + subscriptionDescriptorFileValidator.validate(filePath); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); + final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()) + .registerModule(new Jdk8Module()) + .registerModule(new ParameterNamesModule(PROPERTIES)); - try { - return mapper.readValue(new File(filePath), SubscriptionDescriptorDef.class).getSubscriptionDescriptor(); - } catch (IOException e) { - throw new SubscriptionDescriptorIOException("Failed to read subscriptions yaml file", e); + mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); + + return mapper.readValue(filePath.toFile(), SubscriptionDescriptorDef.class).getSubscriptionDescriptor(); + } catch (final NoSuchFileException e) { + throw new SubscriptionDescriptorException(format("No such subscriptions YAML file %s ", filePath), e); + } catch (final ValidationException e) { + throw new SubscriptionDescriptorException(format("Failed to validate subscriptions yaml file %s ", filePath), e); + } catch (final IOException e) { + throw new SubscriptionDescriptorException(format("Failed to read subscriptions yaml file %s ", filePath), e); } } } diff --git a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParserFactory.java b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParserFactory.java index cc846aa..87742f6 100644 --- a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParserFactory.java +++ b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParserFactory.java @@ -7,6 +7,6 @@ public class SubscriptionDescriptorFileParserFactory implements FileParserFactor @Override public SubscriptionDescriptorFileParser create() { - return new SubscriptionDescriptorFileParser(); + return new SubscriptionDescriptorFileParser(new SubscriptionDescriptorFileValidator(new YamlFileToJsonObjectConverter())); } } diff --git a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileValidator.java b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileValidator.java new file mode 100644 index 0000000..063e49c --- /dev/null +++ b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileValidator.java @@ -0,0 +1,43 @@ +package uk.gov.justice.maven.generator.io.files.parser; + +import static java.lang.String.format; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; + +import org.everit.json.schema.Schema; +import org.everit.json.schema.loader.SchemaLoader; +import org.json.JSONObject; +import org.json.JSONTokener; + +public class SubscriptionDescriptorFileValidator { + + private YamlFileToJsonObjectConverter yamlFileToJsonObjectConverter; + + private static final String SCHEMA_FILE = "/json/schema/subscription-schema.json"; + + public SubscriptionDescriptorFileValidator(final YamlFileToJsonObjectConverter yamlFileToJsonObjectConverter) { + this.yamlFileToJsonObjectConverter = yamlFileToJsonObjectConverter; + } + + public void validate(final Path filePath) { + try { + final JSONObject convert = yamlFileToJsonObjectConverter.convert(filePath); + schema().validate(convert); + } catch (IOException ex) { + throw new SubscriptionDescriptorException(format("Unable to convert to JSON file %s ", filePath.toString()), ex); + } + + } + + private Schema schema() { + try (final InputStream schemaFileStream = this.getClass().getResourceAsStream(SCHEMA_FILE)) { + return SchemaLoader.builder() + .schemaJson(new JSONObject(new JSONTokener(schemaFileStream))) + .build().load().build(); + } catch (final IOException ex) { + throw new SubscriptionDescriptorException(format("Unable to load JSON schema %s from classpath", SCHEMA_FILE), ex); + } + } +} diff --git a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorIOException.java b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorIOException.java deleted file mode 100644 index 4e51bae..0000000 --- a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorIOException.java +++ /dev/null @@ -1,7 +0,0 @@ -package uk.gov.justice.maven.generator.io.files.parser; - -public class SubscriptionDescriptorIOException extends RuntimeException { - public SubscriptionDescriptorIOException(final String message, final Throwable cause) { - super(message, cause); - } -} diff --git a/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/YamlFileToJsonObjectConverter.java b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/YamlFileToJsonObjectConverter.java new file mode 100644 index 0000000..d1c72d1 --- /dev/null +++ b/subscription-descriptor-parser/src/main/java/uk/gov/justice/maven/generator/io/files/parser/YamlFileToJsonObjectConverter.java @@ -0,0 +1,30 @@ +package uk.gov.justice.maven.generator.io.files.parser; + +import static java.nio.file.Files.readAllLines; +import static java.util.stream.Collectors.joining; + +import java.io.IOException; +import java.nio.file.Path; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import org.json.JSONObject; + +public class YamlFileToJsonObjectConverter { + + final String lineSeparator = System.getProperty("line.separator"); + + public JSONObject convert(final Path filePath) throws IOException { + + final String yamlStr = readAllLines(filePath) + .stream() + .collect(joining(lineSeparator)); + + final Object yamlObject = new ObjectMapper(new YAMLFactory()) + .readValue(yamlStr, Object.class); + + final ObjectMapper jsonWriter = new ObjectMapper(); + + return new JSONObject(jsonWriter.writeValueAsString(yamlObject)); + } +} diff --git a/subscription-descriptor-parser/src/main/resources/json/schema/subscription-schema.json b/subscription-descriptor-parser/src/main/resources/json/schema/subscription-schema.json new file mode 100644 index 0000000..3c53019 --- /dev/null +++ b/subscription-descriptor-parser/src/main/resources/json/schema/subscription-schema.json @@ -0,0 +1,147 @@ +{ + "$id": "http://example.com/example.json", + "type": "object", + "definitions": {}, + "$schema": "http://json-schema.org/draft-04/schema#", + "properties": { + "subscription_descriptor": { + "$id": "/properties/subscription_descriptor", + "type": "object", + "properties": { + "spec_version": { + "$id": "/properties/subscription_descriptor/properties/spec_version", + "type": "string", + "title": "The Spec_version Schema ", + "examples": [ + "1.0.0" + ] + }, + "service": { + "$id": "/properties/subscription_descriptor/properties/service", + "type": "string", + "title": "The Service Schema ", + "examples": [ + "examplecontext" + ] + }, + "service_component": { + "$id": "/properties/subscription_descriptor/properties/service_component", + "type": "string", + "title": "The Service_component Schema ", + "examples": [ + "EVENT_LISTENER" + ] + }, + "subscriptions": { + "$id": "/properties/subscription_descriptor/properties/subscriptions", + "type": "array", + "items": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items", + "type": "object", + "properties": { + "name": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/name", + "type": "string", + "title": "The Name Schema ", + "examples": [ + "subscription1" + ] + }, + "events": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/events", + "type": "array", + "items": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/events/items", + "type": "object", + "properties": { + "name": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/events/items/properties/name", + "type": "string", + "title": "The Name Schema ", + "examples": [ + "example.recipe-added" + ] + }, + "schema_uri": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/events/items/properties/schema_uri", + "type": "string", + "title": "The Schema_uri Schema ", + "pattern": "^http|https:(\\/?\\/?)[^\\s]+$", + "examples": [ + "http://justice.gov.uk/json/schemas/domains/example/example.recipe-added.json" + ] + } + }, + "required": [ + "name", + "schema_uri" + ] + } + }, + "eventsource": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/eventsource", + "type": "object", + "properties": { + "name": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/eventsource/properties/name", + "type": "string", + "title": "The Name of event source ", + "examples": [ + "examplecontext" + ] + }, + "location": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/eventsource/properties/location", + "type": "object", + "properties": { + "jms_uri": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/eventsource/properties/location/properties/jms_uri", + "type": "string", + "title": "The Jms_uri Schema ", + "pattern": "^jms:(\\/?\\/?)[^\\s]+$", + "examples": [ + "jms:topic:example.event?timeToLive=1000" + ] + }, + "rest_uri": { + "$id": "/properties/subscription_descriptor/properties/subscriptions/items/properties/eventsource/properties/location/properties/rest_uri", + "type": "string", + "title": "The Rest_uri Schema ", + "pattern": "^http|https:(\\/?\\/?)[^\\s]+$", + "examples": [ + "http://justice.gov.uk/json/schemas/domains/example/example.recipe-added.json" + ] + } + }, + "required": [ + "jms_uri", + "rest_uri" + ] + } + }, + "required": [ + "name", + "location" + ] + } + }, + "required": [ + "name", + "events", + "eventsource" + ] + } + } + }, + "required": [ + "spec_version", + "service", + "service_component", + "subscriptions" + ] + } + }, + "required": [ + "subscription_descriptor" + ] +} diff --git a/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParserTest.java b/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParserTest.java index 3f130ed..f3f2ff7 100644 --- a/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParserTest.java +++ b/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileParserTest.java @@ -14,38 +14,52 @@ import uk.gov.justice.domain.subscriptiondescriptor.Subscription; import uk.gov.justice.domain.subscriptiondescriptor.SubscriptionDescriptor; -import java.io.FileNotFoundException; -import java.net.URISyntaxException; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Collection; import java.util.List; -import com.fasterxml.jackson.databind.JsonMappingException; +import org.everit.json.schema.ValidationException; import org.junit.Test; public class SubscriptionDescriptorFileParserTest { final Path baseDir = get("src/test/resources/"); - final SubscriptionDescriptorFileParser parser = new SubscriptionDescriptorFileParser(); + @Test + public void shouldThrowFileNotFoundException() { + final SubscriptionDescriptorFileParser subscriptionDescriptorFileParser = new SubscriptionDescriptorFileParserFactory().create(); + final List paths = asList(get("no-subscription.yaml")); + try { + subscriptionDescriptorFileParser.parse(baseDir, paths); + fail(); + } catch (SubscriptionDescriptorException re) { + assertThat(re, is(instanceOf(SubscriptionDescriptorException.class))); + assertThat(re.getCause(), is(instanceOf(NoSuchFileException.class))); + } + } @Test - public void shouldFailOnIncorrectSubscriptionYaml() throws URISyntaxException { + public void shouldFailOnIncorrectSubscriptionYaml() { + final SubscriptionDescriptorFileParser subscriptionDescriptorFileParser = new SubscriptionDescriptorFileParserFactory().create(); final List paths = asList(get("incorrect-subscription.yaml")); try { - final Collection subscriptionDescriptors = parser.parse(baseDir, paths); + subscriptionDescriptorFileParser.parse(baseDir, paths); fail(); - } catch (RuntimeException re) { - assertThat(re.getCause(), is(instanceOf(JsonMappingException.class))); + } catch (SubscriptionDescriptorException re) { + assertThat(re, is(instanceOf(SubscriptionDescriptorException.class))); + assertThat(re.getCause(), is(instanceOf(ValidationException.class))); + assertThat(re.getCause().getMessage(), is("#/subscription_descriptor: required key [spec_version] not found")); } } @Test - public void shouldParsePathsToYaml() throws URISyntaxException { + public void shouldParsePathsToYaml() { + final SubscriptionDescriptorFileParser subscriptionDescriptorFileParser = new SubscriptionDescriptorFileParserFactory().create(); final List paths = asList(get("subscription.yaml")); - final Collection subscriptionDescriptors = parser.parse(baseDir, paths); + final Collection subscriptionDescriptors = subscriptionDescriptorFileParser.parse(baseDir, paths); assertThat(subscriptionDescriptors, hasSize(1)); for (final SubscriptionDescriptor subscriptionDescriptor : subscriptionDescriptors) { @@ -104,15 +118,4 @@ private void assertPeopleEvents(Subscription subscription) { assertThat(events.get(1).getName(), is("people.person-removed")); assertThat(events.get(1).getSchemaUri(), is("http://justice.gov.uk/json/schemas/domains/people/people.person-removed.json")); } - - @Test - public void shouldThrowFileNotFoundException() { - final List paths = asList(get("no-subscription.yaml")); - try { - final Collection result = parser.parse(baseDir, paths); - fail(); - } catch (RuntimeException re) { - assertThat(re.getCause(), is(instanceOf(FileNotFoundException.class))); - } - } } \ No newline at end of file diff --git a/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileValidatorTest.java b/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileValidatorTest.java new file mode 100644 index 0000000..ac133b2 --- /dev/null +++ b/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/SubscriptionDescriptorFileValidatorTest.java @@ -0,0 +1,42 @@ +package uk.gov.justice.maven.generator.io.files.parser; + +import static java.nio.file.Paths.get; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.everit.json.schema.ValidationException; +import org.junit.Test; + +public class SubscriptionDescriptorFileValidatorTest { + + @Test + public void shouldNotThrowExceptionOnCorrectSubscriptionYaml() throws IOException { + try { + final SubscriptionDescriptorFileValidator subscriptionDescriptorFileValidator + = new SubscriptionDescriptorFileValidator(new YamlFileToJsonObjectConverter()); + + subscriptionDescriptorFileValidator.validate(get("src/test/resources/subscription.yaml")); + } catch (ValidationException e) { + fail("Unexpected validation exception"); + } + + } + + @Test + public void shouldThrowExceptionOnInCorrectSubscriptionYaml() { + try { + final SubscriptionDescriptorFileValidator subscriptionDescriptorFileValidator + = new SubscriptionDescriptorFileValidator(new YamlFileToJsonObjectConverter()); + + subscriptionDescriptorFileValidator.validate(get("src/test/resources/incorrect-subscription.yaml")); + fail(); + } catch (ValidationException e) { + assertThat(e, is(instanceOf(ValidationException.class))); + assertThat(e.getMessage(), is("#/subscription_descriptor: required key [spec_version] not found")); + } + } +} \ No newline at end of file diff --git a/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/YamlFileToJsonObjectConverterTest.java b/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/YamlFileToJsonObjectConverterTest.java new file mode 100644 index 0000000..595439c --- /dev/null +++ b/subscription-descriptor-parser/src/test/java/uk/gov/justice/maven/generator/io/files/parser/YamlFileToJsonObjectConverterTest.java @@ -0,0 +1,37 @@ +package uk.gov.justice.maven.generator.io.files.parser; + +import static java.nio.file.Paths.get; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.junit.Test; + +public class YamlFileToJsonObjectConverterTest { + + @Test + public void shouldConvertYamlToJsonObject() { + final YamlFileToJsonObjectConverter converter = new YamlFileToJsonObjectConverter(); + try { + converter.convert(get("src/test/resources/subscription.yaml")); + } catch (IOException e) { + fail("Unable to convert to JSON Object"); + } + } + + @Test + public void shouldThrowExceptionWhenlToConvertYamlToJsonObjectAUnavailableFile() { + final YamlFileToJsonObjectConverter converter = new YamlFileToJsonObjectConverter(); + try { + converter.convert(get("src/test/resources/no-subscription.yaml")); + fail("Failure, Converted a unavailable file to JSON Object"); + } catch (IOException e) { + assertThat(e, is(instanceOf(IOException.class))); + assertThat(e.getLocalizedMessage(), is("src/test/resources/no-subscription.yaml")); + } + } + +} \ No newline at end of file diff --git a/subscription-descriptor-parser/src/test/resources/incorrect-subscription.yaml b/subscription-descriptor-parser/src/test/resources/incorrect-subscription.yaml index 4534c54..7adf288 100644 --- a/subscription-descriptor-parser/src/test/resources/incorrect-subscription.yaml +++ b/subscription-descriptor-parser/src/test/resources/incorrect-subscription.yaml @@ -1,9 +1,8 @@ --- subscription_descriptor: - spec_version: 1.0.0 service: examplecontext service_component: EVENT_LISTENER - subscriptions1: + subscriptions: - name: subscription1 events: - name: example.recipe-added