Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Post data migration script for OpenMetadata 2.0.3
-- Backfill for glossaryTermRelationSettings cardinality runs in the Java migration step.
2 changes: 2 additions & 0 deletions bootstrap/sql/migrations/native/2.0.3/mysql/schemaChanges.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Schema changes for OpenMetadata 2.0.3
-- No DDL changes; data backfill runs in the Java migration step.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Post data migration script for OpenMetadata 2.0.3
-- Backfill for glossaryTermRelationSettings cardinality runs in the Java migration step.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Schema changes for OpenMetadata 2.0.3
-- No DDL changes; data backfill runs in the Java migration step.
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,71 @@ void test_glossaryTermRelationSettingsExist() throws Exception {
assertNotNull(relationType.get("category"), "category should exist for " + name);
assertNotNull(relationType.get("isSymmetric"), "isSymmetric should exist for " + name);
assertNotNull(relationType.get("isTransitive"), "isTransitive should exist for " + name);
JsonNode cardinalityNode = relationType.get("cardinality");
assertNotNull(cardinalityNode, "cardinality should exist for " + name);
assertFalse(cardinalityNode.isNull(), "cardinality should not be null for " + name);
}

assertTrue(hasRelatedTo, "Should have 'relatedTo' relation type");
assertTrue(hasSynonym, "Should have 'synonym' relation type");
assertTrue(hasBroader, "Should have 'broader' relation type");
}

@Test
@ResourceLock(
value = SharedResourceLocks.GLOSSARY_TERM_RELATION_SETTINGS,
mode = ResourceAccessMode.READ)
void test_systemRelationTypesAreUnboundedManyToMany() throws Exception {
JsonNode settings = getSettings();
JsonNode relationTypes = settings.get("config_value").get("relationTypes");

Set<String> systemNames =
Set.of(
"relatedTo",
"synonym",
"antonym",
"broader",
"narrower",
"partOf",
"hasPart",
"calculatedFrom",
"usedToCalculate",
"seeAlso");

Set<String> verified = new HashSet<>();
for (JsonNode type : relationTypes) {
String name = type.get("name").asText();
if (!systemNames.contains(name)) {
continue;
}
verified.add(name);

JsonNode cardinality = type.get("cardinality");
assertNotNull(cardinality, "cardinality should exist for " + name);
assertFalse(cardinality.isNull(), "cardinality should not be null for " + name);
assertEquals(
"MANY_TO_MANY",
cardinality.asText(),
"System relation '" + name + "' must be MANY_TO_MANY (unbounded, no enforcement)");

// MANY_TO_MANY carries no bounds - sourceMax/targetMax must stay null so no cardinality
// cap is enforced on terms that already have many of these relations.
JsonNode sourceMax = type.get("sourceMax");
JsonNode targetMax = type.get("targetMax");
assertTrue(
sourceMax == null || sourceMax.isNull(),
"System relation '" + name + "' must have null sourceMax, got: " + sourceMax);
assertTrue(
targetMax == null || targetMax.isNull(),
"System relation '" + name + "' must have null targetMax, got: " + targetMax);
}

Set<String> missing = new HashSet<>(systemNames);
missing.removeAll(verified);
assertTrue(
missing.isEmpty(), "All 10 system relation types must be present. Missing: " + missing);
}

