diff --git a/obp-api/src/main/resources/props/sample.props.template b/obp-api/src/main/resources/props/sample.props.template index 2b9aa1b14..4c081fde8 100644 --- a/obp-api/src/main/resources/props/sample.props.template +++ b/obp-api/src/main/resources/props/sample.props.template @@ -299,8 +299,8 @@ apiPathZero=obp ## This section configures email sending using CommonsEmailWrapper instead of Lift Mailer. ## All email functionality (password reset, validation, notifications) now uses these settings. ## -## Test Mode -## --------- +## Local Host Development Email Test Mode +## --------------------------------------- ## Enable test mode to log emails instead of sending them via SMTP. ## Perfect for localhost development and testing user invitations without an SMTP server. ## When enabled, full email content (from, to, subject, body) is logged to console. diff --git a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala index 61d41d55f..083665354 100644 --- a/obp-api/src/main/scala/code/api/util/ErrorMessages.scala +++ b/obp-api/src/main/scala/code/api/util/ErrorMessages.scala @@ -54,6 +54,7 @@ object ErrorMessages { // WebUiProps Exceptions (OBP-08XXX) val InvalidWebUiProps = "OBP-08001: Incorrect format of name." val WebUiPropsNotFound = "OBP-08002: WebUi props not found. Please specify a valid value for WEB_UI_PROPS_ID." + val WebUiPropsNotFoundByName = "OBP-08003: WebUi prop not found. Please specify a valid value for WEBUI_PROP_NAME." // DynamicEntity Exceptions (OBP-09XXX) val DynamicEntityNotFoundByDynamicEntityId = "OBP-09001: DynamicEntity not found. Please specify a valid value for DYNAMIC_ENTITY_ID." diff --git a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala index 5f8764dfe..6cbe173cb 100644 --- a/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala +++ b/obp-api/src/main/scala/code/api/v6_0_0/APIMethods600.scala @@ -35,9 +35,10 @@ import code.model._ import code.users.{UserAgreement, UserAgreementProvider, Users} import code.ratelimiting.RateLimitingDI import code.util.Helper -import code.util.Helper.{MdcLoggable, SILENCE_IS_GOLDEN} +import code.util.Helper.{MdcLoggable, ObpS, SILENCE_IS_GOLDEN} import code.views.Views import code.views.system.ViewDefinition +import code.webuiprops.{MappedWebUiPropsProvider, WebUiPropsCommons} import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.model.{CustomerAttribute, _} @@ -3339,6 +3340,86 @@ trait APIMethods600 { } } + staticResourceDocs += ResourceDoc( + getWebUiProp, + implementedInApiVersion, + nameOf(getWebUiProp), + "GET", + "/webui-props/WEBUI_PROP_NAME", + "Get WebUiProp by Name", + s""" + | + |Get a single WebUiProp by name. + | + |Properties with names starting with "webui_" can be stored in the database and managed via API. + | + |**Data Sources:** + | + |1. **Explicit WebUiProps (Database)**: Custom values created/updated via the API and stored in the database. + | + |2. **Implicit WebUiProps (Configuration File)**: Default values defined in the `sample.props.template` configuration file. + | + |**Query Parameter:** + | + |* `active` (optional, boolean string, default: "false") + | - If `active=false` or omitted: Returns only explicit prop from the database + | - If `active=true`: Returns explicit prop from database, or if not found, returns implicit (default) prop from configuration file + | - Implicit props are marked with `webUiPropsId = "default"` + | + |**Examples:** + | + |Get database-stored prop only: + |${getObpApiRoot}/v6.0.0/webui-props/webui_api_explorer_url + | + |Get database prop or fallback to default: + |${getObpApiRoot}/v6.0.0/webui-props/webui_api_explorer_url?active=true + | + |""", + EmptyBody, + WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id")), + List( + WebUiPropsNotFoundByName, + UnknownError + ), + List(apiTagWebUiProps) + ) + lazy val getWebUiProp: OBPEndpoint = { + case "webui-props" :: webUiPropName :: Nil JsonGet req => { + cc => implicit val ec = EndpointContext(Some(cc)) + val active = ObpS.param("active").getOrElse("false") + for { + invalidMsg <- Future(s"""$InvalidFilterParameterFormat `active` must be a boolean, but current `active` value is: ${active} """) + isActived <- NewStyle.function.tryons(invalidMsg, 400, cc.callContext) { + active.toBoolean + } + explicitWebUiProps <- Future{ MappedWebUiPropsProvider.getAll() } + explicitProp = explicitWebUiProps.find(_.name == webUiPropName) + result <- { + explicitProp match { + case Some(prop) => + // Found in database + Future.successful(prop) + case None if isActived => + // Not in database, check implicit props if active=true + val implicitWebUiProps = getWebUIPropsPairs.map(webUIPropsPairs => + WebUiPropsCommons(webUIPropsPairs._1, webUIPropsPairs._2, webUiPropsId = Some("default")) + ) + val implicitProp = implicitWebUiProps.find(_.name == webUiPropName) + implicitProp match { + case Some(prop) => Future.successful(prop) + case None => Future.failed(new Exception(s"$WebUiPropsNotFoundByName Current WEBUI_PROP_NAME($webUiPropName)")) + } + case None => + // Not in database and active=false + Future.failed(new Exception(s"$WebUiPropsNotFoundByName Current WEBUI_PROP_NAME($webUiPropName)")) + } + } + } yield { + (result, HttpCode.`200`(cc.callContext)) + } + } + } + } }