com.sap.cds.adapter.odata.v2.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-v2 Show documentation
Show all versions of cds-adapter-odata-v2 Show documentation
OData V2 adapter for CDS Services Java
/**************************************************************************
* (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
**************************************************************************/
package com.sap.cds.adapter.odata.v2.utils;
import static com.fasterxml.jackson.core.json.JsonWriteFeature.ESCAPE_NON_ASCII;
import static java.util.stream.Collectors.toList;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.olingo.odata2.core.commons.ContentType;
import org.apache.olingo.odata2.core.commons.XmlHelper;
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.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 DETAILS = "details";
private static final String MESSAGE = "message";
private static final String SEVERITY = "severity";
private static final String TARGET = "target";
private static final String ENTRY = "entry";
private static final String FEED = "feed";
public static String getSapMessagesHeader(Messages messages, String contentType) {
ContentType ct = ContentType.parse(contentType);
String actualContentType = ct.getType() + "/" + ct.getSubtype();
if (actualContentType.equalsIgnoreCase(ContentType.APPLICATION_JSON.toContentTypeString())) {
return getSapMessagesHeaderJson(messages);
} else if (actualContentType.equalsIgnoreCase(ContentType.APPLICATION_ATOM_XML.toContentTypeString()) ||
actualContentType.equalsIgnoreCase(ContentType.APPLICATION_XML.toContentTypeString())) {
return getSapMessagesHeadersXML(messages);
}
return null;
}
private static String getSapMessagesHeadersXML(Messages messages) {
if (messages.stream().count() > 0) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
XMLStreamWriter writer = XmlHelper.getXMLOutputFactory().createXMLStreamWriter(outputStream, "utf-8");
Iterator iterator = messages.stream().iterator();
boolean isFeed = messages.stream().count() > 1;
if (isFeed) {
writer.writeStartElement(FEED);
}
while (iterator.hasNext()) {
Message message = iterator.next();
writer.writeStartElement(ENTRY);
// mandatory
writer.writeStartElement(CODE);
writer.writeCharacters(getMessageCode(message));
writer.writeEndElement();
writer.writeStartElement(MESSAGE);
writer.writeCharacters(message.getMessage());
writer.writeEndElement();
writer.writeStartElement(SEVERITY);
writer.writeCharacters(String.valueOf(message.getSeverity().toString().toLowerCase(Locale.US)));
writer.writeEndElement();
// optional
String target = getTarget(message.getTarget());
if(target != null) {
writer.writeStartElement(TARGET);
writer.writeCharacters(target);
writer.writeEndElement();
}
writer.writeEndElement();
}
if (isFeed) {
writer.writeEndElement();
}
writer.close();
if (outputStream.size() != 0) {
return outputStream.toString("UTF-8");
}
} catch (XMLStreamException | UnsupportedEncodingException e) {
logger.error("Failed to create sap messages header", e);
}
}
return null;
}
private static void writeJsonMessageBody(Message message, JsonGenerator json) throws IOException {
// mandatory
json.writeStringField(CODE, getMessageCode(message));
json.writeStringField(MESSAGE, message.getMessage());
json.writeStringField(SEVERITY, message.getSeverity().toString().toLowerCase(Locale.US));
// optional
String target = getTarget(message.getTarget());
if (target != null) {
json.writeStringField(TARGET, target);
}
}
private static String getSapMessagesHeaderJson(Messages messages) {
List msgList = messages.stream().collect(toList());
if (msgList.size() > 0) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JsonFactoryBuilder builder = new JsonFactoryBuilder().configure(ESCAPE_NON_ASCII, true);
try (JsonGenerator json = new JsonFactory(builder).createGenerator(outputStream)) {
json.writeStartObject();
Message leadingMessage = msgList.remove(0);
writeJsonMessageBody(leadingMessage, json);
boolean isFirst = true;
Iterator iterator = msgList.iterator();
if (iterator.hasNext()) {
json.writeFieldName(DETAILS);
while (iterator.hasNext()) {
if (isFirst) {
json.writeStartArray();
isFirst = false;
}
Message detailMessage = iterator.next();
json.writeStartObject();
writeJsonMessageBody(detailMessage, json);
json.writeEndObject();
}
if (!isFirst) {
json.writeEndArray();
}
}
json.writeEndObject();
json.close();
if (outputStream.size() != 0) {
return outputStream.toString(StandardCharsets.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();
}
// TODO: this method does not handle all conversions of target to OData V2 notation correctly
// (for example: entity with date keys)
public static String getTarget(MessageTarget messageTarget) {
if(messageTarget == null) {
return null;
}
List extends Segment> segments = messageTarget.getRef() == null ? Collections.emptyList()
: messageTarget.getRef().segments();
String prefix = messageTarget.getParameter();
if(prefix == null && segments.isEmpty()) {
return null;
}
if (MessageTarget.PARAMETER_CQN.equals(prefix)) {
prefix = null;
}
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) {
if (isUUID(string)) {
theValue = "guid" + "'" + theValue + "'";
} else {
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 - 2025 Weber Informatics LLC | Privacy Policy