com.sap.cloud.mt.subscription.SubscriberStreamlinedMtx Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of multi-tenant-subscription Show documentation
Show all versions of multi-tenant-subscription Show documentation
Spring Boot Enablement Parent
/******************************************************************************
* © 2020 SAP SE or an SAP affiliate company. All rights reserved. *
******************************************************************************/
package com.sap.cloud.mt.subscription;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sap.cloud.mt.subscription.HanaEncryptionTool.DbEncryptionMode;
import com.sap.cloud.mt.subscription.exceptions.AuthorityError;
import com.sap.cloud.mt.subscription.exceptions.InternalError;
import com.sap.cloud.mt.subscription.exceptions.NotFound;
import com.sap.cloud.mt.subscription.exceptions.ParameterError;
import com.sap.cloud.mt.subscription.exits.Exits;
import com.sap.cloud.mt.subscription.json.ApplicationDependency;
import com.sap.cloud.mt.subscription.json.DeletePayload;
import com.sap.cloud.mt.subscription.json.SubscriptionPayload;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class SubscriberStreamlinedMtx implements Subscriber {
//Magic tenant id that represents all tenants
public static final String ALL_TENANTS = "all";
private static final Logger logger = LoggerFactory.getLogger(SubscriberStreamlinedMtx.class);
private static final String NO_SAAS_REGISTRY_PROVIDED = "No saas registry provided";
private static final String ID = "ID";
private final Exits exits;
//Used to execute scope checks
private final SecurityChecker securityChecker;
//Proxy used to communicate with the saas registry service
private final Optional saasRegistry;
//Proxy for mtx provisioning service
private final ProvisioningService provisioningService;
private final InstanceLifecycleManager instanceLifecycleManager;
private final MtxTools mtxTools;
private final String baseUiUrl;
private final String urlSeparator;
private final boolean withoutAuthorityCheck;
private SubscriberStreamlinedMtx(Exits exits,
String baseUiUrl, String urlSeparator, SecurityChecker securityChecker, SaasRegistry saasRegistry,
ProvisioningService provisioningService,
InstanceLifecycleManager instanceLifecycleManager,
boolean withoutAuthorityCheck, DbEncryptionMode hanaEncryptionMode) throws InternalError {
this.baseUiUrl = baseUiUrl;
this.urlSeparator = urlSeparator;
this.exits = exits;
this.saasRegistry = Optional.ofNullable(saasRegistry);
this.instanceLifecycleManager = instanceLifecycleManager;
if (exits.getUnSubscribeExit() == null) throw new InternalError("No unsubscribe exit found");
this.securityChecker = securityChecker;
this.mtxTools = new MtxTools(securityChecker, baseUiUrl, urlSeparator, provisioningService.getServiceSpecification().getPolling(), hanaEncryptionMode);
this.provisioningService = provisioningService;
this.withoutAuthorityCheck = withoutAuthorityCheck;
}
@Override
public String subscribe(String tenantId, SubscriptionPayload subscriptionPayload)
throws InternalError, ParameterError, AuthorityError {
return mtxTools.subscribe(tenantId,
instanceCreationOptions -> provisioningService.subscribe(tenantId, subscriptionPayload, instanceCreationOptions),
provisioningService::determineJobStatus,
subscriptionPayload,
withoutAuthorityCheck, exits);
}
@Override
public String getSubscribeUrl(SubscriptionPayload subscriptionPayload) throws InternalError, ParameterError, AuthorityError {
if (!withoutAuthorityCheck) {
securityChecker.checkSubscriptionAuthority();
}
return Tools.getApplicationUrl(subscriptionPayload, exits.getSubscribeExit()::uiURL, exits.getSubscribeExit()::uiURL, baseUiUrl, urlSeparator);
}
@Override
public void unsubscribe(String tenantId, DeletePayload deletePayload) throws InternalError, ParameterError, AuthorityError {
mtxTools.unsubscribe(tenantId,
() -> provisioningService.unsubscribe(tenantId, deletePayload),
provisioningService::determineJobStatus,
deletePayload,
withoutAuthorityCheck, exits);
}
@Override
public List getApplicationDependencies() throws AuthorityError {
if (!withoutAuthorityCheck) {
securityChecker.checkSubscriptionAuthority();
}
return exits.getDependencyExit() != null ? exits.getDependencyExit().onGetDependencies() : new ArrayList<>();
}
@Override
public void setupDbTables(List tenants) throws InternalError, AuthorityError, ParameterError {
setupDbTables(tenants, false);
}
@Override
public String setupDbTablesAsync(List tenants) throws InternalError, AuthorityError, ParameterError {
try {
return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(setupDbTables(tenants, true));
} catch (JsonProcessingException e) {
throw new InternalError(e);
}
}
@Override
public String updateStatus(String jobId) throws InternalError, ParameterError, NotFound, AuthorityError {
if (!withoutAuthorityCheck) {
securityChecker.checkInitDbAuthority();
}
if (StringUtils.isBlank(jobId)) {
logger.warn("An empty jobId was provided");
return "{}";
}
if (!jobId.matches(Tools.SECURE_CHARS)) {
throw new ParameterError("Job id contains illegal characters");
}
try {
return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(provisioningService.determineJobStatus(jobId));
} catch (JsonProcessingException e) {
throw new InternalError(e);
}
}
@Override
public void callSaasRegistry(boolean ok, String message, String applicationUrl, String saasRegistryUrl) throws InternalError {
saasRegistry.orElseThrow(() -> new InternalError(NO_SAAS_REGISTRY_PROVIDED)).callBackSaasRegistry(ok, message, applicationUrl, saasRegistryUrl);
}
@Override
public void checkAuthority(SecurityChecker.Authority authority) throws AuthorityError {
securityChecker.checkAuthority(authority);
}
private Map setupDbTables(List tenants, boolean asynchronously) throws InternalError, AuthorityError, ParameterError {
if (!withoutAuthorityCheck) {
securityChecker.checkInitDbAuthority();
}
Tools.checkExternalTenantIds(tenants);
if (tenants.isEmpty()) {
return new HashMap<>();
}
if (exits.getInitDbExit() != null) {
exits.getInitDbExit().onBeforeInitDb(tenants);
}
Set tenantSet = new HashSet<>(tenants);
Map result;
try {
if (tenantSet.size() == 1 && tenants.get(0).equals(ALL_TENANTS)) {
Set allTenants = instanceLifecycleManager.getAllTenants(true);
if (allTenants.isEmpty()) {
return new HashMap<>();
}
tenantSet = allTenants;
}
result = provisioningService.upgrade(tenantSet, asynchronously);
if (!asynchronously && exits.getInitDbExit() != null) {
exits.getInitDbExit().onAfterInitDb(true);
}
if (result.containsKey(ID)) {
// add field jobID to make it compatible to old version
result.put("jobID", result.get(ID));
}
return result;
} catch (InternalError e) {
if (!asynchronously && exits.getInitDbExit() != null) {
exits.getInitDbExit().onAfterInitDb(false);
}
throw e;
}
}
public static final class Builder {
private Exits exits;
private SecurityChecker securityChecker;
private SaasRegistry saasRegistry;
private String baseUiUrl;
private String urlSeparator;
private ProvisioningService provisioningService;
private InstanceLifecycleManager instanceLifecycleManager;
private boolean withoutAuthorityCheck;
private DbEncryptionMode hanaEncryptionMode;
private Builder() {
}
public static Builder create() {
return new Builder();
}
public Builder exits(Exits exits) {
this.exits = exits;
return this;
}
public Builder securityChecker(SecurityChecker securityChecker) {
this.securityChecker = securityChecker;
return this;
}
public Builder saasRegistry(SaasRegistry saasRegistry) {
this.saasRegistry = saasRegistry;
return this;
}
public Builder provisioningService(ProvisioningService provisioningService) {
this.provisioningService = provisioningService;
return this;
}
public Builder baseUiUrl(String baseUiUrl) {
this.baseUiUrl = baseUiUrl;
return this;
}
public Builder urlSeparator(String urlSeparator) {
this.urlSeparator = urlSeparator;
return this;
}
public Builder instanceLifecycleManager(InstanceLifecycleManager instanceLifecycleManager) {
this.instanceLifecycleManager = instanceLifecycleManager;
return this;
}
public Builder withoutAuthorityCheck(boolean withoutAuthorityCheck) {
this.withoutAuthorityCheck = withoutAuthorityCheck;
return this;
}
public Builder hanaEncryptionMode(DbEncryptionMode hanaEncryptionMode) {
this.hanaEncryptionMode = hanaEncryptionMode;
return this;
}
public SubscriberStreamlinedMtx build() throws InternalError {
if (provisioningService == null) {
throw new InternalError("No provisioning service provided");
}
if (exits == null) {
throw new InternalError("No exits provided");
}
if (securityChecker == null) {
throw new InternalError("No security checker provided");
}
if (instanceLifecycleManager == null) {
throw new InternalError("No instance lifecycle manager provided");
}
return new SubscriberStreamlinedMtx(exits, baseUiUrl, urlSeparator, securityChecker, saasRegistry, provisioningService,
instanceLifecycleManager, withoutAuthorityCheck, hanaEncryptionMode);
}
}
}