com.sap.cds.adapter.odata.v4.CdsODataV4ServletFactory 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;
import java.util.Objects;
import java.util.stream.Stream;
import com.sap.cds.adapter.ServletAdapterFactory;
import com.sap.cds.adapter.UrlResourcePath;
import com.sap.cds.reflect.CdsAction;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsFunction;
import com.sap.cds.services.runtime.CdsRuntime;
import com.sap.cds.services.runtime.CdsRuntimeAware;
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 com.sap.cds.services.utils.path.UrlResourcePathBuilder;
public class CdsODataV4ServletFactory implements ServletAdapterFactory, CdsRuntimeAware {
public static final String PROTOCOL_KEY = "odata-v4";
private CdsRuntime runtime;
@Override
public void setCdsRuntime(CdsRuntime runtime) {
this.runtime = runtime;
}
@Override
public Object create() {
return new CdsODataV4Servlet(runtime, getBasePath());
}
@Override
public boolean isEnabled() {
return runtime.getEnvironment().getCdsProperties().getOdataV4().getEndpoint().isEnabled();
}
@Override
public String getBasePath() {
return UrlPathUtil.normalizeBasePath(runtime.getEnvironment().getCdsProperties().getOdataV4().getEndpoint().getPath());
}
@Override
public String[] getMappings() {
Stream cdsServicePaths = CdsServicePath.servicePaths(runtime, PROTOCOL_KEY);
return cdsServicePaths.map(servicePath -> UrlResourcePathBuilder.path(getBasePath(), servicePath.getPath()).recursive().build().getPath()).toArray(l -> new String[l]);
}
@Override
public UrlResourcePath getServletPath() {
final boolean openMetadataEndpoint = !runtime.getEnvironment().getCdsProperties().getSecurity().getAuthentication().isAuthenticateMetadataEndpoints();
Stream cdsServicePaths = CdsServicePath.servicePaths(runtime, PROTOCOL_KEY);
return UrlResourcePathBuilder.path(getBasePath())
.recursive()
.subPaths(
cdsServicePaths.flatMap(cdsServicePath -> {
boolean servicePublic = cdsServicePath.isPublic() && !cdsServicePath.subPaths().anyMatch(p -> !p.isPublic());
return Stream.of(
// Paths: /service/$metadata
UrlResourcePathBuilder.path(cdsServicePath.getPath(), "$metadata").isPublic(openMetadataEndpoint || cdsServicePath.isPublic()).build(),
// Path: /service/$batch
UrlResourcePathBuilder.path(cdsServicePath.getPath(), "$batch").isPublic(servicePublic).build(),
// Paths: /service/$all/**
UrlResourcePathBuilder.path(cdsServicePath.getPath(), "$all").recursive().isPublic(servicePublic).build(),
// Paths: /service/$crossjoin/**
UrlResourcePathBuilder.path(cdsServicePath.getPath(), "$crossjoin").recursive().isPublic(servicePublic).build(),
// Paths: /service and /service/entity/**
UrlResourcePathBuilder.path(cdsServicePath.getPath())
.isPublic(cdsServicePath.isPublic())
.subPaths( cdsServicePath.subPaths().flatMap(cdsSubPath -> {
if (cdsSubPath.hasType(CdsEntity.class)) {
return Stream.of(
// Path: /service/entity/**
UrlResourcePathBuilder.path(ODataUtils.toODataName(cdsSubPath.getPath()))
.recursive()
.isPublic(cdsServicePath.isPublic() && cdsSubPath.isPublic())
.publicEvents(cdsSubPath.publicEvents().map(UrlPathUtil::cdsEvent2HttpMethod).filter(Objects::nonNull))
.subPaths( cdsSubPath.subPaths().flatMap(p -> { return actionOrFunction(cdsSubPath, p, cdsServicePath.isPublic() && cdsSubPath.isPublic());} ))
.build(),
// Path: /service/entity(*)/**
UrlResourcePathBuilder.path( cdsSubPath.getPath() + "(*)" )
.recursive()
.isPublic(cdsServicePath.isPublic() && cdsSubPath.isPublic())
.publicEvents(cdsSubPath.publicEvents().map(UrlPathUtil::cdsEvent2HttpMethod).filter(Objects::nonNull))
.subPaths( cdsSubPath.subPaths().flatMap(p -> { return actionOrFunction(cdsSubPath, p, cdsServicePath.isPublic() && cdsSubPath.isPublic());}) )
.build()
);
} else {
return actionOrFunction(cdsServicePath, cdsSubPath, cdsServicePath.isPublic());
}
}) )
.build(),
// Paths: /service/
UrlResourcePathBuilder.path(cdsServicePath.getPath(), "/")
.isPublic(cdsServicePath.isPublic())
.build()
);
})
).build();
}
private static Stream actionOrFunction(CdsResourcePath parentPath, CdsResourcePath actionFunctionPath, boolean parentsArePublic) {
if (actionFunctionPath.hasType(CdsAction.class) || actionFunctionPath.hasType(CdsFunction.class)) {
String actionOrFunctionPath = actionFunctionPath.getPath();
if (parentPath.hasType(CdsEntity.class)) {
// bound action or function
actionOrFunctionPath = parentPath.getCdsDefinition().getQualifier() + "." + actionFunctionPath.getPath();
}
String httpMethod = actionFunctionPath.hasType(CdsAction.class) ? UrlPathUtil.HttpMethod.POST.name() : UrlPathUtil.HttpMethod.GET.name();
// function, function(*) resp. action and action(*)
return Stream.of(
UrlResourcePathBuilder.path(actionOrFunctionPath)
.isPublic(false)
.publicEvents( parentsArePublic && actionFunctionPath.isPublic() ? Stream.of(httpMethod) : Stream.empty() )
.build(),
UrlResourcePathBuilder.path(actionOrFunctionPath + "(*)")
.isPublic(false)
.publicEvents( parentsArePublic && actionFunctionPath.isPublic() ? Stream.of(httpMethod) : Stream.empty() )
.build()
);
}
return Stream.empty();
}
}