From b99f83a3884358c9b70559b5d348077c85cc6dd5 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Wed, 29 Apr 2026 12:32:02 +0530 Subject: [PATCH 01/13] fix: validate custom property name charset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tighten custom property name validation to block characters that break downstream parsers, with verified empirical reproduction: - `"` causes HTTP 500 on PUT /metadata/types/{id} - `:` breaks CSV import — exporter writes `key:value;key:value`, importer splits at first colon, treats prefix as the field name - `^` breaks OpenSearch query when the name is in searchSettings.searchFields — Lucene reads `^` as the boost separator in `field^boost` - `$` breaks CSV import via java.util.regex.Matcher.replaceAll which interprets `$` as a backreference Adds a `customPropertyName` definition in basic.json and switches customProperty.json to reference it. Adds a defensive regex check in TypeRepository.validateProperty so the API returns 400 with a clear error message even if schema validation is bypassed. Tests cover allowed-charset acceptance, the four blocked characters, leading-character validation, max-length enforcement, and unbalanced brackets. --- .../openmetadata/it/tests/TypeResourceIT.java | 149 ++++++++++++++++++ .../service/jdbi3/TypeRepository.java | 26 +++ .../resources/json/schema/type/basic.json | 7 + .../json/schema/type/customProperty.json | 4 +- 4 files changed, 184 insertions(+), 2 deletions(-) diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java index 8e6629876fa4..8586568e1c76 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java @@ -324,6 +324,155 @@ void test_typeWithInvalidName_fails(TestNamespace ns) { } } + @Test + void test_customPropertyNameAllowedCharacters_succeeds(TestNamespace ns) throws Exception { + OpenMetadataClient client = SdkClients.adminClient(); + UUID tableTypeId = TABLE_ENTITY_TYPE.getId(); + String prefix = ns.prefix("safe"); + + String[] allowedNames = { + prefix + "Plain", + prefix + "_underscore", + prefix + "-hyphen", + prefix + ".dot", + prefix + "with space", + prefix + "with/slash", + prefix + "with&", + prefix + "with%percent", + prefix + "with#hash", + prefix + "with@at", + prefix + "with!bang", + prefix + "with,comma", + prefix + "with;semi", + prefix + "with=eq", + prefix + "with|pipe", + prefix + "with'quote", + prefix + "with(lparen", + prefix + "with)rparen", + prefix + "withgt", + prefix + "with[lbrack", + prefix + "with]rbrack", + prefix + "with{lbrace", + prefix + "with}rbrace", + prefix + "withMatched(pair)And", + prefix + "withDigits123", + }; + + for (String name : allowedNames) { + CustomProperty property = new CustomProperty(); + property.setName(name); + property.setDescription("Allowed-charset test for custom property name"); + property.setPropertyType(STRING_TYPE.getEntityReference()); + + Type updatedType = addCustomProperty(client, tableTypeId, property); + assertNotNull(updatedType, "Allowed name '" + name + "' must be accepted"); + + boolean present = + updatedType.getCustomProperties().stream().anyMatch(cp -> name.equals(cp.getName())); + assertTrue(present, "Custom property '" + name + "' should be saved on the type"); + } + } + + @Test + void test_customPropertyNameDisallowedCharacters_fails(TestNamespace ns) { + OpenMetadataClient client = SdkClients.adminClient(); + UUID tableTypeId = TABLE_ENTITY_TYPE.getId(); + String prefix = ns.prefix("bad"); + + String[] disallowedNames = { + prefix + "with\"dquote", prefix + "with:colon", prefix + "with^caret", prefix + "with$dollar", + }; + + for (String name : disallowedNames) { + CustomProperty property = new CustomProperty(); + property.setName(name); + property.setDescription("Disallowed-charset test for custom property name"); + property.setPropertyType(STRING_TYPE.getEntityReference()); + + assertThrows( + Exception.class, + () -> addCustomProperty(client, tableTypeId, property), + "Custom property name '" + name + "' should be rejected"); + } + } + + @Test + void test_customPropertyNameMustStartWithAlphanumeric_fails(TestNamespace ns) { + OpenMetadataClient client = SdkClients.adminClient(); + UUID tableTypeId = TABLE_ENTITY_TYPE.getId(); + String prefix = ns.prefix("lead"); + + String[] invalidLeads = { + "_" + prefix, "-" + prefix, "." + prefix, " " + prefix, "(" + prefix, "<" + prefix, + }; + + for (String name : invalidLeads) { + CustomProperty property = new CustomProperty(); + property.setName(name); + property.setDescription("Leading-character validation"); + property.setPropertyType(STRING_TYPE.getEntityReference()); + + assertThrows( + Exception.class, + () -> addCustomProperty(client, tableTypeId, property), + "Custom property name '" + name + "' must start with alphanumeric"); + } + } + + @Test + void test_customPropertyNameTooLong_fails(TestNamespace ns) { + OpenMetadataClient client = SdkClients.adminClient(); + UUID tableTypeId = TABLE_ENTITY_TYPE.getId(); + + StringBuilder longName = new StringBuilder(ns.prefix("long")); + while (longName.length() <= 256) { + longName.append('a'); + } + + CustomProperty property = new CustomProperty(); + property.setName(longName.toString()); + property.setDescription("Length validation"); + property.setPropertyType(STRING_TYPE.getEntityReference()); + + assertThrows( + Exception.class, + () -> addCustomProperty(client, tableTypeId, property), + "Custom property name longer than 256 characters should be rejected"); + } + + @Test + void test_customPropertyNameUnbalancedBrackets_succeeds(TestNamespace ns) throws Exception { + OpenMetadataClient client = SdkClients.adminClient(); + UUID tableTypeId = TABLE_ENTITY_TYPE.getId(); + String prefix = ns.prefix("bracket"); + + String[] unbalancedNames = { + prefix + "openParen(", + prefix + "closeParen)", + prefix + "openLt<", + prefix + "closeGt>", + prefix + "openLbrack[", + prefix + "closeRbrack]", + prefix + "openLbrace{", + prefix + "closeRbrace}", + }; + + for (String name : unbalancedNames) { + CustomProperty property = new CustomProperty(); + property.setName(name); + property.setDescription("Unbalanced-bracket validation"); + property.setPropertyType(STRING_TYPE.getEntityReference()); + + Type updatedType = addCustomProperty(client, tableTypeId, property); + assertNotNull(updatedType); + + boolean present = + updatedType.getCustomProperties().stream().anyMatch(cp -> name.equals(cp.getName())); + assertTrue(present, "Unbalanced bracket name '" + name + "' should be saved"); + } + } + @Test void test_getEntityTypeFields() throws Exception { OpenMetadataClient client = SdkClients.adminClient(); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java index 469f2da1589b..e64b73dd5811 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java @@ -34,6 +34,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.locks.Lock; +import java.util.regex.Pattern; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.Triple; @@ -67,6 +68,10 @@ public class TypeRepository extends EntityRepository { private static final String PATCH_FIELDS = "customProperties"; private static final Striped TYPE_PROPERTY_LOCKS = Striped.lock(4096); + private static final Pattern CUSTOM_PROPERTY_NAME_PATTERN = + Pattern.compile("^[A-Za-z0-9][A-Za-z0-9 _\\-.,;/&%#@!'(){}\\[\\]<>|=]*$"); + private static final int CUSTOM_PROPERTY_NAME_MAX_LENGTH = 256; + public TypeRepository() { super( TypeResource.COLLECTION_PATH, @@ -213,6 +218,7 @@ private List getCustomProperties(Type type) { } private void validateProperty(CustomProperty customProperty) { + validateCustomPropertyName(customProperty.getName()); switch (customProperty.getPropertyType().getName()) { case "enum" -> validateEnumConfig(customProperty.getCustomPropertyConfig()); case "table-cp" -> validateTableTypeConfig(customProperty.getCustomPropertyConfig()); @@ -228,6 +234,26 @@ private void validateProperty(CustomProperty customProperty) { } } + private static void validateCustomPropertyName(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Custom property name must not be empty"); + } + if (name.length() > CUSTOM_PROPERTY_NAME_MAX_LENGTH) { + throw new IllegalArgumentException( + String.format( + "Custom property name '%s' exceeds maximum length of %d characters", + name, CUSTOM_PROPERTY_NAME_MAX_LENGTH)); + } + if (!CUSTOM_PROPERTY_NAME_PATTERN.matcher(name).matches()) { + throw new IllegalArgumentException( + String.format( + "Invalid custom property name '%s'. Name must start with an alphanumeric character " + + "and may contain only: alphanumeric, _ - . / & %% # @ ! , ; = | ' space ( ) < > [ ] { }. " + + "The following characters are not allowed: \" : ^ $", + name)); + } + } + private void validateDateFormat( CustomPropertyConfig config, Set validTokens, String errorMessage) { if (config != null) { diff --git a/openmetadata-spec/src/main/resources/json/schema/type/basic.json b/openmetadata-spec/src/main/resources/json/schema/type/basic.json index 37b6e0aa8f8f..dec5994a912f 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/basic.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/basic.json @@ -126,6 +126,13 @@ "maxLength": 256, "pattern": "^((?!::).)*$" }, + "customPropertyName": { + "description": "Name of a custom property. Allowed characters: alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { }. Must start with an alphanumeric character. Disallowed characters: \" : ^ $", + "type": "string", + "minLength": 1, + "maxLength": 256, + "pattern": "^[A-Za-z0-9][A-Za-z0-9 _\\-.,;/&%#@!'(){}\\[\\]<>|=]*$" + }, "testCaseEntityName": { "description": "Name that identifies a test definition and test case.", "type": "string", diff --git a/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json b/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json index 052e25586cf6..6a93a3a00af5 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json @@ -49,8 +49,8 @@ }, "properties": { "name": { - "description": "Name of the entity property. Note a property name must be unique for an entity. Property name must follow camelCase naming adopted by openMetadata - must start with lower case with no space, underscore, or dots.", - "$ref": "../type/basic.json#/definitions/entityName" + "description": "Name of the entity property. Must be unique for an entity. Allowed characters: alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { }. Must start with an alphanumeric character. Disallowed: \" : ^ $.", + "$ref": "../type/basic.json#/definitions/customPropertyName" }, "displayName": { "description": "Display Name for the custom property.Must be unique for an entity.", From d4c2013ff1b948a2fec86e8c886deb3e7a44c3ea Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 07:07:10 +0000 Subject: [PATCH 02/13] Update generated TypeScript types --- .../src/main/resources/ui/src/generated/entity/type.ts | 6 +++--- .../main/resources/ui/src/generated/type/customProperty.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/entity/type.ts b/openmetadata-ui/src/main/resources/ui/src/generated/entity/type.ts index 94f26411d9a7..dfe83fc1d1fb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/entity/type.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/entity/type.ts @@ -175,9 +175,9 @@ export interface CustomProperty { */ displayName?: string; /** - * Name of the entity property. Note a property name must be unique for an entity. Property - * name must follow camelCase naming adopted by openMetadata - must start with lower case - * with no space, underscore, or dots. + * Name of the entity property. Must be unique for an entity. Allowed characters: + * alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { }. Must start with an + * alphanumeric character. Disallowed: " : ^ $. */ name: string; propertyType: EntityReference; diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/type/customProperty.ts b/openmetadata-ui/src/main/resources/ui/src/generated/type/customProperty.ts index 618ded1dac49..63fbd8b0e3b3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/type/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/type/customProperty.ts @@ -21,9 +21,9 @@ export interface CustomProperty { */ displayName?: string; /** - * Name of the entity property. Note a property name must be unique for an entity. Property - * name must follow camelCase naming adopted by openMetadata - must start with lower case - * with no space, underscore, or dots. + * Name of the entity property. Must be unique for an entity. Allowed characters: + * alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { }. Must start with an + * alphanumeric character. Disallowed: " : ^ $. */ name: string; propertyType: EntityReference; From 9b7eb18d427d42add52c7b17bb940ff1dbddfcb7 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Wed, 29 Apr 2026 12:50:40 +0530 Subject: [PATCH 03/13] test: add schema-vs-Java consistency test for custom property name Guards against drift between basic.json#customPropertyName and the TypeRepository regex/length constants. If either side is updated without the other, CI fails with a message pointing to both files. The Java validator is kept (better error message + covers internal callers that bypass the HTTP layer); the consistency test guarantees the two definitions cannot drift. --- .../service/jdbi3/TypeRepository.java | 7 +- .../service/jdbi3/TypeRepositoryTest.java | 94 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java index e64b73dd5811..52f3dd6391c1 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java @@ -68,9 +68,12 @@ public class TypeRepository extends EntityRepository { private static final String PATCH_FIELDS = "customProperties"; private static final Striped TYPE_PROPERTY_LOCKS = Striped.lock(4096); - private static final Pattern CUSTOM_PROPERTY_NAME_PATTERN = + // Must stay in sync with definitions.customPropertyName.pattern in + // openmetadata-spec/src/main/resources/json/schema/type/basic.json + // Enforced by TypeRepositoryTest.customPropertyNamePatternMatchesSchema. + static final Pattern CUSTOM_PROPERTY_NAME_PATTERN = Pattern.compile("^[A-Za-z0-9][A-Za-z0-9 _\\-.,;/&%#@!'(){}\\[\\]<>|=]*$"); - private static final int CUSTOM_PROPERTY_NAME_MAX_LENGTH = 256; + static final int CUSTOM_PROPERTY_NAME_MAX_LENGTH = 256; public TypeRepository() { super( diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java new file mode 100644 index 000000000000..6258e1ffa8ee --- /dev/null +++ b/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openmetadata.service.jdbi3; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.InputStream; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link TypeRepository}. + * + *

The schema-vs-Java consistency test guarantees that the regex enforced in Java stays in lock + * step with the JSON Schema definition referenced by {@code customProperty.json}. If either side + * is updated without the other, this test fails with a message pointing at both files. + */ +class TypeRepositoryTest { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + private static final String BASIC_SCHEMA_RESOURCE = "json/schema/type/basic.json"; + private static final String CUSTOM_PROPERTY_NAME_DEF = "customPropertyName"; + + @Test + void customPropertyNamePatternMatchesSchema() throws Exception { + JsonNode schemaPattern = readDefinitionField(CUSTOM_PROPERTY_NAME_DEF, "pattern"); + assertNotNull( + schemaPattern, + "customPropertyName.pattern must exist in " + BASIC_SCHEMA_RESOURCE); + + assertEquals( + schemaPattern.asText(), + TypeRepository.CUSTOM_PROPERTY_NAME_PATTERN.pattern(), + "JSON Schema pattern in basic.json#/definitions/" + + CUSTOM_PROPERTY_NAME_DEF + + " has drifted from TypeRepository.CUSTOM_PROPERTY_NAME_PATTERN. " + + "Update both so the API and schema enforce the same rule."); + } + + @Test + void customPropertyNameMaxLengthMatchesSchema() throws Exception { + JsonNode schemaMax = readDefinitionField(CUSTOM_PROPERTY_NAME_DEF, "maxLength"); + assertNotNull( + schemaMax, + "customPropertyName.maxLength must exist in " + BASIC_SCHEMA_RESOURCE); + + assertEquals( + schemaMax.asInt(), + TypeRepository.CUSTOM_PROPERTY_NAME_MAX_LENGTH, + "JSON Schema maxLength has drifted from TypeRepository.CUSTOM_PROPERTY_NAME_MAX_LENGTH. " + + "Update both."); + } + + @Test + void customPropertyNameDefinitionIsRequiredString() throws Exception { + JsonNode definition = readDefinition(CUSTOM_PROPERTY_NAME_DEF); + assertNotNull(definition, "customPropertyName must be defined in " + BASIC_SCHEMA_RESOURCE); + assertEquals("string", definition.get("type").asText()); + assertTrue( + definition.get("minLength").asInt() >= 1, + "customPropertyName must have minLength >= 1"); + } + + private static JsonNode readDefinition(String definitionName) throws Exception { + try (InputStream in = + TypeRepositoryTest.class.getClassLoader().getResourceAsStream(BASIC_SCHEMA_RESOURCE)) { + assertNotNull(in, "Could not load resource " + BASIC_SCHEMA_RESOURCE); + JsonNode root = MAPPER.readTree(in); + return root.path("definitions").get(definitionName); + } + } + + private static JsonNode readDefinitionField(String definitionName, String field) + throws Exception { + JsonNode definition = readDefinition(definitionName); + return definition == null ? null : definition.get(field); + } +} From ec6582f6deac8765ecac351745887eba72b62a79 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Wed, 29 Apr 2026 15:53:06 +0530 Subject: [PATCH 04/13] fix: extend custom property name charset after gap-coverage matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-ran the matrix on previously-untested chars (+ ? * ~ ` \) across all 17 property types × create/patch/CSV/search: - + ? * ~ ` all pass cleanly on every operation × every property type — add to allow list - \ fails CSV roundtrip for entityReference and entityReferenceList types (escape inconsistency in CSV serialization) — add to block list Updates the regex, schema description, Java validator error message, and adds the new chars to the allow/block integration tests. Consistency unit tests in TypeRepositoryTest continue to pass. Final allow set: alphanumeric _ - . / & % # @ ! , ; = | ' + ? * ~ ` space ( ) < > [ ] { } Final block set: " : ^ $ \ --- .../org/openmetadata/it/tests/TypeResourceIT.java | 11 ++++++++++- .../openmetadata/service/jdbi3/TypeRepository.java | 7 ++++--- .../src/main/resources/json/schema/type/basic.json | 4 ++-- .../resources/json/schema/type/customProperty.json | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java index 8586568e1c76..2e63348c81e3 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java @@ -355,6 +355,11 @@ void test_customPropertyNameAllowedCharacters_succeeds(TestNamespace ns) throws prefix + "with]rbrack", prefix + "with{lbrace", prefix + "with}rbrace", + prefix + "with+plus", + prefix + "with?question", + prefix + "with*asterisk", + prefix + "with~tilde", + prefix + "with`backtick", prefix + "withMatched(pair)And", prefix + "withDigits123", }; @@ -381,7 +386,11 @@ void test_customPropertyNameDisallowedCharacters_fails(TestNamespace ns) { String prefix = ns.prefix("bad"); String[] disallowedNames = { - prefix + "with\"dquote", prefix + "with:colon", prefix + "with^caret", prefix + "with$dollar", + prefix + "with\"dquote", + prefix + "with:colon", + prefix + "with^caret", + prefix + "with$dollar", + prefix + "with\\backslash", }; for (String name : disallowedNames) { diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java index 52f3dd6391c1..2cb0858e7fef 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java @@ -72,7 +72,7 @@ public class TypeRepository extends EntityRepository { // openmetadata-spec/src/main/resources/json/schema/type/basic.json // Enforced by TypeRepositoryTest.customPropertyNamePatternMatchesSchema. static final Pattern CUSTOM_PROPERTY_NAME_PATTERN = - Pattern.compile("^[A-Za-z0-9][A-Za-z0-9 _\\-.,;/&%#@!'(){}\\[\\]<>|=]*$"); + Pattern.compile("^[A-Za-z0-9][A-Za-z0-9 _\\-.,;/&%#@!'(){}\\[\\]<>|=+?*~`]*$"); static final int CUSTOM_PROPERTY_NAME_MAX_LENGTH = 256; public TypeRepository() { @@ -251,8 +251,9 @@ private static void validateCustomPropertyName(String name) { throw new IllegalArgumentException( String.format( "Invalid custom property name '%s'. Name must start with an alphanumeric character " - + "and may contain only: alphanumeric, _ - . / & %% # @ ! , ; = | ' space ( ) < > [ ] { }. " - + "The following characters are not allowed: \" : ^ $", + + "and may contain only: alphanumeric, _ - . / & %% # @ ! , ; = | ' + ? * ~ ` " + + "space ( ) < > [ ] { }. " + + "The following characters are not allowed: \" : ^ $ \\", name)); } } diff --git a/openmetadata-spec/src/main/resources/json/schema/type/basic.json b/openmetadata-spec/src/main/resources/json/schema/type/basic.json index dec5994a912f..38bdc4f05f9d 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/basic.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/basic.json @@ -127,11 +127,11 @@ "pattern": "^((?!::).)*$" }, "customPropertyName": { - "description": "Name of a custom property. Allowed characters: alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { }. Must start with an alphanumeric character. Disallowed characters: \" : ^ $", + "description": "Name of a custom property. Allowed characters: alphanumeric, _ - . / & % # @ ! , ; = | ' + ? * ~ ` space ( ) < > [ ] { }. Must start with an alphanumeric character. Disallowed characters: \" : ^ $ \\", "type": "string", "minLength": 1, "maxLength": 256, - "pattern": "^[A-Za-z0-9][A-Za-z0-9 _\\-.,;/&%#@!'(){}\\[\\]<>|=]*$" + "pattern": "^[A-Za-z0-9][A-Za-z0-9 _\\-.,;/&%#@!'(){}\\[\\]<>|=+?*~`]*$" }, "testCaseEntityName": { "description": "Name that identifies a test definition and test case.", diff --git a/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json b/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json index 6a93a3a00af5..6bbb28708648 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json @@ -49,7 +49,7 @@ }, "properties": { "name": { - "description": "Name of the entity property. Must be unique for an entity. Allowed characters: alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { }. Must start with an alphanumeric character. Disallowed: \" : ^ $.", + "description": "Name of the entity property. Must be unique for an entity. Allowed characters: alphanumeric, _ - . / & % # @ ! , ; = | ' + ? * ~ ` space ( ) < > [ ] { }. Must start with an alphanumeric character. Disallowed: \" : ^ $ \\.", "$ref": "../type/basic.json#/definitions/customPropertyName" }, "displayName": { From 452bc2545a7b21a1d8adea6aa1da6b898a1581fe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 10:27:43 +0000 Subject: [PATCH 05/13] Update generated TypeScript types --- .../src/main/resources/ui/src/generated/entity/type.ts | 4 ++-- .../main/resources/ui/src/generated/type/customProperty.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/entity/type.ts b/openmetadata-ui/src/main/resources/ui/src/generated/entity/type.ts index dfe83fc1d1fb..ba71e11b3c27 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/entity/type.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/entity/type.ts @@ -176,8 +176,8 @@ export interface CustomProperty { displayName?: string; /** * Name of the entity property. Must be unique for an entity. Allowed characters: - * alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { }. Must start with an - * alphanumeric character. Disallowed: " : ^ $. + * alphanumeric, _ - . / & % # @ ! , ; = | ' + ? * ~ ` space ( ) < > [ ] { }. Must start + * with an alphanumeric character. Disallowed: " : ^ $ \. */ name: string; propertyType: EntityReference; diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/type/customProperty.ts b/openmetadata-ui/src/main/resources/ui/src/generated/type/customProperty.ts index 63fbd8b0e3b3..739171682f29 100644 --- a/openmetadata-ui/src/main/resources/ui/src/generated/type/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/src/generated/type/customProperty.ts @@ -22,8 +22,8 @@ export interface CustomProperty { displayName?: string; /** * Name of the entity property. Must be unique for an entity. Allowed characters: - * alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { }. Must start with an - * alphanumeric character. Disallowed: " : ^ $. + * alphanumeric, _ - . / & % # @ ! , ; = | ' + ? * ~ ` space ( ) < > [ ] { }. Must start + * with an alphanumeric character. Disallowed: " : ^ $ \. */ name: string; propertyType: EntityReference; From 9aba5b3f702cf5642ce57c47aee643a774a5e989 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Wed, 29 Apr 2026 17:31:16 +0530 Subject: [PATCH 06/13] updated the custom property name validation --- .../ui/playwright/constant/customProperty.ts | 12 +- .../e2e/Pages/CustomProperties.spec.ts | 103 +++++++++++++++++- .../ui/playwright/utils/customProperty.ts | 6 +- .../AddCustomProperty/AddCustomProperty.tsx | 12 +- .../ui/src/constants/regex.constants.ts | 9 ++ .../ui/src/locale/languages/ar-sa.json | 2 +- .../ui/src/locale/languages/de-de.json | 2 +- .../ui/src/locale/languages/en-us.json | 2 +- .../ui/src/locale/languages/es-es.json | 2 +- .../ui/src/locale/languages/fr-fr.json | 2 +- .../ui/src/locale/languages/gl-es.json | 2 +- .../ui/src/locale/languages/he-he.json | 2 +- .../ui/src/locale/languages/ja-jp.json | 2 +- .../ui/src/locale/languages/ko-kr.json | 2 +- .../ui/src/locale/languages/mr-in.json | 2 +- .../ui/src/locale/languages/nl-nl.json | 2 +- .../ui/src/locale/languages/pr-pr.json | 2 +- .../ui/src/locale/languages/pt-br.json | 2 +- .../ui/src/locale/languages/pt-pt.json | 2 +- .../ui/src/locale/languages/ru-ru.json | 2 +- .../ui/src/locale/languages/th-th.json | 2 +- .../ui/src/locale/languages/tr-tr.json | 2 +- .../ui/src/locale/languages/zh-cn.json | 2 +- .../ui/src/locale/languages/zh-tw.json | 2 +- 24 files changed, 152 insertions(+), 28 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts index 88c7781b38de..11928edf9832 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts @@ -213,4 +213,14 @@ export const CUSTOM_PROPERTIES_ENTITIES = { }; export const CUSTOM_PROPERTY_NAME_VALIDATION_ERROR = - "Name must not contain '::'."; + 'Name must start with a letter or number. Invalid characters: " : ^ $'; + +export const CUSTOM_PROPERTY_INVALID_NAMES = { + STARTS_WITH_SPECIAL_CHAR: '_invalidName', + DISALLOWED_COLON: 'name:with:colon', + DISALLOWED_DOLLAR: 'name$invalid', + DISALLOWED_CARET: 'name^invalid', + DISALLOWED_QUOTE: 'name"invalid', +}; + +export const NAME_SUFFIX = ".!@#%`&*()_-=+{}[]~|\\;'<>,.?/"; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts index 82cdfaebad5a..d5a3a4caf81f 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts @@ -26,7 +26,16 @@ */ import { APIRequestContext, expect, test } from '@playwright/test'; -import { CUSTOM_PROPERTIES_ENTITIES } from '../../constant/customProperty'; +import { + INVALID_NAMES, + NAME_MAX_LENGTH_VALIDATION_ERROR, +} from '../../constant/common'; +import { + CUSTOM_PROPERTIES_ENTITIES, + CUSTOM_PROPERTY_INVALID_NAMES, + CUSTOM_PROPERTY_NAME_VALIDATION_ERROR, + NAME_SUFFIX, +} from '../../constant/customProperty'; import { CP_BASE_VALUES, CP_PARTIAL_SEARCH_VALUES, @@ -352,7 +361,7 @@ ALL_ENTITIES.forEach(({ key, makeInstance }) => { BASIC_PROPERTIES.forEach((property) => { test(property, async ({ page }) => { test.slow(); - const propertyName = `cp-${uuid()}-${entity.name}`; + const propertyName = `cp-${uuid()}-${entity.name}${NAME_SUFFIX}`; await settingClick( page, @@ -387,7 +396,7 @@ ALL_ENTITIES.forEach(({ key, makeInstance }) => { CONFIG_PROPERTIES.forEach((propertyConfig) => { test(propertyConfig.name, async ({ page }) => { test.slow(); - const propertyName = `cp-${uuid()}-${entity.name}`; + const propertyName = `cp-${uuid()}-${entity.name}${NAME_SUFFIX}`; await settingClick( page, @@ -3528,3 +3537,91 @@ ALL_ENTITIES.forEach(({ key, makeInstance }) => { } }); }); + +test.describe('Custom property name validation', () => { + test.use({ storageState: 'playwright/.auth/admin.json' }); + + test.beforeEach(async ({ page }) => { + await redirectToHomePage(page); + await settingClick(page, GlobalSettingOptions.TABLES, true); + await page.click('[data-testid="add-field-button"]'); + }); + + const nameInput = '[data-testid="name"] input'; + const nameError = '#name_help'; + + test('should show error when name starts with a non-alphanumeric character', async ({ + page, + }) => { + await page.fill( + nameInput, + CUSTOM_PROPERTY_INVALID_NAMES.STARTS_WITH_SPECIAL_CHAR + ); + + await expect(page.locator(nameError)).toContainText( + CUSTOM_PROPERTY_NAME_VALIDATION_ERROR + ); + }); + + test('should show error when name contains a colon', async ({ page }) => { + await page.fill(nameInput, CUSTOM_PROPERTY_INVALID_NAMES.DISALLOWED_COLON); + + await expect(page.locator(nameError)).toContainText( + CUSTOM_PROPERTY_NAME_VALIDATION_ERROR + ); + }); + + test('should show error when name contains a dollar sign', async ({ + page, + }) => { + await page.fill(nameInput, CUSTOM_PROPERTY_INVALID_NAMES.DISALLOWED_DOLLAR); + + await expect(page.locator(nameError)).toContainText( + CUSTOM_PROPERTY_NAME_VALIDATION_ERROR + ); + }); + + test('should show error when name contains a caret', async ({ page }) => { + await page.fill(nameInput, CUSTOM_PROPERTY_INVALID_NAMES.DISALLOWED_CARET); + + await expect(page.locator(nameError)).toContainText( + CUSTOM_PROPERTY_NAME_VALIDATION_ERROR + ); + }); + + test('should show error when name contains a double quote', async ({ + page, + }) => { + await page.fill(nameInput, CUSTOM_PROPERTY_INVALID_NAMES.DISALLOWED_QUOTE); + + await expect(page.locator(nameError)).toContainText( + CUSTOM_PROPERTY_NAME_VALIDATION_ERROR + ); + }); + + test('should accept a valid name starting with a letter', async ({ + page, + }) => { + await page.fill(nameInput, 'validName_123'); + + await expect(page.locator(nameError)).not.toBeVisible(); + }); + + test('should accept a valid name with allowed special characters', async ({ + page, + }) => { + await page.fill(nameInput, "valid Name_-./&%#@!,;=|'()<>[]{}"); + + await expect(page.locator(nameError)).not.toBeVisible(); + }); + + test('should show error when name exceeds 128 characters', async ({ + page, + }) => { + await page.fill(nameInput, INVALID_NAMES.MAX_LENGTH); + + await expect(page.locator(nameError)).toContainText( + NAME_MAX_LENGTH_VALIDATION_ERROR + ); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts index c2e1040d6860..45d7e2134147 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts @@ -11,15 +11,15 @@ * limitations under the License. */ import { APIRequestContext, expect, Page } from '@playwright/test'; -import { INVALID_NAMES } from '../constant/common'; import { + CUSTOM_PROPERTY_INVALID_NAMES, CUSTOM_PROPERTY_NAME_VALIDATION_ERROR, ENTITY_REFERENCE_PROPERTIES, } from '../constant/customProperty'; import { SidebarItem } from '../constant/sidebar'; import { - EntityTypeEndpoint, ENTITY_PATH, + EntityTypeEndpoint, } from '../support/entity/Entity.interface'; import { UserClass } from '../support/user/UserClass'; import { selectOption, showAdvancedSearchDialog } from './advancedSearch'; @@ -660,7 +660,7 @@ export const addCustomPropertiesForEntity = async ({ // Validation check — only '::' is blocked await page.fill( '[data-testid="name"] input', - INVALID_NAMES.WITH_SPECIAL_CHARS + CUSTOM_PROPERTY_INVALID_NAMES.DISALLOWED_COLON ); await expect(page.locator('#name_help')).toContainText( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx index 2a0385abf6c7..5f7ef2030294 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx @@ -30,7 +30,7 @@ import { TABLE_TYPE_CUSTOM_PROPERTY, } from '../../../../constants/CustomProperty.constants'; import { GlobalSettingsMenuCategory } from '../../../../constants/GlobalSettings.constants'; -import { ENTITY_NAME_REGEX } from '../../../../constants/regex.constants'; +import { CUSTOM_PROPERTY_NAME_REGEX } from '../../../../constants/regex.constants'; import { CUSTOM_PROPERTY_CATEGORY, OPEN_METADATA, @@ -270,7 +270,15 @@ const AddCustomProperty = ({ placeholder: t('label.name'), rules: [ { - pattern: ENTITY_NAME_REGEX, + max: 128, + message: t('message.entity-size-in-between', { + entity: t('label.name'), + min: 1, + max: 128, + }), + }, + { + pattern: CUSTOM_PROPERTY_NAME_REGEX, message: t('message.custom-property-name-validation'), }, ], diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index be9ee2e167f7..91ed23028a11 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -22,6 +22,15 @@ export const EMAIL_REG_EX = /^\S+@\S+\.\S+$/; */ export const ENTITY_NAME_REGEX = /^((?!::).)*$/; +/** + * Custom property name validation: + * - Must start with an alphanumeric character + * - Allowed characters: alphanumeric, _ - . / & % # @ ! , ; = | ' space ( ) < > [ ] { } + * - Disallowed: " : ^ $ + */ +export const CUSTOM_PROPERTY_NAME_REGEX = + /^[A-Za-z0-9][A-Za-z0-9 _\-.,;/&%#@!'(){}\[\]<>|=+?*~`\\]*$/; + /** * Matches any string that does NOT contain the following: * - Double colon (::) diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json index 5b43047a774f..c142f4fe7dd6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "مسار رابط شعار شريط التنقل.", "custom-properties-description": "التقط بيانات وصفية مخصصة لإثراء أصول البيانات الخاصة بك عن طريق توسيع السمات.", "custom-property-is-set-to-message": "تم تعيين {{fieldName}} إلى", - "custom-property-name-validation": "يجب ألا يحتوي الاسم على '::'.", + "custom-property-name-validation": "يجب أن يبدأ الاسم بحرف أو رقم. أحرف غير صالحة: \" : ^ $", "custom-property-update": "تحديث الخاصية المخصصة '{{propertyName}}' في {{entityName}} هو {{status}}", "customize-brand-description": "قم بتخصيص تجربة مستخدم {{brandName}} لتناسب احتياجات مؤسستك وفريقك.", "customize-entity-landing-page-header-for-persona": "تخصيص تجربة صفحة كيان {{entity}} لشخصية <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index a91ced00fd42..3e00fc63ac52 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "URL-Pfad für das Navbar-Logo.", "custom-properties-description": "Erfassen Sie benutzerdefinierte Metadaten, um Ihre Datenobjekte durch die Erweiterung der Attribute zu bereichern.", "custom-property-is-set-to-message": "{{fieldName}} ist gesetzt auf", - "custom-property-name-validation": "Der Name darf nicht '::' enthalten.", + "custom-property-name-validation": "Der Name muss mit einem Buchstaben oder einer Zahl beginnen. Ungültige Zeichen: \" : ^ $", "custom-property-update": "Aktualisierung der benutzerdefinierten Eigenschaft '{{propertyName}}' in {{entityName}} ist {{status}}", "customize-brand-description": "Passen Sie die {{brandName}}-Benutzeroberfläche an Ihre Organisations- und Teambedürfnisse an.", "customize-entity-landing-page-header-for-persona": "Personalisieren Sie die {{entity}}-Entitätsseite für die <0>{{persona}}-Persona", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 7450d715d8e9..766f2f02d9ed 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "URL path for the navbar logo.", "custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.", "custom-property-is-set-to-message": "{{fieldName}} is set to", - "custom-property-name-validation": "Name must not contain '::'.", + "custom-property-name-validation": "Name must start with a letter or number. Invalid characters: \" : ^ $", "custom-property-update": "Custom property '{{propertyName}}' update in {{entityName}} is {{status}}", "customize-brand-description": "Tailor the {{brandName}} UX to suit your organizational and team needs.", "customize-entity-landing-page-header-for-persona": "Personalize the {{entity}} Entity Page experience for the <0>{{persona}} persona", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index ed02fda4f9f7..f07756505221 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Ruta URL para el logotipo de la barra de navegación.", "custom-properties-description": "Captura metadatos personalizados para enriquecer tus activos de datos mediante la extensión de los atributos.", "custom-property-is-set-to-message": "{{fieldName}} está configurada para", - "custom-property-name-validation": "El nombre no debe contener '::'.", + "custom-property-name-validation": "El nombre debe comenzar con una letra o número. Caracteres inválidos: \" : ^ $", "custom-property-update": "La actualización de la propiedad personalizada '{{propertyName}}' en {{entityName}} está {{status}}", "customize-brand-description": "Adapte la experiencia de usuario de {{brandName}} para satisfacer las necesidades de su organización y equipo.", "customize-entity-landing-page-header-for-persona": "Personaliza la experiencia de la Página de Entidad {{entity}} para la persona <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index 105cccf16d1a..23d4e47e4186 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Chemin (URL) du logo de la barre de navigation.", "custom-properties-description": " Capturer des métadonnées personnalisées pour enrichir vos actifs de données en étendant leurs attributs.", "custom-property-is-set-to-message": "{{fieldName}} est réglé sur", - "custom-property-name-validation": "Le nom ne doit pas contenir '::'.", + "custom-property-name-validation": "Le nom doit commencer par une lettre ou un chiffre. Caractères invalides : \" : ^ $", "custom-property-update": "La mise à jour de la propriété personnalisée '{{propertyName}}' dans {{entityName}} est {{status}}", "customize-brand-description": "Ajustez l'expérience utilisateur d'{{brandName}} pour répondre à vos besoins d'équipe et d'organisation.", "customize-entity-landing-page-header-for-persona": "Personnalisez l'expérience de la page d'entité {{entity}} pour la persona <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json index aafe53aa7494..6904b8d27041 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Ruta URL para o logotipo da barra de navegación.", "custom-properties-description": "Captura metadatos personalizados para enriquecer os teus activos de datos estendendo os atributos.", "custom-property-is-set-to-message": "{{fieldName}} está configurado para", - "custom-property-name-validation": "O nome non debe conter '::'.", + "custom-property-name-validation": "O nome debe comezar por letra ou número. Caracteres inválidos: \" : ^ $", "custom-property-update": "A actualización da propiedade personalizada '{{propertyName}}' en {{entityName}} está {{status}}", "customize-brand-description": "Adapta a experiencia de usuario de {{brandName}} ás necesidades da túa organización e equipo.", "customize-entity-landing-page-header-for-persona": "Personaliza a experiencia da páxina de entidade {{entity}} para a persoa <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 7ea178c14606..4d422189fb2b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "נתיב URL עבור לוגו סרגל הניווט.", "custom-properties-description": " לכוד מטא-דאטה מותאמים אישית להעשרת נכסי הנתונים שלך על ידי הרחבת התכונות.", "custom-property-is-set-to-message": "{{fieldName}} מוגדר ל", - "custom-property-name-validation": "השם אינו יכול להכיל '::'.", + "custom-property-name-validation": "השם חייב להתחיל באות או במספר. תווים לא חוקיים: \" : ^ $", "custom-property-update": "עדכון המאפיין המותאם אישית '{{propertyName}}' ב{{entityName}} הוא {{status}}", "customize-brand-description": "התאם את חוויית המשתמש של {{brandName}} לצרכי הארגון והצוות שלך.", "customize-entity-landing-page-header-for-persona": "התאם אישית את חוויית דף הישות של {{entity}} עבור הפרסונה <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index b26d58fe9a94..08d2b2c4809a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "ナビゲーションバーのロゴ用の URL パス", "custom-properties-description": "属性を拡張して、データアセットに独自のメタデータを追加できます。", "custom-property-is-set-to-message": "{{fieldName}} に設定されています", - "custom-property-name-validation": "名前に '::' を含めることはできません。", + "custom-property-name-validation": "名前は文字または数字で始まる必要があります。無効な文字: \" : ^ $", "custom-property-update": "カスタムプロパティ '{{propertyName}}' の {{entityName}} に対する更新は {{status}} です", "customize-brand-description": "{{brandName}} の UX を組織やチームのニーズに合わせて調整します。", "customize-entity-landing-page-header-for-persona": "{{entity}} エンティティページの体験を <0>{{persona}} ペルソナ向けにパーソナライズします", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json index 482a0a2a4efe..c07cb281dd55 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "내비게이션 바 로고의 URL 경로입니다.", "custom-properties-description": "속성을 확장하여 데이터 자산을 풍부하게 하는 사용자 정의 메타데이터를 캡처합니다.", "custom-property-is-set-to-message": "{{fieldName}}이(가) 다음으로 설정되었습니다", - "custom-property-name-validation": "이름에는 '::'가 포함되어서는 안 됩니다.", + "custom-property-name-validation": "이름은 문자나 숫자로 시작해야 합니다. 잘못된 문자: \" : ^ $", "custom-property-update": "{{entityName}}의 사용자 정의 속성 '{{propertyName}}' 업데이트가 {{status}}입니다", "customize-brand-description": "조직 및 팀의 요구에 맞게 {{brandName}} UX를 맞춤 설정하세요.", "customize-entity-landing-page-header-for-persona": "<0>{{persona}} 페르소나를 위한 {{entity}} 엔티티 페이지 경험을 개인화하세요.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json index 1756d50bca42..3ee7b769f1eb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "नेव्हबार लोगो साठी URL पथ.", "custom-properties-description": "तुमच्या डेटा ॲसेटंना समृद्ध करण्यासाठी सानुकूल मेटाडेटा कॅप्चर करा.", "custom-property-is-set-to-message": "{{fieldName}} सेट केले आहे", - "custom-property-name-validation": "नावामध्ये '::' नसावे.", + "custom-property-name-validation": "नाव अक्षर किंवा अंकाने सुरू झाले पाहिजे. अवैध वर्ण: \" : ^ $", "custom-property-update": "सानुकूल गुणधर्म '{{propertyName}}' चे {{entityName}} मधील अद्यतन {{status}} आहे", "customize-brand-description": "तुमच्या संस्थात्मक आणि टीमच्या गरजांसाठी {{brandName}} UX सानुकूलित करा.", "customize-entity-landing-page-header-for-persona": "{{persona}} व्यक्तिमत्वासाठी {{entity}} घटक पृष्ठ अनुभव वैयक्तिकृत करा", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index 369f0d290be0..cbdaa64733c1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "URL-pad voor het navigatiebalk-logo.", "custom-properties-description": "Leg aangepaste metadata vast om uw data-assets te verrijken door het uitbreiden van de attributen.", "custom-property-is-set-to-message": "{{fieldName}} is set to", - "custom-property-name-validation": "De naam mag geen '::' bevatten.", + "custom-property-name-validation": "De naam moet beginnen met een letter of cijfer. Ongeldige tekens: \" : ^ $", "custom-property-update": "Aangepaste eigenschap '{{propertyName}}' update in {{entityName}} is {{status}}", "customize-brand-description": "De {{brandName}} UX aanpassen aan de behoeften van uw organisatie en team.", "customize-entity-landing-page-header-for-persona": "Personaliseer de {{entity}} entiteitspagina-ervaring voor de <0>{{persona}} persona", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json index ab8af6f31a10..7178949f8869 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "مسیر URL برای لوگوی نوار ناوبری.", "custom-properties-description": "متادیتای سفارشی را برای غنی‌سازی دارایی‌های داده‌ای خود با افزودن ویژگی‌های اضافی ضبط کنید.", "custom-property-is-set-to-message": "{{fieldName}} به مقدار", - "custom-property-name-validation": "نام نباید شامل '::' باشد.", + "custom-property-name-validation": "نام باید با یک حرف یا عدد شروع شود. نویسه‌های نامعتبر: \" : ^ $", "custom-property-update": "بروزرسانی ویژگی سفارشی '{{propertyName}}' در {{entityName}} {{status}} است", "customize-brand-description": "تجربه کاربری {{brandName}} را برای نیازهای سازمانی و تیمی خود تنظیم کنید.", "customize-entity-landing-page-header-for-persona": "تجربه صفحه موجودیت {{entity}} را برای شخصیت <0>{{persona}} شخصی‌سازی کنید", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index aefa97c83b9d..dedb6ebea9d9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Caminho da URL para o logotipo da barra de navegação.", "custom-properties-description": " Capture metadados personalizados para enriquecer seus ativos de dados ao estender os atributos.", "custom-property-is-set-to-message": "{{fieldName}} está definido para", - "custom-property-name-validation": "O nome não deve conter '::'.", + "custom-property-name-validation": "O nome deve começar com uma letra ou número. Caracteres inválidos: \" : ^ $", "custom-property-update": "Atualização da propriedade personalizada '{{propertyName}}' em {{entityName}} está {{status}}", "customize-brand-description": "Personalize o layout de {{brandName}} para atender às suas necessidades organizacionais e de equipe.", "customize-entity-landing-page-header-for-persona": "Personalize a experiência da Página de Entidade {{entity}} para a persona <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json index a3c3b74b2401..c290f270b8ad 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Caminho da URL para o logotipo da barra de navegação.", "custom-properties-description": " Capture metadados personalizados para enriquecer os seus ativos de dados estendendo os atributos.", "custom-property-is-set-to-message": "{{fieldName}} está definido como", - "custom-property-name-validation": "O nome não deve conter '::'.", + "custom-property-name-validation": "O nome deve começar com uma letra ou número. Caracteres inválidos: \" : ^ $", "custom-property-update": "A atualização da propriedade personalizada '{{propertyName}}' em {{entityName}} está {{status}}", "customize-brand-description": "Personalize a experiência {{brandName}} para se adequar às necessidades da sua organização e equipa.", "customize-entity-landing-page-header-for-persona": "Personalize a experiência da Página da Entidade {{entity}} para a persona <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index 7da771bc5afc..7b1576aa911f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "URL-путь для логотипа панели навигации.", "custom-properties-description": "Создавайте и настраивайте новые поля для объектов", "custom-property-is-set-to-message": "Значение поля {{fieldName}} изменено на", - "custom-property-name-validation": "Имя не должно содержать '::'.", + "custom-property-name-validation": "Имя должно начинаться с буквы или цифры. Недопустимые символы: \" : ^ $", "custom-property-update": "Обновление дополнительного поля '{{propertyName}}' в {{entityName}} в статусе {{status}}", "customize-brand-description": "Настройте UX {{brandName}} под ваши потребности", "customize-entity-landing-page-header-for-persona": "Персонализируйте страницу объекта «{{entity}}» для персоны <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json index 4cf40d1127de..642530cb7661 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "เส้นทาง URL สำหรับโลโก้ในแถบด้านบน", "custom-properties-description": "จับข้อมูลเมตาที่กำหนดเองเพื่อเสริมสินทรัพย์ข้อมูลของคุณโดยการขยายคุณสมบัติ", "custom-property-is-set-to-message": "{{fieldName}} ถูกตั้งค่าเป็น", - "custom-property-name-validation": "ชื่อต้องไม่มี '::'", + "custom-property-name-validation": "ชื่อต้องขึ้นต้นด้วยตัวอักษรหรือตัวเลข ตัวอักษรที่ไม่ถูกต้อง: \" : ^ $", "custom-property-update": "การอัปเดตคุณสมบัติที่กำหนดเอง '{{propertyName}}' ใน {{entityName}} คือ {{status}}", "customize-brand-description": "ปรับแต่ง UX ของ {{brandName}} ให้เหมาะสมกับความต้องการขององค์กรและทีมของคุณ", "customize-entity-landing-page-header-for-persona": "ปรับแต่งประสบการณ์หน้าเอนทิตี {{entity}} สำหรับบุคคล <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json index 5b6cdfc8ac08..1f251d6f381b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Gezinme çubuğu logosu için URL yolu.", "custom-properties-description": "Nitelikleri genişleterek veri varlıklarınızı zenginleştirmek için özel metadata yakalayın.", "custom-property-is-set-to-message": "{{fieldName}} şuna ayarlandı:", - "custom-property-name-validation": "İsim '::' içermemelidir.", + "custom-property-name-validation": "İsim bir harf veya sayı ile başlamalıdır. Geçersiz karakterler: \" : ^ $", "custom-property-update": "{{entityName}} içindeki '{{propertyName}}' özel özelliği güncellemesi {{status}}", "customize-brand-description": "{{brandName}} kullanıcı deneyimini kurumsal ve ekip ihtiyaçlarınıza göre uyarlayın.", "customize-entity-landing-page-header-for-persona": "<0>{{persona}} personası için {{entity}} Varlık Sayfası deneyimini kişiselleştirin", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 31483e5bd577..671e5dc40b73 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "导航栏 Logo 指向的 URL 地址", "custom-properties-description": " 获取自定义元数据, 通过扩展属性来丰富数据资产", "custom-property-is-set-to-message": "{{fieldName}}设置为", - "custom-property-name-validation": "名称不能包含 '::'。", + "custom-property-name-validation": "名称必须以字母或数字开头。无效字符:\" : ^ $", "custom-property-update": "自定义属性'{{propertyName}}'在{{entityName}}中的更新{{status}}", "customize-brand-description": "自定义 {{brandName}}, 以满足您的组织和团队需求", "customize-entity-landing-page-header-for-persona": "为<0>{{persona}}角色个性化{{entity}}实体页面体验", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json index eb5b9b291336..54ab28ddee70 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "導覽列標誌的 URL 路徑。", "custom-properties-description": "透過擴充屬性來擷取自訂元資料,以豐富您的資料資產。", "custom-property-is-set-to-message": "{{fieldName}} 已設定為", - "custom-property-name-validation": "名稱不能包含 '::'。", + "custom-property-name-validation": "名稱必須以字母或數字開頭。無效字元:\" : ^ $", "custom-property-update": "{{entityName}} 中的自訂屬性 '{{propertyName}}' 更新 {{status}}", "customize-brand-description": "量身打造 {{brandName}} 的使用者體驗,以滿足您的組織和團隊需求。", "customize-entity-landing-page-header-for-persona": "為 <0>{{persona}} 角色個人化 {{entity}} 實體頁面體驗", From 021647a0edcb289a2440fed078f2a3cf5ac81038 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Wed, 29 Apr 2026 19:31:20 +0530 Subject: [PATCH 07/13] added name suffix in custom property name --- .../ui/playwright/e2e/Pages/CustomProperties.spec.ts | 4 ++-- .../src/main/resources/ui/playwright/utils/customProperty.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts index d5a3a4caf81f..2cbf610e4463 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts @@ -276,11 +276,11 @@ ALL_ENTITIES.forEach(({ key, makeInstance }) => { createdCPData: [], }; const propertyNames: Record = {}; - const dashboardSearchPropertyName = `cp-${uuid()}-${entity.name}`; + const dashboardSearchPropertyName = `cp-${uuid()}-${entity.name}${NAME_SUFFIX}`; const dashboardPropertyValue = `EXECUTIVE_DASHBOARD_${uuid()}`; // Pipeline-specific state - const pipelineSearchPropertyName = `cp-${uuid()}-${entity.name}`; + const pipelineSearchPropertyName = `cp-${uuid()}-${entity.name}${NAME_SUFFIX}`; const pipelinePropertyValue = `ETL_PRODUCTION_${uuid()}`; test.beforeAll(async ({ browser }) => { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts index 45d7e2134147..35f61403b30a 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts @@ -15,6 +15,7 @@ import { CUSTOM_PROPERTY_INVALID_NAMES, CUSTOM_PROPERTY_NAME_VALIDATION_ERROR, ENTITY_REFERENCE_PROPERTIES, + NAME_SUFFIX, } from '../constant/customProperty'; import { SidebarItem } from '../constant/sidebar'; import { @@ -530,7 +531,7 @@ export const createCustomPropertyForEntity = async ( }; for (const item of propertyList) { - const customPropertyName = `cp-${item.name}-${uuid()}`; + const customPropertyName = `cp-${item.name}-${uuid()}${NAME_SUFFIX}`; const payload = { name: customPropertyName, description: customPropertyName, From a68bd03437cef68497b7495cf8dfd5902ed45ac4 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Wed, 29 Apr 2026 19:47:37 +0530 Subject: [PATCH 08/13] lint fixes --- .../ui/playwright/e2e/Pages/CustomProperties.spec.ts | 8 ++++++-- .../main/resources/ui/playwright/utils/customProperty.ts | 2 +- .../main/resources/ui/src/constants/regex.constants.ts | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts index 2cbf610e4463..f47e02ae1e44 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts @@ -276,11 +276,15 @@ ALL_ENTITIES.forEach(({ key, makeInstance }) => { createdCPData: [], }; const propertyNames: Record = {}; - const dashboardSearchPropertyName = `cp-${uuid()}-${entity.name}${NAME_SUFFIX}`; + const dashboardSearchPropertyName = `cp-${uuid()}-${ + entity.name + }${NAME_SUFFIX}`; const dashboardPropertyValue = `EXECUTIVE_DASHBOARD_${uuid()}`; // Pipeline-specific state - const pipelineSearchPropertyName = `cp-${uuid()}-${entity.name}${NAME_SUFFIX}`; + const pipelineSearchPropertyName = `cp-${uuid()}-${ + entity.name + }${NAME_SUFFIX}`; const pipelinePropertyValue = `ETL_PRODUCTION_${uuid()}`; test.beforeAll(async ({ browser }) => { diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts index 35f61403b30a..de4960716090 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts @@ -19,8 +19,8 @@ import { } from '../constant/customProperty'; import { SidebarItem } from '../constant/sidebar'; import { - ENTITY_PATH, EntityTypeEndpoint, + ENTITY_PATH, } from '../support/entity/Entity.interface'; import { UserClass } from '../support/user/UserClass'; import { selectOption, showAdvancedSearchDialog } from './advancedSearch'; diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index 91ed23028a11..d71b3557f73a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -29,7 +29,7 @@ export const ENTITY_NAME_REGEX = /^((?!::).)*$/; * - Disallowed: " : ^ $ */ export const CUSTOM_PROPERTY_NAME_REGEX = - /^[A-Za-z0-9][A-Za-z0-9 _\-.,;/&%#@!'(){}\[\]<>|=+?*~`\\]*$/; + /^[A-Za-z0-9][A-Za-z0-9 _\-.,;/&%#@!'(){}[\]<>|=+?*~`\\]*$/; /** * Matches any string that does NOT contain the following: From 4f033ea37670b782713e3c73ee35f8838d30860a Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Thu, 30 Apr 2026 11:47:00 +0530 Subject: [PATCH 09/13] include backslash in invalid char Co-authored-by: Copilot --- .../ui/playwright/constant/customProperty.ts | 5 +++-- .../ui/playwright/e2e/Pages/CustomProperties.spec.ts | 11 +++++++++++ .../resources/ui/src/constants/regex.constants.ts | 2 +- .../main/resources/ui/src/locale/languages/ar-sa.json | 2 +- .../main/resources/ui/src/locale/languages/de-de.json | 2 +- .../main/resources/ui/src/locale/languages/en-us.json | 2 +- .../main/resources/ui/src/locale/languages/es-es.json | 2 +- .../main/resources/ui/src/locale/languages/fr-fr.json | 2 +- .../main/resources/ui/src/locale/languages/gl-es.json | 2 +- .../main/resources/ui/src/locale/languages/he-he.json | 2 +- .../main/resources/ui/src/locale/languages/ja-jp.json | 2 +- .../main/resources/ui/src/locale/languages/ko-kr.json | 2 +- .../main/resources/ui/src/locale/languages/mr-in.json | 2 +- .../main/resources/ui/src/locale/languages/nl-nl.json | 2 +- .../main/resources/ui/src/locale/languages/pr-pr.json | 2 +- .../main/resources/ui/src/locale/languages/pt-br.json | 2 +- .../main/resources/ui/src/locale/languages/pt-pt.json | 2 +- .../main/resources/ui/src/locale/languages/ru-ru.json | 2 +- .../main/resources/ui/src/locale/languages/th-th.json | 2 +- .../main/resources/ui/src/locale/languages/tr-tr.json | 2 +- .../main/resources/ui/src/locale/languages/zh-cn.json | 2 +- .../main/resources/ui/src/locale/languages/zh-tw.json | 2 +- 22 files changed, 34 insertions(+), 22 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts index 11928edf9832..f8a7c15afc3e 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/constant/customProperty.ts @@ -213,7 +213,7 @@ export const CUSTOM_PROPERTIES_ENTITIES = { }; export const CUSTOM_PROPERTY_NAME_VALIDATION_ERROR = - 'Name must start with a letter or number. Invalid characters: " : ^ $'; + 'Name must start with a letter or number. Invalid characters: " : ^ $ \\'; export const CUSTOM_PROPERTY_INVALID_NAMES = { STARTS_WITH_SPECIAL_CHAR: '_invalidName', @@ -221,6 +221,7 @@ export const CUSTOM_PROPERTY_INVALID_NAMES = { DISALLOWED_DOLLAR: 'name$invalid', DISALLOWED_CARET: 'name^invalid', DISALLOWED_QUOTE: 'name"invalid', + DISALLOWED_BACKSLASH: String.raw`name\invalid`, }; -export const NAME_SUFFIX = ".!@#%`&*()_-=+{}[]~|\\;'<>,.?/"; +export const NAME_SUFFIX = ".!@#%`&*()_-=+{}[]~|;'<>,.?/"; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts index f47e02ae1e44..2e45f985fdd6 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/CustomProperties.spec.ts @@ -3603,6 +3603,17 @@ test.describe('Custom property name validation', () => { ); }); + test('should show error when name contains a backslash', async ({ page }) => { + await page.fill( + nameInput, + CUSTOM_PROPERTY_INVALID_NAMES.DISALLOWED_BACKSLASH + ); + + await expect(page.locator(nameError)).toContainText( + CUSTOM_PROPERTY_NAME_VALIDATION_ERROR + ); + }); + test('should accept a valid name starting with a letter', async ({ page, }) => { diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts index d71b3557f73a..6ef4ebf7b09e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/regex.constants.ts @@ -29,7 +29,7 @@ export const ENTITY_NAME_REGEX = /^((?!::).)*$/; * - Disallowed: " : ^ $ */ export const CUSTOM_PROPERTY_NAME_REGEX = - /^[A-Za-z0-9][A-Za-z0-9 _\-.,;/&%#@!'(){}[\]<>|=+?*~`\\]*$/; + /^[A-Za-z0-9][A-Za-z0-9 _\-.,;/&%#@*!'(){}[\]<>|=+?~`]*$/; /** * Matches any string that does NOT contain the following: diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json index c142f4fe7dd6..ae146682652a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ar-sa.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "مسار رابط شعار شريط التنقل.", "custom-properties-description": "التقط بيانات وصفية مخصصة لإثراء أصول البيانات الخاصة بك عن طريق توسيع السمات.", "custom-property-is-set-to-message": "تم تعيين {{fieldName}} إلى", - "custom-property-name-validation": "يجب أن يبدأ الاسم بحرف أو رقم. أحرف غير صالحة: \" : ^ $", + "custom-property-name-validation": "يجب أن يبدأ الاسم بحرف أو رقم. أحرف غير صالحة: \" : ^ $ \\", "custom-property-update": "تحديث الخاصية المخصصة '{{propertyName}}' في {{entityName}} هو {{status}}", "customize-brand-description": "قم بتخصيص تجربة مستخدم {{brandName}} لتناسب احتياجات مؤسستك وفريقك.", "customize-entity-landing-page-header-for-persona": "تخصيص تجربة صفحة كيان {{entity}} لشخصية <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json index 3e00fc63ac52..d5aab0227dc8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/de-de.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "URL-Pfad für das Navbar-Logo.", "custom-properties-description": "Erfassen Sie benutzerdefinierte Metadaten, um Ihre Datenobjekte durch die Erweiterung der Attribute zu bereichern.", "custom-property-is-set-to-message": "{{fieldName}} ist gesetzt auf", - "custom-property-name-validation": "Der Name muss mit einem Buchstaben oder einer Zahl beginnen. Ungültige Zeichen: \" : ^ $", + "custom-property-name-validation": "Der Name muss mit einem Buchstaben oder einer Zahl beginnen. Ungültige Zeichen: \" : ^ $ \\", "custom-property-update": "Aktualisierung der benutzerdefinierten Eigenschaft '{{propertyName}}' in {{entityName}} ist {{status}}", "customize-brand-description": "Passen Sie die {{brandName}}-Benutzeroberfläche an Ihre Organisations- und Teambedürfnisse an.", "customize-entity-landing-page-header-for-persona": "Personalisieren Sie die {{entity}}-Entitätsseite für die <0>{{persona}}-Persona", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json index 766f2f02d9ed..2c24bb62026e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/en-us.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "URL path for the navbar logo.", "custom-properties-description": " Capture custom metadata to enrich your data assets by extending the attributes.", "custom-property-is-set-to-message": "{{fieldName}} is set to", - "custom-property-name-validation": "Name must start with a letter or number. Invalid characters: \" : ^ $", + "custom-property-name-validation": "Name must start with a letter or number. Invalid characters: \" : ^ $ \\", "custom-property-update": "Custom property '{{propertyName}}' update in {{entityName}} is {{status}}", "customize-brand-description": "Tailor the {{brandName}} UX to suit your organizational and team needs.", "customize-entity-landing-page-header-for-persona": "Personalize the {{entity}} Entity Page experience for the <0>{{persona}} persona", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json index f07756505221..daf42129d6c0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/es-es.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Ruta URL para el logotipo de la barra de navegación.", "custom-properties-description": "Captura metadatos personalizados para enriquecer tus activos de datos mediante la extensión de los atributos.", "custom-property-is-set-to-message": "{{fieldName}} está configurada para", - "custom-property-name-validation": "El nombre debe comenzar con una letra o número. Caracteres inválidos: \" : ^ $", + "custom-property-name-validation": "El nombre debe comenzar con una letra o número. Caracteres inválidos: \" : ^ $ \\", "custom-property-update": "La actualización de la propiedad personalizada '{{propertyName}}' en {{entityName}} está {{status}}", "customize-brand-description": "Adapte la experiencia de usuario de {{brandName}} para satisfacer las necesidades de su organización y equipo.", "customize-entity-landing-page-header-for-persona": "Personaliza la experiencia de la Página de Entidad {{entity}} para la persona <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json index 23d4e47e4186..7feb037306ac 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/fr-fr.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Chemin (URL) du logo de la barre de navigation.", "custom-properties-description": " Capturer des métadonnées personnalisées pour enrichir vos actifs de données en étendant leurs attributs.", "custom-property-is-set-to-message": "{{fieldName}} est réglé sur", - "custom-property-name-validation": "Le nom doit commencer par une lettre ou un chiffre. Caractères invalides : \" : ^ $", + "custom-property-name-validation": "Le nom doit commencer par une lettre ou un chiffre. Caractères invalides : \" : ^ $ \\", "custom-property-update": "La mise à jour de la propriété personnalisée '{{propertyName}}' dans {{entityName}} est {{status}}", "customize-brand-description": "Ajustez l'expérience utilisateur d'{{brandName}} pour répondre à vos besoins d'équipe et d'organisation.", "customize-entity-landing-page-header-for-persona": "Personnalisez l'expérience de la page d'entité {{entity}} pour la persona <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json index 6904b8d27041..5acaa2356668 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/gl-es.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Ruta URL para o logotipo da barra de navegación.", "custom-properties-description": "Captura metadatos personalizados para enriquecer os teus activos de datos estendendo os atributos.", "custom-property-is-set-to-message": "{{fieldName}} está configurado para", - "custom-property-name-validation": "O nome debe comezar por letra ou número. Caracteres inválidos: \" : ^ $", + "custom-property-name-validation": "O nome debe comezar por letra ou número. Caracteres inválidos: \" : ^ $ \\", "custom-property-update": "A actualización da propiedade personalizada '{{propertyName}}' en {{entityName}} está {{status}}", "customize-brand-description": "Adapta a experiencia de usuario de {{brandName}} ás necesidades da túa organización e equipo.", "customize-entity-landing-page-header-for-persona": "Personaliza a experiencia da páxina de entidade {{entity}} para a persoa <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json index 4d422189fb2b..eecf7463c9c7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/he-he.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "נתיב URL עבור לוגו סרגל הניווט.", "custom-properties-description": " לכוד מטא-דאטה מותאמים אישית להעשרת נכסי הנתונים שלך על ידי הרחבת התכונות.", "custom-property-is-set-to-message": "{{fieldName}} מוגדר ל", - "custom-property-name-validation": "השם חייב להתחיל באות או במספר. תווים לא חוקיים: \" : ^ $", + "custom-property-name-validation": "השם חייב להתחיל באות או במספר. תווים לא חוקיים: \" : ^ $ \\", "custom-property-update": "עדכון המאפיין המותאם אישית '{{propertyName}}' ב{{entityName}} הוא {{status}}", "customize-brand-description": "התאם את חוויית המשתמש של {{brandName}} לצרכי הארגון והצוות שלך.", "customize-entity-landing-page-header-for-persona": "התאם אישית את חוויית דף הישות של {{entity}} עבור הפרסונה <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json index 08d2b2c4809a..46bf0e10f771 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ja-jp.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "ナビゲーションバーのロゴ用の URL パス", "custom-properties-description": "属性を拡張して、データアセットに独自のメタデータを追加できます。", "custom-property-is-set-to-message": "{{fieldName}} に設定されています", - "custom-property-name-validation": "名前は文字または数字で始まる必要があります。無効な文字: \" : ^ $", + "custom-property-name-validation": "名前は文字または数字で始まる必要があります。無効な文字: \" : ^ $ \\", "custom-property-update": "カスタムプロパティ '{{propertyName}}' の {{entityName}} に対する更新は {{status}} です", "customize-brand-description": "{{brandName}} の UX を組織やチームのニーズに合わせて調整します。", "customize-entity-landing-page-header-for-persona": "{{entity}} エンティティページの体験を <0>{{persona}} ペルソナ向けにパーソナライズします", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json index c07cb281dd55..1e09808a2965 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ko-kr.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "내비게이션 바 로고의 URL 경로입니다.", "custom-properties-description": "속성을 확장하여 데이터 자산을 풍부하게 하는 사용자 정의 메타데이터를 캡처합니다.", "custom-property-is-set-to-message": "{{fieldName}}이(가) 다음으로 설정되었습니다", - "custom-property-name-validation": "이름은 문자나 숫자로 시작해야 합니다. 잘못된 문자: \" : ^ $", + "custom-property-name-validation": "이름은 문자나 숫자로 시작해야 합니다. 잘못된 문자: \" : ^ $ \\", "custom-property-update": "{{entityName}}의 사용자 정의 속성 '{{propertyName}}' 업데이트가 {{status}}입니다", "customize-brand-description": "조직 및 팀의 요구에 맞게 {{brandName}} UX를 맞춤 설정하세요.", "customize-entity-landing-page-header-for-persona": "<0>{{persona}} 페르소나를 위한 {{entity}} 엔티티 페이지 경험을 개인화하세요.", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json index 3ee7b769f1eb..fc47d9dc715b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/mr-in.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "नेव्हबार लोगो साठी URL पथ.", "custom-properties-description": "तुमच्या डेटा ॲसेटंना समृद्ध करण्यासाठी सानुकूल मेटाडेटा कॅप्चर करा.", "custom-property-is-set-to-message": "{{fieldName}} सेट केले आहे", - "custom-property-name-validation": "नाव अक्षर किंवा अंकाने सुरू झाले पाहिजे. अवैध वर्ण: \" : ^ $", + "custom-property-name-validation": "नाव अक्षर किंवा अंकाने सुरू झाले पाहिजे. अवैध वर्ण: \" : ^ $ \\", "custom-property-update": "सानुकूल गुणधर्म '{{propertyName}}' चे {{entityName}} मधील अद्यतन {{status}} आहे", "customize-brand-description": "तुमच्या संस्थात्मक आणि टीमच्या गरजांसाठी {{brandName}} UX सानुकूलित करा.", "customize-entity-landing-page-header-for-persona": "{{persona}} व्यक्तिमत्वासाठी {{entity}} घटक पृष्ठ अनुभव वैयक्तिकृत करा", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json index cbdaa64733c1..9fa787c0cde3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/nl-nl.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "URL-pad voor het navigatiebalk-logo.", "custom-properties-description": "Leg aangepaste metadata vast om uw data-assets te verrijken door het uitbreiden van de attributen.", "custom-property-is-set-to-message": "{{fieldName}} is set to", - "custom-property-name-validation": "De naam moet beginnen met een letter of cijfer. Ongeldige tekens: \" : ^ $", + "custom-property-name-validation": "De naam moet beginnen met een letter of cijfer. Ongeldige tekens: \" : ^ $ \\", "custom-property-update": "Aangepaste eigenschap '{{propertyName}}' update in {{entityName}} is {{status}}", "customize-brand-description": "De {{brandName}} UX aanpassen aan de behoeften van uw organisatie en team.", "customize-entity-landing-page-header-for-persona": "Personaliseer de {{entity}} entiteitspagina-ervaring voor de <0>{{persona}} persona", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json index 7178949f8869..529b880bf5b5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pr-pr.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "مسیر URL برای لوگوی نوار ناوبری.", "custom-properties-description": "متادیتای سفارشی را برای غنی‌سازی دارایی‌های داده‌ای خود با افزودن ویژگی‌های اضافی ضبط کنید.", "custom-property-is-set-to-message": "{{fieldName}} به مقدار", - "custom-property-name-validation": "نام باید با یک حرف یا عدد شروع شود. نویسه‌های نامعتبر: \" : ^ $", + "custom-property-name-validation": "نام باید با یک حرف یا عدد شروع شود. نویسه‌های نامعتبر: \" : ^ $ \\", "custom-property-update": "بروزرسانی ویژگی سفارشی '{{propertyName}}' در {{entityName}} {{status}} است", "customize-brand-description": "تجربه کاربری {{brandName}} را برای نیازهای سازمانی و تیمی خود تنظیم کنید.", "customize-entity-landing-page-header-for-persona": "تجربه صفحه موجودیت {{entity}} را برای شخصیت <0>{{persona}} شخصی‌سازی کنید", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json index dedb6ebea9d9..dbb1302b8cbc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-br.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Caminho da URL para o logotipo da barra de navegação.", "custom-properties-description": " Capture metadados personalizados para enriquecer seus ativos de dados ao estender os atributos.", "custom-property-is-set-to-message": "{{fieldName}} está definido para", - "custom-property-name-validation": "O nome deve começar com uma letra ou número. Caracteres inválidos: \" : ^ $", + "custom-property-name-validation": "O nome deve começar com uma letra ou número. Caracteres inválidos: \" : ^ $ \\", "custom-property-update": "Atualização da propriedade personalizada '{{propertyName}}' em {{entityName}} está {{status}}", "customize-brand-description": "Personalize o layout de {{brandName}} para atender às suas necessidades organizacionais e de equipe.", "customize-entity-landing-page-header-for-persona": "Personalize a experiência da Página de Entidade {{entity}} para a persona <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json index c290f270b8ad..7cd92813865e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/pt-pt.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Caminho da URL para o logotipo da barra de navegação.", "custom-properties-description": " Capture metadados personalizados para enriquecer os seus ativos de dados estendendo os atributos.", "custom-property-is-set-to-message": "{{fieldName}} está definido como", - "custom-property-name-validation": "O nome deve começar com uma letra ou número. Caracteres inválidos: \" : ^ $", + "custom-property-name-validation": "O nome deve começar com uma letra ou número. Caracteres inválidos: \" : ^ $ \\", "custom-property-update": "A atualização da propriedade personalizada '{{propertyName}}' em {{entityName}} está {{status}}", "customize-brand-description": "Personalize a experiência {{brandName}} para se adequar às necessidades da sua organização e equipa.", "customize-entity-landing-page-header-for-persona": "Personalize a experiência da Página da Entidade {{entity}} para a persona <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json index 7b1576aa911f..2c843c4ba5dc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/ru-ru.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "URL-путь для логотипа панели навигации.", "custom-properties-description": "Создавайте и настраивайте новые поля для объектов", "custom-property-is-set-to-message": "Значение поля {{fieldName}} изменено на", - "custom-property-name-validation": "Имя должно начинаться с буквы или цифры. Недопустимые символы: \" : ^ $", + "custom-property-name-validation": "Имя должно начинаться с буквы или цифры. Недопустимые символы: \" : ^ $ \\", "custom-property-update": "Обновление дополнительного поля '{{propertyName}}' в {{entityName}} в статусе {{status}}", "customize-brand-description": "Настройте UX {{brandName}} под ваши потребности", "customize-entity-landing-page-header-for-persona": "Персонализируйте страницу объекта «{{entity}}» для персоны <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json index 642530cb7661..1f55c6e19869 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/th-th.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "เส้นทาง URL สำหรับโลโก้ในแถบด้านบน", "custom-properties-description": "จับข้อมูลเมตาที่กำหนดเองเพื่อเสริมสินทรัพย์ข้อมูลของคุณโดยการขยายคุณสมบัติ", "custom-property-is-set-to-message": "{{fieldName}} ถูกตั้งค่าเป็น", - "custom-property-name-validation": "ชื่อต้องขึ้นต้นด้วยตัวอักษรหรือตัวเลข ตัวอักษรที่ไม่ถูกต้อง: \" : ^ $", + "custom-property-name-validation": "ชื่อต้องขึ้นต้นด้วยตัวอักษรหรือตัวเลข ตัวอักษรที่ไม่ถูกต้อง: \" : ^ $ \\", "custom-property-update": "การอัปเดตคุณสมบัติที่กำหนดเอง '{{propertyName}}' ใน {{entityName}} คือ {{status}}", "customize-brand-description": "ปรับแต่ง UX ของ {{brandName}} ให้เหมาะสมกับความต้องการขององค์กรและทีมของคุณ", "customize-entity-landing-page-header-for-persona": "ปรับแต่งประสบการณ์หน้าเอนทิตี {{entity}} สำหรับบุคคล <0>{{persona}}", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json index 1f251d6f381b..d73e2eaf290b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/tr-tr.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "Gezinme çubuğu logosu için URL yolu.", "custom-properties-description": "Nitelikleri genişleterek veri varlıklarınızı zenginleştirmek için özel metadata yakalayın.", "custom-property-is-set-to-message": "{{fieldName}} şuna ayarlandı:", - "custom-property-name-validation": "İsim bir harf veya sayı ile başlamalıdır. Geçersiz karakterler: \" : ^ $", + "custom-property-name-validation": "İsim bir harf veya sayı ile başlamalıdır. Geçersiz karakterler: \" : ^ $ \\", "custom-property-update": "{{entityName}} içindeki '{{propertyName}}' özel özelliği güncellemesi {{status}}", "customize-brand-description": "{{brandName}} kullanıcı deneyimini kurumsal ve ekip ihtiyaçlarınıza göre uyarlayın.", "customize-entity-landing-page-header-for-persona": "<0>{{persona}} personası için {{entity}} Varlık Sayfası deneyimini kişiselleştirin", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json index 671e5dc40b73..f4cbf271c106 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-cn.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "导航栏 Logo 指向的 URL 地址", "custom-properties-description": " 获取自定义元数据, 通过扩展属性来丰富数据资产", "custom-property-is-set-to-message": "{{fieldName}}设置为", - "custom-property-name-validation": "名称必须以字母或数字开头。无效字符:\" : ^ $", + "custom-property-name-validation": "名称必须以字母或数字开头。无效字符:\" : ^ $ \\", "custom-property-update": "自定义属性'{{propertyName}}'在{{entityName}}中的更新{{status}}", "customize-brand-description": "自定义 {{brandName}}, 以满足您的组织和团队需求", "customize-entity-landing-page-header-for-persona": "为<0>{{persona}}角色个性化{{entity}}实体页面体验", diff --git a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json index 54ab28ddee70..360d97b54c2b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json +++ b/openmetadata-ui/src/main/resources/ui/src/locale/languages/zh-tw.json @@ -2544,7 +2544,7 @@ "custom-monogram-url-path-message": "導覽列標誌的 URL 路徑。", "custom-properties-description": "透過擴充屬性來擷取自訂元資料,以豐富您的資料資產。", "custom-property-is-set-to-message": "{{fieldName}} 已設定為", - "custom-property-name-validation": "名稱必須以字母或數字開頭。無效字元:\" : ^ $", + "custom-property-name-validation": "名稱必須以字母或數字開頭。無效字元:\" : ^ $ \\", "custom-property-update": "{{entityName}} 中的自訂屬性 '{{propertyName}}' 更新 {{status}}", "customize-brand-description": "量身打造 {{brandName}} 的使用者體驗,以滿足您的組織和團隊需求。", "customize-entity-landing-page-header-for-persona": "為 <0>{{persona}} 角色個人化 {{entity}} 實體頁面體驗", From be23e01d0bdc2c3e00910265341ffa4108fd1170 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Fri, 1 May 2026 00:12:20 +0530 Subject: [PATCH 10/13] fixed the playwright issue Co-authored-by: Copilot --- .../resources/ui/playwright/utils/advancedSearch.ts | 7 +------ .../resources/ui/playwright/utils/customProperty.ts | 9 ++------- .../AddCustomProperty/AddCustomProperty.tsx | 2 ++ .../EditCustomPropertyModal.tsx | 1 + .../components/common/MUITextField/MUITextField.tsx | 8 ++++++-- .../common/SanitizedInput/SanitizedInput.tsx | 11 +++++++++-- 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/advancedSearch.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/advancedSearch.ts index 021422169d1b..57a1578802f0 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/advancedSearch.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/advancedSearch.ts @@ -210,12 +210,7 @@ export const selectOption = async ( // Use .first() to handle multiple matches (acceptable when scoped to visible dropdown) const optionLocator = page .locator('.ant-select-dropdown:visible') - .locator('.ant-select-item-option') - .filter({ - hasText: new RegExp( - `^${optionTitle.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&')}$` - ), - }) + .locator(`[title="${optionTitle}"]`) .first(); await expect(optionLocator).toBeVisible(); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts index de4960716090..2deda34d0f60 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts @@ -19,8 +19,8 @@ import { } from '../constant/customProperty'; import { SidebarItem } from '../constant/sidebar'; import { - EntityTypeEndpoint, ENTITY_PATH, + EntityTypeEndpoint, } from '../support/entity/Entity.interface'; import { UserClass } from '../support/user/UserClass'; import { selectOption, showAdvancedSearchDialog } from './advancedSearch'; @@ -773,12 +773,7 @@ export const addCustomPropertiesForEntity = async ({ expect(response.status()).toBe(200); await expect( - page.getByRole('row', { - name: new RegExp( - propertyName.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&'), - 'i' - ), - }) + page.locator('tr').filter({ hasText: propertyName }) ).toBeVisible(); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx index 5f7ef2030294..01f7b5261699 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/AddCustomProperty/AddCustomProperty.tsx @@ -266,6 +266,7 @@ const AddCustomProperty = ({ props: { 'data-testid': 'name', autoComplete: 'off', + shouldSanitize: false, }, placeholder: t('label.name'), rules: [ @@ -292,6 +293,7 @@ const AddCustomProperty = ({ type: FieldTypes.TEXT_MUI, props: { 'data-testid': 'display-name', + shouldSanitize: false, }, }, { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/EditCustomPropertyModal/EditCustomPropertyModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/EditCustomPropertyModal/EditCustomPropertyModal.tsx index 0673f5ea44b9..5f66d8a95fba 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/EditCustomPropertyModal/EditCustomPropertyModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/EditCustomPropertyModal/EditCustomPropertyModal.tsx @@ -84,6 +84,7 @@ const EditCustomPropertyModal: FC = ({ type: FieldTypes.TEXT, props: { 'data-testid': 'display-name', + shouldSanitize: false, }, }, { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/MUITextField/MUITextField.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/MUITextField/MUITextField.tsx index e7e4f7b23bf3..7dd193054e6a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/MUITextField/MUITextField.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/MUITextField/MUITextField.tsx @@ -17,6 +17,7 @@ import { getSanitizeContent } from '../../../utils/sanitize.utils'; interface MUITextFieldProps extends Omit { variant?: 'outlined' | 'filled' | 'standard'; size?: 'small' | 'medium'; + shouldSanitize?: boolean; } const MUITextField: FC = ({ @@ -24,16 +25,19 @@ const MUITextField: FC = ({ onChange, variant, size = 'small', + shouldSanitize = true, ...props }) => { const handleChange = useCallback( (e: ChangeEvent) => { - const sanitizedValue = getSanitizeContent(e.target.value); + const sanitizedValue = shouldSanitize + ? getSanitizeContent(e.target.value) + : e.target.value; if (onChange) { onChange({ ...e, target: { ...e.target, value: sanitizedValue } }); } }, - [onChange] + [onChange, shouldSanitize] ); return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/SanitizedInput/SanitizedInput.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/SanitizedInput/SanitizedInput.tsx index 463858e9ab9a..6dce8f4e725e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/SanitizedInput/SanitizedInput.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/SanitizedInput/SanitizedInput.tsx @@ -14,10 +14,17 @@ import { Input, InputProps } from 'antd'; import { ChangeEvent, FC, memo, useCallback } from 'react'; import { getSanitizeContent } from '../../../utils/sanitize.utils'; -const SanitizedInput: FC = ({ value, onChange, ...props }) => { +const SanitizedInput: FC = ({ + value, + onChange, + shouldSanitize = true, + ...props +}) => { const handleChange = useCallback( (e: ChangeEvent) => { - const sanitizedValue = getSanitizeContent(e.target.value); + const sanitizedValue = shouldSanitize + ? getSanitizeContent(e.target.value) + : e.target.value; if (onChange) { onChange({ ...e, target: { ...e.target, value: sanitizedValue } }); } From 1823307d5a9302ae4dfa08ff72804f575c3f5c82 Mon Sep 17 00:00:00 2001 From: Rohit0301 Date: Fri, 1 May 2026 00:14:48 +0530 Subject: [PATCH 11/13] lint fix --- .../src/main/resources/ui/playwright/utils/customProperty.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts index 2deda34d0f60..28d86710aa53 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts @@ -19,8 +19,8 @@ import { } from '../constant/customProperty'; import { SidebarItem } from '../constant/sidebar'; import { - ENTITY_PATH, EntityTypeEndpoint, + ENTITY_PATH, } from '../support/entity/Entity.interface'; import { UserClass } from '../support/user/UserClass'; import { selectOption, showAdvancedSearchDialog } from './advancedSearch'; From fa820463eeef9b6cfdc180229b9716c482bbf6ec Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Mon, 4 May 2026 11:16:37 +0530 Subject: [PATCH 12/13] fix check style --- .../openmetadata/service/jdbi3/TypeRepositoryTest.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java index 6258e1ffa8ee..d63306faef80 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java @@ -41,8 +41,7 @@ class TypeRepositoryTest { void customPropertyNamePatternMatchesSchema() throws Exception { JsonNode schemaPattern = readDefinitionField(CUSTOM_PROPERTY_NAME_DEF, "pattern"); assertNotNull( - schemaPattern, - "customPropertyName.pattern must exist in " + BASIC_SCHEMA_RESOURCE); + schemaPattern, "customPropertyName.pattern must exist in " + BASIC_SCHEMA_RESOURCE); assertEquals( schemaPattern.asText(), @@ -56,9 +55,7 @@ void customPropertyNamePatternMatchesSchema() throws Exception { @Test void customPropertyNameMaxLengthMatchesSchema() throws Exception { JsonNode schemaMax = readDefinitionField(CUSTOM_PROPERTY_NAME_DEF, "maxLength"); - assertNotNull( - schemaMax, - "customPropertyName.maxLength must exist in " + BASIC_SCHEMA_RESOURCE); + assertNotNull(schemaMax, "customPropertyName.maxLength must exist in " + BASIC_SCHEMA_RESOURCE); assertEquals( schemaMax.asInt(), @@ -73,8 +70,7 @@ void customPropertyNameDefinitionIsRequiredString() throws Exception { assertNotNull(definition, "customPropertyName must be defined in " + BASIC_SCHEMA_RESOURCE); assertEquals("string", definition.get("type").asText()); assertTrue( - definition.get("minLength").asInt() >= 1, - "customPropertyName must have minLength >= 1"); + definition.get("minLength").asInt() >= 1, "customPropertyName must have minLength >= 1"); } private static JsonNode readDefinition(String definitionName) throws Exception { From 3c92e1b7ac37eb5ffec753907d887e707c39ee37 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Mon, 4 May 2026 17:05:45 +0530 Subject: [PATCH 13/13] Drop redundant Java validator for custom property name; tighten IT assertions Schema is the single source of truth: jsonschema2pojo emits @Pattern + @Size on CustomProperty.name from basic.json#/definitions/customPropertyName, and @Valid on TypeResource.addOrUpdateProperty enforces them at the HTTP boundary. The hand-written Pattern constant, validateCustomPropertyName, and the schema-vs-Java sync test were duplicating that rule and could never reach the HTTP user (Bean Validation always fires first via @Valid). Tighten the new TypeResourceIT cases from assertThrows(Exception.class) to assertThrows(InvalidRequestException.class) so a regression to a different exception type or status code fails loudly. --- .../openmetadata/it/tests/TypeResourceIT.java | 13 +-- .../service/jdbi3/TypeRepository.java | 30 ------- .../service/jdbi3/TypeRepositoryTest.java | 90 ------------------- 3 files changed, 7 insertions(+), 126 deletions(-) delete mode 100644 openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java index 2e63348c81e3..67349cda48c6 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TypeResourceIT.java @@ -32,6 +32,7 @@ import org.openmetadata.schema.type.CustomPropertyConfig; import org.openmetadata.schema.type.customProperties.EnumConfig; import org.openmetadata.sdk.client.OpenMetadataClient; +import org.openmetadata.sdk.exceptions.InvalidRequestException; import org.openmetadata.sdk.network.HttpMethod; /** @@ -400,9 +401,9 @@ void test_customPropertyNameDisallowedCharacters_fails(TestNamespace ns) { property.setPropertyType(STRING_TYPE.getEntityReference()); assertThrows( - Exception.class, + InvalidRequestException.class, () -> addCustomProperty(client, tableTypeId, property), - "Custom property name '" + name + "' should be rejected"); + "Custom property name '" + name + "' should be rejected with HTTP 400"); } } @@ -423,9 +424,9 @@ void test_customPropertyNameMustStartWithAlphanumeric_fails(TestNamespace ns) { property.setPropertyType(STRING_TYPE.getEntityReference()); assertThrows( - Exception.class, + InvalidRequestException.class, () -> addCustomProperty(client, tableTypeId, property), - "Custom property name '" + name + "' must start with alphanumeric"); + "Custom property name '" + name + "' must start with alphanumeric (HTTP 400 expected)"); } } @@ -445,9 +446,9 @@ void test_customPropertyNameTooLong_fails(TestNamespace ns) { property.setPropertyType(STRING_TYPE.getEntityReference()); assertThrows( - Exception.class, + InvalidRequestException.class, () -> addCustomProperty(client, tableTypeId, property), - "Custom property name longer than 256 characters should be rejected"); + "Custom property name longer than 256 characters should be rejected with HTTP 400"); } @Test diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java index 2cb0858e7fef..469f2da1589b 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TypeRepository.java @@ -34,7 +34,6 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.locks.Lock; -import java.util.regex.Pattern; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.tuple.Triple; @@ -68,13 +67,6 @@ public class TypeRepository extends EntityRepository { private static final String PATCH_FIELDS = "customProperties"; private static final Striped TYPE_PROPERTY_LOCKS = Striped.lock(4096); - // Must stay in sync with definitions.customPropertyName.pattern in - // openmetadata-spec/src/main/resources/json/schema/type/basic.json - // Enforced by TypeRepositoryTest.customPropertyNamePatternMatchesSchema. - static final Pattern CUSTOM_PROPERTY_NAME_PATTERN = - Pattern.compile("^[A-Za-z0-9][A-Za-z0-9 _\\-.,;/&%#@!'(){}\\[\\]<>|=+?*~`]*$"); - static final int CUSTOM_PROPERTY_NAME_MAX_LENGTH = 256; - public TypeRepository() { super( TypeResource.COLLECTION_PATH, @@ -221,7 +213,6 @@ private List getCustomProperties(Type type) { } private void validateProperty(CustomProperty customProperty) { - validateCustomPropertyName(customProperty.getName()); switch (customProperty.getPropertyType().getName()) { case "enum" -> validateEnumConfig(customProperty.getCustomPropertyConfig()); case "table-cp" -> validateTableTypeConfig(customProperty.getCustomPropertyConfig()); @@ -237,27 +228,6 @@ private void validateProperty(CustomProperty customProperty) { } } - private static void validateCustomPropertyName(String name) { - if (name == null || name.isEmpty()) { - throw new IllegalArgumentException("Custom property name must not be empty"); - } - if (name.length() > CUSTOM_PROPERTY_NAME_MAX_LENGTH) { - throw new IllegalArgumentException( - String.format( - "Custom property name '%s' exceeds maximum length of %d characters", - name, CUSTOM_PROPERTY_NAME_MAX_LENGTH)); - } - if (!CUSTOM_PROPERTY_NAME_PATTERN.matcher(name).matches()) { - throw new IllegalArgumentException( - String.format( - "Invalid custom property name '%s'. Name must start with an alphanumeric character " - + "and may contain only: alphanumeric, _ - . / & %% # @ ! , ; = | ' + ? * ~ ` " - + "space ( ) < > [ ] { }. " - + "The following characters are not allowed: \" : ^ $ \\", - name)); - } - } - private void validateDateFormat( CustomPropertyConfig config, Set validTokens, String errorMessage) { if (config != null) { diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java deleted file mode 100644 index d63306faef80..000000000000 --- a/openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/TypeRepositoryTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.openmetadata.service.jdbi3; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.io.InputStream; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for {@link TypeRepository}. - * - *

The schema-vs-Java consistency test guarantees that the regex enforced in Java stays in lock - * step with the JSON Schema definition referenced by {@code customProperty.json}. If either side - * is updated without the other, this test fails with a message pointing at both files. - */ -class TypeRepositoryTest { - - private static final ObjectMapper MAPPER = new ObjectMapper(); - private static final String BASIC_SCHEMA_RESOURCE = "json/schema/type/basic.json"; - private static final String CUSTOM_PROPERTY_NAME_DEF = "customPropertyName"; - - @Test - void customPropertyNamePatternMatchesSchema() throws Exception { - JsonNode schemaPattern = readDefinitionField(CUSTOM_PROPERTY_NAME_DEF, "pattern"); - assertNotNull( - schemaPattern, "customPropertyName.pattern must exist in " + BASIC_SCHEMA_RESOURCE); - - assertEquals( - schemaPattern.asText(), - TypeRepository.CUSTOM_PROPERTY_NAME_PATTERN.pattern(), - "JSON Schema pattern in basic.json#/definitions/" - + CUSTOM_PROPERTY_NAME_DEF - + " has drifted from TypeRepository.CUSTOM_PROPERTY_NAME_PATTERN. " - + "Update both so the API and schema enforce the same rule."); - } - - @Test - void customPropertyNameMaxLengthMatchesSchema() throws Exception { - JsonNode schemaMax = readDefinitionField(CUSTOM_PROPERTY_NAME_DEF, "maxLength"); - assertNotNull(schemaMax, "customPropertyName.maxLength must exist in " + BASIC_SCHEMA_RESOURCE); - - assertEquals( - schemaMax.asInt(), - TypeRepository.CUSTOM_PROPERTY_NAME_MAX_LENGTH, - "JSON Schema maxLength has drifted from TypeRepository.CUSTOM_PROPERTY_NAME_MAX_LENGTH. " - + "Update both."); - } - - @Test - void customPropertyNameDefinitionIsRequiredString() throws Exception { - JsonNode definition = readDefinition(CUSTOM_PROPERTY_NAME_DEF); - assertNotNull(definition, "customPropertyName must be defined in " + BASIC_SCHEMA_RESOURCE); - assertEquals("string", definition.get("type").asText()); - assertTrue( - definition.get("minLength").asInt() >= 1, "customPropertyName must have minLength >= 1"); - } - - private static JsonNode readDefinition(String definitionName) throws Exception { - try (InputStream in = - TypeRepositoryTest.class.getClassLoader().getResourceAsStream(BASIC_SCHEMA_RESOURCE)) { - assertNotNull(in, "Could not load resource " + BASIC_SCHEMA_RESOURCE); - JsonNode root = MAPPER.readTree(in); - return root.path("definitions").get(definitionName); - } - } - - private static JsonNode readDefinitionField(String definitionName, String field) - throws Exception { - JsonNode definition = readDefinition(definitionName); - return definition == null ? null : definition.get(field); - } -}