From 1890ea989efb6e4d55d14f0a64b0a48a3256fca5 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:34:39 +0530 Subject: [PATCH 1/9] Fix: align glossary term relation type colors with design system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit System-defined relation types (relatedTo, synonym, antonym, etc.) were initialized with old Ant Design palette colors (#1890ff, #722ed1, …) while the frontend RELATION_META constants had been updated to the new design system colors (#1570ef, #b42318, …). Because renderColorBadge used record.color (from the backend) unconditionally, the stale Ant Design colors were always displayed instead of the intended ones. - Frontend: renderColorBadge now treats RELATION_META as authoritative for system-defined types so the correct design-system color is always shown, regardless of what color value is stored in the backend. - Backend (SettingsCache.java): default colors updated for new installs. - DB migration (2.0.0): postDataMigrationSQLScript added for MySQL and PostgreSQL to update colors in existing deployments without touching user-added custom relation types. - Tests: unit tests for renderColorBadge color-resolution logic; integration test asserting all ten system-defined types return the expected hex values from the API. Fixes #openmetadata/OpenMetadata --- .../mysql/postDataMigrationSQLScript.sql | 74 ++++++++ .../postgres/postDataMigrationSQLScript.sql | 123 +++++++++++++ .../tests/GlossaryTermRelationSettingsIT.java | 40 +++++ .../resources/settings/SettingsCache.java | 20 +-- .../GlossaryTermRelationSettings.test.tsx | 166 ++++++++++++++++++ .../GlossaryTermRelationSettings.tsx | 4 +- 6 files changed, 416 insertions(+), 11 deletions(-) create mode 100644 bootstrap/sql/migrations/native/2.0.0/mysql/postDataMigrationSQLScript.sql create mode 100644 bootstrap/sql/migrations/native/2.0.0/postgres/postDataMigrationSQLScript.sql create mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx diff --git a/bootstrap/sql/migrations/native/2.0.0/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/2.0.0/mysql/postDataMigrationSQLScript.sql new file mode 100644 index 000000000000..358bf777513c --- /dev/null +++ b/bootstrap/sql/migrations/native/2.0.0/mysql/postDataMigrationSQLScript.sql @@ -0,0 +1,74 @@ +-- Update system-defined glossary term relation type colors to match the design system. +-- The old colors were Ant Design palette values; the new colors match the frontend RELATION_META constants. +-- Each statement uses JSON_SEARCH to locate the named relation type by index, then JSON_SET to update its color. +-- REGEXP_REPLACE('[^0-9]', '') strips the "$[N]" path syntax returned by JSON_SEARCH down to the bare index N. + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'relatedTo')), '[^0-9]', ''), '].color'), + '#1570ef') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'relatedTo') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'synonym')), '[^0-9]', ''), '].color'), + '#b42318') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'synonym') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'antonym')), '[^0-9]', ''), '].color'), + '#b54708') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'antonym') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'broader')), '[^0-9]', ''), '].color'), + '#067647') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'broader') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'narrower')), '[^0-9]', ''), '].color'), + '#4e5ba6') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'narrower') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'partOf')), '[^0-9]', ''), '].color'), + '#026aa2') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'partOf') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'hasPart')), '[^0-9]', ''), '].color'), + '#155eef') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'hasPart') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'calculatedFrom')), '[^0-9]', ''), '].color'), + '#6938ef') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'calculatedFrom') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'usedToCalculate')), '[^0-9]', ''), '].color'), + '#ba24d5') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'usedToCalculate') IS NOT NULL; + +UPDATE openmetadata_settings +SET json = JSON_SET(json, + CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'seeAlso')), '[^0-9]', ''), '].color'), + '#c11574') +WHERE configType = 'glossaryTermRelationSettings' + AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'seeAlso') IS NOT NULL; diff --git a/bootstrap/sql/migrations/native/2.0.0/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/2.0.0/postgres/postDataMigrationSQLScript.sql new file mode 100644 index 000000000000..21752ecaf585 --- /dev/null +++ b/bootstrap/sql/migrations/native/2.0.0/postgres/postDataMigrationSQLScript.sql @@ -0,0 +1,123 @@ +-- Update system-defined glossary term relation type colors to match the design system. +-- The old colors were Ant Design palette values; the new colors match the frontend RELATION_META constants. +-- Each CTE finds the 0-based array index of the named relation type, then jsonb_set updates its color. + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'relatedTo' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#1570ef"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'synonym' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#b42318"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'antonym' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#b54708"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'broader' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#067647"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'narrower' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#4e5ba6"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'partOf' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#026aa2"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'hasPart' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#155eef"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'calculatedFrom' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#6938ef"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'usedToCalculate' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#ba24d5"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; + +WITH idx AS ( + SELECT (t.idx - 1) AS i + FROM openmetadata_settings, + jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) + WHERE configtype = 'glossaryTermRelationSettings' + AND elem->>'name' = 'seeAlso' +) +UPDATE openmetadata_settings +SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#c11574"') +FROM idx +WHERE configtype = 'glossaryTermRelationSettings'; diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermRelationSettingsIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermRelationSettingsIT.java index b836c3eeb743..932ed4b8f57f 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermRelationSettingsIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermRelationSettingsIT.java @@ -464,6 +464,46 @@ void test_systemDefinedRelationTypeCannotBeDeleted() throws Exception { } } + @Test + void test_systemDefinedRelationTypesHaveCorrectDesignSystemColors() throws Exception { + JsonNode settings = getSettings(); + JsonNode relationTypes = settings.get("config_value").get("relationTypes"); + + Map expectedColors = + Map.of( + "relatedTo", "#1570ef", + "synonym", "#b42318", + "antonym", "#b54708", + "broader", "#067647", + "narrower", "#4e5ba6", + "partOf", "#026aa2", + "hasPart", "#155eef", + "calculatedFrom", "#6938ef", + "usedToCalculate", "#ba24d5", + "seeAlso", "#c11574"); + + for (JsonNode type : relationTypes) { + String name = type.get("name").asText(); + if (!expectedColors.containsKey(name)) { + continue; + } + JsonNode colorNode = type.get("color"); + assertNotNull(colorNode, "color should exist for system-defined type: " + name); + String actualColor = colorNode.asText().toLowerCase(); + String expected = expectedColors.get(name); + assertEquals( + expected, + actualColor, + "System-defined type '" + + name + + "' should use design-system color " + + expected + + " but got " + + actualColor + + ". Old Ant Design colors (e.g. #1890ff, #722ed1) indicate the 2.0.0 migration did not run."); + } + } + @Test void test_defaultRelationTypesHaveExpectedProperties() throws Exception { JsonNode settings = getSettings(); diff --git a/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java b/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java index a8738ea499cc..1400c9c4e5d9 100644 --- a/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java +++ b/openmetadata-service/src/main/java/org/openmetadata/service/resources/settings/SettingsCache.java @@ -364,7 +364,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app false, RelationCategory.ASSOCIATIVE, true, - "#1890ff", + "#1570ef", null, null), createRelationType( @@ -377,7 +377,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app false, RelationCategory.EQUIVALENCE, true, - "#722ed1", + "#b42318", null, null), createRelationType( @@ -390,7 +390,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app false, RelationCategory.ASSOCIATIVE, true, - "#f5222d", + "#b54708", null, null), createRelationType( @@ -403,7 +403,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app true, RelationCategory.HIERARCHICAL, true, - "#597ef7", + "#067647", null, null), createRelationType( @@ -416,7 +416,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app true, RelationCategory.HIERARCHICAL, true, - "#85a5ff", + "#4e5ba6", null, null), createRelationType( @@ -429,7 +429,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app false, RelationCategory.HIERARCHICAL, true, - "#13c2c2", + "#026aa2", null, null), createRelationType( @@ -442,7 +442,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app false, RelationCategory.HIERARCHICAL, true, - "#36cfc9", + "#155eef", null, null), createRelationType( @@ -455,7 +455,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app false, RelationCategory.ASSOCIATIVE, true, - "#faad14", + "#6938ef", null, null), createRelationType( @@ -468,7 +468,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app false, RelationCategory.ASSOCIATIVE, true, - "#ffc53d", + "#ba24d5", null, null), createRelationType( @@ -481,7 +481,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app false, RelationCategory.ASSOCIATIVE, true, - "#eb2f96", + "#c11574", null, null)); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx new file mode 100644 index 000000000000..70e4c5d634ad --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx @@ -0,0 +1,166 @@ +/* + * Copyright 2025 Collate. + * Licensed 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. + */ + +import { act, render, screen } from '@testing-library/react'; +import { MemoryRouter } from 'react-router-dom'; +import { + RelationCardinality, + RelationCategory, +} from '../../generated/configuration/glossaryTermRelationSettings'; +import { + getGlossaryTermRelationSettings, + getRelationTypeUsageCounts, +} from '../../rest/glossaryAPI'; +import GlossaryTermRelationSettingsPage from './GlossaryTermRelationSettings'; + +jest.mock('../../rest/glossaryAPI', () => ({ + getGlossaryTermRelationSettings: jest.fn(), + getRelationTypeUsageCounts: jest.fn(), + updateGlossaryTermRelationSettings: jest.fn(), +})); + +jest.mock('../../hooks/authHooks', () => ({ + useAuth: jest.fn().mockReturnValue({ isAdminUser: true }), +})); + +jest.mock('../../utils/GlobalSettingsUtils', () => ({ + getSettingPageEntityBreadCrumb: jest.fn().mockReturnValue([]), +})); + +jest.mock( + '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component', + () => jest.fn().mockReturnValue(
TitleBreadcrumb
) +); + +jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => + jest.fn().mockImplementation(({ children }) =>
{children}
) +); + +jest.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key: string) => key, + }), +})); + +const SYSTEM_RELATION_TYPE = { + name: 'relatedTo', + displayName: 'Related To', + description: 'General associative relationship', + isSymmetric: true, + isTransitive: false, + isCrossGlossaryAllowed: true, + category: RelationCategory.Associative, + isSystemDefined: true, + // old Ant Design color that was previously stored in the backend + color: '#1890ff', + cardinality: RelationCardinality.ManyToMany, +}; + +const CUSTOM_RELATION_TYPE = { + name: 'myCustomType', + displayName: 'My Custom Type', + description: 'A user-defined relation type', + isSymmetric: false, + isTransitive: false, + isCrossGlossaryAllowed: true, + category: RelationCategory.Associative, + isSystemDefined: false, + color: '#bc1b06', + cardinality: RelationCardinality.ManyToMany, +}; + +const renderPage = async () => { + await act(async () => { + render( + + + + ); + }); +}; + +describe('GlossaryTermRelationSettingsPage', () => { + beforeEach(() => { + jest.clearAllMocks(); + (getRelationTypeUsageCounts as jest.Mock).mockResolvedValue({}); + }); + + describe('renderColorBadge — system-defined relation types', () => { + it('shows design-system color label for system-defined type even when backend returns stale Ant Design color', async () => { + (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ + relationTypes: [SYSTEM_RELATION_TYPE], + }); + + await renderPage(); + + // RELATION_META['relatedTo'].color is '#1570ef', which maps to 'label.color-blue' in COLOR_META_BY_HEX. + // The old backend color '#1890ff' is NOT in COLOR_META_BY_HEX and would fall through to show the raw hex. + // If the fix is working, we see the translated label key — not the raw stale hex. + expect(screen.getByText('label.color-blue')).toBeInTheDocument(); + expect(screen.queryByText('#1890ff')).not.toBeInTheDocument(); + }); + + it('does not display the raw stale backend hex for system-defined types', async () => { + (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ + relationTypes: [SYSTEM_RELATION_TYPE], + }); + + await renderPage(); + + expect(screen.queryByText('#1890ff')).not.toBeInTheDocument(); + }); + }); + + describe('renderColorBadge — custom relation types', () => { + it('shows user-set color for custom (non-system-defined) relation type', async () => { + (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ + relationTypes: [CUSTOM_RELATION_TYPE], + }); + + await renderPage(); + + // '#bc1b06' is in COLOR_META_BY_HEX mapped to 'label.color-orange' + expect(screen.getByText('label.color-orange')).toBeInTheDocument(); + }); + + it('shows raw hex when custom type color is not in design palette', async () => { + (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ + relationTypes: [ + { ...CUSTOM_RELATION_TYPE, color: '#abcdef' }, + ], + }); + + await renderPage(); + + expect(screen.getByText('#abcdef')).toBeInTheDocument(); + }); + }); + + describe('renderColorBadge — no color set', () => { + it('renders a dash when neither record.color nor RELATION_META has a color for the name', async () => { + (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ + relationTypes: [ + { + ...CUSTOM_RELATION_TYPE, + name: 'unknownType', + color: undefined, + }, + ], + }); + + await renderPage(); + + expect(screen.getByText('—')).toBeInTheDocument(); + }); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.tsx index 59ab11672177..a56592040df7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.tsx @@ -218,7 +218,9 @@ function GlossaryTermRelationSettingsPage() { const renderColorBadge = useCallback( (record: GlossaryTermRelationType) => { - const effectiveColor = record.color ?? RELATION_META[record.name]?.color; + const effectiveColor = record.isSystemDefined + ? (RELATION_META[record.name]?.color ?? record.color) + : (record.color ?? RELATION_META[record.name]?.color); if (!effectiveColor) { return ( From d84d3917ff2a5f7529f7587afe1fb379c4ed45a9 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:37:34 +0530 Subject: [PATCH 2/9] Remove dev-only MySQL 2.0.0 migration script --- .../mysql/postDataMigrationSQLScript.sql | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 bootstrap/sql/migrations/native/2.0.0/mysql/postDataMigrationSQLScript.sql diff --git a/bootstrap/sql/migrations/native/2.0.0/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/2.0.0/mysql/postDataMigrationSQLScript.sql deleted file mode 100644 index 358bf777513c..000000000000 --- a/bootstrap/sql/migrations/native/2.0.0/mysql/postDataMigrationSQLScript.sql +++ /dev/null @@ -1,74 +0,0 @@ --- Update system-defined glossary term relation type colors to match the design system. --- The old colors were Ant Design palette values; the new colors match the frontend RELATION_META constants. --- Each statement uses JSON_SEARCH to locate the named relation type by index, then JSON_SET to update its color. --- REGEXP_REPLACE('[^0-9]', '') strips the "$[N]" path syntax returned by JSON_SEARCH down to the bare index N. - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'relatedTo')), '[^0-9]', ''), '].color'), - '#1570ef') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'relatedTo') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'synonym')), '[^0-9]', ''), '].color'), - '#b42318') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'synonym') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'antonym')), '[^0-9]', ''), '].color'), - '#b54708') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'antonym') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'broader')), '[^0-9]', ''), '].color'), - '#067647') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'broader') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'narrower')), '[^0-9]', ''), '].color'), - '#4e5ba6') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'narrower') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'partOf')), '[^0-9]', ''), '].color'), - '#026aa2') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'partOf') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'hasPart')), '[^0-9]', ''), '].color'), - '#155eef') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'hasPart') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'calculatedFrom')), '[^0-9]', ''), '].color'), - '#6938ef') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'calculatedFrom') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'usedToCalculate')), '[^0-9]', ''), '].color'), - '#ba24d5') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'usedToCalculate') IS NOT NULL; - -UPDATE openmetadata_settings -SET json = JSON_SET(json, - CONCAT('$.relationTypes[', REGEXP_REPLACE(JSON_UNQUOTE(JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'seeAlso')), '[^0-9]', ''), '].color'), - '#c11574') -WHERE configType = 'glossaryTermRelationSettings' - AND JSON_SEARCH(JSON_EXTRACT(json, '$.relationTypes[*].name'), 'one', 'seeAlso') IS NOT NULL; From 4c08d1f7a2b64ca2b23a407026bd2bf612165387 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Wed, 8 Apr 2026 00:38:26 +0530 Subject: [PATCH 3/9] Remove dev-only PostgreSQL 2.0.0 migration script --- .../postgres/postDataMigrationSQLScript.sql | 123 ------------------ 1 file changed, 123 deletions(-) delete mode 100644 bootstrap/sql/migrations/native/2.0.0/postgres/postDataMigrationSQLScript.sql diff --git a/bootstrap/sql/migrations/native/2.0.0/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/2.0.0/postgres/postDataMigrationSQLScript.sql deleted file mode 100644 index 21752ecaf585..000000000000 --- a/bootstrap/sql/migrations/native/2.0.0/postgres/postDataMigrationSQLScript.sql +++ /dev/null @@ -1,123 +0,0 @@ --- Update system-defined glossary term relation type colors to match the design system. --- The old colors were Ant Design palette values; the new colors match the frontend RELATION_META constants. --- Each CTE finds the 0-based array index of the named relation type, then jsonb_set updates its color. - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'relatedTo' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#1570ef"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'synonym' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#b42318"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'antonym' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#b54708"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'broader' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#067647"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'narrower' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#4e5ba6"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'partOf' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#026aa2"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'hasPart' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#155eef"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'calculatedFrom' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#6938ef"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'usedToCalculate' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#ba24d5"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; - -WITH idx AS ( - SELECT (t.idx - 1) AS i - FROM openmetadata_settings, - jsonb_array_elements(json::jsonb->'relationTypes') WITH ORDINALITY AS t(elem, idx) - WHERE configtype = 'glossaryTermRelationSettings' - AND elem->>'name' = 'seeAlso' -) -UPDATE openmetadata_settings -SET json = jsonb_set(json::jsonb, ('{relationTypes,' || idx.i || ',color}')::text[], '"#c11574"') -FROM idx -WHERE configtype = 'glossaryTermRelationSettings'; From a0da00bfc969713c06d209d7fd3ade3b0f10ee74 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Fri, 10 Apr 2026 15:10:16 +0530 Subject: [PATCH 4/9] Fix: align glossary term relation settings colors and remove duplicate 1.13.0 migration; Remove glossary term relation migrations mistakenly re-added in 1.13.0 and update relation type colors in the 1.14.0 migration INSERT to use design system tokens instead of old Ant Design colors. --- .../mysql/postDataMigrationSQLScript.sql | 31 ------------------- .../postgres/postDataMigrationSQLScript.sql | 31 +------------------ .../mysql/postDataMigrationSQLScript.sql | 2 +- .../postgres/postDataMigrationSQLScript.sql | 2 +- 4 files changed, 3 insertions(+), 63 deletions(-) diff --git a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql index afa338d52181..472a85d15a25 100644 --- a/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/mysql/postDataMigrationSQLScript.sql @@ -36,34 +36,3 @@ WHERE JSON_UNQUOTE(JSON_EXTRACT(json, '$.serviceType')) = 'Iceberg'; UPDATE stored_procedure_entity SET json = JSON_SET(json, '$.serviceType', 'CustomDatabase') WHERE JSON_UNQUOTE(JSON_EXTRACT(json, '$.serviceType')) = 'Iceberg'; - - --- Migrate existing glossary term RELATED_TO relationships to include relationType --- For backward compatibility, existing relations without a relationType are set to "relatedTo" - -UPDATE entity_relationship -SET json = JSON_SET(COALESCE(json, '{}'), '$.relationType', 'relatedTo') -WHERE fromEntity = 'glossaryTerm' - AND toEntity = 'glossaryTerm' - AND relation = 15 - AND (json IS NULL OR JSON_EXTRACT(json, '$.relationType') IS NULL); - --- Insert default glossary term relation settings if they don't exist --- This preserves any existing user customizations -INSERT INTO openmetadata_settings (configType, json) -SELECT 'glossaryTermRelationSettings', '{"relationTypes":[{"name":"relatedTo","displayName":"Related To","description":"General association between terms that are conceptually connected.","rdfPredicate":"https://open-metadata.org/ontology/relatedTo","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#1890ff"},{"name":"synonym","displayName":"Synonym","description":"Terms that have the same meaning and can be used interchangeably.","rdfPredicate":"http://www.w3.org/2004/02/skos/core#exactMatch","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"equivalence","isSystemDefined":true,"color":"#722ed1"},{"name":"antonym","displayName":"Antonym","description":"Terms that have opposite meanings.","rdfPredicate":"https://open-metadata.org/ontology/antonym","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#f5222d"},{"name":"broader","displayName":"Broader","description":"A more general term (hypernym).","inverseRelation":"narrower","rdfPredicate":"http://www.w3.org/2004/02/skos/core#broader","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#597ef7"},{"name":"narrower","displayName":"Narrower","description":"A more specific term (hyponym).","inverseRelation":"broader","rdfPredicate":"http://www.w3.org/2004/02/skos/core#narrower","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#85a5ff"},{"name":"partOf","displayName":"Part Of","description":"This term is a part or component of another term.","inverseRelation":"hasPart","rdfPredicate":"https://open-metadata.org/ontology/partOf","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#13c2c2"},{"name":"hasPart","displayName":"Has Part","description":"This term has the other term as a part or component.","inverseRelation":"partOf","rdfPredicate":"https://open-metadata.org/ontology/hasPart","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#36cfc9"},{"name":"calculatedFrom","displayName":"Calculated From","description":"This term/metric is calculated or derived from another term.","inverseRelation":"usedToCalculate","rdfPredicate":"https://open-metadata.org/ontology/calculatedFrom","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#faad14"},{"name":"usedToCalculate","displayName":"Used To Calculate","description":"This term is used in the calculation of another term.","inverseRelation":"calculatedFrom","rdfPredicate":"https://open-metadata.org/ontology/usedToCalculate","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#ffc53d"},{"name":"seeAlso","displayName":"See Also","description":"Related term that may provide additional context.","rdfPredicate":"http://www.w3.org/2000/01/rdf-schema#seeAlso","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#eb2f96"}]}' -WHERE NOT EXISTS ( - SELECT 1 FROM openmetadata_settings WHERE configType = 'glossaryTermRelationSettings' -); - --- Strip stale relatedTerms from glossary term entity JSON. --- relatedTerms is now loaded from entity_relationship table, not from entity JSON. --- Old data stored relatedTerms as EntityReference objects which fail to deserialize as TermRelation. -UPDATE glossary_term_entity -SET json = JSON_REMOVE(json, '$.relatedTerms') -WHERE JSON_EXTRACT(json, '$.relatedTerms') IS NOT NULL; - --- Backfill conceptMappings for existing glossary terms -UPDATE glossary_term_entity -SET json = JSON_SET(COALESCE(json, '{}'), '$.conceptMappings', JSON_ARRAY()) -WHERE JSON_EXTRACT(json, '$.conceptMappings') IS NULL; diff --git a/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql index 3c1e8811796e..19d458757ccb 100644 --- a/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.13.0/postgres/postDataMigrationSQLScript.sql @@ -37,33 +37,4 @@ WHERE json->>'serviceType' = 'Iceberg'; UPDATE stored_procedure_entity SET json = jsonb_set(json::jsonb, '{serviceType}', '"CustomDatabase"')::json -WHERE json->>'serviceType' = 'Iceberg'; --- Migrate existing glossary term RELATED_TO relationships to include relationType --- For backward compatibility, existing relations without a relationType are set to "relatedTo" - -UPDATE entity_relationship -SET json = jsonb_set(COALESCE(json::jsonb, '{}'::jsonb), '{relationType}', '"relatedTo"') -WHERE fromentity = 'glossaryTerm' - AND toentity = 'glossaryTerm' - AND relation = 15 - AND (json IS NULL OR json::jsonb->>'relationType' IS NULL); - --- Insert default glossary term relation settings if they don't exist --- This preserves any existing user customizations -INSERT INTO openmetadata_settings (configtype, json) -SELECT 'glossaryTermRelationSettings', '{"relationTypes":[{"name":"relatedTo","displayName":"Related To","description":"General association between terms that are conceptually connected.","rdfPredicate":"https://open-metadata.org/ontology/relatedTo","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#1890ff"},{"name":"synonym","displayName":"Synonym","description":"Terms that have the same meaning and can be used interchangeably.","rdfPredicate":"http://www.w3.org/2004/02/skos/core#exactMatch","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"equivalence","isSystemDefined":true,"color":"#722ed1"},{"name":"antonym","displayName":"Antonym","description":"Terms that have opposite meanings.","rdfPredicate":"https://open-metadata.org/ontology/antonym","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#f5222d"},{"name":"broader","displayName":"Broader","description":"A more general term (hypernym).","inverseRelation":"narrower","rdfPredicate":"http://www.w3.org/2004/02/skos/core#broader","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#597ef7"},{"name":"narrower","displayName":"Narrower","description":"A more specific term (hyponym).","inverseRelation":"broader","rdfPredicate":"http://www.w3.org/2004/02/skos/core#narrower","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#85a5ff"},{"name":"partOf","displayName":"Part Of","description":"This term is a part or component of another term.","inverseRelation":"hasPart","rdfPredicate":"https://open-metadata.org/ontology/partOf","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#13c2c2"},{"name":"hasPart","displayName":"Has Part","description":"This term has the other term as a part or component.","inverseRelation":"partOf","rdfPredicate":"https://open-metadata.org/ontology/hasPart","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#36cfc9"},{"name":"calculatedFrom","displayName":"Calculated From","description":"This term/metric is calculated or derived from another term.","inverseRelation":"usedToCalculate","rdfPredicate":"https://open-metadata.org/ontology/calculatedFrom","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#faad14"},{"name":"usedToCalculate","displayName":"Used To Calculate","description":"This term is used in the calculation of another term.","inverseRelation":"calculatedFrom","rdfPredicate":"https://open-metadata.org/ontology/usedToCalculate","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#ffc53d"},{"name":"seeAlso","displayName":"See Also","description":"Related term that may provide additional context.","rdfPredicate":"http://www.w3.org/2000/01/rdf-schema#seeAlso","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#eb2f96"}]}'::jsonb -WHERE NOT EXISTS ( - SELECT 1 FROM openmetadata_settings WHERE configtype = 'glossaryTermRelationSettings' -); - --- Strip stale relatedTerms from glossary term entity JSON. --- relatedTerms is now loaded from entity_relationship table, not from entity JSON. --- Old data stored relatedTerms as EntityReference objects which fail to deserialize as TermRelation. -UPDATE glossary_term_entity -SET json = (json::jsonb - 'relatedTerms')::json -WHERE jsonb_exists(json::jsonb, 'relatedTerms'); - --- Backfill conceptMappings for existing glossary terms -UPDATE glossary_term_entity -SET json = jsonb_set(COALESCE(json::jsonb, '{}'::jsonb), '{conceptMappings}', '[]'::jsonb) -WHERE json IS NULL OR json::jsonb->'conceptMappings' IS NULL; +WHERE json->>'serviceType' = 'Iceberg'; \ No newline at end of file diff --git a/bootstrap/sql/migrations/native/1.14.0/mysql/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.14.0/mysql/postDataMigrationSQLScript.sql index 3dafde01b07e..0a95477d29b9 100644 --- a/bootstrap/sql/migrations/native/1.14.0/mysql/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.14.0/mysql/postDataMigrationSQLScript.sql @@ -11,7 +11,7 @@ WHERE fromEntity = 'glossaryTerm' -- Insert default glossary term relation settings if they don't exist -- This preserves any existing user customizations INSERT INTO openmetadata_settings (configType, json) -SELECT 'glossaryTermRelationSettings', '{"relationTypes":[{"name":"relatedTo","displayName":"Related To","description":"General association between terms that are conceptually connected.","rdfPredicate":"https://open-metadata.org/ontology/relatedTo","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#1890ff"},{"name":"synonym","displayName":"Synonym","description":"Terms that have the same meaning and can be used interchangeably.","rdfPredicate":"http://www.w3.org/2004/02/skos/core#exactMatch","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"equivalence","isSystemDefined":true,"color":"#722ed1"},{"name":"antonym","displayName":"Antonym","description":"Terms that have opposite meanings.","rdfPredicate":"https://open-metadata.org/ontology/antonym","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#f5222d"},{"name":"broader","displayName":"Broader","description":"A more general term (hypernym).","inverseRelation":"narrower","rdfPredicate":"http://www.w3.org/2004/02/skos/core#broader","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#597ef7"},{"name":"narrower","displayName":"Narrower","description":"A more specific term (hyponym).","inverseRelation":"broader","rdfPredicate":"http://www.w3.org/2004/02/skos/core#narrower","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#85a5ff"},{"name":"partOf","displayName":"Part Of","description":"This term is a part or component of another term.","inverseRelation":"hasPart","rdfPredicate":"https://open-metadata.org/ontology/partOf","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#13c2c2"},{"name":"hasPart","displayName":"Has Part","description":"This term has the other term as a part or component.","inverseRelation":"partOf","rdfPredicate":"https://open-metadata.org/ontology/hasPart","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#36cfc9"},{"name":"calculatedFrom","displayName":"Calculated From","description":"This term/metric is calculated or derived from another term.","inverseRelation":"usedToCalculate","rdfPredicate":"https://open-metadata.org/ontology/calculatedFrom","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#faad14"},{"name":"usedToCalculate","displayName":"Used To Calculate","description":"This term is used in the calculation of another term.","inverseRelation":"calculatedFrom","rdfPredicate":"https://open-metadata.org/ontology/usedToCalculate","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#ffc53d"},{"name":"seeAlso","displayName":"See Also","description":"Related term that may provide additional context.","rdfPredicate":"http://www.w3.org/2000/01/rdf-schema#seeAlso","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#eb2f96"}]}' +SELECT 'glossaryTermRelationSettings', '{"relationTypes":[{"name":"relatedTo","displayName":"Related To","description":"General association between terms that are conceptually connected.","rdfPredicate":"https://open-metadata.org/ontology/relatedTo","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#1570ef"},{"name":"synonym","displayName":"Synonym","description":"Terms that have the same meaning and can be used interchangeably.","rdfPredicate":"http://www.w3.org/2004/02/skos/core#exactMatch","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"equivalence","isSystemDefined":true,"color":"#b42318"},{"name":"antonym","displayName":"Antonym","description":"Terms that have opposite meanings.","rdfPredicate":"https://open-metadata.org/ontology/antonym","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#b54708"},{"name":"broader","displayName":"Broader","description":"A more general term (hypernym).","inverseRelation":"narrower","rdfPredicate":"http://www.w3.org/2004/02/skos/core#broader","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#067647"},{"name":"narrower","displayName":"Narrower","description":"A more specific term (hyponym).","inverseRelation":"broader","rdfPredicate":"http://www.w3.org/2004/02/skos/core#narrower","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#4e5ba6"},{"name":"partOf","displayName":"Part Of","description":"This term is a part or component of another term.","inverseRelation":"hasPart","rdfPredicate":"https://open-metadata.org/ontology/partOf","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#026aa2"},{"name":"hasPart","displayName":"Has Part","description":"This term has the other term as a part or component.","inverseRelation":"partOf","rdfPredicate":"https://open-metadata.org/ontology/hasPart","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#155eef"},{"name":"calculatedFrom","displayName":"Calculated From","description":"This term/metric is calculated or derived from another term.","inverseRelation":"usedToCalculate","rdfPredicate":"https://open-metadata.org/ontology/calculatedFrom","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#6938ef"},{"name":"usedToCalculate","displayName":"Used To Calculate","description":"This term is used in the calculation of another term.","inverseRelation":"calculatedFrom","rdfPredicate":"https://open-metadata.org/ontology/usedToCalculate","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#ba24d5"},{"name":"seeAlso","displayName":"See Also","description":"Related term that may provide additional context.","rdfPredicate":"http://www.w3.org/2000/01/rdf-schema#seeAlso","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#c11574"}]}' WHERE NOT EXISTS ( SELECT 1 FROM openmetadata_settings WHERE configType = 'glossaryTermRelationSettings' ); diff --git a/bootstrap/sql/migrations/native/1.14.0/postgres/postDataMigrationSQLScript.sql b/bootstrap/sql/migrations/native/1.14.0/postgres/postDataMigrationSQLScript.sql index 033ab9e95e0e..217566fe0565 100644 --- a/bootstrap/sql/migrations/native/1.14.0/postgres/postDataMigrationSQLScript.sql +++ b/bootstrap/sql/migrations/native/1.14.0/postgres/postDataMigrationSQLScript.sql @@ -11,7 +11,7 @@ WHERE fromentity = 'glossaryTerm' -- Insert default glossary term relation settings if they don't exist -- This preserves any existing user customizations INSERT INTO openmetadata_settings (configtype, json) -SELECT 'glossaryTermRelationSettings', '{"relationTypes":[{"name":"relatedTo","displayName":"Related To","description":"General association between terms that are conceptually connected.","rdfPredicate":"https://open-metadata.org/ontology/relatedTo","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#1890ff"},{"name":"synonym","displayName":"Synonym","description":"Terms that have the same meaning and can be used interchangeably.","rdfPredicate":"http://www.w3.org/2004/02/skos/core#exactMatch","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"equivalence","isSystemDefined":true,"color":"#722ed1"},{"name":"antonym","displayName":"Antonym","description":"Terms that have opposite meanings.","rdfPredicate":"https://open-metadata.org/ontology/antonym","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#f5222d"},{"name":"broader","displayName":"Broader","description":"A more general term (hypernym).","inverseRelation":"narrower","rdfPredicate":"http://www.w3.org/2004/02/skos/core#broader","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#597ef7"},{"name":"narrower","displayName":"Narrower","description":"A more specific term (hyponym).","inverseRelation":"broader","rdfPredicate":"http://www.w3.org/2004/02/skos/core#narrower","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#85a5ff"},{"name":"partOf","displayName":"Part Of","description":"This term is a part or component of another term.","inverseRelation":"hasPart","rdfPredicate":"https://open-metadata.org/ontology/partOf","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#13c2c2"},{"name":"hasPart","displayName":"Has Part","description":"This term has the other term as a part or component.","inverseRelation":"partOf","rdfPredicate":"https://open-metadata.org/ontology/hasPart","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#36cfc9"},{"name":"calculatedFrom","displayName":"Calculated From","description":"This term/metric is calculated or derived from another term.","inverseRelation":"usedToCalculate","rdfPredicate":"https://open-metadata.org/ontology/calculatedFrom","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#faad14"},{"name":"usedToCalculate","displayName":"Used To Calculate","description":"This term is used in the calculation of another term.","inverseRelation":"calculatedFrom","rdfPredicate":"https://open-metadata.org/ontology/usedToCalculate","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#ffc53d"},{"name":"seeAlso","displayName":"See Also","description":"Related term that may provide additional context.","rdfPredicate":"http://www.w3.org/2000/01/rdf-schema#seeAlso","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#eb2f96"}]}'::jsonb +SELECT 'glossaryTermRelationSettings', '{"relationTypes":[{"name":"relatedTo","displayName":"Related To","description":"General association between terms that are conceptually connected.","rdfPredicate":"https://open-metadata.org/ontology/relatedTo","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#1570ef"},{"name":"synonym","displayName":"Synonym","description":"Terms that have the same meaning and can be used interchangeably.","rdfPredicate":"http://www.w3.org/2004/02/skos/core#exactMatch","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"equivalence","isSystemDefined":true,"color":"#b42318"},{"name":"antonym","displayName":"Antonym","description":"Terms that have opposite meanings.","rdfPredicate":"https://open-metadata.org/ontology/antonym","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#b54708"},{"name":"broader","displayName":"Broader","description":"A more general term (hypernym).","inverseRelation":"narrower","rdfPredicate":"http://www.w3.org/2004/02/skos/core#broader","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#067647"},{"name":"narrower","displayName":"Narrower","description":"A more specific term (hyponym).","inverseRelation":"broader","rdfPredicate":"http://www.w3.org/2004/02/skos/core#narrower","isSymmetric":false,"isTransitive":true,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#4e5ba6"},{"name":"partOf","displayName":"Part Of","description":"This term is a part or component of another term.","inverseRelation":"hasPart","rdfPredicate":"https://open-metadata.org/ontology/partOf","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#026aa2"},{"name":"hasPart","displayName":"Has Part","description":"This term has the other term as a part or component.","inverseRelation":"partOf","rdfPredicate":"https://open-metadata.org/ontology/hasPart","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"hierarchical","isSystemDefined":true,"color":"#155eef"},{"name":"calculatedFrom","displayName":"Calculated From","description":"This term/metric is calculated or derived from another term.","inverseRelation":"usedToCalculate","rdfPredicate":"https://open-metadata.org/ontology/calculatedFrom","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#6938ef"},{"name":"usedToCalculate","displayName":"Used To Calculate","description":"This term is used in the calculation of another term.","inverseRelation":"calculatedFrom","rdfPredicate":"https://open-metadata.org/ontology/usedToCalculate","isSymmetric":false,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#ba24d5"},{"name":"seeAlso","displayName":"See Also","description":"Related term that may provide additional context.","rdfPredicate":"http://www.w3.org/2000/01/rdf-schema#seeAlso","isSymmetric":true,"isTransitive":false,"isCrossGlossaryAllowed":true,"category":"associative","isSystemDefined":true,"color":"#c11574"}]}'::jsonb WHERE NOT EXISTS ( SELECT 1 FROM openmetadata_settings WHERE configtype = 'glossaryTermRelationSettings' ); From be38ac9e6c3faf198292a1f3f278af7b356892b8 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Mon, 13 Apr 2026 08:05:41 +0530 Subject: [PATCH 5/9] fix lint --- .../GlossaryTermRelationSettings.test.tsx | 4 +--- .../GlossaryTermRelationSettings.tsx | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx index 70e4c5d634ad..8dbf21a35842 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx @@ -135,9 +135,7 @@ describe('GlossaryTermRelationSettingsPage', () => { it('shows raw hex when custom type color is not in design palette', async () => { (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ - relationTypes: [ - { ...CUSTOM_RELATION_TYPE, color: '#abcdef' }, - ], + relationTypes: [{ ...CUSTOM_RELATION_TYPE, color: '#abcdef' }], }); await renderPage(); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.tsx index a56592040df7..d8251fb33f8e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.tsx @@ -219,8 +219,8 @@ function GlossaryTermRelationSettingsPage() { const renderColorBadge = useCallback( (record: GlossaryTermRelationType) => { const effectiveColor = record.isSystemDefined - ? (RELATION_META[record.name]?.color ?? record.color) - : (record.color ?? RELATION_META[record.name]?.color); + ? RELATION_META[record.name]?.color ?? record.color + : record.color ?? RELATION_META[record.name]?.color; if (!effectiveColor) { return ( From 2b19cd91b3f6f760a65661e16fa91ccd11e53d60 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Mon, 13 Apr 2026 08:32:01 +0530 Subject: [PATCH 6/9] add more test --- .../it/tests/GlossaryTermRelationSettingsIT.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermRelationSettingsIT.java b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermRelationSettingsIT.java index 932ed4b8f57f..f0a192f38ec0 100644 --- a/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermRelationSettingsIT.java +++ b/openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/GlossaryTermRelationSettingsIT.java @@ -14,7 +14,9 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.parallel.Execution; @@ -482,11 +484,13 @@ void test_systemDefinedRelationTypesHaveCorrectDesignSystemColors() throws Excep "usedToCalculate", "#ba24d5", "seeAlso", "#c11574"); + Set verified = new HashSet<>(); for (JsonNode type : relationTypes) { String name = type.get("name").asText(); if (!expectedColors.containsKey(name)) { continue; } + verified.add(name); JsonNode colorNode = type.get("color"); assertNotNull(colorNode, "color should exist for system-defined type: " + name); String actualColor = colorNode.asText().toLowerCase(); @@ -502,6 +506,12 @@ void test_systemDefinedRelationTypesHaveCorrectDesignSystemColors() throws Excep + actualColor + ". Old Ant Design colors (e.g. #1890ff, #722ed1) indicate the 2.0.0 migration did not run."); } + + Set missing = new HashSet<>(expectedColors.keySet()); + missing.removeAll(verified); + assertTrue( + missing.isEmpty(), + "All 10 system-defined relation types must be present. Missing: " + missing); } @Test From 2ae02da85f21537aaad45bec7eff9b30b588627e Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Mon, 13 Apr 2026 08:39:12 +0530 Subject: [PATCH 7/9] address feedback --- .../GlossaryTermRelationSettings.test.tsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx index 8dbf21a35842..a853b41fab4b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx @@ -11,7 +11,7 @@ * limitations under the License. */ -import { act, render, screen } from '@testing-library/react'; +import { act, render, screen, waitFor } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { RelationCardinality, @@ -106,8 +106,11 @@ describe('GlossaryTermRelationSettingsPage', () => { // RELATION_META['relatedTo'].color is '#1570ef', which maps to 'label.color-blue' in COLOR_META_BY_HEX. // The old backend color '#1890ff' is NOT in COLOR_META_BY_HEX and would fall through to show the raw hex. // If the fix is working, we see the translated label key — not the raw stale hex. - expect(screen.getByText('label.color-blue')).toBeInTheDocument(); - expect(screen.queryByText('#1890ff')).not.toBeInTheDocument(); + expect(await screen.findByText('label.color-blue')).toBeInTheDocument(); + + await waitFor(() => { + expect(screen.queryByText('#1890ff')).not.toBeInTheDocument(); + }); }); it('does not display the raw stale backend hex for system-defined types', async () => { @@ -117,7 +120,9 @@ describe('GlossaryTermRelationSettingsPage', () => { await renderPage(); - expect(screen.queryByText('#1890ff')).not.toBeInTheDocument(); + await waitFor(() => { + expect(screen.queryByText('#1890ff')).not.toBeInTheDocument(); + }); }); }); @@ -130,7 +135,9 @@ describe('GlossaryTermRelationSettingsPage', () => { await renderPage(); // '#bc1b06' is in COLOR_META_BY_HEX mapped to 'label.color-orange' - expect(screen.getByText('label.color-orange')).toBeInTheDocument(); + expect( + await screen.findByText('label.color-orange') + ).toBeInTheDocument(); }); it('shows raw hex when custom type color is not in design palette', async () => { @@ -140,7 +147,7 @@ describe('GlossaryTermRelationSettingsPage', () => { await renderPage(); - expect(screen.getByText('#abcdef')).toBeInTheDocument(); + expect(await screen.findByText('#abcdef')).toBeInTheDocument(); }); }); @@ -158,7 +165,7 @@ describe('GlossaryTermRelationSettingsPage', () => { await renderPage(); - expect(screen.getByText('—')).toBeInTheDocument(); + expect(await screen.findByText('—')).toBeInTheDocument(); }); }); }); From 5b977140d2d0db809404b984e2a1fa77df7ad4ef Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Mon, 13 Apr 2026 08:53:03 +0530 Subject: [PATCH 8/9] fix prettier formatting in test file Co-Authored-By: Claude Opus 4.6 (1M context) --- .../GlossaryTermRelationSettings.test.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx index a853b41fab4b..f87bc99b98e2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx @@ -135,9 +135,7 @@ describe('GlossaryTermRelationSettingsPage', () => { await renderPage(); // '#bc1b06' is in COLOR_META_BY_HEX mapped to 'label.color-orange' - expect( - await screen.findByText('label.color-orange') - ).toBeInTheDocument(); + expect(await screen.findByText('label.color-orange')).toBeInTheDocument(); }); it('shows raw hex when custom type color is not in design palette', async () => { From ab6ebbd47c797f714c75425f30deb2f70eee2331 Mon Sep 17 00:00:00 2001 From: sonika-shah <58761340+sonika-shah@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:47:16 +0530 Subject: [PATCH 9/9] remove GlossaryTermRelationSettings test file from branch Co-Authored-By: Claude Opus 4.6 (1M context) --- .../GlossaryTermRelationSettings.test.tsx | 169 ------------------ 1 file changed, 169 deletions(-) delete mode 100644 openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx deleted file mode 100644 index f87bc99b98e2..000000000000 --- a/openmetadata-ui/src/main/resources/ui/src/pages/GlossaryTermRelationSettings/GlossaryTermRelationSettings.test.tsx +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2025 Collate. - * Licensed 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. - */ - -import { act, render, screen, waitFor } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import { - RelationCardinality, - RelationCategory, -} from '../../generated/configuration/glossaryTermRelationSettings'; -import { - getGlossaryTermRelationSettings, - getRelationTypeUsageCounts, -} from '../../rest/glossaryAPI'; -import GlossaryTermRelationSettingsPage from './GlossaryTermRelationSettings'; - -jest.mock('../../rest/glossaryAPI', () => ({ - getGlossaryTermRelationSettings: jest.fn(), - getRelationTypeUsageCounts: jest.fn(), - updateGlossaryTermRelationSettings: jest.fn(), -})); - -jest.mock('../../hooks/authHooks', () => ({ - useAuth: jest.fn().mockReturnValue({ isAdminUser: true }), -})); - -jest.mock('../../utils/GlobalSettingsUtils', () => ({ - getSettingPageEntityBreadCrumb: jest.fn().mockReturnValue([]), -})); - -jest.mock( - '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component', - () => jest.fn().mockReturnValue(
TitleBreadcrumb
) -); - -jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => - jest.fn().mockImplementation(({ children }) =>
{children}
) -); - -jest.mock('react-i18next', () => ({ - useTranslation: () => ({ - t: (key: string) => key, - }), -})); - -const SYSTEM_RELATION_TYPE = { - name: 'relatedTo', - displayName: 'Related To', - description: 'General associative relationship', - isSymmetric: true, - isTransitive: false, - isCrossGlossaryAllowed: true, - category: RelationCategory.Associative, - isSystemDefined: true, - // old Ant Design color that was previously stored in the backend - color: '#1890ff', - cardinality: RelationCardinality.ManyToMany, -}; - -const CUSTOM_RELATION_TYPE = { - name: 'myCustomType', - displayName: 'My Custom Type', - description: 'A user-defined relation type', - isSymmetric: false, - isTransitive: false, - isCrossGlossaryAllowed: true, - category: RelationCategory.Associative, - isSystemDefined: false, - color: '#bc1b06', - cardinality: RelationCardinality.ManyToMany, -}; - -const renderPage = async () => { - await act(async () => { - render( - - - - ); - }); -}; - -describe('GlossaryTermRelationSettingsPage', () => { - beforeEach(() => { - jest.clearAllMocks(); - (getRelationTypeUsageCounts as jest.Mock).mockResolvedValue({}); - }); - - describe('renderColorBadge — system-defined relation types', () => { - it('shows design-system color label for system-defined type even when backend returns stale Ant Design color', async () => { - (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ - relationTypes: [SYSTEM_RELATION_TYPE], - }); - - await renderPage(); - - // RELATION_META['relatedTo'].color is '#1570ef', which maps to 'label.color-blue' in COLOR_META_BY_HEX. - // The old backend color '#1890ff' is NOT in COLOR_META_BY_HEX and would fall through to show the raw hex. - // If the fix is working, we see the translated label key — not the raw stale hex. - expect(await screen.findByText('label.color-blue')).toBeInTheDocument(); - - await waitFor(() => { - expect(screen.queryByText('#1890ff')).not.toBeInTheDocument(); - }); - }); - - it('does not display the raw stale backend hex for system-defined types', async () => { - (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ - relationTypes: [SYSTEM_RELATION_TYPE], - }); - - await renderPage(); - - await waitFor(() => { - expect(screen.queryByText('#1890ff')).not.toBeInTheDocument(); - }); - }); - }); - - describe('renderColorBadge — custom relation types', () => { - it('shows user-set color for custom (non-system-defined) relation type', async () => { - (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ - relationTypes: [CUSTOM_RELATION_TYPE], - }); - - await renderPage(); - - // '#bc1b06' is in COLOR_META_BY_HEX mapped to 'label.color-orange' - expect(await screen.findByText('label.color-orange')).toBeInTheDocument(); - }); - - it('shows raw hex when custom type color is not in design palette', async () => { - (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ - relationTypes: [{ ...CUSTOM_RELATION_TYPE, color: '#abcdef' }], - }); - - await renderPage(); - - expect(await screen.findByText('#abcdef')).toBeInTheDocument(); - }); - }); - - describe('renderColorBadge — no color set', () => { - it('renders a dash when neither record.color nor RELATION_META has a color for the name', async () => { - (getGlossaryTermRelationSettings as jest.Mock).mockResolvedValue({ - relationTypes: [ - { - ...CUSTOM_RELATION_TYPE, - name: 'unknownType', - color: undefined, - }, - ], - }); - - await renderPage(); - - expect(await screen.findByText('—')).toBeInTheDocument(); - }); - }); -});