Commit bbc4c727 authored by Komal Makkar's avatar Komal Makkar
Browse files

Added tests and some utils

parent 2a6dd09d
Pipeline #5780 failed with stage
in 2 minutes and 1 second
......@@ -87,7 +87,7 @@ public class PubsubEndpoint {
@PreAuthorize("@authorizationFilter.hasAnyPermission('" + Config.OPS + "', '" + Config.PUBSUB + "')")
public ResponseEntity recordChanged() throws Exception {
if(this.pubsubRequestBodyExtractor.isHandshakeRequest()) {
this.pubsubHandshakeHandler.getHandshakeResponse();
return ResponseEntity.ok(this.pubsubHandshakeHandler.getHandshakeResponse());
}
String notificationId = this.pubsubRequestBodyExtractor.extractNotificationIdFromRequestBody();
......
......@@ -14,52 +14,39 @@
package org.opengroup.osdu.notification.provider.azure.pubsub;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.notification.provider.azure.util.RequestAttributesDispatcher;
import org.opengroup.osdu.notification.pubsub.IPubsubHandshakeHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Component
@Lazy
public class EventGridHandshakeHandler implements IPubsubHandshakeHandler {
private JsonObject root = null;
@Autowired
private HttpServletRequest request;
private RequestAttributesDispatcher requestDispatcher;
/**
* Extract Handshake response string form Handshake request.
*
* TODO: Check if there is a need to verify subscription name with
* registration service, before verifying the endpoint.
* @return validation string
*/
@Override
public String getHandshakeResponse() {
if (this.root == null) {
this.root = this.extractRootJsonElementFromRequestBody();
}
String response = null;
String response = null;
try {
JsonObject data = (JsonObject) this.root.get("data");
JsonObject requestRoot = requestDispatcher.getRequestRoot();
JsonObject data = (JsonObject) requestRoot.get("data");
String validationCode = data.get("validationCode").getAsString();
JsonObject jsonResponse = new JsonObject();
jsonResponse.addProperty("ValidationResponse", validationCode);
response = validationCode.toString();
response = jsonResponse.toString();
} catch (Exception exception) {
throw new AppException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Request payload parsing error",
"Unable to parse request payload.", exception);
......@@ -67,29 +54,4 @@ public class EventGridHandshakeHandler implements IPubsubHandshakeHandler {
return response;
}
/**
* Utility method to extract root element from the request.
*
* @return Root JsonObject
*/
private JsonObject extractRootJsonElementFromRequestBody() {
try {
JsonParser jsonParser = new JsonParser();
BufferedReader reader = request.getReader();
Stream<String> lines = reader.lines();
String requestBody = lines.collect(Collectors.joining("\n"));
JsonArray requestArray = (JsonArray) jsonParser.parse(requestBody);
JsonElement rootElement = requestArray.get(0);
if (!(rootElement instanceof JsonObject)) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), "RequestBody is not JsonObject.",
"Request Body should be JsonObject to be processed.");
}
return rootElement.getAsJsonObject();
} catch (IOException e) {
throw new AppException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Request payload parsing error",
"Unable to parse request payload.", e);
}
}
}
......@@ -18,40 +18,44 @@ import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.opengroup.osdu.core.common.logging.JaxRsDpsLog;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.notification.models.MessageContent;
import org.opengroup.osdu.notification.provider.azure.util.RequestAttributesDispatcher;
import org.opengroup.osdu.notification.pubsub.IPubsubRequestBodyExtractor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// TODO : Make error messages as constants.
// TODO : Add TracePoints to exception logs so that monitoring is done well.
@Component
@RequestScope
public class EventGridRequestBodyExtractor implements IPubsubRequestBodyExtractor {
private static final String INVALID_PUBSUB_MESSAGE = "Invalid pubsub message";
private static final String INVALID_EVENTGRID_MESSAGE = "Invalid Event Grid Message";
private static final String EVENTGRID_VALIDATION_EVENT = "Microsoft.EventGrid.SubscriptionValidationEvent";
private static final Gson GSON = new Gson();
private MessageContent messageContent;
private JsonObject root = null;
@Autowired
private HttpServletRequest request;
private RequestAttributesDispatcher requestDispatcher;
@Autowired
private JaxRsDpsLog log;
/**
* Extracts the attributes from the request that are filled in by publisher of the message.
*
* @throws AppException
* @return Request Attributes Map
*/
public Map<String, String> extractAttributesFromRequestBody() {
if (this.messageContent == null) {
this.messageContent = this.extractPubsubMessageFromRequestBody();
......@@ -59,6 +63,12 @@ public class EventGridRequestBodyExtractor implements IPubsubRequestBodyExtracto
return this.messageContent.getAttributes();
}
/**
* Extracts the data from the request that are filled in by publisher of the message,
*
* @throws AppException
* @return Request Data String
*/
public String extractDataFromRequestBody() {
if (this.messageContent == null) {
this.messageContent = this.extractPubsubMessageFromRequestBody();
......@@ -66,49 +76,70 @@ public class EventGridRequestBodyExtractor implements IPubsubRequestBodyExtracto
return this.messageContent.getData();
}
/**
* Extracts the notificationId from the request that are filled in by EventGrid.
*
* @throws AppException
* @return Request NotificationId String.
*/
public String extractNotificationIdFromRequestBody() {
return this.requestDispatcher.getRequestSubscriptionId();
}
/**
* Checks if the request is for handshake.
*
* @throws AppException
* @return Request Type Boolean
*/
public boolean isHandshakeRequest() {
if (this.root == null) {
this.root = this.extractRootJsonElementFromRequestBody();
this.root = this.requestDispatcher.getRequestRoot();
}
try {
JsonElement data = this.root.get("eventType");
if (EVENTGRID_VALIDATION_EVENT.equals(data.getAsString())) {
return true;
}
} catch (Exception exception) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), "RequestBody is not JsonObject.",
"Request Body should be JsonObject to be processed.");
JsonElement data = this.root.get("eventType");
if (EVENTGRID_VALIDATION_EVENT.equals(data.getAsString())) {
return true;
}
return false;
}
/**
* Utility method that wxtracts the core content in the request.
* This is what the publisher will publish.
*
* @throws AppException
* @return Message Content Object
*/
private MessageContent extractPubsubMessageFromRequestBody() {
/* if (this.root == null) {
this.root = this.extractRootJsonElementFromRequestBody();
if (this.root == null) {
this.root = this.requestDispatcher.getRequestRoot();
}
JsonElement message = this.root.get("message");
JsonElement message = this.root.get("data");
if (message == null) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_PUBSUB_MESSAGE, "message object not found");
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_EVENTGRID_MESSAGE, "message object not found");
}
MessageContent content = GSON.fromJson(message.toString(), MessageContent.class);
String eventTime = this.root.getAsJsonObject().get("eventTime").getAsString();
if (Strings.isNullOrEmpty(eventTime)) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_EVENTGRID_MESSAGE, "Event time not found");
}
content.setPublishTime(eventTime);
Map<String, String> attributes = content.getAttributes();
if (attributes == null || attributes.isEmpty()) {
log.error("Incorrect Message: " + message.toString() );
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_PUBSUB_MESSAGE, "attribute map not found");
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_EVENTGRID_MESSAGE, "attribute map not found");
}
String data = content.getData();
if (Strings.isNullOrEmpty(data)) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_PUBSUB_MESSAGE, "data field not found");
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_EVENTGRID_MESSAGE, "data field not found");
}
Map<String, String> lowerCase = new HashMap<>();
attributes.forEach((key, value) -> lowerCase.put(key.toLowerCase(), value));
if (Strings.isNullOrEmpty(attributes.get("data-partition-id"))) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_PUBSUB_MESSAGE,
throw new AppException(HttpStatus.BAD_REQUEST.value(), INVALID_EVENTGRID_MESSAGE,
"No tenant information from pubsub message.");
}
content.setAttributes(lowerCase);
......@@ -116,24 +147,8 @@ public class EventGridRequestBodyExtractor implements IPubsubRequestBodyExtracto
String decoded = new String(Base64.getDecoder().decode(data));
content.setData(decoded);
return content;*/
return content;
}
private JsonObject extractRootJsonElementFromRequestBody() {
try {
JsonParser jsonParser = new JsonParser();
BufferedReader reader = request.getReader();
Stream<String> lines = reader.lines();
String requestBody = lines.collect(Collectors.joining("\n"));
JsonElement rootElement = jsonParser.parse(requestBody);
if (!(rootElement instanceof JsonObject)) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), "RequestBody is not JsonObject.",
"Request Body should be JsonObject to be processed.");
}
return rootElement.getAsJsonObject();
} catch (IOException e) {
throw new AppException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Request payload parsing error",
"Unable to parse request payload.", e);
}
}
}
// Copyright © Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.notification.provider.azure.util;
import com.google.common.base.Strings;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Component
@RequestScope
public class RequestAttributesDispatcher {
private JsonObject requestRoot;
@Autowired
private HttpServletRequest request;
/**
* Extracts Request's root.
*
* @throws AppException
* @throws IOException
* @return Request Root JsonObject
*/
public JsonObject getRequestRoot(){
if(requestRoot == null) {
try {
JsonArray requestArray = getRequest();
JsonElement rootElement = requestArray.get(0);
if (!(rootElement instanceof JsonObject)) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), "RequestBody is not JsonObject.",
"Request Body should be JsonObject to be processed.");
}
this.requestRoot = rootElement.getAsJsonObject();
} catch (Exception e) {
throw new AppException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Request payload parsing error",
"Unable to parse request payload.", e);
}
}
return requestRoot;
}
/**
* Extracts Subscription Name from the header.
*
* @throws AppException
* @throws IOException
* @return Sunscription Name String
*/
public String getRequestSubscriptionId(){
String subscriptionId = request.getHeader("Aeg-Subscription-Name");
if (Strings.isNullOrEmpty(subscriptionId)) {
throw new AppException(HttpStatus.BAD_REQUEST.value(), "Invalid Event Grid Message", "Subscription ID not found");
}
return subscriptionId;
}
/**
* Utility method to read the request's body as stream and
* convert it into JsonArray
* @throws AppException
* @throws IOException
* @return Request JsonArray
*/
private JsonArray getRequest() throws IOException {
JsonParser jsonParser = new JsonParser();
JsonArray requestArray;
try {
BufferedReader reader = request.getReader();
Stream<String> lines = reader.lines();
String requestBody = lines.collect(Collectors.joining("\n"));
requestArray = (JsonArray) jsonParser.parse(requestBody);
} catch (Exception e){
throw new AppException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "Request payload parsing error",
"Unable to read from the request.", e);
}
return requestArray;
}
}
// Copyright © Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.notification.pubsub;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.notification.provider.azure.pubsub.EventGridHandshakeHandler;
import org.opengroup.osdu.notification.provider.azure.util.RequestAttributesDispatcher;
import org.springframework.http.HttpStatus;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class EventGridHandshakeHandlerTest {
@Mock
RequestAttributesDispatcher requestAttributesDispatcherMock;
@InjectMocks
@Spy
private EventGridHandshakeHandler sut;
@Test
public void should_returnValidResponse_getHandshakeResponse() {
// Set up
String validHandshakeRequestRoot =
" {\n" +
" \"id\": \"testId\",\n" +
" \"topic\": \"testTopic\",\n" +
" \"subject\": \"\",\n" +
" \"data\": {\n" +
" \"validationCode\": \"testValidationCode\",\n" +
" \"validationUrl\": \"testURL\"\n" +
" },\n" +
" \"eventType\": \"Microsoft.EventGrid.SubscriptionValidationEvent\",\n" +
" \"eventTime\": \"2020-08-14T11:18:55.9278057Z\",\n" +
" \"metadataVersion\": \"1\",\n" +
" \"dataVersion\": \"2\"\n" +
" }";
JsonObject requestRoot = new JsonParser().parse(validHandshakeRequestRoot).getAsJsonObject();
when(this.requestAttributesDispatcherMock.getRequestRoot()).thenReturn(requestRoot);
String expectedResponse = "{\"ValidationResponse\":\"testValidationCode\"}";
// Act
String observedResponse = this.sut.getHandshakeResponse();
// Assert
Assert.assertEquals(observedResponse, expectedResponse);
}
@Test
public void should_throwWhenDataIsMissing_getHandshakeResponse() {
// Set up
String validHandshakeRequestRoot =
" {\n" +
" \"id\": \"testId\",\n" +
" \"topic\": \"testTopic\",\n" +
" \"subject\": \"\",\n" +
" \"eventType\": \"Microsoft.EventGrid.SubscriptionValidationEvent\",\n" +
" \"eventTime\": \"2020-08-14T11:18:55.9278057Z\",\n" +
" \"metadataVersion\": \"1\",\n" +
" \"dataVersion\": \"2\"\n" +
" }";
JsonObject requestRoot = new JsonParser().parse(validHandshakeRequestRoot).getAsJsonObject();
when(this.requestAttributesDispatcherMock.getRequestRoot()).thenReturn(requestRoot);
try {
// Act
this.sut.getHandshakeResponse();
// Asset
fail("Should Throw Exception");
} catch (
AppException appException){
Assert.assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), appException.getError().getCode());
Assert.assertEquals("Unable to parse request payload." , appException.getError().getMessage());
} catch (Exception exception) {
fail("Should Throw AppException");
}
}
}
// Copyright © Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.opengroup.osdu.notification.pubsub;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import org.opengroup.osdu.core.common.model.http.AppException;
import org.opengroup.osdu.notification.provider.azure.pubsub.EventGridRequestBodyExtractor;
import org.opengroup.osdu.notification.provider.azure.util.RequestAttributesDispatcher;
import org.springframework.http.HttpStatus;
import java.util.Map;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class EventGridRequestBodyExtractorTest {
private static final String INVALID_EVENTGRID_MESSAGE = "Invalid Event Grid Message";
@Mock
RequestAttributesDispatcher requestAttributesDispatcherMock;
@InjectMocks
@Spy
private EventGridRequestBodyExtractor sut;
@Test
public void should_returntrue_isHandshakeRequest() {
// Set up
String validHandshakeRequestRoot =
" {\n" +
" \"id\": \"testId\",\n" +
" \"topic\": \"testTopic\",\n" +