mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 11:06:49 +00:00
forcing lower_case entity names
This commit is contained in:
parent
606f7089e0
commit
afa73894c5
@ -75,6 +75,7 @@ object ErrorMessages {
|
||||
val DynamicDataNotFound = "OBP-09015: Dynamic Data not found. Please specify a valid value."
|
||||
val DuplicateQueryParameters = "OBP-09016: Duplicate Query Parameters are not allowed."
|
||||
val DuplicateHeaderKeys = "OBP-09017: Duplicate Header Keys are not allowed."
|
||||
val InvalidDynamicEntityName = "OBP-09018: Invalid entity_name format. Entity names must be lowercase with underscores (snake_case), e.g. 'customer_preferences'. No uppercase letters or spaces allowed."
|
||||
|
||||
|
||||
// General messages (OBP-10XXX)
|
||||
|
||||
@ -4567,7 +4567,7 @@ trait APIMethods600 {
|
||||
dynamic_entities = List(
|
||||
DynamicEntityDefinitionWithCountJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = None,
|
||||
has_personal_entity = true,
|
||||
@ -4632,7 +4632,7 @@ trait APIMethods600 {
|
||||
dynamic_entities = List(
|
||||
DynamicEntityDefinitionWithCountJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = Some("gh.29.uk"),
|
||||
has_personal_entity = true,
|
||||
@ -4757,7 +4757,7 @@ trait APIMethods600 {
|
||||
|**Request format:**
|
||||
|```json
|
||||
|{
|
||||
| "entity_name": "CustomerPreferences",
|
||||
| "entity_name": "customer_preferences",
|
||||
| "has_personal_entity": true,
|
||||
| "schema": {
|
||||
| "description": "User preferences",
|
||||
@ -4770,17 +4770,19 @@ trait APIMethods600 {
|
||||
|}
|
||||
|```
|
||||
|
|
||||
|**Important:** Each property MUST include an `example` field with a valid example value.
|
||||
|**Important:**
|
||||
|* The `entity_name` must be lowercase with underscores (snake_case), e.g. `customer_preferences`. No uppercase letters or spaces allowed.
|
||||
|* Each property MUST include an `example` field with a valid example value.
|
||||
|
|
||||
|For more information see ${Glossary.getGlossaryItemLink("Dynamic-Entities")}""",
|
||||
CreateDynamicEntityRequestJsonV600(
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
has_personal_entity = Some(true),
|
||||
schema = net.liftweb.json.parse("""{"description": "User preferences", "required": ["theme"], "properties": {"theme": {"type": "string", "example": "dark"}, "language": {"type": "string", "example": "en"}}}""").asInstanceOf[net.liftweb.json.JsonAST.JObject]
|
||||
),
|
||||
DynamicEntityDefinitionJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = None,
|
||||
has_personal_entity = true,
|
||||
@ -4796,6 +4798,17 @@ trait APIMethods600 {
|
||||
Some(List(canCreateSystemLevelDynamicEntity))
|
||||
)
|
||||
|
||||
// v6.0.0 entity names must be lowercase with underscores (snake_case)
|
||||
private val validEntityNamePattern = "^[a-z][a-z0-9_]*$".r.pattern
|
||||
|
||||
private def validateEntityNameV600(entityName: String, callContext: Option[CallContext]): Future[Unit] = {
|
||||
if (validEntityNamePattern.matcher(entityName).matches()) {
|
||||
Future.successful(())
|
||||
} else {
|
||||
Future.failed(new RuntimeException(s"$InvalidDynamicEntityName Current value: '$entityName'"))
|
||||
}
|
||||
}
|
||||
|
||||
lazy val createSystemDynamicEntity: OBPEndpoint = {
|
||||
case "management" :: "system-dynamic-entities" :: Nil JsonPost json -> _ => { cc =>
|
||||
implicit val ec = EndpointContext(Some(cc))
|
||||
@ -4803,6 +4816,7 @@ trait APIMethods600 {
|
||||
request <- NewStyle.function.tryons(s"$InvalidJsonFormat", 400, cc.callContext) {
|
||||
json.extract[CreateDynamicEntityRequestJsonV600]
|
||||
}
|
||||
_ <- validateEntityNameV600(request.entity_name, cc.callContext)
|
||||
internalJson = JSONFactory600.convertV600RequestToInternal(request)
|
||||
dynamicEntity = DynamicEntityCommons(internalJson, None, cc.userId, None)
|
||||
result <- createDynamicEntityV600(cc, dynamicEntity)
|
||||
@ -4824,7 +4838,7 @@ trait APIMethods600 {
|
||||
|**Request format:**
|
||||
|```json
|
||||
|{
|
||||
| "entity_name": "CustomerPreferences",
|
||||
| "entity_name": "customer_preferences",
|
||||
| "has_personal_entity": true,
|
||||
| "schema": {
|
||||
| "description": "User preferences",
|
||||
@ -4837,17 +4851,19 @@ trait APIMethods600 {
|
||||
|}
|
||||
|```
|
||||
|
|
||||
|**Important:** Each property MUST include an `example` field with a valid example value.
|
||||
|**Important:**
|
||||
|* The `entity_name` must be lowercase with underscores (snake_case), e.g. `customer_preferences`. No uppercase letters or spaces allowed.
|
||||
|* Each property MUST include an `example` field with a valid example value.
|
||||
|
|
||||
|For more information see ${Glossary.getGlossaryItemLink("Dynamic-Entities")}""",
|
||||
CreateDynamicEntityRequestJsonV600(
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
has_personal_entity = Some(true),
|
||||
schema = net.liftweb.json.parse("""{"description": "User preferences", "required": ["theme"], "properties": {"theme": {"type": "string", "example": "dark"}, "language": {"type": "string", "example": "en"}}}""").asInstanceOf[net.liftweb.json.JsonAST.JObject]
|
||||
),
|
||||
DynamicEntityDefinitionJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = Some("gh.29.uk"),
|
||||
has_personal_entity = true,
|
||||
@ -4871,6 +4887,7 @@ trait APIMethods600 {
|
||||
request <- NewStyle.function.tryons(s"$InvalidJsonFormat", 400, cc.callContext) {
|
||||
json.extract[CreateDynamicEntityRequestJsonV600]
|
||||
}
|
||||
_ <- validateEntityNameV600(request.entity_name, cc.callContext)
|
||||
internalJson = JSONFactory600.convertV600RequestToInternal(request)
|
||||
dynamicEntity = DynamicEntityCommons(internalJson, None, cc.userId, Some(bankId))
|
||||
result <- createDynamicEntityV600(cc, dynamicEntity)
|
||||
@ -4892,7 +4909,7 @@ trait APIMethods600 {
|
||||
|**Request format:**
|
||||
|```json
|
||||
|{
|
||||
| "entity_name": "CustomerPreferences",
|
||||
| "entity_name": "customer_preferences",
|
||||
| "has_personal_entity": true,
|
||||
| "schema": {
|
||||
| "description": "User preferences updated",
|
||||
@ -4906,15 +4923,17 @@ trait APIMethods600 {
|
||||
|}
|
||||
|```
|
||||
|
|
||||
|**Important:** The `entity_name` must be lowercase with underscores (snake_case), e.g. `customer_preferences`. No uppercase letters or spaces allowed.
|
||||
|
|
||||
|For more information see ${Glossary.getGlossaryItemLink("Dynamic-Entities")}""",
|
||||
UpdateDynamicEntityRequestJsonV600(
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
has_personal_entity = Some(true),
|
||||
schema = net.liftweb.json.parse("""{"description": "User preferences updated", "required": ["theme"], "properties": {"theme": {"type": "string", "example": "dark"}, "language": {"type": "string", "example": "en"}, "notifications_enabled": {"type": "boolean", "example": "true"}}}""").asInstanceOf[net.liftweb.json.JsonAST.JObject]
|
||||
),
|
||||
DynamicEntityDefinitionJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = None,
|
||||
has_personal_entity = true,
|
||||
@ -4937,6 +4956,7 @@ trait APIMethods600 {
|
||||
request <- NewStyle.function.tryons(s"$InvalidJsonFormat", 400, cc.callContext) {
|
||||
json.extract[UpdateDynamicEntityRequestJsonV600]
|
||||
}
|
||||
_ <- validateEntityNameV600(request.entity_name, cc.callContext)
|
||||
internalJson = JSONFactory600.convertV600UpdateRequestToInternal(request)
|
||||
dynamicEntity = DynamicEntityCommons(internalJson, Some(dynamicEntityId), cc.userId, None)
|
||||
result <- updateDynamicEntityV600(cc, dynamicEntity)
|
||||
@ -4958,7 +4978,7 @@ trait APIMethods600 {
|
||||
|**Request format:**
|
||||
|```json
|
||||
|{
|
||||
| "entity_name": "CustomerPreferences",
|
||||
| "entity_name": "customer_preferences",
|
||||
| "has_personal_entity": true,
|
||||
| "schema": {
|
||||
| "description": "User preferences updated",
|
||||
@ -4972,15 +4992,17 @@ trait APIMethods600 {
|
||||
|}
|
||||
|```
|
||||
|
|
||||
|**Important:** The `entity_name` must be lowercase with underscores (snake_case), e.g. `customer_preferences`. No uppercase letters or spaces allowed.
|
||||
|
|
||||
|For more information see ${Glossary.getGlossaryItemLink("Dynamic-Entities")}""",
|
||||
UpdateDynamicEntityRequestJsonV600(
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
has_personal_entity = Some(true),
|
||||
schema = net.liftweb.json.parse("""{"description": "User preferences updated", "required": ["theme"], "properties": {"theme": {"type": "string", "example": "dark"}, "language": {"type": "string", "example": "en"}, "notifications_enabled": {"type": "boolean", "example": "true"}}}""").asInstanceOf[net.liftweb.json.JsonAST.JObject]
|
||||
),
|
||||
DynamicEntityDefinitionJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = Some("gh.29.uk"),
|
||||
has_personal_entity = true,
|
||||
@ -5004,6 +5026,7 @@ trait APIMethods600 {
|
||||
request <- NewStyle.function.tryons(s"$InvalidJsonFormat", 400, cc.callContext) {
|
||||
json.extract[UpdateDynamicEntityRequestJsonV600]
|
||||
}
|
||||
_ <- validateEntityNameV600(request.entity_name, cc.callContext)
|
||||
internalJson = JSONFactory600.convertV600UpdateRequestToInternal(request)
|
||||
dynamicEntity = DynamicEntityCommons(internalJson, Some(dynamicEntityId), cc.userId, Some(bankId))
|
||||
result <- updateDynamicEntityV600(cc, dynamicEntity)
|
||||
@ -5025,7 +5048,7 @@ trait APIMethods600 {
|
||||
|**Request format:**
|
||||
|```json
|
||||
|{
|
||||
| "entity_name": "CustomerPreferences",
|
||||
| "entity_name": "customer_preferences",
|
||||
| "has_personal_entity": true,
|
||||
| "schema": {
|
||||
| "description": "User preferences updated",
|
||||
@ -5039,15 +5062,17 @@ trait APIMethods600 {
|
||||
|}
|
||||
|```
|
||||
|
|
||||
|**Important:** The `entity_name` must be lowercase with underscores (snake_case), e.g. `customer_preferences`. No uppercase letters or spaces allowed.
|
||||
|
|
||||
|For more information see ${Glossary.getGlossaryItemLink("My-Dynamic-Entities")}""",
|
||||
UpdateDynamicEntityRequestJsonV600(
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
has_personal_entity = Some(true),
|
||||
schema = net.liftweb.json.parse("""{"description": "User preferences updated", "required": ["theme"], "properties": {"theme": {"type": "string", "example": "dark"}, "language": {"type": "string", "example": "en"}, "notifications_enabled": {"type": "boolean", "example": "true"}}}""").asInstanceOf[net.liftweb.json.JsonAST.JObject]
|
||||
),
|
||||
DynamicEntityDefinitionJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = None,
|
||||
has_personal_entity = true,
|
||||
@ -5075,6 +5100,7 @@ trait APIMethods600 {
|
||||
request <- NewStyle.function.tryons(s"$InvalidJsonFormat", 400, cc.callContext) {
|
||||
json.extract[UpdateDynamicEntityRequestJsonV600]
|
||||
}
|
||||
_ <- validateEntityNameV600(request.entity_name, cc.callContext)
|
||||
internalJson = JSONFactory600.convertV600UpdateRequestToInternal(request)
|
||||
dynamicEntity = DynamicEntityCommons(internalJson, Some(dynamicEntityId), cc.userId, existingEntity.get.bankId)
|
||||
result <- updateDynamicEntityV600(cc, dynamicEntity)
|
||||
@ -6894,7 +6920,7 @@ trait APIMethods600 {
|
||||
dynamic_entities = List(
|
||||
DynamicEntityDefinitionJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = None,
|
||||
has_personal_entity = true,
|
||||
@ -6949,7 +6975,7 @@ trait APIMethods600 {
|
||||
dynamic_entities = List(
|
||||
DynamicEntityDefinitionJsonV600(
|
||||
dynamic_entity_id = "abc-123-def",
|
||||
entity_name = "CustomerPreferences",
|
||||
entity_name = "customer_preferences",
|
||||
user_id = "user-456",
|
||||
bank_id = None,
|
||||
has_personal_entity = true,
|
||||
|
||||
@ -72,7 +72,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
val rightEntityV600 = parse(
|
||||
"""
|
||||
|{
|
||||
| "entity_name": "FooBar",
|
||||
| "entity_name": "foo_bar",
|
||||
| "has_personal_entity": true,
|
||||
| "schema": {
|
||||
| "description": "description of this entity, can be markdown text.",
|
||||
@ -100,7 +100,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
val entityWithoutPersonalV600 = parse(
|
||||
"""
|
||||
|{
|
||||
| "entity_name": "SharedEntity",
|
||||
| "entity_name": "shared_entity",
|
||||
| "has_personal_entity": false,
|
||||
| "schema": {
|
||||
| "description": "A shared entity without personal endpoints.",
|
||||
@ -121,7 +121,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
val wrongRequiredEntityV600 = parse(
|
||||
"""
|
||||
|{
|
||||
| "entity_name": "FooBar",
|
||||
| "entity_name": "foo_bar",
|
||||
| "has_personal_entity": true,
|
||||
| "schema": {
|
||||
| "description": "description of this entity.",
|
||||
@ -142,7 +142,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
val updatedEntityV600 = parse(
|
||||
"""
|
||||
|{
|
||||
| "entity_name": "FooBar",
|
||||
| "entity_name": "foo_bar",
|
||||
| "has_personal_entity": true,
|
||||
| "schema": {
|
||||
| "description": "Updated description of this entity.",
|
||||
@ -206,7 +206,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
(responseJson \ "dynamic_entity_id") shouldBe a[JString]
|
||||
|
||||
And("Response should have snake_case field: entity_name")
|
||||
(responseJson \ "entity_name").extract[String] should equal("FooBar")
|
||||
(responseJson \ "entity_name").extract[String] should equal("foo_bar")
|
||||
|
||||
And("Response should have snake_case field: user_id")
|
||||
(responseJson \ "user_id").extract[String] should equal(resourceUser1.userId)
|
||||
@ -220,9 +220,9 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
(schemaField \ "required") shouldBe a[JArray]
|
||||
(schemaField \ "properties") shouldBe a[JObject]
|
||||
|
||||
// Verify schema does NOT contain the entity name as a key (old format would have FooBar as key)
|
||||
// Verify schema does NOT contain the entity name as a key (old format would have foo_bar as key)
|
||||
And("Schema should NOT contain entity name as a dynamic key")
|
||||
(schemaField \ "FooBar") should equal(JNothing)
|
||||
(schemaField \ "foo_bar") should equal(JNothing)
|
||||
|
||||
val dynamicEntityId = (responseJson \ "dynamic_entity_id").extract[String]
|
||||
|
||||
@ -245,7 +245,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
val entity = entities.head
|
||||
And("GET response should also use snake_case fields")
|
||||
(entity \ "dynamic_entity_id").extract[String] should equal(dynamicEntityId)
|
||||
(entity \ "entity_name").extract[String] should equal("FooBar")
|
||||
(entity \ "entity_name").extract[String] should equal("foo_bar")
|
||||
(entity \ "has_personal_entity").extract[Boolean] should equal(true)
|
||||
|
||||
And("GET response should include record_count field")
|
||||
@ -279,7 +279,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
|
||||
And("Updated response should use snake_case fields")
|
||||
(responseJson \ "dynamic_entity_id").extract[String] should equal(dynamicEntityId)
|
||||
(responseJson \ "entity_name").extract[String] should equal("FooBar")
|
||||
(responseJson \ "entity_name").extract[String] should equal("foo_bar")
|
||||
|
||||
And("Schema should be updated")
|
||||
val schemaField = responseJson \ "schema"
|
||||
@ -333,7 +333,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
(responseJson \ "bank_id").extract[String] should equal(bankId)
|
||||
|
||||
And("Response should have entity_name")
|
||||
(responseJson \ "entity_name").extract[String] should equal("FooBar")
|
||||
(responseJson \ "entity_name").extract[String] should equal("foo_bar")
|
||||
|
||||
val dynamicEntityId = (responseJson \ "dynamic_entity_id").extract[String]
|
||||
|
||||
@ -352,7 +352,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
|
||||
val entity = entities.head
|
||||
(entity \ "bank_id").extract[String] should equal(bankId)
|
||||
(entity \ "entity_name").extract[String] should equal("FooBar")
|
||||
(entity \ "entity_name").extract[String] should equal("foo_bar")
|
||||
(entity \ "record_count") shouldBe a[JInt]
|
||||
|
||||
// Cleanup
|
||||
@ -380,7 +380,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
updateResponse.code should equal(200)
|
||||
|
||||
And("Updated response should have snake_case fields")
|
||||
(updateResponse.body \ "entity_name").extract[String] should equal("FooBar")
|
||||
(updateResponse.body \ "entity_name").extract[String] should equal("foo_bar")
|
||||
(updateResponse.body \ "bank_id").extract[String] should equal(bankId)
|
||||
|
||||
// Cleanup
|
||||
@ -425,16 +425,16 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
entities.size should be >= 1
|
||||
|
||||
And("Response should use snake_case fields")
|
||||
val entity = entities.find(e => (e \ "entity_name").extract[String] == "FooBar").get
|
||||
val entity = entities.find(e => (e \ "entity_name").extract[String] == "foo_bar").get
|
||||
(entity \ "dynamic_entity_id") shouldBe a[JString]
|
||||
(entity \ "entity_name").extract[String] should equal("FooBar")
|
||||
(entity \ "entity_name").extract[String] should equal("foo_bar")
|
||||
(entity \ "user_id").extract[String] should equal(resourceUser1.userId)
|
||||
(entity \ "has_personal_entity").extract[Boolean] should equal(true)
|
||||
|
||||
And("Schema field should contain only the schema structure")
|
||||
val schemaField = entity \ "schema"
|
||||
(schemaField \ "description") shouldBe a[JString]
|
||||
(schemaField \ "FooBar") should equal(JNothing) // Should NOT have entity name as key
|
||||
(schemaField \ "foo_bar") should equal(JNothing) // Should NOT have entity name as key
|
||||
|
||||
// Test Update My Dynamic Entity
|
||||
When("We update my dynamic entity")
|
||||
@ -445,7 +445,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
updateResponse.code should equal(200)
|
||||
|
||||
And("Updated response should use snake_case fields")
|
||||
(updateResponse.body \ "entity_name").extract[String] should equal("FooBar")
|
||||
(updateResponse.body \ "entity_name").extract[String] should equal("foo_bar")
|
||||
(updateResponse.body \ "schema" \ "description").extract[String] should equal("Updated description of this entity.")
|
||||
|
||||
// Cleanup
|
||||
@ -492,8 +492,8 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
|
||||
And("Response should contain only entities with has_personal_entity = true")
|
||||
val entityNames = entities.map(e => (e \ "entity_name").extract[String])
|
||||
entityNames should contain("FooBar")
|
||||
entityNames should not contain("SharedEntity")
|
||||
entityNames should contain("foo_bar")
|
||||
entityNames should not contain("shared_entity")
|
||||
|
||||
And("All returned entities should have has_personal_entity = true")
|
||||
entities.foreach { entity =>
|
||||
@ -535,7 +535,7 @@ class DynamicEntityTest extends V600ServerSetup {
|
||||
(schemaField \ "properties") shouldBe a[JObject]
|
||||
|
||||
And("Schema should NOT contain the entity name as a nested key (old v4.0.0 format)")
|
||||
(schemaField \ "FooBar") should equal(JNothing)
|
||||
(schemaField \ "foo_bar") should equal(JNothing)
|
||||
|
||||
And("Schema should NOT contain hasPersonalEntity (that's a separate top-level field)")
|
||||
(schemaField \ "hasPersonalEntity") should equal(JNothing)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user