diff --git a/adapters/rest-adapter-core/pom.xml b/adapters/rest-adapter-core/pom.xml index 1c87e2f16..74a6d6c8d 100644 --- a/adapters/rest-adapter-core/pom.xml +++ b/adapters/rest-adapter-core/pom.xml @@ -17,6 +17,11 @@ messaging-core ${project.version} + + uk.gov.justice.services + core + ${project.version} + javax javaee-api @@ -54,7 +59,6 @@ org.jboss.resteasy resteasy-jaxrs - 3.0.16.Final test diff --git a/adapters/rest-adapter-core/src/main/java/uk/gov/justice/services/adapter/rest/RestProcessor.java b/adapters/rest-adapter-core/src/main/java/uk/gov/justice/services/adapter/rest/RestProcessor.java index 3861de2da..e261868cd 100644 --- a/adapters/rest-adapter-core/src/main/java/uk/gov/justice/services/adapter/rest/RestProcessor.java +++ b/adapters/rest-adapter-core/src/main/java/uk/gov/justice/services/adapter/rest/RestProcessor.java @@ -2,11 +2,9 @@ import org.slf4j.Logger; import uk.gov.justice.services.adapter.rest.envelope.RestEnvelopeBuilderFactory; -import uk.gov.justice.services.common.converter.JsonObjectToStringConverter; import uk.gov.justice.services.messaging.JsonEnvelope; -import uk.gov.justice.services.messaging.JsonObjectEnvelopeConverter; -import javax.inject.Inject; +import javax.enterprise.inject.Alternative; import javax.json.JsonObject; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; @@ -28,18 +26,19 @@ * all the logic for building an envelope from the REST request, passing it to a consumer and building a suitable * response. This allows testing of this logic independently from the automated generation code. */ +@Alternative public class RestProcessor { private static final Logger LOGGER = getLogger(RestProcessor.class); - @Inject - RestEnvelopeBuilderFactory envelopeBuilderFactory; + private final RestEnvelopeBuilderFactory envelopeBuilderFactory; - @Inject - JsonObjectEnvelopeConverter jsonObjectEnvelopeConverter; + private final Function responsebodyGenerator; - @Inject - JsonObjectToStringConverter jsonObjectToStringConverter; + RestProcessor(RestEnvelopeBuilderFactory envelopeBuilderFactory, Function responsebodyGenerator) { + this.envelopeBuilderFactory = envelopeBuilderFactory; + this.responsebodyGenerator = responsebodyGenerator; + } /** * Process an incoming REST request by combining the payload, headers and path parameters into an envelope and @@ -83,7 +82,7 @@ public Response processSynchronously(final Function } else if (result.payload() == NULL) { return status(NOT_FOUND).build(); } else { - return status(OK).entity(jsonObjectToStringConverter.convert(jsonObjectEnvelopeConverter.fromEnvelope(result))).build(); + return status(OK).entity(responsebodyGenerator.apply(result)).build(); } } } diff --git a/adapters/rest-adapter-core/src/main/java/uk/gov/justice/services/adapter/rest/RestProcessorProducer.java b/adapters/rest-adapter-core/src/main/java/uk/gov/justice/services/adapter/rest/RestProcessorProducer.java new file mode 100644 index 000000000..c39d5118e --- /dev/null +++ b/adapters/rest-adapter-core/src/main/java/uk/gov/justice/services/adapter/rest/RestProcessorProducer.java @@ -0,0 +1,61 @@ +package uk.gov.justice.services.adapter.rest; + +import uk.gov.justice.services.adapter.rest.envelope.RestEnvelopeBuilderFactory; +import uk.gov.justice.services.common.converter.JsonObjectToStringConverter; +import uk.gov.justice.services.messaging.JsonObjectEnvelopeConverter; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.inject.Produces; +import javax.enterprise.inject.spi.InjectionPoint; +import javax.inject.Inject; + +import static uk.gov.justice.services.core.annotation.Component.QUERY_API; +import static uk.gov.justice.services.core.annotation.Component.componentFrom; + +/** + * Produces the correct implementation of RestProcessor depending on the Adapter's Component type. + */ +@ApplicationScoped +public class RestProcessorProducer { + + @Inject + JsonObjectToStringConverter jsonObjectToStringConverter; + + @Inject + JsonObjectEnvelopeConverter jsonObjectEnvelopeConverter; + + @Inject + RestEnvelopeBuilderFactory envelopeBuilderFactory; + + private RestProcessor defaultRestProcessor; + + private RestProcessor payloadOnlyRestProcessor; + + /** + * Produces the correct implementation of a {@link RestProcessor} depending on the + * Adapter annotation at the injection point. + * + * @param injectionPoint class where the {@link RestProcessor} is being injected + * @return the correct RestProcessor instance + * @throws IllegalStateException if the injection point does not have an Adapter annotation + */ + @Produces + public RestProcessor produceRestProcessor(final InjectionPoint injectionPoint) { + return componentFrom(injectionPoint) == QUERY_API ? payloadOnlyRestProcessor() : defaultRestProcessor(); + } + + private RestProcessor defaultRestProcessor() { + if (defaultRestProcessor == null) { + defaultRestProcessor = new RestProcessor(envelopeBuilderFactory, envelope -> jsonObjectToStringConverter.convert(jsonObjectEnvelopeConverter.fromEnvelope(envelope))); + } + return defaultRestProcessor; + } + + private RestProcessor payloadOnlyRestProcessor() { + if (payloadOnlyRestProcessor == null) { + payloadOnlyRestProcessor = new RestProcessor(envelopeBuilderFactory, envelope -> envelope.payloadAsJsonObject().toString()); + } + return payloadOnlyRestProcessor; + } + +} diff --git a/adapters/rest-adapter-core/src/test/java/uk/gov/justice/services/adapter/rest/RestProcessorProducerTest.java b/adapters/rest-adapter-core/src/test/java/uk/gov/justice/services/adapter/rest/RestProcessorProducerTest.java new file mode 100644 index 000000000..c9fb60838 --- /dev/null +++ b/adapters/rest-adapter-core/src/test/java/uk/gov/justice/services/adapter/rest/RestProcessorProducerTest.java @@ -0,0 +1,175 @@ +package uk.gov.justice.services.adapter.rest; + +import com.jayway.jsonassert.JsonAssert; +import org.hamcrest.collection.IsCollectionWithSize; +import org.hamcrest.core.IsCollectionContaining; +import org.jboss.resteasy.specimpl.MultivaluedMapImpl; +import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import uk.gov.justice.services.adapter.rest.envelope.RestEnvelopeBuilderFactory; +import uk.gov.justice.services.common.converter.JsonObjectToStringConverter; +import uk.gov.justice.services.core.annotation.Adapter; +import uk.gov.justice.services.messaging.DefaultJsonEnvelope; +import uk.gov.justice.services.messaging.JsonEnvelope; +import uk.gov.justice.services.messaging.JsonObjectEnvelopeConverter; +import uk.gov.justice.services.messaging.JsonObjectMetadata; + +import javax.enterprise.inject.spi.InjectionPoint; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.Response; +import java.lang.reflect.Member; +import java.util.HashMap; +import java.util.function.Function; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.when; +import static uk.gov.justice.services.core.annotation.Component.QUERY_API; +import static uk.gov.justice.services.core.annotation.Component.QUERY_CONTROLLER; +import static uk.gov.justice.services.messaging.JsonObjectEnvelopeConverter.METADATA; +import static uk.gov.justice.services.messaging.JsonObjectEnvelopeConverter.RESULTS; +import static uk.gov.justice.services.messaging.JsonObjectMetadata.ID; +import static uk.gov.justice.services.messaging.JsonObjectMetadata.NAME; + +@RunWith(MockitoJUnitRunner.class) +public class RestProcessorProducerTest { + + private static final String ID_VALUE = "861c9430-7bc6-4bf0-b549-6534394b8d65"; + private static final String NAME_VALUE = "test.command.do-something"; + private static final String ARRAY_ITEM_1 = "Array Item 1"; + private static final String ARRAY_ITEM_2 = "Array Item 2"; + private static final HashMap NOT_USED_PATH_PARAMS = new HashMap<>(); + private static final String FIELD_NAME = "name"; + private static final String FIELD_VALUE = "TEST NAME"; + + @Mock + private InjectionPoint queryApiInjectionPoint; + + @Mock + private InjectionPoint queryControllerInjectionPoint; + + @Mock + private Member queryApiMember; + + @Mock + private Member queryControllerMember; + + @Mock + private Function function; + + @Mock + private HttpHeaders httpHeaders; + + @InjectMocks + private RestProcessorProducer restProcessorProducer; + + + @Before + public void setup() { + + when(queryApiInjectionPoint.getMember()).thenReturn(queryApiMember); + when(queryControllerInjectionPoint.getMember()).thenReturn(queryControllerMember); + + doReturn(QueryApiAdapter.class).when(queryApiMember).getDeclaringClass(); + doReturn(QueryControllerAdapter.class).when(queryControllerMember).getDeclaringClass(); + + restProcessorProducer.envelopeBuilderFactory = new RestEnvelopeBuilderFactory(); + restProcessorProducer.jsonObjectToStringConverter = new JsonObjectToStringConverter(); + restProcessorProducer.jsonObjectEnvelopeConverter = new JsonObjectEnvelopeConverter(); + } + + @Test + public void shouldReturnPayloadOnlyRestProcessorForJsonArray() { + when(function.apply(any())).thenReturn(envelopeWithArrayPayload()); + + Response response = restProcessorProducer.produceRestProcessor(queryApiInjectionPoint) + .processSynchronously(function, headersWith("Accept", "application/vnd.somecontext.query.somequery+json"), NOT_USED_PATH_PARAMS); + + assertThat(response, notNullValue()); + JsonAssert.with(response.getEntity().toString()) + .assertThat("$." + RESULTS, IsCollectionWithSize.hasSize(2)) + .assertThat("$." + RESULTS, IsCollectionContaining.hasItems(ARRAY_ITEM_1, ARRAY_ITEM_2)); + } + + @Test + public void shouldReturnPayloadOnlyRestProcessorForJsonObject() { + when(function.apply(any())).thenReturn(envelopeWithJsonObjectPayload()); + + Response response = restProcessorProducer.produceRestProcessor(queryApiInjectionPoint) + .processSynchronously(function, headersWith("Accept", "application/vnd.somecontext.query.somequery+json"), NOT_USED_PATH_PARAMS); + + assertThat(response, notNullValue()); + JsonAssert.with(response.getEntity().toString()) + .assertThat("$." + FIELD_NAME, equalTo(FIELD_VALUE)); + } + + @Test + public void shouldReturnDefaultRestProcessor() { + when(function.apply(any())).thenReturn(envelopeWithJsonObjectPayload()); + + Response response = restProcessorProducer.produceRestProcessor(queryControllerInjectionPoint) + .processSynchronously(function, headersWith("Accept", "application/vnd.somecontext.query.somequery+json"), NOT_USED_PATH_PARAMS); + + assertThat(response, notNullValue()); + JsonAssert.with(response.getEntity().toString()) + .assertThat("$." + FIELD_NAME, equalTo(FIELD_VALUE)) + .assertThat("$." + METADATA + "." + ID, equalTo(ID_VALUE)) + .assertThat("$." + METADATA + "." + NAME, equalTo(NAME_VALUE)); + } + + private HttpHeaders headersWith(String headerName, String headerValue) { + MultivaluedMapImpl headersMap = new MultivaluedMapImpl(); + headersMap.add(headerName, headerValue); + return new ResteasyHttpHeaders(headersMap); + } + + private JsonEnvelope envelopeWithArrayPayload() { + JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder(); + + JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder(); + jsonArrayBuilder.add(ARRAY_ITEM_1); + jsonArrayBuilder.add(ARRAY_ITEM_2); + + jsonObjectBuilder.add(RESULTS, jsonArrayBuilder.build()); + + return DefaultJsonEnvelope.envelopeFrom(JsonObjectMetadata.metadataFrom(metadata()), jsonObjectBuilder.build()); + } + + private JsonEnvelope envelopeWithJsonObjectPayload() { + JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder(); + jsonObjectBuilder.add(FIELD_NAME, FIELD_VALUE); + + return DefaultJsonEnvelope.envelopeFrom(JsonObjectMetadata.metadataFrom(metadata()), jsonObjectBuilder.build()); + } + + private JsonObject metadata() { + JsonObjectBuilder metadataBuilder = Json.createObjectBuilder(); + metadataBuilder.add(ID, ID_VALUE); + metadataBuilder.add(NAME, NAME_VALUE); + + return metadataBuilder.build(); + } + + @Adapter(QUERY_API) + private class QueryApiAdapter { + + } + + @Adapter(QUERY_CONTROLLER) + private class QueryControllerAdapter { + + } + +} \ No newline at end of file diff --git a/adapters/rest-adapter-core/src/test/java/uk/gov/justice/services/adapter/rest/RestProcessorTest.java b/adapters/rest-adapter-core/src/test/java/uk/gov/justice/services/adapter/rest/RestProcessorTest.java index 91c92f8ce..845ace57a 100644 --- a/adapters/rest-adapter-core/src/test/java/uk/gov/justice/services/adapter/rest/RestProcessorTest.java +++ b/adapters/rest-adapter-core/src/test/java/uk/gov/justice/services/adapter/rest/RestProcessorTest.java @@ -9,9 +9,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import uk.gov.justice.services.adapter.rest.envelope.RestEnvelopeBuilderFactory; -import uk.gov.justice.services.common.converter.JsonObjectToStringConverter; import uk.gov.justice.services.messaging.JsonEnvelope; -import uk.gov.justice.services.messaging.JsonObjectEnvelopeConverter; import uk.gov.justice.services.messaging.JsonObjectMetadata; import uk.gov.justice.services.messaging.Metadata; @@ -65,10 +63,7 @@ public class RestProcessorTest { @Before public void setup() { - restProcessor = new RestProcessor(); - restProcessor.envelopeBuilderFactory = new RestEnvelopeBuilderFactory(); - restProcessor.jsonObjectEnvelopeConverter = new JsonObjectEnvelopeConverter(); - restProcessor.jsonObjectToStringConverter = new JsonObjectToStringConverter(); + restProcessor = new RestProcessor(new RestEnvelopeBuilderFactory(), envelope -> envelope.payload().toString()); metadata = JsonObjectMetadata.metadataFrom(Json.createObjectBuilder() .add(ID, UUID.randomUUID().toString()) diff --git a/adapters/rest-adapter-generator/src/test/java/uk/gov/justice/api/resource/DefaultUsersUserIdResourceIT.java b/adapters/rest-adapter-generator/src/test/java/uk/gov/justice/api/resource/DefaultUsersUserIdResourceIT.java index db66b5f8e..e649231aa 100644 --- a/adapters/rest-adapter-generator/src/test/java/uk/gov/justice/api/resource/DefaultUsersUserIdResourceIT.java +++ b/adapters/rest-adapter-generator/src/test/java/uk/gov/justice/api/resource/DefaultUsersUserIdResourceIT.java @@ -16,6 +16,7 @@ import org.junit.runner.RunWith; import uk.gov.justice.api.QueryApiRestExampleApplication; import uk.gov.justice.services.adapter.rest.RestProcessor; +import uk.gov.justice.services.adapter.rest.RestProcessorProducer; import uk.gov.justice.services.adapter.rest.envelope.RestEnvelopeBuilderFactory; import uk.gov.justice.services.adapters.test.utils.dispatcher.AsynchronousRecordingDispatcher; import uk.gov.justice.services.adapters.test.utils.dispatcher.SynchronousRecordingDispatcher; @@ -96,6 +97,7 @@ public Properties properties() { @Module @Classes(cdi = true, value = { RestProcessor.class, + RestProcessorProducer.class, RestEnvelopeBuilderFactory.class, AsynchronousRecordingDispatcher.class, SynchronousRecordingDispatcher.class, diff --git a/messaging/messaging-core/src/main/java/uk/gov/justice/services/messaging/JsonObjectEnvelopeConverter.java b/messaging/messaging-core/src/main/java/uk/gov/justice/services/messaging/JsonObjectEnvelopeConverter.java index eb9f83d7b..57be82f6e 100644 --- a/messaging/messaging-core/src/main/java/uk/gov/justice/services/messaging/JsonObjectEnvelopeConverter.java +++ b/messaging/messaging-core/src/main/java/uk/gov/justice/services/messaging/JsonObjectEnvelopeConverter.java @@ -15,8 +15,8 @@ */ public class JsonObjectEnvelopeConverter { - private static final String METADATA = "_metadata"; - private static final String RESULTS = "results"; + public static final String METADATA = "_metadata"; + public static final String RESULTS = "results"; /** * Converts a jsonObject into {@link JsonEnvelope}