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

com.sap.cds.adapter.odata.v4.utils.EdmUtils 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.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import org.apache.olingo.commons.api.edm.EdmBindingTarget;
import org.apache.olingo.commons.api.edm.EdmElement;
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmOperation;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
import org.apache.olingo.commons.api.edm.EdmType;
import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
import org.apache.olingo.commons.core.edm.primitivetype.EdmStream;
import org.apache.olingo.server.api.uri.UriInfo;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceAction;
import org.apache.olingo.server.api.uri.UriResourceComplexProperty;
import org.apache.olingo.server.api.uri.UriResourceFunction;
import org.apache.olingo.server.api.uri.UriResourceKind;
import org.apache.olingo.server.api.uri.UriResourcePartTyped;
import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty;
import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
import org.apache.olingo.server.core.deserializer.helper.ExpandTreeBuilder;
import org.apache.olingo.server.core.deserializer.helper.ExpandTreeBuilderImpl;

import com.sap.cds.Result;
import com.sap.cds.adapter.odata.v4.CdsRequestGlobals;
import com.sap.cds.adapter.odata.v4.processors.request.CdsODataRequest;
import com.sap.cds.reflect.CdsAction;
import com.sap.cds.reflect.CdsArrayedType;
import com.sap.cds.reflect.CdsDefinition;
import com.sap.cds.reflect.CdsFunction;
import com.sap.cds.reflect.CdsKind;
import com.sap.cds.reflect.CdsOperationNotFoundException;
import com.sap.cds.reflect.CdsParameter;
import com.sap.cds.reflect.CdsService;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.reflect.CdsType;
import com.sap.cds.services.utils.CdsErrorStatuses;
import com.sap.cds.services.utils.ErrorStatusException;

public class EdmUtils {

	private static final String PARAMETERS = "Parameters"; // suffix for parameter entity type
	private static final String TYPE = "Type"; // suffix for entity type of Set navigation
	private static final String SET = "Set"; // navigation property on Parameter entity type

	private final CdsRequestGlobals globals;

	public EdmUtils(CdsRequestGlobals globals) {
		this.globals = globals;
	}

	/**
	 * Get the target entity set for a given entity type. Generally the entity set
	 * name is equal to entity type name. Exception is view with parameters, where
	 * the entity set name is an entity type name without "Parameters" suffix.
	 * 
	 * @param entityType entity type
	 * @return entity set name
	 */
	public EdmBindingTarget getEdmBindingTarget(EdmEntityType entityType) {
		EdmBindingTarget target = null;
		EdmEntityContainer container = globals.getServiceMetadata().getEdm().getEntityContainer();

		// General case:  == 
		String entityTypeName = entityType.getName();
		if ((target = container.getEntitySet(entityTypeName)) != null) {
			return target;
		}
		if ((target = container.getSingleton(entityTypeName)) != null) {
			return target;
		}

		// View with parameter:  =  - "Parameters"
		if (isParametersEntityType(entityType)) {
			entityTypeName = entityTypeName.substring(0, entityTypeName.length() - PARAMETERS.length());
			if ((target = container.getEntitySet(entityTypeName)) != null) {
				return target;
			}
		}

		return target;
	}

	public boolean isParametersEntityType(EdmStructuredType entityType) {
		String name = entityType.getFullQualifiedName().getFullQualifiedNameAsString();
		// potential [CdsView]Parameters type
		if (name.endsWith(PARAMETERS)) {
			EdmNavigationProperty setNavigation = entityType.getNavigationProperty(SET);
			// has Set navigation
			if (setNavigation != null) {
				String setName = setNavigation.getType().getFullQualifiedName().getFullQualifiedNameAsString();
				// Set type is named [CdsView]Type and both refer to the same [CdsView]
				if (setName.endsWith(TYPE) && name.startsWith(setName.substring(0, setName.length() - TYPE.length()))) {
					return true;
				}
			}
		}
		return false;
	}

	public boolean isSetEntityType(EdmStructuredType entityType) {
		String name = entityType.getFullQualifiedName().getFullQualifiedNameAsString();
		// potential [CdsView]Type type
		if (name.endsWith(TYPE)) {
			EdmNavigationProperty paramNavigation = entityType.getNavigationProperty(PARAMETERS);
			// has Parameters navigation
			if (paramNavigation != null) {
				String paramName = paramNavigation.getType().getFullQualifiedName().getFullQualifiedNameAsString();
				// Parameters type is named [CdsView]Parameters and both refer to the same
				// [CdsView]
				if (paramName.endsWith(PARAMETERS)
						&& name.startsWith(paramName.substring(0, paramName.length() - PARAMETERS.length()))) {
					return true;
				}
			}
		}
		return false;
	}

	public String getCdsEntityName(EdmStructuredType entityType) {
		String name = entityType.getFullQualifiedName().getFullQualifiedNameAsString();
		if (isSetEntityType(entityType)) {
			name = name.substring(0, name.length() - TYPE.length());
		} else if (isParametersEntityType(entityType)) {
			throw new ErrorStatusException(CdsErrorStatuses.INVALID_PARAMETERIZED_VIEW);
		}
		String cdsName = globals.getCdsEntityNames().get(name);
		if (cdsName != null) {
			name = cdsName;
		}
		return name;
	}

	public CdsStructuredType findStructuredType(EdmType type) {
		if (type.getKind() == EdmTypeKind.ENTITY) {
			String entityName = getCdsEntityName((EdmEntityType) type);
			return globals.getModel().getEntity(entityName);
		} else if (type.getKind() == EdmTypeKind.COMPLEX) {
			String structName = type.getFullQualifiedName().getFullQualifiedNameAsString();
			return globals.getModel().getStructuredType(structName);
		}
		return null;
	}

