mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2026-01-07 04:56:54 +00:00
GEN-1894 GEN-771 : Improvement - Add displayName field for custom property (#18496)
This commit is contained in:
parent
729a06b5f0
commit
e0d175f78e
@ -300,6 +300,7 @@ public class TypeRepository extends EntityRepository<Type> {
|
||||
continue;
|
||||
}
|
||||
updateCustomPropertyDescription(updated, storedProperty, updateProperty);
|
||||
updateDisplayName(updated, storedProperty, updateProperty);
|
||||
updateCustomPropertyConfig(updated, storedProperty, updateProperty);
|
||||
}
|
||||
}
|
||||
@ -373,6 +374,32 @@ public class TypeRepository extends EntityRepository<Type> {
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +116,8 @@ public class TypeResourceTest extends EntityResourceTest<Type, CreateType> {
|
||||
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<Type, CreateType> {
|
||||
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<Type, CreateType> {
|
||||
.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 =
|
||||
|
||||
@ -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"
|
||||
},
|
||||
|
||||
@ -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');
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -75,6 +75,7 @@ export const CustomPropertyTable: FC<CustomPropertyTableProp> = ({
|
||||
return {
|
||||
...property,
|
||||
description: data.description,
|
||||
displayName: data.displayName,
|
||||
...(config
|
||||
? {
|
||||
customPropertyConfig: {
|
||||
|
||||
@ -33,6 +33,7 @@ export interface FormData {
|
||||
description: string;
|
||||
customPropertyConfig: string[];
|
||||
multiSelect?: boolean;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
interface EditCustomPropertyModalProps {
|
||||
@ -71,6 +72,17 @@ const EditCustomPropertyModal: FC<EditCustomPropertyModalProps> = ({
|
||||
}, [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<EditCustomPropertyModalProps> = ({
|
||||
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]);
|
||||
|
||||
|
||||
@ -212,7 +212,7 @@ export const PropertyValue: FC<PropertyValueProps> = ({
|
||||
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<PropertyValueProps> = ({
|
||||
{hasEditPermissions && !showInput && (
|
||||
<Tooltip
|
||||
placement="left"
|
||||
title={t('label.edit-entity', { entity: propertyName })}>
|
||||
title={t('label.edit-entity', {
|
||||
entity: getEntityName(property),
|
||||
})}>
|
||||
<Icon
|
||||
component={EditIconComponent}
|
||||
data-testid={`edit-icon${
|
||||
|
||||
@ -18,6 +18,7 @@ import { isEmpty, omit } from 'lodash';
|
||||
import React, { FC, MutableRefObject, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CustomProperty } from '../../../../generated/type/customProperty';
|
||||
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||
import { TableTypePropertyValueType } from '../CustomPropertyTable.interface';
|
||||
import './edit-table-type-property.less';
|
||||
import TableTypePropertyView from './TableTypePropertyView';
|
||||
@ -198,7 +199,7 @@ const EditTableTypePropertyModal: FC<EditTableTypePropertyModalProps> = ({
|
||||
<Typography.Text>
|
||||
{t('label.edit-entity-name', {
|
||||
entityType: t('label.property'),
|
||||
entityName: property.name,
|
||||
entityName: getEntityName(property),
|
||||
})}
|
||||
</Typography.Text>
|
||||
}
|
||||
|
||||
@ -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 = () => {
|
||||
})}
|
||||
</Typography.Text>
|
||||
) : (
|
||||
description
|
||||
<RichTextEditorPreviewer markdown={description} />
|
||||
),
|
||||
},
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user