All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.ipe.ble.irp.handler.GenericHandler Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
package com.sap.ipe.ble.irp.handler;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;
import java.util.Optional;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Objects;
import java.util.stream.Stream;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sap.cds.ql.Update;
import com.sap.cds.ql.cqn.CqnAnalyzer;
import com.sap.cds.ql.cqn.CqnSelect;
import com.sap.cds.ql.cqn.CqnUpdate;
import com.sap.cds.ql.cqn.CqnStatement;
import com.sap.cds.reflect.CdsAction;
import com.sap.cds.reflect.CdsAnnotation;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsType;
import com.sap.cds.services.authentication.JwtTokenAuthenticationInfo;
import com.sap.cds.services.persistence.PersistenceService;
import com.sap.ipe.ble.irp.exceptions.ExtensionException;
import com.sap.ipe.ble.irp.handler.configuration.SourceConfig;
import com.sap.ipe.ble.irp.handler.constants.Constants;
import com.sap.ipe.ble.irp.handler.services.DeepSelectionService;
import com.sap.ipe.ble.irp.handler.services.HttpDestinationService;
import com.sap.ipe.ble.irp.handler.services.MessageService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sap.ipe.ble.irp.handler.services.ResultService;
import com.sap.ipe.ble.irp.secuirty.auditlog.logger.AuditEventLogger;
import com.sap.ipe.ble.irp.utils.Translator;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import com.sap.cds.Result;
import com.sap.cds.Row;
import com.sap.cds.impl.parser.token.Jsonizer;
import com.sap.cds.services.EventContext;
import com.sap.cds.services.ServiceException;
import com.sap.cds.services.cds.ApplicationService;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.On;
import com.sap.cds.services.handler.annotations.ServiceName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/***
 * GenericHandler is used to call any custom specific events
 * Based on the annotations defined, custom specific actions will be triggered.
 */
@Service
@ServiceName(value = "*", type = ApplicationService.class)
public class GenericHandler implements EventHandler {

	@Autowired
    DeepSelectionService deepSelection;


	@Autowired
	HttpDestinationService httpDestinationService;


	@Autowired
    MessageService messageService;


	@Autowired
    ResultService resultData;


	@Autowired
    SourceConfig sourceConfig;


	@Autowired
	PersistenceService db;

	@Autowired
	AuditEventLogger auditEventLogger;

	private static final String CLASSNAME = "GenericHandler";

	private final Logger log = LoggerFactory.getLogger(CLASSNAME);

	private static final ObjectMapper objectMapper = new ObjectMapper ();

	/***
	 * This method is called when any action is performed by the user
	 * Based on the annotation destination passed, the call is triggered for custom actions
	 * @param context event context called
	 */
	@On(entity = "*")
	public void onHandler(EventContext context){
		List elements = new ArrayList<>();
		CdsAction actionName;
		Optional> destination;
		Optional> timeOutAnnotation;
		CdsEntity targetEntity;
		int timeOutConfig = 60000;
		String payload;
		String auditLogData;
		Result result;
		Row resultSet;
		HttpResponse httpResponse;

		targetEntity = context.getTarget();
		try {
			if (targetEntity != null) {

				String entityName = targetEntity.getQualifiedName();
				String targetName = targetEntity.getName();
				String entitySetName = targetName.replace(targetName.charAt(0), targetName.toLowerCase().charAt(0));
				String event = context.getEvent(); //actionName

				if (targetEntity.findAction(event).isPresent()) {

					actionName = targetEntity.getAction(event);
					destination = actionName.findAnnotation(Constants.DESTINATION_NAME);

					if (destination.isPresent()) {
						Object cqn = context.get(Constants.CQN);
						if (cqn instanceof CqnStatement) {

							timeOutAnnotation = actionName.findAnnotation(Constants.TIME_OUT);
							if (timeOutAnnotation.isPresent() && (Integer) timeOutAnnotation.get().getValue() < timeOutConfig) {
								timeOutConfig = (Integer) timeOutAnnotation.get().getValue();
							}

							result = fetchResultFromCqn(context, actionName, targetEntity, cqn);
							resultSet = result.single();

							// Collect target elements from the select query result
							result.rowType().elements().forEach(it -> elements.add(it.getName()));


							//Create payload Data to pass to Remote Handler
							payload = formRemoteExtensionPayload(context, resultSet, entityName, event, entitySetName);

							//Audit Event Log before calling the remote handler
							auditLogData = Constants.remoteActionTriggeredData + payload;
							auditEventLogger.emitSecurityEvent(Constants.remoteActionTriggered,auditLogData);

							//Fetch the response from Remote Handler
							httpResponse = httpDestinationService.callRemoteExtension(timeOutConfig, payload, destination);

							//Process the received response
							processRemoteExtensionResponse(context, httpResponse, targetEntity, event, resultSet, entityName, elements);
						}
					}
				}
			}
		}
		catch(ExtensionException extensionException){
			log.error(Constants.GENERIC_HANDLER_ERROR + extensionException.getLocalizedMessage());
			throw new ServiceException(extensionException.getLocalizedMessage());
		}
		catch (Exception exception){
			log.error(Constants.UNEXPECTED_ERROR + exception.getLocalizedMessage());
			throw new ServiceException(Translator.toLocale(Constants.PROCESS_REQUEST_ERROR));
		}
	}

