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

com.sap.cds.feature.messaging.em.client.EnterpriseMessagingWebhookManagementClient Maven / Gradle / Ivy

/**************************************************************************
 * (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
 **************************************************************************/
package com.sap.cds.feature.messaging.em.client;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.sap.cds.feature.messaging.em.client.EnterpriseMessagingOAuth2PropertySupplier.EnterpriseMessagingOptions;
import com.sap.cds.integration.cloudsdk.rest.client.JsonRestClient;
import com.sap.cds.integration.cloudsdk.rest.client.JsonRestClientResponseException;
import com.sap.cds.services.environment.CdsProperties.ConnectionPool;
import com.sap.cloud.environment.servicebinding.api.ServiceBinding;
import com.sap.cloud.sdk.cloudplatform.connectivity.OnBehalfOf;
import com.sap.cloud.sdk.cloudplatform.connectivity.ServiceBindingDestinationOptions;


/**
 * This class provides the REST client implementation providing the access to the SaaS
 * and webhook management of enterprise messaging.
 */
public class EnterpriseMessagingWebhookManagementClient extends JsonRestClient {

	private static final Logger logger = LoggerFactory.getLogger(EnterpriseMessagingWebhookManagementClient.class);
	private static final String WEBHOOKS = "/messagingrest/v1/subscriptions";
	private static final String TOPICS = "/messagingrest/v1/topics/{topic}/messages";
	private static final String OAUTH_PATH = "/oauth/token";

	static {
		EnterpriseMessagingOAuth2PropertySupplier.initialize();
	}

	private final String name;
	private final Map uaaCredentials;

	@SuppressWarnings("unchecked")
	public EnterpriseMessagingWebhookManagementClient(ServiceBinding binding, ConnectionPool connectionPoolConfig) {
		super(ServiceBindingDestinationOptions.forService(binding)
			.onBehalfOf(OnBehalfOf.TECHNICAL_USER_CURRENT_TENANT)
			.withOption(EnterpriseMessagingOptions.WEBHOOK_MANAGEMENT_API)
			.build(), connectionPoolConfig);
		this.name = binding.getName().get(); // NOSONAR
		this.uaaCredentials = (Map) binding.getCredentials().get("uaa");
	}

	/**
	 * Sends the specified string message to the specified topic.
	 *
	 * @param topic topic the message should be sent to
	 * @param message message to be sent
	 * @throws IOException throws when any connection problems occur
	 */
	public void sendMessage(String topic, String message) throws IOException {
		logger.debug("Sending message from client '{}' to topic '{}'", name, topic);
		Map headers = new HashMap<>();
		headers.put("Content-Type", "text/plain");
		headers.put("x-qos", "0");
		postRequest(TOPICS.replace("{topic}", URLEncoder.encode(topic, StandardCharsets.UTF_8.toString())), message, headers);
	}

	/**
	 * Retrieves all registered webhooks.
	 *
	 * @return all registered webhooks
	 *
	 * @throws IOException throws when any connection problems occur
	 */
	public ArrayNode getRegisteredWebhooks() throws IOException {
		logger.debug("Retrieving all registered webhooks of service binding '{}'", name);
		return (ArrayNode) getRequest(WEBHOOKS);
	}

	/**
	 * Deletes the specified webhook.
	 *
	 * @param webhookName the name of the webhook to be deleted.
	 * @return true if deleted and false otherwise
	 *
	 * @throws IOException throws when any connection problems occur
	 */
	public boolean deleteWebhookRegistration(String webhookName) throws IOException {
		logger.info("Deleting webhook for service '{}' of service binding '{}'", webhookName, name);
		try {
			deleteRequest(WEBHOOKS + "/" + webhookName);
		} catch (JsonRestClientResponseException e) {
			if (e.getResponseCode() == HttpStatus.SC_NOT_FOUND) {
				// the registration is not available
				return false;
			}
			throw e;
		}

		return true;
	}

	/**
	 * Creates or updates a new webhook registration.
	 *
	 * @param webhookName webhook name
	 * @param queue full qualified queue name
	 * @param hookUrl callback url
	 * @param tenantSubdomain the subdomain of the tenant, used to enrich the token endpoint URL
	 *
	 * @throws IOException throws when any connection problems occur
	 */
	public void createOrUpdateWebhookRegistration(String webhookName, String queue, String hookUrl, String tenantSubdomain) throws IOException {
		// the webhook API does not support the PUT request. As workaround we delete the hook and finally create a new one.
		deleteWebhookRegistration(webhookName);

		logger.info("Creating webhook for service '{}' at URL '{}' for queue '{}' on service binding '{}'", webhookName, hookUrl, queue, name);

		ObjectNode data = mapper.createObjectNode();
		data.put("name", webhookName);
		data.put("address", "queue:" + queue);
		data.put("qos", 1);

		ObjectNode pushConfig = mapper.createObjectNode();
		data.set("pushConfig", pushConfig);

		pushConfig.put("type", "webhook");
		pushConfig.put("endpoint", hookUrl);
		pushConfig.put("exemptHandshake", true);

		addSecuritySchema(pushConfig, tenantSubdomain);

		postRequest(WEBHOOKS, data);
	}

	private String replaceSubdomain(String url, String subdomain) {
		if (subdomain != null) {
			url = url.replaceFirst("://[^\\.]*\\.", "://" + subdomain + '.');
		}
		return url;
	}

	private void addSecuritySchema(ObjectNode config, String tenantSubdomain) {
		ObjectNode securitySchema = mapper.createObjectNode();
		config.set("securitySchema", securitySchema);

		securitySchema.put("grantType", "client_credentials");
		securitySchema.put("clientId", (String) uaaCredentials.get("clientid"));

		// check type
		if ("x509".equals(uaaCredentials.get("credential-type"))) {
			securitySchema.put("tokenUrl", replaceSubdomain((String) uaaCredentials.get("certurl"), tenantSubdomain) + OAUTH_PATH);
			securitySchema.put("type", "oauth2-x509");
			securitySchema.put("certificate", (String) uaaCredentials.get("certificate"));
			securitySchema.put("key", (String) uaaCredentials.get("key"));
		} else  {
			securitySchema.put("tokenUrl", replaceSubdomain((String) uaaCredentials.get("url"), tenantSubdomain) + OAUTH_PATH);
			securitySchema.put("type", "oauth2");
			securitySchema.put("clientSecret", (String) uaaCredentials.get("clientsecret"));
		}
	}
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy