From effeb2586253750415ff640700e8095a090882bb Mon Sep 17 00:00:00 2001 From: Bill Avramenko Date: Mon, 23 Dec 2024 00:45:31 +0200 Subject: [PATCH 1/2] RANCHER-1961. Add Configurations.groovy to manage tenant configurations Introduced a new class `Configurations` for handling tenant-specific configurations such as RMAPI, WorldCat, SMTP settings, and password reset links. This addition supports streamlined integration and management of these configurations using existing Kong and Keycloak services. Also includes methods for checking and setting these configurations. --- .../rest_v2/eureka/kong/Configurations.groovy | 236 ++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 src/org/folio/rest_v2/eureka/kong/Configurations.groovy diff --git a/src/org/folio/rest_v2/eureka/kong/Configurations.groovy b/src/org/folio/rest_v2/eureka/kong/Configurations.groovy new file mode 100644 index 000000000..8e70cc28f --- /dev/null +++ b/src/org/folio/rest_v2/eureka/kong/Configurations.groovy @@ -0,0 +1,236 @@ +package org.folio.rest_v2.eureka.kong + +import com.cloudbees.groovy.cps.NonCPS +import groovy.text.GStringTemplateEngine +import org.folio.models.EurekaTenant +import org.folio.rest_v2.Constants +import org.folio.rest_v2.eureka.Keycloak +import org.folio.rest_v2.eureka.Kong +import org.folio.utilities.RequestException + +/** + * The Configurations class is responsible for managing various configurations + * related to a tenant such as RMAPI configuration, WorldCat, SMTP settings, and password reset link. + */ +class Configurations extends Kong { + + static final String KB_CREDENTIALS_ID = '80898dee-449f-44dd-9c8e-37d5eb469b1d' + + static final String COPYCAT_PROFILE_ID = 'f26df83c-aa25-40b6-876e-96852c3d4fd4' + + Configurations(def context, String kongUrl, Keycloak keycloak, boolean debug = false){ + super(context, kongUrl, keycloak, debug) + } + + Configurations(def context, String kongUrl, String keycloakUrl, boolean debug = false){ + super(context, kongUrl, keycloakUrl, debug) + } + + Configurations(Kong kong){ + this(kong.context, kong.kongUrl, kong.keycloak, kong.getDebug()) + } + + boolean isRmapiConfigExist(EurekaTenant tenant) { + String url = generateUrl("/eholdings/kb-credentials/${KB_CREDENTIALS_ID}") + + Map headers = getTenantHttpHeaders(tenant) + + try { + restClient.get(url, headers) + + return true + } catch (RequestException e) { + logger.warning(e.getMessage()) + + return false + } + } + + /** + * Sets the RMAPI configuration for the specified tenant. + * + * @param tenant The tenant for which the configuration is to be set. + */ + void setRmapiConfig(EurekaTenant tenant) { + if (!tenant.okapiConfig?.kbApiKey) { + logger.warning("KB Api key not set. Skipping.") + return + } + if (!isRmapiConfigExist(tenant)) { + logger.warning("KB Credentials configuration not exists") + return + } + String url = generateUrl("/eholdings/kb-credentials/${KB_CREDENTIALS_ID}") + Map headers = getTenantHttpHeaders(tenant) + headers["Content-Type"] = "application/vnd.api+json" + Map body = [data: [type : "kbCredentials", + attributes: [name : "Knowledge Base", + apiKey : tenant.okapiConfig.kbApiKey, + url : Constants.KB_API_URL, + customerId: Constants.KB_CUSTOMER_ID] + + ]] + + logger.info("Setting rmapi key") + restClient.put(url, body, headers) + logger.info("KB rmapi key successfully set") + } + + /** + * Checks if Worldcat is exists for the specified tenant. + * + * @param tenant The tenant for which to check the Worldcat availability. + * @return boolean value indicating if Worldcat is available. + */ + boolean isWorldcatExists(EurekaTenant tenant) { + String url = generateUrl("/copycat/profiles/${COPYCAT_PROFILE_ID}") + + Map headers = getTenantHttpHeaders(tenant) + + try { + restClient.get(url, headers) + logger.info("Worldcat exists") + return true + } catch (RequestException e) { + logger.info("Worldcat - copycat/profiles/${COPYCAT_PROFILE_ID} is not exists") + return false + } + } + + /** + * Sets the Worldcat configuration for the specified tenant. + * + * @param tenant The tenant for which the configuration is to be set. + */ + void setWorldcat(EurekaTenant tenant) { + if (!isWorldcatExists(tenant)) { + logger.warning("Worldcat's id not exits, reference data not performed") + return + } + + String url = generateUrl("/copycat/profiles/${COPYCAT_PROFILE_ID}") + Map headers = getTenantHttpHeaders(tenant) + Map body = Constants.WORLDCAT + + restClient.put(url, body, headers) + + logger.info("Worldcat successfully set") + } + + /** + * Sets the SMTP settings for the specified tenant. + * + * @param tenant The tenant for which the SMTP settings are to be set. + */ + Configurations setSmtp(EurekaTenant tenant) { + if (!tenant.okapiConfig?.smtp) + return this + + logger.info("Add SMTP configuration on tenant ${tenant.tenantId} with ${tenant.uuid}...") + + Map binding = [email_smtp_host: tenant.okapiConfig.smtp.host, + email_smtp_port: tenant.okapiConfig.smtp.port, + email_username : tenant.okapiConfig.smtp.username, + email_password : tenant.okapiConfig.smtp.password, + email_from : tenant.okapiConfig.smtp.from] + + Constants.CONFIGURATIONS.smtpConfig.each { config -> + def content = context.readFile file: tools.copyResourceFileToCurrentDirectory("okapi/configurations/" + config) + String entries = new GStringTemplateEngine().createTemplate(content).make(binding).toString() + + setConfigurationEntries(tenant, entries) + } + + return this + } + + /** + * Sets the reset password link for the specified tenant. + * + * @param tenant The tenant for which to set reset password link. + */ + Configurations setResetPasswordLink(EurekaTenant tenant) { + if (!tenant.okapiConfig?.resetPasswordLink) + return this + + logger.info("Add reset password configuration on tenant ${tenant.tenantId} with ${tenant.uuid}...") + + Map binding = [stripes_url: tenant.okapiConfig.resetPasswordLink] + + Constants.CONFIGURATIONS.resetPassword.each { config -> + def content = context.readFile file: tools.copyResourceFileToCurrentDirectory("okapi/configurations/" + config) + String entries = new GStringTemplateEngine().createTemplate(content).make(binding).toString() + + setConfigurationEntries(tenant, entries){ + String entryId = getUsersConfigurationEntries(tenant).find {entry -> entry.value ==~ /http.*/ }.id + deleteConfigurationEntries(tenant, entryId) + + setConfigurationEntries(tenant, entries) + } + } + + return this + } + + Map getUsersConfigurationEntries(EurekaTenant tenant){ + return getConfigurationEntries(tenant, "configs=module=USERSBL") + } + + Map getConfigurationEntries(EurekaTenant tenant, String query = "") { + logger.info("Get configuration entries on tenant ${tenant.tenantId} with ${tenant.uuid}${query ? " with query=${query}" : ""}...") + + Map headers = getTenantHttpHeaders(tenant) + + String url = generateUrl("/configurations/entries${query ? "?query=${query}" : ""}") + + return restClient.get(url, headers).body.configs + } + + Configurations setConfigurationEntries(EurekaTenant tenant, String entries = null, Closure existAction = {}){ + if(!entries) + return this + + logger.info("""Set configuration's entires on tenant ${tenant.tenantId} with ${tenant.uuid} and the following entries + $entries""") + + Map headers = getTenantHttpHeaders(tenant) + String url = generateUrl("/configurations/entries") + + def response = restClient.post(url, entries, headers, [201, 422]) + String contentStr = response.body.toString() + + if (response.responseCode == 422) { + logger.warning(""" + Configuration entries already presents on tenant, no actions needed.. + Status: ${response.responseCode} + Response content: + ${contentStr}""") + + existAction.run() + } else + logger.info("Setting configuration entries on tenant ${tenant.tenantId} with ${tenant.uuid} were finished successfully") + + return this + } + + Configurations deleteConfigurationEntries(EurekaTenant tenant, String entryId){ + if(!entryId) + return this + + logger.info("Delete configuration's entires $entryId on tenant ${tenant.tenantId} with ${tenant.uuid}...") + + Map headers = getTenantHttpHeaders(tenant) + String url = generateUrl("/configurations/entries/$entryId") + + restClient.delete(url, headers) + + logger.info("Deleting configuration entries $entryId on tenant ${tenant.tenantId} with ${tenant.uuid} were finished successfully") + + return this + } + + @NonCPS + static Configurations get(Kong kong){ + return new Configurations(kong) + } +} From cfd8de53ddc111639aa3a5fd52a95ac20a4e95b9 Mon Sep 17 00:00:00 2001 From: Bill Avramenko Date: Mon, 23 Dec 2024 00:47:54 +0200 Subject: [PATCH 2/2] RANCHER-1961. Introduce `configureTenant` to set up tenant-specific configurations, including SMTP, reset password links, eHolding and copycat. --- src/org/folio/rest_v2/eureka/Eureka.groovy | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/org/folio/rest_v2/eureka/Eureka.groovy b/src/org/folio/rest_v2/eureka/Eureka.groovy index 7aa0ef828..06c0ef376 100644 --- a/src/org/folio/rest_v2/eureka/Eureka.groovy +++ b/src/org/folio/rest_v2/eureka/Eureka.groovy @@ -64,6 +64,22 @@ class Eureka extends Base { , Permissions.get(kong).getCapabilitiesId(tenant) , Permissions.get(kong).getCapabilitySetsId(tenant)) + configureTenant(tenant) + + return this + } + + Eureka configureTenant(EurekaTenant tenant){ + Configurations.get(kong) + .setSmtp(tenant) + .setResetPasswordLink(tenant) + + if(tenant.getModules().getModuleByName('mod-copycat')) + Configurations.get(kong).setWorldcat(tenant) + + if(tenant.getModules().getModuleByName('mod-kb-ebsco-java')) + Configurations.get(kong).setRmapiConfig(tenant) + return this }