Skip to content
This repository has been archived by the owner on May 26, 2020. It is now read-only.

Commit

Permalink
add yaml parser
Browse files Browse the repository at this point in the history
  • Loading branch information
MohamedFarouk-HMCTS committed May 3, 2019
1 parent 86ac7d8 commit 02ddc82
Show file tree
Hide file tree
Showing 22 changed files with 764 additions and 14 deletions.
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,11 @@
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
15 changes: 7 additions & 8 deletions utilities-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@

<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<scope>provided</scope>
<groupId>com.github.everit-org.json-schema</groupId>
<artifactId>org.everit.json.schema</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
Expand Down Expand Up @@ -64,6 +63,10 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>

<!-- Test Dependencies -->
<dependency>
Expand Down Expand Up @@ -102,11 +105,7 @@
<artifactId>commons-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ser.ZonedDateTimeSerializer;
Expand All @@ -44,6 +46,11 @@ public ObjectMapper objectMapperWith(final JsonFactory jsonFactory) {
return configureObjectMapper(new ObjectMapper(jsonFactory));
}

public ObjectMapper yamlObjectMapper() {
return configureObjectMapper(new ObjectMapper(new YAMLFactory()))
.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
}

private ObjectMapper configureObjectMapper(final ObjectMapper objectMapper) {
return objectMapper
.registerModule(javaTimeModuleWithFormattedDateTime())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package uk.gov.justice.services.yaml;

import static java.lang.String.format;

import java.io.IOException;
import java.net.URL;

import org.json.JSONObject;

public class YamlFileValidator {
private final YamlToJsonObjectConverter yamlToJsonObjectConverter;
private final YamlSchemaLoader yamlSchemaLoader;

public YamlFileValidator(final YamlToJsonObjectConverter yamlToJsonObjectConverter,
final YamlSchemaLoader yamlSchemaLoader) {
this.yamlToJsonObjectConverter = yamlToJsonObjectConverter;
this.yamlSchemaLoader = yamlSchemaLoader;
}

public void validate(final String schemaFileLocation, final URL yamlUrl) {

final JSONObject yamlAsJson = yamlToJsonObjectConverter.convert(yamlUrl);
try {
yamlSchemaLoader.loadSchema(schemaFileLocation).validate(yamlAsJson);
} catch (final IOException ex) {
throw new YamlParserException(format("Unable to load JSON schema %s from classpath", schemaFileLocation), ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package uk.gov.justice.services.yaml;

import static java.lang.String.format;

import uk.gov.justice.services.common.converter.jackson.ObjectMapperProducer;

import java.io.IOException;
import java.net.URL;

import com.fasterxml.jackson.core.type.TypeReference;

public class YamlParser {

public <T> T parseYamlFrom(final URL yamlUrl, final TypeReference typeReference) {
try {
return new ObjectMapperProducer().yamlObjectMapper().readValue(yamlUrl, typeReference);
} catch (final IOException e) {
throw new YamlParserException(format("Failed to read YAML file %s ", yamlUrl), e);
}
}

public <T> T parseYamlFrom(final URL yamlUrl, final Class<T> classType) {
try {
return new ObjectMapperProducer().yamlObjectMapper().readValue(yamlUrl, classType);

} catch (final IOException e) {
throw new YamlParserException(format("Failed to read YAML file %s ", yamlUrl), e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package uk.gov.justice.services.yaml;

public class YamlParserException extends RuntimeException{

public YamlParserException(final String message, final Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package uk.gov.justice.services.yaml;

import java.io.IOException;
import java.io.InputStream;

import org.everit.json.schema.Schema;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONObject;
import org.json.JSONTokener;

public class YamlSchemaLoader {

public Schema loadSchema(final String schemaFileLocation) throws IOException {
try (final InputStream schemaFileStream = this.getClass().getResourceAsStream(schemaFileLocation)) {
return SchemaLoader.builder()
.schemaJson(new JSONObject(new JSONTokener(schemaFileStream)))
.build().load().build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package uk.gov.justice.services.yaml;

import static java.lang.String.format;

import java.net.URL;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.json.JSONObject;

public class YamlToJsonObjectConverter {

private final YamlParser yamlParser;
private final ObjectMapper objectMapper;

public YamlToJsonObjectConverter(final YamlParser yamlParser, final ObjectMapper objectMapper) {
this.yamlParser = yamlParser;
this.objectMapper = objectMapper;
}

public JSONObject convert(final URL url) {
final Object yamlObject = yamlParser.parseYamlFrom(url, Object.class);

try {
return new JSONObject(objectMapper.writeValueAsString(yamlObject));
} catch (final JsonProcessingException e) {
throw new YamlToJsonObjectException(format("Failed to convert YAML to JSON for %s", url), e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package uk.gov.justice.services.yaml;

public class YamlToJsonObjectException extends RuntimeException {

public YamlToJsonObjectException(final String message, final Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package uk.gov.justice.services.yaml;

import static junit.framework.TestCase.fail;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.net.URL;

import org.everit.json.schema.Schema;
import org.json.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class YamlFileValidatorTest {

private static final String SUBSCRIPTION_SCHEMA_PATH = "/schema/subscription-schema.json";

@Mock
private YamlSchemaLoader yamlSchemaLoader;

@Mock
private YamlToJsonObjectConverter yamlToJsonObjectConverter;

@InjectMocks
private YamlFileValidator yamlFileValidator;


@Test
public void shouldValidateSubscriptionYamlFile() throws IOException {
final URL yamlUrl = new URL("file:/test");
final JSONObject jsonObject = mock(JSONObject.class);
final Schema schema = mock(Schema.class);

when(yamlToJsonObjectConverter.convert(yamlUrl)).thenReturn(jsonObject);
when(yamlSchemaLoader.loadSchema(SUBSCRIPTION_SCHEMA_PATH)).thenReturn(schema);

yamlFileValidator.validate(SUBSCRIPTION_SCHEMA_PATH, yamlUrl);

verify(schema).validate(jsonObject);
}

@Test
public void shouldThrowYamlParserException() throws IOException {
try {
final URL yamlUrl = new URL("file:/test");
final JSONObject jsonObject = mock(JSONObject.class);

when(yamlToJsonObjectConverter.convert(yamlUrl)).thenReturn(jsonObject);
when(yamlSchemaLoader.loadSchema(SUBSCRIPTION_SCHEMA_PATH)).thenThrow(new IOException());

yamlFileValidator.validate(SUBSCRIPTION_SCHEMA_PATH, yamlUrl);
fail();
} catch (final Exception expected) {
assertThat(expected, is(instanceOf(YamlParserException.class)));
assertThat(expected.getMessage(), containsString("Unable to load JSON schema"));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package uk.gov.justice.services.yaml;

import static java.nio.file.Paths.get;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.fail;

import uk.gov.justice.services.yaml.subscriptiondescriptor.Event;
import uk.gov.justice.services.yaml.subscriptiondescriptor.Subscription;
import uk.gov.justice.services.yaml.subscriptiondescriptor.SubscriptionDescriptorDef;
import uk.gov.justice.services.yaml.subscriptiondescriptor.SubscriptionsDescriptor;

import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.Test;

public class YamlParserTest {

@Test
public void shouldParseSubscriptionPathAsSubscriptionDescriptorDefUsingClassType() throws Exception {

final URL url = getFromClasspath("yaml/subscriptions-descriptor.yaml");

final SubscriptionDescriptorDef subscriptionDescriptorDefinitionDef = yamlParser().parseYamlFrom(url, SubscriptionDescriptorDef.class);
final SubscriptionsDescriptor subscriptionsDescriptor = subscriptionDescriptorDefinitionDef.getSubscriptionsDescriptor();
assertThat(subscriptionsDescriptor.getService(), is("examplecontext"));
assertThat(subscriptionsDescriptor.getServiceComponent(), is("EVENT_LISTENER"));
assertThat(subscriptionsDescriptor.getSpecVersion(), is("1.0.0"));

final List<Subscription> subscriptions = subscriptionsDescriptor.getSubscriptions();
assertThat(subscriptions.size(), is(2));

final Subscription subscription_1 = subscriptions.get(0);
assertThat(subscription_1.getName(), is("subscription1"));
assertThat(subscription_1.getEventSourceName(), is("example"));

final List<Event> events_1 = subscription_1.getEvents();
assertThat(events_1.size(), is(2));
assertThat(events_1.get(0).getName(), is("example.recipe-added"));
assertThat(events_1.get(0).getSchemaUri(), is("http://justice.gov.uk/json/schemas/domains/example/example.recipe-added.json"));
assertThat(events_1.get(1).getName(), is("example.recipe-deleted"));
assertThat(events_1.get(1).getSchemaUri(), is("http://justice.gov.uk/json/schemas/domains/example/example.recipe-deleted.json"));
}


@Test
public void shouldParseSubscriptionPathAsSubscriptionDescriptorDef() throws Exception {

final URL url = getFromClasspath("yaml/subscriptions-descriptor.yaml");

final TypeReference<Map<String, SubscriptionsDescriptor>> typeReference
= new TypeReference<Map<String, SubscriptionsDescriptor>>() {
};

final Map<String, SubscriptionsDescriptor> subscriptionDescriptorDefinitionMap = yamlParser().parseYamlFrom(url, typeReference);
final SubscriptionsDescriptor subscriptionsDescriptor = subscriptionDescriptorDefinitionMap.get("subscriptions_descriptor");
assertThat(subscriptionsDescriptor.getService(), is("examplecontext"));
assertThat(subscriptionsDescriptor.getServiceComponent(), is("EVENT_LISTENER"));
assertThat(subscriptionsDescriptor.getSpecVersion(), is("1.0.0"));

final List<Subscription> subscriptions = subscriptionsDescriptor.getSubscriptions();
assertThat(subscriptions.size(), is(2));

final Subscription subscription_1 = subscriptions.get(0);
assertThat(subscription_1.getName(), is("subscription1"));
assertThat(subscription_1.getEventSourceName(), is("example"));

final List<Event> events_1 = subscription_1.getEvents();
assertThat(events_1.size(), is(2));
assertThat(events_1.get(0).getName(), is("example.recipe-added"));
assertThat(events_1.get(0).getSchemaUri(), is("http://justice.gov.uk/json/schemas/domains/example/example.recipe-added.json"));
assertThat(events_1.get(1).getName(), is("example.recipe-deleted"));
assertThat(events_1.get(1).getSchemaUri(), is("http://justice.gov.uk/json/schemas/domains/example/example.recipe-deleted.json"));
}


@Test
public void shouldThrowFileNotFoundExceptionForTypeReference() throws Exception {

final URL url = get("this-subscription-does-not-exist.yaml").toUri().toURL();
final TypeReference<SubscriptionsDescriptor> typeReference
= new TypeReference<SubscriptionsDescriptor>() {
};

try {
yamlParser().parseYamlFrom(url, typeReference);
fail();
} catch (final YamlParserException e) {
assertThat(e.getCause(), is(instanceOf(FileNotFoundException.class)));
assertThat(e.getMessage(), containsString("Failed to read YAML file"));
assertThat(e.getMessage(), containsString("this-subscription-does-not-exist.yaml"));
}
}

@Test
public void shouldThrowFileNotFoundExceptionForClassType() throws Exception {

final URL url = get("this-subscription-does-not-exist.yaml").toUri().toURL();
try {
yamlParser().parseYamlFrom(url, SubscriptionsDescriptor.class);
fail();
} catch (final YamlParserException e) {
assertThat(e.getCause(), is(instanceOf(FileNotFoundException.class)));
assertThat(e.getMessage(), containsString("Failed to read YAML file"));
assertThat(e.getMessage(), containsString("this-subscription-does-not-exist.yaml"));
}
}

private YamlParser yamlParser() {
final YamlParser yamlParser = new YamlParser();
return yamlParser;
}

@SuppressWarnings("ConstantConditions")
private URL getFromClasspath(final String name) throws MalformedURLException {
return get(getClass().getClassLoader().getResource(name).getPath()).toUri().toURL();
}
}
Loading

0 comments on commit 02ddc82

Please sign in to comment.