	private Result selectQueryData(CqnSelect data){
		return db.run(data);
	}

	private Result updateQueryData(CqnUpdate data){
		return db.run(data);
	}

	private Result fetchResultFromCqn(EventContext context, CdsAction actionName, CdsEntity targetEntity, Object cqn){
		CqnSelect query;
		query = (CqnSelect) cqn;
		Map contextParameters;
		Optional> deepSelect;

		deepSelect = actionName.findAnnotation(Constants.DEEP_SELECT_NAME);

		if (deepSelect.isPresent() && deepSelect.get().getValue().equals(true)) {
			// If deepSelect at entity is present & is marked to true, only then fetch the entire data
			contextParameters = CqnAnalyzer.create(context.getModel()).analyze(query).targetKeyValues();

			//Include only composition data and not association data during deep Select
			query = deepSelection.getDeepSelectData(contextParameters,targetEntity);
		}

		return selectQueryData(query);
	}


	private String formRemoteExtensionPayload(EventContext context, Row resultSet, String entityName,String event, String entitySetName) throws ExtensionException {
		try {
			String token;
			String payload;
			Map resultMap = new HashMap<>();
			Map dataPayload;

			// Pass the JWT token to Remote Handler
			token = ((JwtTokenAuthenticationInfo) context.getCdsRuntime().getProvidedAuthenticationInfo()).getToken();

			payload = Jsonizer.object(Constants.DATA, resultSet).put(Constants.SERVICE, entityName)
					.put(Constants.ENTITY, event).put(Constants.USER_CLAIM, token).toJson();

			dataPayload = objectMapper.readValue(payload, Map.class);
			resultMap.put(entitySetName, dataPayload.get(Constants.DATA));

			// Read the additional parameters from dialog and add it to arguments
			// So that it can be pass down as data map to the remote handler
			Stream contextAdditionalParams = context.keySet().stream();
			for (Iterator iterator = contextAdditionalParams.iterator(); iterator.hasNext(); ) {
				String actionParameterName = iterator.next();
				if (!Objects.equals(actionParameterName, Constants.CQN)
						&& !actionParameterName.matches(entitySetName)) {
					resultMap.put(actionParameterName, context.get(actionParameterName));
				}
			}

			// final Payload data to send to Remote Handler
			dataPayload.put(Constants.DATA, resultMap);
			payload = objectMapper.writeValueAsString(dataPayload);
			return payload;
		}
		catch (JsonProcessingException exception) {
			log.error(Constants.PAYLOAD_ERROR + exception.getLocalizedMessage());
			throw new ExtensionException(Translator.toLocale(Constants.FAIL_ON_UNKNOWN_PROPERTIES));
		}
	}

	private void processRemoteExtensionResponse(EventContext context, HttpResponse httpResponse, CdsEntity targetEntity, String event, Row resultSet, String entityName, List elements ) throws ExtensionException {
		int statusCode;
		JSONObject response;
		Optional entityReturnType;

		statusCode = httpResponse.getStatusLine().getStatusCode();

		try {
			// Receive the data as response
			InputStream responseEntityStream = httpResponse.getEntity().getContent();
			response = new JSONObject(
					IOUtils.toString(responseEntityStream, StandardCharsets.UTF_8));
		}
		catch (IOException | JSONException exception) {
			log.error(Constants.RESPONSE_ERROR + exception.getLocalizedMessage());
			throw new ExtensionException(Translator.toLocale(Constants.INTERNAL_SERVER_ERROR));
		}

		if ((statusCode >= Constants.STATUS_CODE_LOW && statusCode <= Constants.STATUS_CODE_HIGH)) {

			if (response.has(Constants.DATA)) {
				// get the return type of the event
				entityReturnType = targetEntity.getAction(event).returnType();

				// If "Data" is null, show the corresponding Messages from Remote Handler.
				// If "Data" is not null, show the updated data and messages from Remote Handler
				if (!response.get(Constants.DATA).equals(null)
						&& entityReturnType.isPresent()) {

					JSONObject data = resultData.getFinalData(elements,response);

					//Put the extended field data to resultSet from the select Query
					resultSet.putAll(data.toMap());
					CqnUpdate update = Update.entity(entityName).entry(resultSet);

					// Update the data back to the table
					Result queryUpdate = updateQueryData(update);
					context.put(Constants.RESULT, queryUpdate);

				} else if (entityReturnType.isPresent()) {
					//If return type is present and data is "null", set the same result fetched during select.
					context.put(Constants.RESULT, resultSet);
				}

				//Show messages received from Remote Handler
				messageService.showMessages(context, response);

				// Set the context to complete.
				context.setCompleted();

			}

		}
		else{
			//If response has error, get the code and show the corresponding service exception
			JSONObject errorStatus = response.getJSONObject(Constants.ERROR);
			throw new ExtensionException(Integer.valueOf(errorStatus.get(Constants.CODE).toString()),
					(String) errorStatus.get(Constants.MESSAGE));
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy