From e0d175f78e1a5981cee080fc277756d4518cfc7d Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:59:59 +0530 Subject: [PATCH] GEN-1894 GEN-771 : Improvement - Add displayName field for custom property (#18496) --- .../service/jdbi3/TypeRepository.java | 27 ++++++++++++++ .../migration/mysql/v160/Migration.java | 2 ++ .../migration/postgres/v160/Migration.java | 2 ++ .../migration/utils/v160/MigrationUtil.java | 36 +++++++++++++++++++ .../resources/metadata/TypeResourceTest.java | 24 +++++++++---- .../json/schema/type/customProperty.json | 4 +++ .../ui/playwright/utils/customProperty.ts | 9 ++++- .../en-US/OpenMetadata/CustomProperty.md | 6 ++++ .../AddCustomProperty/AddCustomProperty.tsx | 11 ++++++ .../CustomProperty/CustomPropertyTable.tsx | 1 + .../EditCustomPropertyModal.tsx | 14 ++++++++ .../CustomPropertyTable/PropertyValue.tsx | 6 ++-- .../EditTableTypePropertyModal.tsx | 3 +- .../MetricListPage/MetricListPage.tsx | 3 +- 14 files changed, 137 insertions(+), 11 deletions(-) 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 c835589fad5..66b546094cf 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 @@ -300,6 +300,7 @@ public class TypeRepository extends EntityRepository { continue; } updateCustomPropertyDescription(updated, storedProperty, updateProperty); + updateDisplayName(updated, storedProperty, updateProperty); updateCustomPropertyConfig(updated, storedProperty, updateProperty); } } @@ -373,6 +374,32 @@ public class TypeRepository extends EntityRepository { } } + private void updateDisplayName( + Type entity, CustomProperty origProperty, CustomProperty updatedProperty) { + String fieldName = getCustomField(origProperty, "displayName"); + if (recordChange( + fieldName, origProperty.getDisplayName(), updatedProperty.getDisplayName())) { + String customPropertyFQN = + getCustomPropertyFQN(entity.getName(), updatedProperty.getName()); + EntityReference propertyType = + updatedProperty.getPropertyType(); // Don't store entity reference + String customPropertyJson = JsonUtils.pojoToJson(updatedProperty.withPropertyType(null)); + updatedProperty.withPropertyType(propertyType); // Restore entity reference + daoCollection + .fieldRelationshipDAO() + .upsert( + customPropertyFQN, + updatedProperty.getPropertyType().getName(), + customPropertyFQN, + updatedProperty.getPropertyType().getName(), + Entity.TYPE, + Entity.TYPE, + Relationship.HAS.ordinal(), + "customProperty", + customPropertyJson); + } + } + private void updateCustomPropertyConfig( Type entity, CustomProperty origProperty, CustomProperty updatedProperty) { String fieldName = getCustomField(origProperty, "customPropertyConfig"); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v160/Migration.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v160/Migration.java index 4e9d1eb7512..f357b819719 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v160/Migration.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/mysql/v160/Migration.java @@ -1,5 +1,6 @@ package org.openmetadata.service.migration.mysql.v160; +import static org.openmetadata.service.migration.utils.v160.MigrationUtil.addDisplayNameToCustomProperty; import static org.openmetadata.service.migration.utils.v160.MigrationUtil.addViewAllRuleToOrgPolicy; import static org.openmetadata.service.migration.utils.v160.MigrationUtil.migrateServiceTypesAndConnections; @@ -18,5 +19,6 @@ public class Migration extends MigrationProcessImpl { public void runDataMigration() { migrateServiceTypesAndConnections(handle, false); addViewAllRuleToOrgPolicy(collectionDAO); + addDisplayNameToCustomProperty(handle, false); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v160/Migration.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v160/Migration.java index ffdb988d21f..a91b58ab3af 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v160/Migration.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/postgres/v160/Migration.java @@ -1,5 +1,6 @@ package org.openmetadata.service.migration.postgres.v160; +import static org.openmetadata.service.migration.utils.v160.MigrationUtil.addDisplayNameToCustomProperty; import static org.openmetadata.service.migration.utils.v160.MigrationUtil.addViewAllRuleToOrgPolicy; import static org.openmetadata.service.migration.utils.v160.MigrationUtil.migrateServiceTypesAndConnections; @@ -18,5 +19,6 @@ public class Migration extends MigrationProcessImpl { public void runDataMigration() { migrateServiceTypesAndConnections(handle, true); addViewAllRuleToOrgPolicy(collectionDAO); + addDisplayNameToCustomProperty(handle, true); } } diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v160/MigrationUtil.java b/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v160/MigrationUtil.java index 8a8ecb466ce..fdeeeeef708 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v160/MigrationUtil.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/migration/utils/v160/MigrationUtil.java @@ -8,6 +8,7 @@ import org.openmetadata.schema.entity.policies.Policy; import org.openmetadata.schema.entity.policies.accessControl.Rule; import org.openmetadata.schema.type.Include; import org.openmetadata.schema.type.MetadataOperation; +import org.openmetadata.schema.type.Relationship; import org.openmetadata.service.Entity; import org.openmetadata.service.exception.EntityNotFoundException; import org.openmetadata.service.jdbi3.CollectionDAO; @@ -138,4 +139,39 @@ public class MigrationUtil { LOG.error("Error updating", e); } } + + public static void addDisplayNameToCustomProperty(Handle handle, boolean postgresql) { + String query; + if (postgresql) { + query = + "UPDATE field_relationship " + + "SET json = CASE " + + " WHEN json->>'displayName' IS NULL OR json->'displayName' = '\"\"' " + + " THEN jsonb_set(json, '{displayName}', json->'name', true) " + + " ELSE json " + + " END " + + "WHERE fromType = :fromType AND toType = :toType AND relation = :relation;"; + } else { + query = + "UPDATE field_relationship " + + "SET json = CASE " + + " WHEN JSON_UNQUOTE(JSON_EXTRACT(json, '$.displayName')) IS NULL " + + " OR JSON_UNQUOTE(JSON_EXTRACT(json, '$.displayName')) = '' " + + " THEN JSON_SET(json, '$.displayName', JSON_EXTRACT(json, '$.name')) " + + " ELSE json " + + " END " + + "WHERE fromType = :fromType AND toType = :toType AND relation = :relation;"; + } + + try { + handle + .createUpdate(query) + .bind("fromType", Entity.TYPE) + .bind("toType", Entity.TYPE) + .bind("relation", Relationship.HAS.ordinal()) + .execute(); + } catch (Exception e) { + LOG.error("Error updating displayName of custom properties", e); + } + } } diff --git a/openmetadata-service/src/test/java/org/openmetadata/service/resources/metadata/TypeResourceTest.java b/openmetadata-service/src/test/java/org/openmetadata/service/resources/metadata/TypeResourceTest.java index 1cd3e7d8264..f36480852e0 100644 --- a/openmetadata-service/src/test/java/org/openmetadata/service/resources/metadata/TypeResourceTest.java +++ b/openmetadata-service/src/test/java/org/openmetadata/service/resources/metadata/TypeResourceTest.java @@ -116,7 +116,8 @@ public class TypeResourceTest extends EntityResourceTest { new CustomProperty() .withName("intA") .withDescription("intA") - .withPropertyType(INT_TYPE.getEntityReference()); + .withPropertyType(INT_TYPE.getEntityReference()) + .withDisplayName("Integer A"); ChangeDescription change = getChangeDescription(topicEntity, MINOR_UPDATE); fieldAdded(change, "customProperties", new ArrayList<>(List.of(fieldA))); topicEntity = @@ -124,22 +125,29 @@ public class TypeResourceTest extends EntityResourceTest { topicEntity.getId(), fieldA, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); assertCustomProperties(new ArrayList<>(List.of(fieldA)), topicEntity.getCustomProperties()); - // Changing custom property description with PUT - fieldA.withDescription("updated"); + // Changing custom property description and displayName with PUT + fieldA.withDescription("updated").withDisplayName("Updated Integer A"); change = getChangeDescription(topicEntity, MINOR_UPDATE); fieldUpdated(change, EntityUtil.getCustomField(fieldA, "description"), "intA", "updated"); + fieldUpdated( + change, EntityUtil.getCustomField(fieldA, "displayName"), "Integer A", "Updated Integer A"); topicEntity = addCustomPropertyAndCheck( topicEntity.getId(), fieldA, ADMIN_AUTH_HEADERS, MINOR_UPDATE, change); assertCustomProperties(new ArrayList<>(List.of(fieldA)), topicEntity.getCustomProperties()); - // Changing custom property description with PATCH + // Changing custom property description and displayName with PATCH // Changes from this PATCH is consolidated with the previous changes - fieldA.withDescription("updated2"); + fieldA.withDescription("updated2").withDisplayName("Updated Integer A 2"); String json = JsonUtils.pojoToJson(topicEntity); topicEntity.setCustomProperties(List.of(fieldA)); change = getChangeDescription(topicEntity, CHANGE_CONSOLIDATED); fieldUpdated(change, EntityUtil.getCustomField(fieldA, "description"), "intA", "updated2"); + fieldUpdated( + change, + EntityUtil.getCustomField(fieldA, "displayName"), + "Integer A", + "Updated Integer A 2"); topicEntity = patchEntityAndCheck(topicEntity, json, ADMIN_AUTH_HEADERS, CHANGE_CONSOLIDATED, change); @@ -150,7 +158,11 @@ public class TypeResourceTest extends EntityResourceTest { .withType(INT_TYPE.getEntityReference().getType()) .withId(INT_TYPE.getEntityReference().getId()); CustomProperty fieldB = - new CustomProperty().withName("intB").withDescription("intB").withPropertyType(typeRef); + new CustomProperty() + .withName("intB") + .withDescription("intB") + .withPropertyType(typeRef) + .withDisplayName("Integer B"); change = getChangeDescription(topicEntity, MINOR_UPDATE); fieldAdded(change, "customProperties", new ArrayList<>(List.of(fieldB))); topicEntity = 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 6cc2ba6624c..052e25586cf 100644 --- a/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json +++ b/openmetadata-spec/src/main/resources/json/schema/type/customProperty.json @@ -52,6 +52,10 @@ "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" }, + "displayName": { + "description": "Display Name for the custom property.Must be unique for an entity.", + "type": "string" + }, "description": { "$ref": "../type/basic.json#/definitions/markdown" }, 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 02d135680ef..4f35d58e485 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/customProperty.ts @@ -17,8 +17,8 @@ import { ENTITY_REFERENCE_PROPERTIES, } from '../constant/customProperty'; import { - EntityTypeEndpoint, ENTITY_PATH, + EntityTypeEndpoint, } from '../support/entity/Entity.interface'; import { UserClass } from '../support/user/UserClass'; import { clickOutside, descriptionBox, uuid } from './common'; @@ -614,6 +614,9 @@ export const addCustomPropertiesForEntity = async ({ // Correct name await page.fill('[data-testid="name"]', propertyName); + // displayName + await page.fill('[data-testid="display-name"]', propertyName); + // Select custom type await page.locator('[id="root\\/propertyType"]').fill(customType); await page.getByTitle(`${customType}`, { exact: true }).click(); @@ -713,6 +716,10 @@ export const editCreatedProperty = async ( await editButton.click(); + // displayName + await page.fill('[data-testid="display-name"]', ''); + await page.fill('[data-testid="display-name"]', propertyName); + await page.locator(descriptionBox).fill(''); await page.locator(descriptionBox).fill('This is new description'); diff --git a/openmetadata-ui/src/main/resources/ui/public/locales/en-US/OpenMetadata/CustomProperty.md b/openmetadata-ui/src/main/resources/ui/public/locales/en-US/OpenMetadata/CustomProperty.md index 98b2208bc03..aaf0601359e 100644 --- a/openmetadata-ui/src/main/resources/ui/public/locales/en-US/OpenMetadata/CustomProperty.md +++ b/openmetadata-ui/src/main/resources/ui/public/locales/en-US/OpenMetadata/CustomProperty.md @@ -8,6 +8,12 @@ $$section The name must start with a lowercase letter, as preferred in the camelCase format. Uppercase letters and numbers can be included in the field name; but spaces, underscores, and dots are not supported. $$ +$$section +### Display Name $(id="displayName") + +Display Name that identifies this custom property. +$$ + $$section ### Type $(id="propertyType") 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 bdc949df91e..26ac289a00a 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 @@ -282,6 +282,17 @@ const AddCustomProperty = () => { }, ], }, + { + name: 'displayName', + id: 'root/displayName', + label: t('label.display-name'), + required: false, + placeholder: t('label.display-name'), + type: FieldTypes.TEXT, + props: { + 'data-testid': 'display-name', + }, + }, { name: 'propertyType', required: true, diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/CustomPropertyTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/CustomPropertyTable.tsx index 8bf877de094..d0f40ce9586 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/CustomPropertyTable.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/CustomProperty/CustomPropertyTable.tsx @@ -75,6 +75,7 @@ export const CustomPropertyTable: FC = ({ return { ...property, description: data.description, + displayName: data.displayName, ...(config ? { customPropertyConfig: { 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 c875ebb9f8a..c3720bbda6d 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 @@ -33,6 +33,7 @@ export interface FormData { description: string; customPropertyConfig: string[]; multiSelect?: boolean; + displayName?: string; } interface EditCustomPropertyModalProps { @@ -71,6 +72,17 @@ const EditCustomPropertyModal: FC = ({ }, [customProperty]); const formFields: FieldProp[] = [ + { + name: 'displayName', + id: 'root/displayName', + label: t('label.display-name'), + required: false, + placeholder: t('label.display-name'), + type: FieldTypes.TEXT, + props: { + 'data-testid': 'display-name', + }, + }, { name: 'description', required: true, @@ -164,12 +176,14 @@ const EditCustomPropertyModal: FC = ({ description: customProperty.description, customPropertyConfig: enumConfig?.values ?? [], multiSelect: Boolean(enumConfig?.multiSelect), + displayName: customProperty.displayName, }; } return { description: customProperty.description, customPropertyConfig: customProperty.customPropertyConfig?.config, + displayName: customProperty.displayName, }; }, [customProperty, hasEnumConfig]); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx index e0b0e4f0768..6b726c4fe3d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/CustomPropertyTable/PropertyValue.tsx @@ -212,7 +212,7 @@ export const PropertyValue: FC = ({ case 'markdown': { const header = t('label.edit-entity-name', { entityType: t('label.property'), - entityName: propertyName, + entityName: getEntityName(property), }); return ( @@ -1000,7 +1000,9 @@ export const PropertyValue: FC = ({ {hasEditPermissions && !showInput && ( + title={t('label.edit-entity', { + entity: getEntityName(property), + })}> = ({ {t('label.edit-entity-name', { entityType: t('label.property'), - entityName: property.name, + entityName: getEntityName(property), })} } diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricListPage/MetricListPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricListPage/MetricListPage.tsx index 2c479c463ff..fbc3e0f5630 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricListPage/MetricListPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricListPage/MetricListPage.tsx @@ -21,6 +21,7 @@ import Loader from '../../../components/common/Loader/Loader'; import NextPrevious from '../../../components/common/NextPrevious/NextPrevious'; import { PagingHandlerParams } from '../../../components/common/NextPrevious/NextPrevious.interface'; import { OwnerLabel } from '../../../components/common/OwnerLabel/OwnerLabel.component'; +import RichTextEditorPreviewer from '../../../components/common/RichTextEditor/RichTextEditorPreviewer'; import TableTags from '../../../components/Database/TableTags/TableTags.component'; import PageHeader from '../../../components/PageHeader/PageHeader.component'; import PageLayoutV1 from '../../../components/PageLayoutV1/PageLayoutV1'; @@ -168,7 +169,7 @@ const MetricListPage = () => { })} ) : ( - description + ), }, {