@Test
@ResourceLock(
value = SharedResourceLocks.GLOSSARY_TERM_RELATION_SETTINGS,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.openmetadata.service.migration.mysql.v203;

import lombok.SneakyThrows;
import org.openmetadata.service.migration.api.MigrationProcessImpl;
import org.openmetadata.service.migration.utils.MigrationFile;
import org.openmetadata.service.migration.utils.v203.MigrationUtil;

public class Migration extends MigrationProcessImpl {

public Migration(MigrationFile migrationFile) {
super(migrationFile);
}

@Override
@SneakyThrows
public void runDataMigration() {
MigrationUtil migrationUtil = new MigrationUtil(handle);
migrationUtil.backfillGlossaryTermRelationCardinality();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.openmetadata.service.migration.postgres.v203;

import lombok.SneakyThrows;
import org.openmetadata.service.migration.api.MigrationProcessImpl;
import org.openmetadata.service.migration.utils.MigrationFile;
import org.openmetadata.service.migration.utils.v203.MigrationUtil;

public class Migration extends MigrationProcessImpl {

public Migration(MigrationFile migrationFile) {
super(migrationFile);
}

@Override
@SneakyThrows
public void runDataMigration() {
MigrationUtil migrationUtil = new MigrationUtil(handle);
migrationUtil.backfillGlossaryTermRelationCardinality();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.openmetadata.service.migration.utils.v203;

import lombok.extern.slf4j.Slf4j;
import org.jdbi.v3.core.Handle;
import org.openmetadata.schema.configuration.GlossaryTermRelationSettings;
import org.openmetadata.schema.configuration.GlossaryTermRelationType;
import org.openmetadata.schema.configuration.RelationCardinality;
import org.openmetadata.schema.utils.JsonUtils;
import org.openmetadata.service.resources.databases.DatasourceConfig;
import org.openmetadata.service.util.GlossaryTermRelationSettingsUtil;

@Slf4j
public class MigrationUtil {

private static final String GLOSSARY_TERM_RELATION_SETTINGS = "glossaryTermRelationSettings";

// Postgres stores the settings column as jsonb and won't implicitly cast a bound string
// (::jsonb is required); MySQL's JSON column parses the string directly.
private static final String UPDATE_MYSQL =
"UPDATE openmetadata_settings SET json = :json WHERE configType = :configType";
private static final String UPDATE_POSTGRES =
"UPDATE openmetadata_settings SET json = :json::jsonb WHERE configType = :configType";

private final Handle handle;

public MigrationUtil(Handle handle) {
this.handle = handle;
}

/**
* Older installs seeded the system glossary relation types before the cardinality field existed,
* so GET returned {@code cardinality: null} and the UI fell back to MANY_TO_MANY. Persist that
* same MANY_TO_MANY on the system defaults whose cardinality is still null. Bounds are derived by
* the canonical normalizer (the settings PUT path), leaving these relations unbounded - metadata
* only, no new enforcement.
*/
public void backfillGlossaryTermRelationCardinality() {
GlossaryTermRelationSettings settings = loadSettings();
if (settings == null || settings.getRelationTypes() == null) {
return;
}
Comment on lines +37 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Oversized migration methods

The new backfill method combines loading, filtering, mutation, normalization, logging, and persistence in roughly 25 lines, and it exits through an early guard. The repository’s Java guide requires focused methods of about 15 lines or fewer and one trailing return per method. loadSettings also introduces an early return, and the new integration-test method exceeds the method-size guideline. This repository requirement must be satisfied before merging.

Context Used: AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


boolean changed = false;
for (GlossaryTermRelationType relationType : settings.getRelationTypes()) {
if (relationType != null
&& Boolean.TRUE.equals(relationType.getIsSystemDefined())
&& relationType.getCardinality() == null) {
relationType.setCardinality(RelationCardinality.MANY_TO_MANY);
GlossaryTermRelationSettingsUtil.normalize(relationType);
changed = true;
LOG.info(
"Backfilled MANY_TO_MANY cardinality on system relation '{}'", relationType.getName());
}
}

if (changed) {
persist(settings);
}
}

private GlossaryTermRelationSettings loadSettings() {
String json =
handle
.createQuery("SELECT json FROM openmetadata_settings WHERE configType = :configType")
.bind("configType", GLOSSARY_TERM_RELATION_SETTINGS)
.mapTo(String.class)
.findOne()
.orElse(null);
if (json == null) {
LOG.info("No glossaryTermRelationSettings row found; skipping cardinality backfill");
return null;
}
return JsonUtils.readValue(json, GlossaryTermRelationSettings.class);
}

private void persist(GlossaryTermRelationSettings settings) {
boolean isMySQL = Boolean.TRUE.equals(DatasourceConfig.getInstance().isMySQL());
handle
.createUpdate(isMySQL ? UPDATE_MYSQL : UPDATE_POSTGRES)
.bind("configType", GLOSSARY_TERM_RELATION_SETTINGS)
.bind("json", JsonUtils.pojoToJson(settings))
.execute();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import org.openmetadata.schema.configuration.GlossaryTermRelationType;
import org.openmetadata.schema.configuration.HistoryCleanUpConfiguration;
import org.openmetadata.schema.configuration.OpenLineageSettings;
import org.openmetadata.schema.configuration.RelationCardinality;
import org.openmetadata.schema.configuration.RelationCategory;
import org.openmetadata.schema.configuration.WorkflowSettings;
import org.openmetadata.schema.email.SmtpSettings;
Expand Down Expand Up @@ -370,6 +371,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.ASSOCIATIVE,
true,
"#1570ef",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -383,6 +385,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.EQUIVALENCE,
true,
"#b42318",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -396,6 +399,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.ASSOCIATIVE,
true,
"#b54708",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -409,6 +413,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.HIERARCHICAL,
true,
"#067647",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -422,6 +427,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.HIERARCHICAL,
true,
"#4e5ba6",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -435,6 +441,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.HIERARCHICAL,
true,
"#026aa2",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -448,6 +455,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.HIERARCHICAL,
true,
"#155eef",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -461,6 +469,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.ASSOCIATIVE,
true,
"#6938ef",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -474,6 +483,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.ASSOCIATIVE,
true,
"#ba24d5",
RelationCardinality.MANY_TO_MANY,
null,
null),
createRelationType(
Expand All @@ -487,6 +497,7 @@ private static void createDefaultConfiguration(OpenMetadataApplicationConfig app
RelationCategory.ASSOCIATIVE,
true,
"#c11574",
RelationCardinality.MANY_TO_MANY,
null,
null));

Expand Down Expand Up @@ -532,6 +543,7 @@ private static GlossaryTermRelationType createRelationType(
RelationCategory category,
boolean isSystemDefined,
String color,
RelationCardinality cardinality,
Integer sourceMax,
Integer targetMax) {
return new GlossaryTermRelationType()
Expand All @@ -545,6 +557,7 @@ private static GlossaryTermRelationType createRelationType(
.withCategory(category)
.withIsSystemDefined(isSystemDefined)
.withColor(color)
.withCardinality(cardinality)
.withSourceMax(sourceMax)
.withTargetMax(targetMax);
}
Expand Down
Loading