	public EdmOperation getEdmOperation(UriResource resource) {
		if (resource.getKind() == UriResourceKind.action) {
			return ((UriResourceAction) resource).getAction();
		}
		if (resource.getKind() == UriResourceKind.function) {
			return ((UriResourceFunction) resource).getFunction();
		}
		throw new ErrorStatusException(CdsErrorStatuses.UNEXPECTED_URI_RESOURCE, resource.getKind());
	}

	public CdsDefinition getCdsOperation(CdsODataRequest request) {
		EdmOperation operation = getEdmOperation(request.getLastTypedResource());
		if (operation.getKind() == EdmTypeKind.ACTION) {
			if (operation.isBound()) {
				return request.getLastEntity().getAction(operation.getName());
			} else {
				CdsService service = globals.getApplicationService().getDefinition();
				return service
						.actions().filter(a -> a.getName().equals(operation.getName()))
						.findFirst().orElseThrow(
								() -> new CdsOperationNotFoundException(CdsKind.ACTION, operation.getName(), service));
			}
		} else if (operation.getKind() == EdmTypeKind.FUNCTION) {
			if (operation.isBound()) {
				return request.getLastEntity().getFunction(operation.getName());
			} else {
				CdsService service = globals.getApplicationService().getDefinition();
				return service
						.functions().filter(a -> a.getName().equals(operation.getName()))
						.findFirst().orElseThrow(() -> new CdsOperationNotFoundException(CdsKind.FUNCTION,
								operation.getName(), service));
			}
		}
		return null;
	}

	public CdsType getCdsOperationReturnType(CdsDefinition operation) {
		CdsType type = null;
		if (operation instanceof CdsAction) {
			type = operation.as(CdsAction.class).returnType().orElse(null);
		} else if (operation instanceof CdsFunction) {
			type = operation.as(CdsFunction.class).getReturnType();
		}
		return stripArrayed(type);
	}

	public Map getCdsOperationParameters(CdsDefinition operation) {
		Stream parameters = Stream.empty();
		if (operation instanceof CdsAction) {
			parameters = operation.as(CdsAction.class).parameters();
		} else if (operation instanceof CdsFunction) {
			parameters = operation.as(CdsFunction.class).parameters();
		}
		Map parameterMap = new HashMap<>();
		parameters.forEach(p -> parameterMap.put(p.getName(), stripArrayed(p.getType())));
		return parameterMap;
	}

	private CdsType stripArrayed(CdsType type) {
		if (type != null && type.isArrayed()) {
			return stripArrayed(type.as(CdsArrayedType.class).getItemsType());
		}
		return type;
	}

	public Optional getEdmProperty(UriResourcePartTyped resource) {
		if (resource instanceof UriResourcePrimitiveProperty property) {
			return Optional.of(property.getProperty());
		}
		if (resource instanceof UriResourceComplexProperty property) {
			return Optional.of(property.getProperty());
		}
		return Optional.empty();
	}

	public boolean isEdmStream(Optional edmProperty) {
		if (edmProperty.isPresent()) {
			return edmProperty.get().getType() instanceof EdmStream;
		}
		return false;
	}

	public static boolean hasApply(UriInfo uriInfo) {
		return uriInfo != null && uriInfo.getApplyOption() != null;
	}

	public static ExpandOption createExpand(EdmStructuredType type, Result entityRows) {
		ExpandTreeBuilder expand = ExpandTreeBuilderImpl.create();
		traverseRows(type, entityRows.list(), expand);
		return expand.build();
	}

	public static ExpandOption createExpand(EdmStructuredType type, Map row) {
		ExpandTreeBuilder expand = ExpandTreeBuilderImpl.create();
		traverseRow(type, row, expand);
		return expand.build();
	}

	private static void traverseRows(EdmStructuredType type, List result,
			ExpandTreeBuilder expand) {
		result.forEach(row -> traverseRow(type, (Map) row, expand));
	}

	private static void traverseRow(EdmStructuredType type, Map entityRow, ExpandTreeBuilder expand) {
		// navigation properties
		List navigationPropertyNames = type.getNavigationPropertyNames();
		for (String navigationPropertyName : navigationPropertyNames) {
			Object navigationValue = entityRow.get(navigationPropertyName);
			if (navigationValue != null) {
				EdmNavigationProperty navigationProperty = type.getNavigationProperty(navigationPropertyName);
				followNavigation(navigationProperty, navigationValue, expand);
			}
		}
		for (String propertyName : type.getPropertyNames()) {
			EdmElement property = type.getProperty(propertyName);
			EdmType propertyType = property.getType();
			if (propertyType.getKind() == EdmTypeKind.COMPLEX) {
				if (property.isCollection()) {
					List complexValues = (List) entityRow.get(propertyName);
					if (null != complexValues) {
						traverseRows((EdmStructuredType)propertyType, complexValues, expand);
					}
				} else {
					Map complexValue = (Map) entityRow.get(propertyName);
					if (null != complexValue) {
						traverseRow((EdmStructuredType)propertyType, complexValue, expand);
					}
				}
			}
		}
	}

	private static void followNavigation(EdmNavigationProperty navigationProperty, Object navigationValue,
			ExpandTreeBuilder expand) {
		ExpandTreeBuilder childExpand = (expand != null) ? expand.expand(navigationProperty) : null;
		EdmEntityType edmEntityType = navigationProperty.getType();

		if (navigationProperty.isCollection()) {
			List navigationValueList;
			if (navigationValue instanceof List list) {
				navigationValueList = list;
			} else {
				navigationValueList = Arrays.asList(navigationValue);
			}
			traverseRows(edmEntityType, navigationValueList, childExpand);
		} else {
			traverseRow(edmEntityType, (Map) navigationValue, childExpand);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy