From e80763b414ee07895f1b17732daa54120de44105 Mon Sep 17 00:00:00 2001 From: Sai Kaushik Ponnekanti Date: Tue, 14 Apr 2026 22:18:15 -0700 Subject: [PATCH 1/2] feat: add nodeId to Dataset, DatasetVersion, and Job API response models Add computed getNodeId() methods to Dataset, DatasetVersion, and Job service models. This allows API consumers to directly use the nodeId to query the Lineage API without having to manually construct it. - Dataset: returns "dataset:namespace:name" - DatasetVersion: returns "dataset:namespace:name#version" - Job: returns "job:namespace:name" The nodeId is computed from existing fields via NodeId.of(), so no database changes are needed. Closes #1461 Signed-off-by: Sai Kaushik Ponnekanti --- .../java/marquez/service/models/Dataset.java | 5 + .../service/models/DatasetVersion.java | 11 ++ .../main/java/marquez/service/models/Job.java | 5 + .../service/models/NodeIdOnModelsTest.java | 122 ++++++++++++++++++ 4 files changed, 143 insertions(+) create mode 100644 api/src/test/java/marquez/service/models/NodeIdOnModelsTest.java diff --git a/api/src/main/java/marquez/service/models/Dataset.java b/api/src/main/java/marquez/service/models/Dataset.java index d5985f4c2c..60a7203a38 100644 --- a/api/src/main/java/marquez/service/models/Dataset.java +++ b/api/src/main/java/marquez/service/models/Dataset.java @@ -93,6 +93,11 @@ public Dataset( this.isDeleted = isDeleted; } + /** Returns the {@link NodeId} for this dataset, allowing direct queries to the Lineage API. */ + public NodeId getNodeId() { + return NodeId.of(id); + } + public Optional getLastModifiedAt() { return Optional.ofNullable(lastModifiedAt); } diff --git a/api/src/main/java/marquez/service/models/DatasetVersion.java b/api/src/main/java/marquez/service/models/DatasetVersion.java index 3575d3d6c8..1095b64171 100644 --- a/api/src/main/java/marquez/service/models/DatasetVersion.java +++ b/api/src/main/java/marquez/service/models/DatasetVersion.java @@ -23,6 +23,7 @@ import marquez.common.models.DatasetId; import marquez.common.models.DatasetName; import marquez.common.models.DatasetType; +import marquez.common.models.DatasetVersionId; import marquez.common.models.Field; import marquez.common.models.NamespaceName; import marquez.common.models.SourceName; @@ -89,6 +90,16 @@ public DatasetVersion( this.facets = (facets == null) ? ImmutableMap.of() : facets; } + /** + * Returns the {@link NodeId} for this dataset version, allowing direct queries to the Lineage + * API. The returned nodeId includes the version UUID (e.g., {@code + * dataset:namespace:name#version}). + */ + public NodeId getNodeId() { + return NodeId.of( + new DatasetVersionId(id.getNamespace(), id.getName(), version.getValue())); + } + public Optional getDescription() { return Optional.ofNullable(description); } diff --git a/api/src/main/java/marquez/service/models/Job.java b/api/src/main/java/marquez/service/models/Job.java index ccebe337db..ec5a2d92b0 100644 --- a/api/src/main/java/marquez/service/models/Job.java +++ b/api/src/main/java/marquez/service/models/Job.java @@ -90,6 +90,11 @@ public Job( this.tags = (tags == null) ? ImmutableSet.of() : tags; } + /** Returns the {@link NodeId} for this job, allowing direct queries to the Lineage API. */ + public NodeId getNodeId() { + return NodeId.of(id); + } + public Optional getLocation() { return Optional.ofNullable(location); } diff --git a/api/src/test/java/marquez/service/models/NodeIdOnModelsTest.java b/api/src/test/java/marquez/service/models/NodeIdOnModelsTest.java new file mode 100644 index 0000000000..763e55d59c --- /dev/null +++ b/api/src/test/java/marquez/service/models/NodeIdOnModelsTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2018-2023 contributors to the Marquez project + * SPDX-License-Identifier: Apache-2.0 + */ + +package marquez.service.models; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.net.URL; +import java.time.Instant; +import java.util.UUID; +import marquez.common.models.DatasetId; +import marquez.common.models.DatasetName; +import marquez.common.models.DatasetType; +import marquez.common.models.JobId; +import marquez.common.models.JobName; +import marquez.common.models.JobType; +import marquez.common.models.NamespaceName; +import marquez.common.models.SourceName; +import marquez.common.models.Version; +import org.junit.jupiter.api.Test; + +/** Tests that the {@code getNodeId()} method returns the correct {@link NodeId} for API models. */ +class NodeIdOnModelsTest { + + private static final NamespaceName NAMESPACE = NamespaceName.of("test-namespace"); + private static final Instant NOW = Instant.now(); + + @Test + void datasetNodeIdHasCorrectFormat() { + DatasetName datasetName = DatasetName.of("test-dataset"); + DatasetId datasetId = new DatasetId(NAMESPACE, datasetName); + + DbTable dataset = + new DbTable( + datasetId, + datasetName, + DatasetName.of("public.test_dataset"), + NOW, + NOW, + SourceName.of("test-source"), + ImmutableList.of(), + ImmutableSet.of(), + null, + null, + null, + null, + null, + ImmutableMap.of(), + false); + + NodeId nodeId = dataset.getNodeId(); + assertThat(nodeId).isNotNull(); + assertThat(nodeId.isDatasetType()).isTrue(); + assertThat(nodeId.getValue()).isEqualTo("dataset:test-namespace:test-dataset"); + } + + @Test + void datasetVersionNodeIdIncludesVersion() { + DatasetName datasetName = DatasetName.of("test-dataset"); + DatasetId datasetId = new DatasetId(NAMESPACE, datasetName); + UUID versionUuid = UUID.randomUUID(); + + DbTableVersion datasetVersion = + new DbTableVersion( + datasetId, + datasetName, + DatasetName.of("public.test_dataset"), + NOW, + new Version(versionUuid), + SourceName.of("test-source"), + ImmutableList.of(), + ImmutableSet.of(), + null, + null, + null, + null, + ImmutableMap.of()); + + NodeId nodeId = datasetVersion.getNodeId(); + assertThat(nodeId).isNotNull(); + assertThat(nodeId.hasVersion()).isTrue(); + assertThat(nodeId.getValue()) + .isEqualTo("dataset:test-namespace:test-dataset#" + versionUuid); + } + + @Test + void jobNodeIdHasCorrectFormat() { + JobName jobName = JobName.of("test-job"); + JobId jobId = new JobId(NAMESPACE, jobName); + + Job job = + new Job( + jobId, + JobType.BATCH, + jobName, + "test-job", + null, + null, + NOW, + NOW, + ImmutableSet.of(), + ImmutableSet.of(), + null, + null, + null, + null, + ImmutableMap.of(), + null, + null, + null); + + NodeId nodeId = job.getNodeId(); + assertThat(nodeId).isNotNull(); + assertThat(nodeId.isJobType()).isTrue(); + assertThat(nodeId.getValue()).isEqualTo("job:test-namespace:test-job"); + } +} From 290613515d0e15dd30b43f62a30890de2b206c4e Mon Sep 17 00:00:00 2001 From: Sai Kaushik Ponnekanti Date: Tue, 14 Apr 2026 22:39:37 -0700 Subject: [PATCH 2/2] fix: match DbTable constructor signature in test (14 params, not 15) DbTable's constructor takes 14 parameters. The test was passing 15 by including an extra null for columnLineage, which is handled internally by DbTable when calling super(). Also removed unused import java.net.URL. Signed-off-by: Sai Kaushik Ponnekanti --- .../test/java/marquez/service/models/NodeIdOnModelsTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/src/test/java/marquez/service/models/NodeIdOnModelsTest.java b/api/src/test/java/marquez/service/models/NodeIdOnModelsTest.java index 763e55d59c..27fab446a7 100644 --- a/api/src/test/java/marquez/service/models/NodeIdOnModelsTest.java +++ b/api/src/test/java/marquez/service/models/NodeIdOnModelsTest.java @@ -10,12 +10,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import java.net.URL; import java.time.Instant; import java.util.UUID; import marquez.common.models.DatasetId; import marquez.common.models.DatasetName; -import marquez.common.models.DatasetType; import marquez.common.models.JobId; import marquez.common.models.JobName; import marquez.common.models.JobType; @@ -49,7 +47,6 @@ void datasetNodeIdHasCorrectFormat() { null, null, null, - null, ImmutableMap.of(), false);