fix(Siblings): Have sibling hook use entity client (#5279)

* fixing dbt platform issues

* have sibling hook use entity client over entity service

* switching search service as well

* lint

* more lint

* more specific exceptions
This commit is contained in:
Gabe Lyons 2022-06-29 10:02:45 -07:00 committed by GitHub
parent c1f8227693
commit 9e58cd6ff1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 215 additions and 63 deletions

View File

@ -1,7 +1,9 @@
package com.linkedin.metadata.kafka.hook.siblings;
import com.datahub.authentication.Authentication;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.linkedin.common.AuditStamp;
import com.linkedin.common.Siblings;
import com.linkedin.common.SubTypes;
@ -10,21 +12,25 @@ import com.linkedin.common.urn.DatasetUrn;
import com.linkedin.common.urn.Urn;
import com.linkedin.dataset.UpstreamArray;
import com.linkedin.dataset.UpstreamLineage;
import com.linkedin.entity.EntityResponse;
import com.linkedin.entity.client.RestliEntityClient;
import com.linkedin.events.metadata.ChangeType;
import com.linkedin.gms.factory.entity.EntityServiceFactory;
import com.linkedin.gms.factory.auth.SystemAuthenticationFactory;
import com.linkedin.gms.factory.entity.RestliEntityClientFactory;
import com.linkedin.gms.factory.entityregistry.EntityRegistryFactory;
import com.linkedin.gms.factory.search.SearchServiceFactory;
import com.linkedin.metadata.entity.EntityService;
import com.linkedin.gms.factory.search.EntitySearchServiceFactory;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.kafka.hook.MetadataChangeLogHook;
import com.linkedin.metadata.models.EntitySpec;
import com.linkedin.metadata.models.registry.EntityRegistry;
import com.linkedin.metadata.search.EntitySearchService;
import com.linkedin.metadata.search.SearchResult;
import com.linkedin.metadata.search.SearchService;
import com.linkedin.metadata.utils.EntityKeyUtils;
import com.linkedin.metadata.utils.GenericRecordUtils;
import com.linkedin.mxe.GenericAspect;
import com.linkedin.mxe.MetadataChangeLog;
import com.linkedin.mxe.MetadataChangeProposal;
import com.linkedin.r2.RemoteInvocationException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.stream.Collectors;
@ -53,7 +59,7 @@ import static com.linkedin.metadata.Constants.*;
@Slf4j
@Component
@Singleton
@Import({EntityRegistryFactory.class, EntityServiceFactory.class, SearchServiceFactory.class})
@Import({EntityRegistryFactory.class, RestliEntityClientFactory.class, EntitySearchServiceFactory.class, SystemAuthenticationFactory.class})
public class SiblingAssociationHook implements MetadataChangeLogHook {
public static final String SIBLING_ASSOCIATION_SYSTEM_ACTOR = "urn:li:corpuser:__datahub_system_sibling_hook";
@ -61,18 +67,21 @@ public class SiblingAssociationHook implements MetadataChangeLogHook {
public static final String SOURCE_SUBTYPE = "source";
private final EntityRegistry _entityRegistry;
private final EntityService _entityService;
private final SearchService _searchService;
private final RestliEntityClient _entityClient;
private final EntitySearchService _searchService;
private final Authentication _systemAuthentication;
@Autowired
public SiblingAssociationHook(
@Nonnull final EntityRegistry entityRegistry,
@Nonnull final EntityService entityService,
@Nonnull final SearchService searchService
@Nonnull final RestliEntityClient entityClient,
@Nonnull final EntitySearchService searchService,
@Nonnull final Authentication systemAuthentication
) {
_entityRegistry = entityRegistry;
_entityService = entityService;
_entityClient = entityClient;
_searchService = searchService;
_systemAuthentication = systemAuthentication;
}
@Value("${siblings.enabled:false}")
@ -123,8 +132,7 @@ public class SiblingAssociationHook implements MetadataChangeLogHook {
entitiesWithYouAsSiblingFilter,
null,
0,
10,
null);
10);
// we have a match of an entity with you as a sibling, associate yourself back
searchResult.getEntities().forEach(entity -> {
@ -146,21 +154,12 @@ public class SiblingAssociationHook implements MetadataChangeLogHook {
if (event.getAspectName().equals(UPSTREAM_LINEAGE_ASPECT_NAME)) {
upstreamLineage = getUpstreamLineageFromEvent(event);
subTypesAspectOfEntity =
(SubTypes) _entityService.getLatestAspect(
datasetUrn,
SUB_TYPES_ASPECT_NAME
);
subTypesAspectOfEntity = getSubtypesFromEntityClient(datasetUrn);
}
if (event.getAspectName().equals(SUB_TYPES_ASPECT_NAME)) {
subTypesAspectOfEntity = getSubtypesFromEvent(event);
upstreamLineage =
(UpstreamLineage) _entityService.getLatestAspect(
datasetUrn,
UPSTREAM_LINEAGE_ASPECT_NAME
);
upstreamLineage = getUpstreamLineageFromEntityClient(datasetUrn);
}
if (
@ -195,10 +194,8 @@ public class SiblingAssociationHook implements MetadataChangeLogHook {
}
private void setSiblingsAndSoftDeleteSibling(Urn dbtUrn, Urn sourceUrn) {
Siblings existingDbtSiblingAspect =
(Siblings) _entityService.getLatestAspect(dbtUrn, SIBLINGS_ASPECT_NAME);
Siblings existingSourceSiblingAspect =
(Siblings) _entityService.getLatestAspect(sourceUrn, SIBLINGS_ASPECT_NAME);
Siblings existingDbtSiblingAspect = getSiblingsFromEntityClient(dbtUrn);
Siblings existingSourceSiblingAspect = getSiblingsFromEntityClient(sourceUrn);
log.info("Associating {} and {} as siblings.", dbtUrn.toString(), sourceUrn.toString());
@ -228,7 +225,12 @@ public class SiblingAssociationHook implements MetadataChangeLogHook {
dbtSiblingProposal.setChangeType(ChangeType.UPSERT);
dbtSiblingProposal.setEntityUrn(dbtUrn);
_entityService.ingestProposal(dbtSiblingProposal, auditStamp);
try {
_entityClient.ingestProposal(dbtSiblingProposal, _systemAuthentication);
} catch (RemoteInvocationException e) {
log.error("Error while associating {} with {}: {}", dbtUrn.toString(), sourceUrn.toString(), e.toString());
throw new RuntimeException("Error ingesting sibling proposal. Skipping processing.", e);
}
// set dbt as a sibling of source
@ -245,7 +247,14 @@ public class SiblingAssociationHook implements MetadataChangeLogHook {
// clean up any references to stale siblings that have been deleted
List<Urn> filteredNewSiblingsArray =
newSiblingsUrnArray.stream().filter(urn -> _entityService.exists(urn)).collect(Collectors.toList());
newSiblingsUrnArray.stream().filter(urn -> {
try {
return _entityClient.exists(urn, _systemAuthentication);
} catch (RemoteInvocationException e) {
log.error("Error while checking existence of {}: {}", urn.toString(), e.toString());
throw new RuntimeException("Error checking existence. Skipping processing.", e);
}
}).collect(Collectors.toList());
sourceSiblingAspect.setSiblings(new UrnArray(filteredNewSiblingsArray));
sourceSiblingAspect.setPrimary(false);
@ -259,7 +268,12 @@ public class SiblingAssociationHook implements MetadataChangeLogHook {
sourceSiblingProposal.setChangeType(ChangeType.UPSERT);
sourceSiblingProposal.setEntityUrn(sourceUrn);
_entityService.ingestProposal(sourceSiblingProposal, auditStamp);
try {
_entityClient.ingestProposal(sourceSiblingProposal, _systemAuthentication);
} catch (RemoteInvocationException e) {
log.error("Error while associating {} with {}: {}", dbtUrn.toString(), sourceUrn.toString(), e.toString());
throw new RuntimeException("Error ingesting sibling proposal. Skipping processing.", e);
}
}
@ -362,4 +376,67 @@ public class SiblingAssociationHook implements MetadataChangeLogHook {
return filter;
}
private SubTypes getSubtypesFromEntityClient(
final Urn urn
) {
try {
EntityResponse entityResponse = _entityClient.getV2(
DATASET_ENTITY_NAME,
urn,
ImmutableSet.of(SUB_TYPES_ASPECT_NAME),
_systemAuthentication
);
if (entityResponse != null && entityResponse.hasAspects() && entityResponse.getAspects().containsKey(Constants.SUB_TYPES_ASPECT_NAME)) {
return new SubTypes(entityResponse.getAspects().get(Constants.SUB_TYPES_ASPECT_NAME).getValue().data());
} else {
return null;
}
} catch (RemoteInvocationException | URISyntaxException e) {
throw new RuntimeException("Failed to retrieve Subtypes", e);
}
}
private UpstreamLineage getUpstreamLineageFromEntityClient(
final Urn urn
) {
try {
EntityResponse entityResponse = _entityClient.getV2(
DATASET_ENTITY_NAME,
urn,
ImmutableSet.of(UPSTREAM_LINEAGE_ASPECT_NAME),
_systemAuthentication
);
if (entityResponse != null && entityResponse.hasAspects() && entityResponse.getAspects().containsKey(Constants.UPSTREAM_LINEAGE_ASPECT_NAME)) {
return new UpstreamLineage(entityResponse.getAspects().get(Constants.UPSTREAM_LINEAGE_ASPECT_NAME).getValue().data());
} else {
return null;
}
} catch (RemoteInvocationException | URISyntaxException e) {
throw new RuntimeException("Failed to retrieve UpstreamLineage", e);
}
}
private Siblings getSiblingsFromEntityClient(
final Urn urn
) {
try {
EntityResponse entityResponse = _entityClient.getV2(
DATASET_ENTITY_NAME,
urn,
ImmutableSet.of(SIBLINGS_ASPECT_NAME),
_systemAuthentication
);
if (entityResponse != null && entityResponse.hasAspects() && entityResponse.getAspects().containsKey(Constants.SIBLINGS_ASPECT_NAME)) {
return new Siblings(entityResponse.getAspects().get(Constants.SIBLINGS_ASPECT_NAME).getValue().data());
} else {
return null;
}
} catch (RemoteInvocationException | URISyntaxException e) {
throw new RuntimeException("Failed to retrieve UpstreamLineage", e);
}
}
}

View File

@ -1,7 +1,8 @@
package com.linkedin.metadata.kafka.hook.siblings;
import com.datahub.authentication.Authentication;
import com.google.common.collect.ImmutableList;
import com.linkedin.common.AuditStamp;
import com.google.common.collect.ImmutableSet;
import com.linkedin.common.FabricType;
import com.linkedin.common.Siblings;
import com.linkedin.common.SubTypes;
@ -14,15 +15,19 @@ import com.linkedin.dataset.DatasetLineageType;
import com.linkedin.dataset.Upstream;
import com.linkedin.dataset.UpstreamArray;
import com.linkedin.dataset.UpstreamLineage;
import com.linkedin.entity.Aspect;
import com.linkedin.entity.EntityResponse;
import com.linkedin.entity.EnvelopedAspect;
import com.linkedin.entity.EnvelopedAspectMap;
import com.linkedin.entity.client.RestliEntityClient;
import com.linkedin.events.metadata.ChangeType;
import com.linkedin.metadata.entity.EntityService;
import com.linkedin.metadata.key.DatasetKey;
import com.linkedin.metadata.models.registry.ConfigEntityRegistry;
import com.linkedin.metadata.models.registry.EntityRegistry;
import com.linkedin.metadata.search.EntitySearchService;
import com.linkedin.metadata.search.SearchEntity;
import com.linkedin.metadata.search.SearchEntityArray;
import com.linkedin.metadata.search.SearchResult;
import com.linkedin.metadata.search.SearchService;
import com.linkedin.metadata.utils.GenericRecordUtils;
import com.linkedin.mxe.MetadataChangeLog;
import com.linkedin.mxe.MetadataChangeProposal;
@ -36,16 +41,18 @@ import static org.mockito.ArgumentMatchers.*;
public class SiblingAssociationHookTest {
private SiblingAssociationHook _siblingAssociationHook;
EntityService _mockEntityService;
SearchService _mockSearchService;
RestliEntityClient _mockEntityClient;
EntitySearchService _mockSearchService;
Authentication _mockAuthentication;
@BeforeMethod
public void setupTest() {
EntityRegistry registry = new ConfigEntityRegistry(
SiblingAssociationHookTest.class.getClassLoader().getResourceAsStream("test-entity-registry-siblings.yml"));
_mockEntityService = Mockito.mock(EntityService.class);
_mockSearchService = Mockito.mock(SearchService.class);
_siblingAssociationHook = new SiblingAssociationHook(registry, _mockEntityService, _mockSearchService);
_mockEntityClient = Mockito.mock(RestliEntityClient.class);
_mockSearchService = Mockito.mock(EntitySearchService.class);
_mockAuthentication = Mockito.mock(Authentication.class);
_siblingAssociationHook = new SiblingAssociationHook(registry, _mockEntityClient, _mockSearchService, _mockAuthentication);
_siblingAssociationHook.setEnabled(true);
}
@ -53,14 +60,21 @@ public class SiblingAssociationHookTest {
public void testInvokeWhenThereIsAPairWithDbtSourceNode() throws Exception {
SubTypes mockSourceSubtypesAspect = new SubTypes();
mockSourceSubtypesAspect.setTypeNames(new StringArray(ImmutableList.of("source")));
EnvelopedAspectMap mockResponseMap = new EnvelopedAspectMap();
mockResponseMap.put(SUB_TYPES_ASPECT_NAME, new EnvelopedAspect().setValue(new Aspect(mockSourceSubtypesAspect.data())));
EntityResponse mockResponse = new EntityResponse();
mockResponse.setAspects(mockResponseMap);
Mockito.when(_mockEntityClient.exists(Mockito.any(), Mockito.any())).thenReturn(true);
Mockito.when(_mockEntityService.exists(Mockito.any())).thenReturn(true);
Mockito.when(
_mockEntityService.getLatestAspect(
_mockEntityClient.getV2(
DATASET_ENTITY_NAME,
Urn.createFromString("urn:li:dataset:(urn:li:dataPlatform:dbt,my-proj.jaffle_shop.customers,PROD)"),
SUB_TYPES_ASPECT_NAME
)).thenReturn(mockSourceSubtypesAspect);
ImmutableSet.of(SUB_TYPES_ASPECT_NAME),
_mockAuthentication
)).thenReturn(mockResponse);
MetadataChangeLog event = new MetadataChangeLog();
event.setEntityType(DATASET_ENTITY_NAME);
@ -90,9 +104,9 @@ public class SiblingAssociationHookTest {
proposal.setAspect(GenericRecordUtils.serializeAspect(dbtSiblingsAspect));
proposal.setChangeType(ChangeType.UPSERT);
Mockito.verify(_mockEntityService, Mockito.times(1)).ingestProposal(
Mockito.verify(_mockEntityClient, Mockito.times(1)).ingestProposal(
Mockito.eq(proposal),
Mockito.any(AuditStamp.class)
Mockito.eq(_mockAuthentication)
);
final Siblings sourceSiblingsAspect = new Siblings()
@ -106,9 +120,9 @@ public class SiblingAssociationHookTest {
proposal2.setAspect(GenericRecordUtils.serializeAspect(sourceSiblingsAspect));
proposal2.setChangeType(ChangeType.UPSERT);
Mockito.verify(_mockEntityService, Mockito.times(1)).ingestProposal(
Mockito.verify(_mockEntityClient, Mockito.times(1)).ingestProposal(
Mockito.eq(proposal2),
Mockito.any(AuditStamp.class)
Mockito.eq(_mockAuthentication)
);
}
@ -117,13 +131,23 @@ public class SiblingAssociationHookTest {
SubTypes mockSourceSubtypesAspect = new SubTypes();
mockSourceSubtypesAspect.setTypeNames(new StringArray(ImmutableList.of("model")));
Mockito.when(_mockEntityService.exists(Mockito.any())).thenReturn(true);
Mockito.when(_mockEntityClient.exists(Mockito.any(), Mockito.any())).thenReturn(true);
EnvelopedAspectMap mockResponseMap = new EnvelopedAspectMap();
mockResponseMap.put(SUB_TYPES_ASPECT_NAME, new EnvelopedAspect().setValue(new Aspect(mockSourceSubtypesAspect.data())));
EntityResponse mockResponse = new EntityResponse();
mockResponse.setAspects(mockResponseMap);
Mockito.when(_mockEntityClient.exists(Mockito.any(), Mockito.any())).thenReturn(true);
Mockito.when(
_mockEntityService.getLatestAspect(
_mockEntityClient.getV2(
DATASET_ENTITY_NAME,
Urn.createFromString("urn:li:dataset:(urn:li:dataPlatform:dbt,my-proj.jaffle_shop.customers,PROD)"),
SUB_TYPES_ASPECT_NAME
)).thenReturn(mockSourceSubtypesAspect);
ImmutableSet.of(SUB_TYPES_ASPECT_NAME),
_mockAuthentication
)).thenReturn(mockResponse);
MetadataChangeLog event = new MetadataChangeLog();
event.setEntityType(DATASET_ENTITY_NAME);
@ -153,15 +177,15 @@ public class SiblingAssociationHookTest {
proposal.setAspect(GenericRecordUtils.serializeAspect(dbtSiblingsAspect));
proposal.setChangeType(ChangeType.UPSERT);
Mockito.verify(_mockEntityService, Mockito.times(0)).ingestProposal(
Mockito.verify(_mockEntityClient, Mockito.times(0)).ingestProposal(
Mockito.eq(proposal),
Mockito.any(AuditStamp.class)
Mockito.eq(_mockAuthentication)
);
}
@Test
public void testInvokeWhenThereIsAPairWithBigqueryDownstreamNode() throws Exception {
Mockito.when(_mockEntityService.exists(Mockito.any())).thenReturn(true);
Mockito.when(_mockEntityClient.exists(Mockito.any(), Mockito.any())).thenReturn(true);
MetadataChangeLog event = new MetadataChangeLog();
event.setEntityType(DATASET_ENTITY_NAME);
@ -191,9 +215,9 @@ public class SiblingAssociationHookTest {
proposal.setAspect(GenericRecordUtils.serializeAspect(dbtSiblingsAspect));
proposal.setChangeType(ChangeType.UPSERT);
Mockito.verify(_mockEntityService, Mockito.times(1)).ingestProposal(
Mockito.verify(_mockEntityClient, Mockito.times(1)).ingestProposal(
Mockito.eq(proposal),
Mockito.any(AuditStamp.class)
Mockito.eq(_mockAuthentication)
);
final Siblings sourceSiblingsAspect = new Siblings()
@ -207,15 +231,15 @@ public class SiblingAssociationHookTest {
proposal2.setAspect(GenericRecordUtils.serializeAspect(sourceSiblingsAspect));
proposal2.setChangeType(ChangeType.UPSERT);
Mockito.verify(_mockEntityService, Mockito.times(1)).ingestProposal(
Mockito.verify(_mockEntityClient, Mockito.times(1)).ingestProposal(
Mockito.eq(proposal2),
Mockito.any(AuditStamp.class)
Mockito.eq(_mockAuthentication)
);
}
@Test
public void testInvokeWhenThereIsAKeyBeingReingested() throws Exception {
Mockito.when(_mockEntityService.exists(Mockito.any())).thenReturn(true);
Mockito.when(_mockEntityClient.exists(Mockito.any(), Mockito.any())).thenReturn(true);
SearchResult returnSearchResult = new SearchResult();
SearchEntityArray returnEntityArray = new SearchEntityArray();
@ -229,7 +253,7 @@ public class SiblingAssociationHookTest {
Mockito.when(
_mockSearchService.search(
anyString(), anyString(), any(), any(), anyInt(), anyInt(), any()
anyString(), anyString(), any(), any(), anyInt(), anyInt()
)).thenReturn(returnSearchResult);
MetadataChangeLog event = new MetadataChangeLog();
@ -256,9 +280,9 @@ public class SiblingAssociationHookTest {
proposal.setAspect(GenericRecordUtils.serializeAspect(dbtSiblingsAspect));
proposal.setChangeType(ChangeType.UPSERT);
Mockito.verify(_mockEntityService, Mockito.times(1)).ingestProposal(
Mockito.verify(_mockEntityClient, Mockito.times(1)).ingestProposal(
Mockito.eq(proposal),
Mockito.any(AuditStamp.class)
Mockito.eq(_mockAuthentication)
);
final Siblings sourceSiblingsAspect = new Siblings()
@ -272,9 +296,9 @@ public class SiblingAssociationHookTest {
proposal2.setAspect(GenericRecordUtils.serializeAspect(sourceSiblingsAspect));
proposal2.setChangeType(ChangeType.UPSERT);
Mockito.verify(_mockEntityService, Mockito.times(1)).ingestProposal(
Mockito.verify(_mockEntityClient, Mockito.times(1)).ingestProposal(
Mockito.eq(proposal2),
Mockito.any(AuditStamp.class)
Mockito.eq(_mockAuthentication)
);
}
}

View File

@ -114,6 +114,13 @@
"optional" : true
} ],
"returns" : "com.linkedin.metadata.run.DeleteReferencesResponse"
}, {
"name" : "exists",
"parameters" : [ {
"name" : "urn",
"type" : "string"
} ],
"returns" : "boolean"
}, {
"name" : "filter",
"parameters" : [ {

View File

@ -5679,6 +5679,13 @@
"optional" : true
} ],
"returns" : "com.linkedin.metadata.run.DeleteReferencesResponse"
}, {
"name" : "exists",
"parameters" : [ {
"name" : "urn",
"type" : "string"
} ],
"returns" : "boolean"
}, {
"name" : "filter",
"parameters" : [ {

View File

@ -296,4 +296,6 @@ public interface EntityClient {
public void producePlatformEvent(@Nonnull String name, @Nullable String key, @Nonnull PlatformEvent event,
@Nonnull Authentication authentication) throws Exception;
Boolean exists(Urn urn, @Nonnull Authentication authentication) throws RemoteInvocationException;
}

View File

@ -463,4 +463,9 @@ public class JavaEntityClient implements EntityClient {
@Nonnull Authentication authentication) throws Exception {
_eventProducer.producePlatformEvent(name, key, event);
}
@Override
public Boolean exists(Urn urn, @Nonnull Authentication authentication) throws RemoteInvocationException {
return _entityService.exists(urn);
}
}

View File

@ -20,6 +20,7 @@ import com.linkedin.entity.EntitiesDoBatchIngestRequestBuilder;
import com.linkedin.entity.EntitiesDoBrowseRequestBuilder;
import com.linkedin.entity.EntitiesDoDeleteReferencesRequestBuilder;
import com.linkedin.entity.EntitiesDoDeleteRequestBuilder;
import com.linkedin.entity.EntitiesDoExistsRequestBuilder;
import com.linkedin.entity.EntitiesDoFilterRequestBuilder;
import com.linkedin.entity.EntitiesDoGetBrowsePathsRequestBuilder;
import com.linkedin.entity.EntitiesDoIngestRequestBuilder;
@ -662,4 +663,11 @@ public class RestliEntityClient extends BaseClient implements EntityClient {
}
sendClientRequest(requestBuilder, authentication);
}
@Override
public Boolean exists(Urn urn, @Nonnull Authentication authentication) throws RemoteInvocationException {
final EntitiesDoExistsRequestBuilder requestBuilder =
ENTITIES_REQUEST_BUILDERS.actionExists().urnParam(urn.toString());
return sendClientRequest(requestBuilder, authentication).getEntity();
}
}

View File

@ -89,6 +89,7 @@ public class EntityResource extends CollectionResourceTaskTemplate<String, Entit
private static final String PARAM_COUNT = "count";
private static final String PARAM_VALUE = "value";
private static final String SYSTEM_METADATA = "systemMetadata";
private static final String ACTION_EXISTS = "exists";
private final Clock _clock = Clock.systemUTC();
@ -483,4 +484,14 @@ public class EntityResource extends CollectionResourceTaskTemplate<String, Entit
return RestliUtil.toTask(() -> _entitySearchService.filter(entityName, filter, sortCriterion, start, count),
MetricRegistry.name(this.getClass(), "search"));
}
@Action(name = ACTION_EXISTS)
@Nonnull
@WithSpan
public Task<Boolean> exists(@ActionParam(PARAM_URN) @Nonnull String urnStr) throws URISyntaxException {
log.info("EXISTS for {}", urnStr);
Urn urn = Urn.createFromString(urnStr);
return RestliUtil.toTask(() -> _entityService.exists(urn),
MetricRegistry.name(this.getClass(), "exists"));
}
}

View File

@ -0,0 +1,11 @@
# see https://datahubproject.io/docs/generated/ingestion/sources/file for complete documentation
source:
type: "file"
config:
filename: "./cypress_dbt_data.json"
# see https://datahubproject.io/docs/metadata-ingestion/sink_docs/datahub for complete documentation
sink:
type: "datahub-rest"
config:
server: "http://localhost:8080"