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

com.sap.cds.adapter.odata.v4.preview.CdsFioriPreviewServlet Maven / Gradle / Ivy

There is a newer version: 3.2.0
Show newest version
package com.sap.cds.adapter.odata.v4.preview;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import com.sap.cds.adapter.odata.v4.ODataV4IndexContentProvider;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsService;
import com.sap.cds.services.ErrorStatuses;
import com.sap.cds.services.runtime.CdsRuntime;
import com.sap.cds.services.utils.ErrorStatusException;
import com.sap.cds.services.utils.ODataUtils;
import com.sap.cds.services.utils.path.CdsResourcePath;
import com.sap.cds.services.utils.path.CdsServicePath;
import com.sap.cds.services.utils.path.UrlPathUtil;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

class CdsFioriPreviewServlet extends HttpServlet {

	private final static String COMPONENT_JS = "/app/Component.js";
	private final static String MANIFEST_JSON = "/app/manifest.json";
	private final static String FIORI_PATH = "/%s/%s";

	private final static String ROUTE_TEMPLATE = """
			,
							{
								"name": "${navProperty}Route",
								"target": "${navProperty}Target",
								"pattern": "${entityName}({key})/${navProperty}({key2}):?query:"
							}\
			""";

	private final static String TARGET_TEMPLATE = """
			,
							"${navProperty}Target": {
								"type": "Component",
								"id": "${navProperty}Target",
								"name": "sap.fe.templates.ObjectPage",
								"options": {
									"settings": {
										"entitySet": "${targetEntity}"
									}
								}
							}\
			""";

	private final static String OPTIONS_TEMPLATE = """

											"${navProperty}": {
												"detail": {
													"route": "${navProperty}Route"
												}
											},\
			""";

	private final CdsRuntime runtime;
	private final List previews;

	CdsFioriPreviewServlet(CdsRuntime runtime) {
		this.runtime = runtime;
		this.previews = findFioriPreviews(runtime);
	}

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String pathInfo = req.getPathInfo();
		FioriPreview preview = previews.stream()
				.filter(p -> pathInfo.equals(p.fioriPath) || pathInfo.equals(p.fioriPath + COMPONENT_JS) || pathInfo.equals(p.fioriPath + MANIFEST_JSON))
				.findFirst().orElse(null);

		if (preview == null || !req.getMethod().equals("GET")) {
			resp.setStatus(404);
			return;
		}

		String page;
		String contentType;
		String contextPath = getServletConfig() == null ? req.getContextPath() : getServletConfig().getServletContext().getContextPath();
		if (pathInfo.equals(preview.fioriPath)) {
			contentType = "text/html;charset=UTF-8";
			page = resource("index.html")
					.replace("${serviceName}", preview.serviceName)
					.replace("${entityName}", preview.entityName)
					.replace("${componentPath}", contextPath + CdsFioriPreviewServletFactory.BASE_PATH + preview.fioriPath + "/app");

		} else if (pathInfo.equals(preview.fioriPath + MANIFEST_JSON)) {
			contentType = "application/json;charset=UTF-8";
			page = resource("manifest.json")
					.replace("${serviceName}", preview.serviceName)
					.replace("${entityName}", preview.entityName)
					.replace("${servicePath}", odataServicePath(preview.servicePath, contextPath));

			String navRoutes = "";
			String navTargets = "";
			String navOptions = "";
			for (CdsElement assoc : preview.entity.associations().filter(this::isExposedAssociation).toList()) {
				String navProperty = assoc.getName();
				String targetEntity = ODataUtils.toODataName(assoc.getType().as(CdsAssociationType.class).getTarget().getName());
				navRoutes += ROUTE_TEMPLATE
						.replace("${navProperty}", navProperty)
						.replace("${entityName}", preview.entityName);

				navTargets += TARGET_TEMPLATE
						.replace("${navProperty}", navProperty)
						.replace("${targetEntity}", targetEntity);

				navOptions += OPTIONS_TEMPLATE
						.replace("${navProperty}", navProperty);

			}
			navOptions = !navOptions.isEmpty() ? navOptions.substring(0, navOptions.length() - 1) : navOptions;
			page = page.replace("${navRoutes}", navRoutes)
					.replace("${navTargets}", navTargets)
					.replace("${navOptions}", navOptions);

		} else {
			contentType = "text/javascript;charset=UTF-8";
			page = resource("Component.js");
		}
		resp.setStatus(200);
		resp.setContentType(contentType);
		resp.getWriter().write(page);
	}

	private String odataServicePath(String servicePath, String contextPath) {
		String basePath = UrlPathUtil.normalizeBasePath(runtime.getEnvironment().getCdsProperties().getOdataV4().getEndpoint().getPath());
		String theBasePath = contextPath + (basePath.equals("/") ? "" : basePath);
		return theBasePath + "/" + servicePath;
	}

	private String resource(String file) {
		try {
			return new String(CdsFioriPreviewServlet.class.getClassLoader()
					.getResourceAsStream("com.sap.cds/fiori-preview/" + file).readAllBytes(), StandardCharsets.UTF_8);
		} catch (IOException e) {
			throw new ErrorStatusException(ErrorStatuses.NOT_FOUND, e);
		}
	}

	private boolean isExposedAssociation(CdsElement e) {
		String target = e.getType().as(CdsAssociationType.class).getTarget().getQualifiedName();
		return !e.getName().equals("SiblingEntity") &&
				!target.endsWith(".texts") &&
				!target.endsWith("_texts") &&
				!target.endsWith("DraftAdministrativeData");
	}

	record FioriPreview(String serviceName, String entityName, CdsEntity entity, String servicePath, String fioriPath) {};

	private static List findFioriPreviews(CdsRuntime runtime) {
		List previews = new ArrayList<>();
		Stream cdsServicePaths = CdsServicePath.servicePaths(runtime, "odata-v4");
		cdsServicePaths
			.filter(p -> p.getCdsDefinition() instanceof CdsService)
			.forEach(s -> {
				String serviceName = s.getCdsDefinition().getQualifiedName();
				String servicePath = s.getPath();
				s.subPaths()
					.filter(e -> ODataV4IndexContentProvider.isExposedEntity(e.getCdsDefinition()))
					.forEach(e -> {
						String entityName = ODataUtils.toODataName(e.getPath());
						previews.add(new FioriPreview(serviceName, entityName, e.getCdsDefinition().as(CdsEntity.class),
								servicePath, FIORI_PATH.formatted(serviceName, entityName)));
					});
			});
		return previews;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy