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

com.sap.cds.adapter.odata.v4.utils.MessagesUtils Maven / Gradle / Ivy

There is a newer version: 3.2.0
Show newest version
/**************************************************************************
 * (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
 **************************************************************************/
package com.sap.cds.adapter.odata.v4.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonFactoryBuilder;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.json.JsonWriteFeature;
import com.sap.cds.ql.cqn.CqnComparisonPredicate;
import com.sap.cds.ql.cqn.CqnComparisonPredicate.Operator;
import com.sap.cds.ql.cqn.CqnElementRef;
import com.sap.cds.ql.cqn.CqnLiteral;
import com.sap.cds.ql.cqn.CqnNullValue;
import com.sap.cds.ql.cqn.CqnPlain;
import com.sap.cds.ql.cqn.CqnReference.Segment;
import com.sap.cds.ql.cqn.CqnValue;
import com.sap.cds.ql.cqn.CqnVisitor;
import com.sap.cds.services.messages.Message;
import com.sap.cds.services.messages.MessageTarget;
import com.sap.cds.services.messages.Messages;
import com.sap.cds.services.utils.StringUtils;

public class MessagesUtils {

	private static final Logger logger = LoggerFactory.getLogger(MessagesUtils.class);
	private static final String CODE = "code";
	private static final String MESSAGE = "message";
	private static final String NUMERIC_SEVERITY = "numericSeverity";
	private static final String TARGET = "target";
	private static final String LONG_TEXT_URL = "longtextUrl";

	public static String getSapMessagesHeader(String bindingParameter, Messages messages) {
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		JsonFactoryBuilder builder = new JsonFactoryBuilder().configure(JsonWriteFeature.ESCAPE_NON_ASCII, true);
		try (JsonGenerator json = new JsonFactory(builder).createGenerator(outputStream)) {
			boolean isFirst = true;
			Iterator iterator = messages.stream().iterator();
			while (iterator.hasNext()) {
				if (isFirst) {
					json.writeStartArray();
					isFirst = false;
				}
				Message message = iterator.next();
				json.writeStartObject();
				// mandatory
				json.writeStringField(CODE, getMessageCode(message));
				json.writeStringField(MESSAGE, message.getMessage());
				json.writeNumberField(NUMERIC_SEVERITY, message.getSeverity().getNumericSeverity());
				// optional
				String target = getTarget(bindingParameter, message.getTarget());
				if(target != null) {
					json.writeStringField(TARGET, target);
				}
				if(!StringUtils.isEmpty(message.getLongTextUrl())) {
					json.writeStringField(LONG_TEXT_URL, message.getLongTextUrl());
				}
				json.writeEndObject();
			}
			if (!isFirst) {
				json.writeEndArray();
			}
			json.close();
			if (outputStream.size() != 0) {
				return new String(outputStream.toByteArray(), "UTF-8");
			}
		} catch (IOException e) {
			logger.error("Failed to create sap messages header", e);
		}
		return null;
	}

	public static String getMessageCode(Message message) {
		return StringUtils.isEmpty(message.getCode()) ? "" : message.getCode();
	}

	public static String getTarget(String bindingParameter, MessageTarget messageTarget) {
		if (messageTarget == null) {
			return null;
		}

		List segments = messageTarget.getRef() == null ? Collections.emptyList()
				: messageTarget.getRef().segments();

		if (segments.isEmpty()) {
			return messageTarget.getParameter();
		}

		String prefix;
		if (MessageTarget.PARAMETER_CQN.equals(messageTarget.getParameter())) {
			prefix = bindingParameter;
		} else {
			prefix = messageTarget.getParameter();
		}

		StringBuilder target = new StringBuilder(StringUtils.isEmpty(prefix) ? "" : prefix);

		if (!segments.isEmpty() && target.length() > 0 && target.charAt(target.length() - 1) != '/') {
			target.append("/");
		}

		for(Segment segment : segments) {
			target.append(segment.id());
			if(segment.filter().isPresent()) {
				target.append("(");
				AtomicInteger beforeIsActiveEntityPosition = new AtomicInteger(-1);
				segment.filter().get().accept(new CqnVisitor() {

					@Override
					public void visit(CqnComparisonPredicate predicate) {
						if(predicate.operator() == Operator.EQ || predicate.operator() == Operator.IS) {
							CqnValue left = predicate.left();
							CqnValue right = predicate.right();
							CqnValue value = null;
							String key = null;
							if (left instanceof CqnElementRef ref) {
								key = ref.firstSegment();
								value = right;
							} else if (right instanceof CqnElementRef ref) {
								key = ref.firstSegment();
								value = left;
							}
							if(key != null) {
								Object theValue = null;
								if(value instanceof CqnLiteral literal) {
									theValue = literal.value();
									if(theValue instanceof String string && !isUUID(string)) {
										theValue = "'" + theValue + "'";
									}
								} else if(value instanceof CqnNullValue) {
									theValue = "null";
								} else if(value instanceof CqnPlain plain) {
									theValue = plain.plain();
								}
								if(theValue != null) {
									String toAppend = key + "=" + theValue + ",";
									// TODO dirty workaround for Fiori requiring certain key order
									// will only work for single key or single key draft enabled entities
									int insertAt = beforeIsActiveEntityPosition.get();
									if(insertAt > -1) {
										target.insert(insertAt, toAppend);
										beforeIsActiveEntityPosition.set(insertAt + toAppend.length());
									} else {
										if(key.equals("IsActiveEntity")) {
											beforeIsActiveEntityPosition.set(target.length());
										}
										target.append(toAppend);
									}
								}
							}
						}
					}

				});
				target.deleteCharAt(target.length() - 1);
				target.append(")");
			}
			target.append("/");
		}

		if(!segments.isEmpty()) {
			target.deleteCharAt(target.length() - 1);
		}

		return target.toString();
	}

	// TODO this check should actually be done against the type in the model
	private static boolean isUUID(String s) {
		try {
			UUID.fromString(s);
			return true;
		} catch (IllegalArgumentException e) { // NOSONAR
			return false;
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy