com.sap.cds.adapter.odata.v4.utils.MessagesUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cds-adapter-odata-v4 Show documentation
Show all versions of cds-adapter-odata-v4 Show documentation
OData V4 adapter for CDS Services Java
/**************************************************************************
* (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 extends Segment> 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;
}
}
}