mirror of
https://github.com/datahub-project/datahub.git
synced 2025-06-27 05:03:31 +00:00
feat(structuredProperties) Add new settings aspect plus graphql changes for structured props (#12052)
This commit is contained in:
parent
b091e4615d
commit
f1ef4f8e5f
@ -1,7 +1,8 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.structuredproperties;
|
||||
|
||||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.bindArgument;
|
||||
import static com.linkedin.metadata.Constants.STRUCTURED_PROPERTY_ENTITY_NAME;
|
||||
import static com.linkedin.datahub.graphql.resolvers.mutate.MutationUtils.buildMetadataChangeProposalWithUrn;
|
||||
import static com.linkedin.metadata.Constants.*;
|
||||
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.data.template.SetMode;
|
||||
@ -12,20 +13,24 @@ import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.CreateStructuredPropertyInput;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertyEntity;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertySettingsInput;
|
||||
import com.linkedin.datahub.graphql.types.structuredproperty.StructuredPropertyMapper;
|
||||
import com.linkedin.entity.EntityResponse;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.metadata.aspect.patch.builder.StructuredPropertyDefinitionPatchBuilder;
|
||||
import com.linkedin.metadata.models.StructuredPropertyUtils;
|
||||
import com.linkedin.metadata.utils.EntityKeyUtils;
|
||||
import com.linkedin.mxe.MetadataChangeProposal;
|
||||
import com.linkedin.structured.PrimitivePropertyValue;
|
||||
import com.linkedin.structured.PropertyCardinality;
|
||||
import com.linkedin.structured.PropertyValue;
|
||||
import com.linkedin.structured.StructuredPropertyKey;
|
||||
import com.linkedin.structured.StructuredPropertySettings;
|
||||
import graphql.schema.DataFetcher;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@ -54,14 +59,83 @@ public class CreateStructuredPropertyResolver
|
||||
"Unable to create structured property. Please contact your admin.");
|
||||
}
|
||||
final StructuredPropertyKey key = new StructuredPropertyKey();
|
||||
final String id = input.getId() != null ? input.getId() : UUID.randomUUID().toString();
|
||||
final String id =
|
||||
StructuredPropertyUtils.getPropertyId(input.getId(), input.getQualifiedName());
|
||||
key.setId(id);
|
||||
final Urn propertyUrn =
|
||||
EntityKeyUtils.convertEntityKeyToUrn(key, STRUCTURED_PROPERTY_ENTITY_NAME);
|
||||
|
||||
if (_entityClient.exists(context.getOperationContext(), propertyUrn)) {
|
||||
throw new IllegalArgumentException(
|
||||
"A structured property already exists with this urn");
|
||||
}
|
||||
|
||||
List<MetadataChangeProposal> mcps = new ArrayList<>();
|
||||
|
||||
// first, create the property definition itself
|
||||
mcps.add(createPropertyDefinition(context, propertyUrn, id, input));
|
||||
|
||||
// then add the settings aspect if we're adding any settings inputs
|
||||
if (input.getSettings() != null) {
|
||||
mcps.add(createPropertySettings(context, propertyUrn, input.getSettings()));
|
||||
}
|
||||
|
||||
_entityClient.batchIngestProposals(context.getOperationContext(), mcps, false);
|
||||
|
||||
EntityResponse response =
|
||||
_entityClient.getV2(
|
||||
context.getOperationContext(),
|
||||
STRUCTURED_PROPERTY_ENTITY_NAME,
|
||||
propertyUrn,
|
||||
null);
|
||||
return StructuredPropertyMapper.map(context, response);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format("Failed to perform update against input %s", input), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private MetadataChangeProposal createPropertySettings(
|
||||
@Nonnull final QueryContext context,
|
||||
@Nonnull final Urn propertyUrn,
|
||||
final StructuredPropertySettingsInput settingsInput)
|
||||
throws Exception {
|
||||
StructuredPropertySettings settings = new StructuredPropertySettings();
|
||||
|
||||
if (settingsInput.getIsHidden() != null) {
|
||||
settings.setIsHidden(settingsInput.getIsHidden());
|
||||
}
|
||||
if (settingsInput.getShowInSearchFilters() != null) {
|
||||
settings.setShowInSearchFilters(settingsInput.getShowInSearchFilters());
|
||||
}
|
||||
if (settingsInput.getShowInAssetSummary() != null) {
|
||||
settings.setShowInAssetSummary(settingsInput.getShowInAssetSummary());
|
||||
}
|
||||
if (settingsInput.getShowAsAssetBadge() != null) {
|
||||
settings.setShowAsAssetBadge(settingsInput.getShowAsAssetBadge());
|
||||
}
|
||||
if (settingsInput.getShowInColumnsTable() != null) {
|
||||
settings.setShowInColumnsTable(settingsInput.getShowInColumnsTable());
|
||||
}
|
||||
settings.setLastModified(context.getOperationContext().getAuditStamp());
|
||||
|
||||
StructuredPropertyUtils.validatePropertySettings(settings, true);
|
||||
|
||||
return buildMetadataChangeProposalWithUrn(
|
||||
propertyUrn, STRUCTURED_PROPERTY_SETTINGS_ASPECT_NAME, settings);
|
||||
}
|
||||
|
||||
private MetadataChangeProposal createPropertyDefinition(
|
||||
@Nonnull final QueryContext context,
|
||||
@Nonnull final Urn propertyUrn,
|
||||
@Nonnull final String id,
|
||||
final CreateStructuredPropertyInput input)
|
||||
throws Exception {
|
||||
StructuredPropertyDefinitionPatchBuilder builder =
|
||||
new StructuredPropertyDefinitionPatchBuilder().urn(propertyUrn);
|
||||
|
||||
builder.setQualifiedName(input.getQualifiedName());
|
||||
builder.setQualifiedName(id);
|
||||
builder.setValueType(input.getValueType());
|
||||
input.getEntityTypes().forEach(builder::addEntityType);
|
||||
if (input.getDisplayName() != null) {
|
||||
@ -80,27 +154,12 @@ public class CreateStructuredPropertyResolver
|
||||
buildAllowedValues(input, builder);
|
||||
}
|
||||
if (input.getCardinality() != null) {
|
||||
builder.setCardinality(
|
||||
PropertyCardinality.valueOf(input.getCardinality().toString()));
|
||||
builder.setCardinality(PropertyCardinality.valueOf(input.getCardinality().toString()));
|
||||
}
|
||||
builder.setCreated(context.getOperationContext().getAuditStamp());
|
||||
builder.setLastModified(context.getOperationContext().getAuditStamp());
|
||||
|
||||
MetadataChangeProposal mcp = builder.build();
|
||||
_entityClient.ingestProposal(context.getOperationContext(), mcp, false);
|
||||
|
||||
EntityResponse response =
|
||||
_entityClient.getV2(
|
||||
context.getOperationContext(),
|
||||
STRUCTURED_PROPERTY_ENTITY_NAME,
|
||||
propertyUrn,
|
||||
null);
|
||||
return StructuredPropertyMapper.map(context, response);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format("Failed to perform update against input %s", input), e);
|
||||
}
|
||||
});
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void buildTypeQualifier(
|
||||
|
@ -6,6 +6,7 @@ import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.common.urn.UrnUtils;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.datahub.graphql.concurrency.GraphQLConcurrencyUtils;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.DeleteStructuredPropertyInput;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
@ -42,6 +43,23 @@ public class DeleteStructuredPropertyResolver implements DataFetcher<Completable
|
||||
"Unable to delete structured property. Please contact your admin.");
|
||||
}
|
||||
_entityClient.deleteEntity(context.getOperationContext(), propertyUrn);
|
||||
// Asynchronously Delete all references to the entity (to return quickly)
|
||||
GraphQLConcurrencyUtils.supplyAsync(
|
||||
() -> {
|
||||
try {
|
||||
_entityClient.deleteEntityReferences(
|
||||
context.getOperationContext(), propertyUrn);
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
String.format(
|
||||
"Caught exception while attempting to clear all entity references for Structured Property with urn %s",
|
||||
propertyUrn),
|
||||
e);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
this.getClass().getSimpleName(),
|
||||
"get");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
|
@ -93,7 +93,7 @@ public class RemoveStructuredPropertiesResolver
|
||||
.getValue()
|
||||
.data());
|
||||
|
||||
return StructuredPropertiesMapper.map(context, structuredProperties);
|
||||
return StructuredPropertiesMapper.map(context, structuredProperties, assetUrn);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format("Failed to perform update against input %s", input), e);
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.structuredproperties;
|
||||
|
||||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.bindArgument;
|
||||
import static com.linkedin.metadata.Constants.STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME;
|
||||
import static com.linkedin.metadata.Constants.STRUCTURED_PROPERTY_ENTITY_NAME;
|
||||
import static com.linkedin.datahub.graphql.resolvers.mutate.MutationUtils.buildMetadataChangeProposalWithUrn;
|
||||
import static com.linkedin.metadata.Constants.*;
|
||||
|
||||
import com.linkedin.common.urn.Urn;
|
||||
import com.linkedin.common.urn.UrnUtils;
|
||||
@ -13,18 +13,23 @@ import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
|
||||
import com.linkedin.datahub.graphql.exception.AuthorizationException;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertyEntity;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertySettingsInput;
|
||||
import com.linkedin.datahub.graphql.generated.UpdateStructuredPropertyInput;
|
||||
import com.linkedin.datahub.graphql.types.structuredproperty.StructuredPropertyMapper;
|
||||
import com.linkedin.entity.EntityResponse;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.metadata.aspect.patch.builder.StructuredPropertyDefinitionPatchBuilder;
|
||||
import com.linkedin.metadata.models.StructuredPropertyUtils;
|
||||
import com.linkedin.mxe.MetadataChangeProposal;
|
||||
import com.linkedin.structured.PrimitivePropertyValue;
|
||||
import com.linkedin.structured.PropertyCardinality;
|
||||
import com.linkedin.structured.PropertyValue;
|
||||
import com.linkedin.structured.StructuredPropertyDefinition;
|
||||
import com.linkedin.structured.StructuredPropertySettings;
|
||||
import graphql.schema.DataFetcher;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import javax.annotation.Nonnull;
|
||||
@ -57,36 +62,24 @@ public class UpdateStructuredPropertyResolver
|
||||
"Unable to update structured property. Please contact your admin.");
|
||||
}
|
||||
final Urn propertyUrn = UrnUtils.getUrn(input.getUrn());
|
||||
StructuredPropertyDefinition existingDefinition =
|
||||
final EntityResponse entityResponse =
|
||||
getExistingStructuredProperty(context, propertyUrn);
|
||||
StructuredPropertyDefinitionPatchBuilder builder =
|
||||
new StructuredPropertyDefinitionPatchBuilder().urn(propertyUrn);
|
||||
|
||||
if (input.getDisplayName() != null) {
|
||||
builder.setDisplayName(input.getDisplayName());
|
||||
}
|
||||
if (input.getDescription() != null) {
|
||||
builder.setDescription(input.getDescription());
|
||||
}
|
||||
if (input.getImmutable() != null) {
|
||||
builder.setImmutable(input.getImmutable());
|
||||
}
|
||||
if (input.getTypeQualifier() != null) {
|
||||
buildTypeQualifier(input, builder, existingDefinition);
|
||||
}
|
||||
if (input.getNewAllowedValues() != null) {
|
||||
buildAllowedValues(input, builder);
|
||||
}
|
||||
if (input.getSetCardinalityAsMultiple() != null) {
|
||||
builder.setCardinality(PropertyCardinality.MULTIPLE);
|
||||
}
|
||||
if (input.getNewEntityTypes() != null) {
|
||||
input.getNewEntityTypes().forEach(builder::addEntityType);
|
||||
}
|
||||
builder.setLastModified(context.getOperationContext().getAuditStamp());
|
||||
List<MetadataChangeProposal> mcps = new ArrayList<>();
|
||||
|
||||
MetadataChangeProposal mcp = builder.build();
|
||||
_entityClient.ingestProposal(context.getOperationContext(), mcp, false);
|
||||
// first update the definition aspect if we need to
|
||||
MetadataChangeProposal definitionMcp =
|
||||
updateDefinition(input, context, propertyUrn, entityResponse);
|
||||
if (definitionMcp != null) {
|
||||
mcps.add(definitionMcp);
|
||||
}
|
||||
|
||||
// then update the settings aspect if we need to
|
||||
if (input.getSettings() != null) {
|
||||
mcps.add(updateSettings(context, input.getSettings(), propertyUrn, entityResponse));
|
||||
}
|
||||
|
||||
_entityClient.batchIngestProposals(context.getOperationContext(), mcps, false);
|
||||
|
||||
EntityResponse response =
|
||||
_entityClient.getV2(
|
||||
@ -102,6 +95,120 @@ public class UpdateStructuredPropertyResolver
|
||||
});
|
||||
}
|
||||
|
||||
private boolean hasSettingsChanged(
|
||||
StructuredPropertySettings existingSettings, StructuredPropertySettingsInput settingsInput) {
|
||||
if (settingsInput.getIsHidden() != null
|
||||
&& !existingSettings.isIsHidden().equals(settingsInput.getIsHidden())) {
|
||||
return true;
|
||||
}
|
||||
if (settingsInput.getShowInSearchFilters() != null
|
||||
&& !existingSettings
|
||||
.isShowInSearchFilters()
|
||||
.equals(settingsInput.getShowInSearchFilters())) {
|
||||
return true;
|
||||
}
|
||||
if (settingsInput.getShowInAssetSummary() != null
|
||||
&& !existingSettings.isShowInAssetSummary().equals(settingsInput.getShowInAssetSummary())) {
|
||||
return true;
|
||||
}
|
||||
if (settingsInput.getShowAsAssetBadge() != null
|
||||
&& !existingSettings.isShowAsAssetBadge().equals(settingsInput.getShowAsAssetBadge())) {
|
||||
return true;
|
||||
}
|
||||
if (settingsInput.getShowInColumnsTable() != null
|
||||
&& !existingSettings.isShowInColumnsTable().equals(settingsInput.getShowInColumnsTable())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MetadataChangeProposal updateSettings(
|
||||
@Nonnull final QueryContext context,
|
||||
@Nonnull final StructuredPropertySettingsInput settingsInput,
|
||||
@Nonnull final Urn propertyUrn,
|
||||
@Nonnull final EntityResponse entityResponse)
|
||||
throws Exception {
|
||||
StructuredPropertySettings existingSettings =
|
||||
getExistingStructuredPropertySettings(entityResponse);
|
||||
// check if settings has changed to determine if we should update the timestamp
|
||||
boolean hasChanged = hasSettingsChanged(existingSettings, settingsInput);
|
||||
if (hasChanged) {
|
||||
existingSettings.setLastModified(context.getOperationContext().getAuditStamp());
|
||||
}
|
||||
|
||||
if (settingsInput.getIsHidden() != null) {
|
||||
existingSettings.setIsHidden(settingsInput.getIsHidden());
|
||||
}
|
||||
if (settingsInput.getShowInSearchFilters() != null) {
|
||||
existingSettings.setShowInSearchFilters(settingsInput.getShowInSearchFilters());
|
||||
}
|
||||
if (settingsInput.getShowInAssetSummary() != null) {
|
||||
existingSettings.setShowInAssetSummary(settingsInput.getShowInAssetSummary());
|
||||
}
|
||||
if (settingsInput.getShowAsAssetBadge() != null) {
|
||||
existingSettings.setShowAsAssetBadge(settingsInput.getShowAsAssetBadge());
|
||||
}
|
||||
if (settingsInput.getShowInColumnsTable() != null) {
|
||||
existingSettings.setShowInColumnsTable(settingsInput.getShowInColumnsTable());
|
||||
}
|
||||
|
||||
StructuredPropertyUtils.validatePropertySettings(existingSettings, true);
|
||||
|
||||
return buildMetadataChangeProposalWithUrn(
|
||||
propertyUrn, STRUCTURED_PROPERTY_SETTINGS_ASPECT_NAME, existingSettings);
|
||||
}
|
||||
|
||||
private MetadataChangeProposal updateDefinition(
|
||||
@Nonnull final UpdateStructuredPropertyInput input,
|
||||
@Nonnull final QueryContext context,
|
||||
@Nonnull final Urn propertyUrn,
|
||||
@Nonnull final EntityResponse entityResponse)
|
||||
throws Exception {
|
||||
StructuredPropertyDefinition existingDefinition =
|
||||
getExistingStructuredPropertyDefinition(entityResponse);
|
||||
StructuredPropertyDefinitionPatchBuilder builder =
|
||||
new StructuredPropertyDefinitionPatchBuilder().urn(propertyUrn);
|
||||
|
||||
boolean hasUpdatedDefinition = false;
|
||||
|
||||
if (input.getDisplayName() != null) {
|
||||
builder.setDisplayName(input.getDisplayName());
|
||||
hasUpdatedDefinition = true;
|
||||
}
|
||||
if (input.getDescription() != null) {
|
||||
builder.setDescription(input.getDescription());
|
||||
hasUpdatedDefinition = true;
|
||||
}
|
||||
if (input.getImmutable() != null) {
|
||||
builder.setImmutable(input.getImmutable());
|
||||
hasUpdatedDefinition = true;
|
||||
}
|
||||
if (input.getTypeQualifier() != null) {
|
||||
buildTypeQualifier(input, builder, existingDefinition);
|
||||
hasUpdatedDefinition = true;
|
||||
}
|
||||
if (input.getNewAllowedValues() != null) {
|
||||
buildAllowedValues(input, builder);
|
||||
hasUpdatedDefinition = true;
|
||||
}
|
||||
if (input.getSetCardinalityAsMultiple() != null
|
||||
&& input.getSetCardinalityAsMultiple().equals(true)) {
|
||||
builder.setCardinality(PropertyCardinality.MULTIPLE);
|
||||
hasUpdatedDefinition = true;
|
||||
}
|
||||
if (input.getNewEntityTypes() != null) {
|
||||
input.getNewEntityTypes().forEach(builder::addEntityType);
|
||||
hasUpdatedDefinition = true;
|
||||
}
|
||||
|
||||
if (hasUpdatedDefinition) {
|
||||
builder.setLastModified(context.getOperationContext().getAuditStamp());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void buildTypeQualifier(
|
||||
@Nonnull final UpdateStructuredPropertyInput input,
|
||||
@Nonnull final StructuredPropertyDefinitionPatchBuilder builder,
|
||||
@ -141,17 +248,40 @@ public class UpdateStructuredPropertyResolver
|
||||
});
|
||||
}
|
||||
|
||||
private StructuredPropertyDefinition getExistingStructuredProperty(
|
||||
private EntityResponse getExistingStructuredProperty(
|
||||
@Nonnull final QueryContext context, @Nonnull final Urn propertyUrn) throws Exception {
|
||||
EntityResponse response =
|
||||
_entityClient.getV2(
|
||||
return _entityClient.getV2(
|
||||
context.getOperationContext(), STRUCTURED_PROPERTY_ENTITY_NAME, propertyUrn, null);
|
||||
}
|
||||
|
||||
private StructuredPropertyDefinition getExistingStructuredPropertyDefinition(
|
||||
EntityResponse response) throws Exception {
|
||||
if (response != null
|
||||
&& response.getAspects().containsKey(STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME)) {
|
||||
return new StructuredPropertyDefinition(
|
||||
response.getAspects().get(STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME).getValue().data());
|
||||
response
|
||||
.getAspects()
|
||||
.get(STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME)
|
||||
.getValue()
|
||||
.data()
|
||||
.copy());
|
||||
}
|
||||
return null;
|
||||
throw new IllegalArgumentException(
|
||||
"Attempting to update a structured property with no definition aspect.");
|
||||
}
|
||||
|
||||
private StructuredPropertySettings getExistingStructuredPropertySettings(EntityResponse response)
|
||||
throws Exception {
|
||||
if (response != null
|
||||
&& response.getAspects().containsKey(STRUCTURED_PROPERTY_SETTINGS_ASPECT_NAME)) {
|
||||
return new StructuredPropertySettings(
|
||||
response
|
||||
.getAspects()
|
||||
.get(STRUCTURED_PROPERTY_SETTINGS_ASPECT_NAME)
|
||||
.getValue()
|
||||
.data()
|
||||
.copy());
|
||||
}
|
||||
return new StructuredPropertySettings();
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ public class UpsertStructuredPropertiesResolver
|
||||
_entityClient.ingestProposal(
|
||||
context.getOperationContext(), structuredPropertiesProposal, false);
|
||||
|
||||
return StructuredPropertiesMapper.map(context, structuredProperties);
|
||||
return StructuredPropertiesMapper.map(context, structuredProperties, assetUrn);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
String.format("Failed to perform update against input %s", input), e);
|
||||
|
@ -142,7 +142,8 @@ public class ChartMapper implements ModelMapper<EntityResponse, Chart> {
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((chart, dataMap) ->
|
||||
chart.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -161,7 +161,9 @@ public class ContainerMapper {
|
||||
if (envelopedStructuredProps != null) {
|
||||
result.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(envelopedStructuredProps.getValue().data())));
|
||||
context,
|
||||
new StructuredProperties(envelopedStructuredProps.getValue().data()),
|
||||
entityUrn));
|
||||
}
|
||||
|
||||
final EnvelopedAspect envelopedForms = aspects.get(FORMS_ASPECT_NAME);
|
||||
|
@ -59,7 +59,8 @@ public class CorpGroupMapper implements ModelMapper<EntityResponse, CorpGroup> {
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
entity.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -88,7 +88,8 @@ public class CorpUserMapper {
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
entity.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -142,7 +142,8 @@ public class DashboardMapper implements ModelMapper<EntityResponse, Dashboard> {
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((dashboard, dataMap) ->
|
||||
dashboard.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -114,7 +114,8 @@ public class DataFlowMapper implements ModelMapper<EntityResponse, DataFlow> {
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
entity.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -135,7 +135,8 @@ public class DataJobMapper implements ModelMapper<EntityResponse, DataJob> {
|
||||
result.setSubTypes(SubTypesMapper.map(context, new SubTypes(data)));
|
||||
} else if (STRUCTURED_PROPERTIES_ASPECT_NAME.equals(name)) {
|
||||
result.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(data)));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(data), entityUrn));
|
||||
} else if (FORMS_ASPECT_NAME.equals(name)) {
|
||||
result.setForms(FormsMapper.map(new Forms(data), entityUrn.toString()));
|
||||
}
|
||||
|
@ -92,7 +92,8 @@ public class DataProductMapper implements ModelMapper<EntityResponse, DataProduc
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
entity.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -173,7 +173,8 @@ public class DatasetMapper implements ModelMapper<EntityResponse, Dataset> {
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
entity.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((dataset, dataMap) ->
|
||||
|
@ -71,7 +71,9 @@ public class DomainMapper {
|
||||
if (envelopedStructuredProps != null) {
|
||||
result.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(envelopedStructuredProps.getValue().data())));
|
||||
context,
|
||||
new StructuredProperties(envelopedStructuredProps.getValue().data()),
|
||||
entityUrn));
|
||||
}
|
||||
|
||||
final EnvelopedAspect envelopedForms = aspects.get(FORMS_ASPECT_NAME);
|
||||
|
@ -59,7 +59,8 @@ public class GlossaryNodeMapper implements ModelMapper<EntityResponse, GlossaryN
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
entity.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -90,7 +90,8 @@ public class GlossaryTermMapper implements ModelMapper<EntityResponse, GlossaryT
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
entity.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -115,7 +115,8 @@ public class MLFeatureMapper implements ModelMapper<EntityResponse, MLFeature> {
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((mlFeature, dataMap) ->
|
||||
mlFeature.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -117,7 +117,8 @@ public class MLFeatureTableMapper implements ModelMapper<EntityResponse, MLFeatu
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((mlFeatureTable, dataMap) ->
|
||||
mlFeatureTable.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -112,7 +112,8 @@ public class MLModelGroupMapper implements ModelMapper<EntityResponse, MLModelGr
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((mlModelGroup, dataMap) ->
|
||||
mlModelGroup.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -174,7 +174,8 @@ public class MLModelMapper implements ModelMapper<EntityResponse, MLModel> {
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((dataset, dataMap) ->
|
||||
dataset.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -112,7 +112,8 @@ public class MLPrimaryKeyMapper implements ModelMapper<EntityResponse, MLPrimary
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
entity.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
FORMS_ASPECT_NAME,
|
||||
((entity, dataMap) ->
|
||||
|
@ -41,7 +41,8 @@ public class SchemaFieldMapper implements ModelMapper<EntityResponse, SchemaFiel
|
||||
STRUCTURED_PROPERTIES_ASPECT_NAME,
|
||||
((schemaField, dataMap) ->
|
||||
schemaField.setStructuredProperties(
|
||||
StructuredPropertiesMapper.map(context, new StructuredProperties(dataMap)))));
|
||||
StructuredPropertiesMapper.map(
|
||||
context, new StructuredProperties(dataMap), entityUrn))));
|
||||
mappingHelper.mapToResult(
|
||||
BUSINESS_ATTRIBUTE_ASPECT,
|
||||
(((schemaField, dataMap) ->
|
||||
|
@ -25,23 +25,29 @@ public class StructuredPropertiesMapper {
|
||||
public static final StructuredPropertiesMapper INSTANCE = new StructuredPropertiesMapper();
|
||||
|
||||
public static com.linkedin.datahub.graphql.generated.StructuredProperties map(
|
||||
@Nullable QueryContext context, @Nonnull final StructuredProperties structuredProperties) {
|
||||
return INSTANCE.apply(context, structuredProperties);
|
||||
@Nullable QueryContext context,
|
||||
@Nonnull final StructuredProperties structuredProperties,
|
||||
@Nonnull final Urn entityUrn) {
|
||||
return INSTANCE.apply(context, structuredProperties, entityUrn);
|
||||
}
|
||||
|
||||
public com.linkedin.datahub.graphql.generated.StructuredProperties apply(
|
||||
@Nullable QueryContext context, @Nonnull final StructuredProperties structuredProperties) {
|
||||
@Nullable QueryContext context,
|
||||
@Nonnull final StructuredProperties structuredProperties,
|
||||
@Nonnull final Urn entityUrn) {
|
||||
com.linkedin.datahub.graphql.generated.StructuredProperties result =
|
||||
new com.linkedin.datahub.graphql.generated.StructuredProperties();
|
||||
result.setProperties(
|
||||
structuredProperties.getProperties().stream()
|
||||
.map(p -> mapStructuredProperty(context, p))
|
||||
.map(p -> mapStructuredProperty(context, p, entityUrn))
|
||||
.collect(Collectors.toList()));
|
||||
return result;
|
||||
}
|
||||
|
||||
private StructuredPropertiesEntry mapStructuredProperty(
|
||||
@Nullable QueryContext context, StructuredPropertyValueAssignment valueAssignment) {
|
||||
@Nullable QueryContext context,
|
||||
StructuredPropertyValueAssignment valueAssignment,
|
||||
@Nonnull final Urn entityUrn) {
|
||||
StructuredPropertiesEntry entry = new StructuredPropertiesEntry();
|
||||
entry.setStructuredProperty(createStructuredPropertyEntity(valueAssignment));
|
||||
final List<PropertyValue> values = new ArrayList<>();
|
||||
@ -58,6 +64,7 @@ public class StructuredPropertiesMapper {
|
||||
});
|
||||
entry.setValues(values);
|
||||
entry.setValueEntities(entities);
|
||||
entry.setAssociatedUrn(entityUrn.toString());
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ import com.linkedin.datahub.graphql.generated.PropertyCardinality;
|
||||
import com.linkedin.datahub.graphql.generated.StringValue;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertyDefinition;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertyEntity;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertySettings;
|
||||
import com.linkedin.datahub.graphql.generated.TypeQualifier;
|
||||
import com.linkedin.datahub.graphql.types.common.mappers.util.MappingHelper;
|
||||
import com.linkedin.datahub.graphql.types.mappers.MapperUtils;
|
||||
@ -55,6 +56,8 @@ public class StructuredPropertyMapper
|
||||
MappingHelper<StructuredPropertyEntity> mappingHelper = new MappingHelper<>(aspectMap, result);
|
||||
mappingHelper.mapToResult(
|
||||
STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME, (this::mapStructuredPropertyDefinition));
|
||||
mappingHelper.mapToResult(
|
||||
STRUCTURED_PROPERTY_SETTINGS_ASPECT_NAME, (this::mapStructuredPropertySettings));
|
||||
return mappingHelper.getResult();
|
||||
}
|
||||
|
||||
@ -112,6 +115,21 @@ public class StructuredPropertyMapper
|
||||
return allowedValues;
|
||||
}
|
||||
|
||||
private void mapStructuredPropertySettings(
|
||||
@Nonnull StructuredPropertyEntity extendedProperty, @Nonnull DataMap dataMap) {
|
||||
com.linkedin.structured.StructuredPropertySettings gmsSettings =
|
||||
new com.linkedin.structured.StructuredPropertySettings(dataMap);
|
||||
StructuredPropertySettings settings = new StructuredPropertySettings();
|
||||
|
||||
settings.setIsHidden(gmsSettings.isIsHidden());
|
||||
settings.setShowInSearchFilters(gmsSettings.isShowInSearchFilters());
|
||||
settings.setShowInAssetSummary(gmsSettings.isShowInAssetSummary());
|
||||
settings.setShowAsAssetBadge(gmsSettings.isShowAsAssetBadge());
|
||||
settings.setShowInColumnsTable(gmsSettings.isShowInColumnsTable());
|
||||
|
||||
extendedProperty.setSettings(settings);
|
||||
}
|
||||
|
||||
private DataTypeEntity createDataTypeEntity(final Urn dataTypeUrn) {
|
||||
final DataTypeEntity dataType = new DataTypeEntity();
|
||||
dataType.setUrn(dataTypeUrn.toString());
|
||||
|
@ -27,7 +27,8 @@ public class StructuredPropertyType
|
||||
implements com.linkedin.datahub.graphql.types.EntityType<StructuredPropertyEntity, String> {
|
||||
|
||||
public static final Set<String> ASPECTS_TO_FETCH =
|
||||
ImmutableSet.of(STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME);
|
||||
ImmutableSet.of(
|
||||
STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME, STRUCTURED_PROPERTY_SETTINGS_ASPECT_NAME);
|
||||
private final EntityClient _entityClient;
|
||||
|
||||
@Override
|
||||
|
@ -49,6 +49,11 @@ type StructuredPropertyEntity implements Entity {
|
||||
"""
|
||||
definition: StructuredPropertyDefinition!
|
||||
|
||||
"""
|
||||
Definition of this structured property including its name
|
||||
"""
|
||||
settings: StructuredPropertySettings
|
||||
|
||||
"""
|
||||
Granular API for querying edges extending from this entity
|
||||
"""
|
||||
@ -117,6 +122,36 @@ type StructuredPropertyDefinition {
|
||||
lastModified: ResolvedAuditStamp
|
||||
}
|
||||
|
||||
"""
|
||||
Settings specific to a structured property entity
|
||||
"""
|
||||
type StructuredPropertySettings {
|
||||
"""
|
||||
Whether or not this asset should be hidden in the main application
|
||||
"""
|
||||
isHidden: Boolean!
|
||||
|
||||
"""
|
||||
Whether or not this asset should be displayed as a search filter
|
||||
"""
|
||||
showInSearchFilters: Boolean!
|
||||
|
||||
"""
|
||||
Whether or not this asset should be displayed in the asset sidebar
|
||||
"""
|
||||
showInAssetSummary: Boolean!
|
||||
|
||||
"""
|
||||
Whether or not this asset should be displayed as an asset badge on other asset's headers
|
||||
"""
|
||||
showAsAssetBadge: Boolean!
|
||||
|
||||
"""
|
||||
Whether or not this asset should be displayed as a column in the schema field table in a Dataset's "Columns" tab.
|
||||
"""
|
||||
showInColumnsTable: Boolean!
|
||||
}
|
||||
|
||||
"""
|
||||
An entry for an allowed value for a structured property
|
||||
"""
|
||||
@ -202,6 +237,11 @@ type StructuredPropertiesEntry {
|
||||
The optional entities associated with the values if the values are entity urns
|
||||
"""
|
||||
valueEntities: [Entity]
|
||||
|
||||
"""
|
||||
The urn of the entity this property came from for tracking purposes e.g. when sibling nodes are merged together
|
||||
"""
|
||||
associatedUrn: String!
|
||||
}
|
||||
|
||||
"""
|
||||
@ -330,8 +370,9 @@ input CreateStructuredPropertyInput {
|
||||
|
||||
"""
|
||||
The unique fully qualified name of this structured property, dot delimited.
|
||||
This will be required to match the ID of this structured property.
|
||||
"""
|
||||
qualifiedName: String!
|
||||
qualifiedName: String
|
||||
|
||||
"""
|
||||
The optional display name for this property
|
||||
@ -375,6 +416,11 @@ input CreateStructuredPropertyInput {
|
||||
For example: ["urn:li:entityType:datahub.dataset"]
|
||||
"""
|
||||
entityTypes: [String!]!
|
||||
|
||||
"""
|
||||
Settings for this structured property
|
||||
"""
|
||||
settings: StructuredPropertySettingsInput
|
||||
}
|
||||
|
||||
"""
|
||||
@ -455,6 +501,11 @@ input UpdateStructuredPropertyInput {
|
||||
For backwards compatibility, this is append only.
|
||||
"""
|
||||
newEntityTypes: [String!]
|
||||
|
||||
"""
|
||||
Settings for this structured property
|
||||
"""
|
||||
settings: StructuredPropertySettingsInput
|
||||
}
|
||||
|
||||
"""
|
||||
@ -477,3 +528,34 @@ input DeleteStructuredPropertyInput {
|
||||
"""
|
||||
urn: String!
|
||||
}
|
||||
|
||||
"""
|
||||
Settings for a structured property
|
||||
"""
|
||||
input StructuredPropertySettingsInput {
|
||||
"""
|
||||
Whether or not this asset should be hidden in the main application
|
||||
"""
|
||||
isHidden: Boolean
|
||||
|
||||
"""
|
||||
Whether or not this asset should be displayed as a search filter
|
||||
"""
|
||||
showInSearchFilters: Boolean
|
||||
|
||||
"""
|
||||
Whether or not this asset should be displayed in the asset sidebar
|
||||
"""
|
||||
showInAssetSummary: Boolean
|
||||
|
||||
"""
|
||||
Whether or not this asset should be displayed as an asset badge on other asset's headers
|
||||
"""
|
||||
showAsAssetBadge: Boolean
|
||||
|
||||
"""
|
||||
Whether or not this asset should be displayed as a column in the schema field table in a Dataset's "Columns" tab.
|
||||
"""
|
||||
showInColumnsTable: Boolean
|
||||
}
|
||||
|
||||
|
@ -10,11 +10,11 @@ import com.linkedin.common.urn.UrnUtils;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.generated.CreateStructuredPropertyInput;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertyEntity;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertySettingsInput;
|
||||
import com.linkedin.entity.EntityResponse;
|
||||
import com.linkedin.entity.EnvelopedAspectMap;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.mxe.MetadataChangeProposal;
|
||||
import com.linkedin.r2.RemoteInvocationException;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.ArrayList;
|
||||
@ -36,7 +36,8 @@ public class CreateStructuredPropertyResolverTest {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new ArrayList<>());
|
||||
new ArrayList<>(),
|
||||
null);
|
||||
|
||||
@Test
|
||||
public void testGetSuccess() throws Exception {
|
||||
@ -56,7 +57,40 @@ public class CreateStructuredPropertyResolverTest {
|
||||
|
||||
// Validate that we called ingest
|
||||
Mockito.verify(mockEntityClient, Mockito.times(1))
|
||||
.ingestProposal(any(), any(MetadataChangeProposal.class), Mockito.eq(false));
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMismatchIdAndQualifiedName() throws Exception {
|
||||
EntityClient mockEntityClient = initMockEntityClient(true);
|
||||
CreateStructuredPropertyResolver resolver =
|
||||
new CreateStructuredPropertyResolver(mockEntityClient);
|
||||
|
||||
CreateStructuredPropertyInput testInput =
|
||||
new CreateStructuredPropertyInput(
|
||||
"mismatched",
|
||||
"io.acryl.test",
|
||||
"Display Name",
|
||||
"description",
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new ArrayList<>(),
|
||||
null);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(testInput);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
|
||||
// Validate ingest is not called
|
||||
Mockito.verify(mockEntityClient, Mockito.times(0))
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -75,7 +109,7 @@ public class CreateStructuredPropertyResolverTest {
|
||||
|
||||
// Validate that we did NOT call ingest
|
||||
Mockito.verify(mockEntityClient, Mockito.times(0))
|
||||
.ingestProposal(any(), any(MetadataChangeProposal.class), Mockito.eq(false));
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -94,7 +128,83 @@ public class CreateStructuredPropertyResolverTest {
|
||||
|
||||
// Validate that ingest was called, but that caused a failure
|
||||
Mockito.verify(mockEntityClient, Mockito.times(1))
|
||||
.ingestProposal(any(), any(MetadataChangeProposal.class), Mockito.eq(false));
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInvalidSettingsInput() throws Exception {
|
||||
EntityClient mockEntityClient = initMockEntityClient(true);
|
||||
CreateStructuredPropertyResolver resolver =
|
||||
new CreateStructuredPropertyResolver(mockEntityClient);
|
||||
|
||||
// if isHidden is true, other fields should not be true
|
||||
StructuredPropertySettingsInput settingsInput = new StructuredPropertySettingsInput();
|
||||
settingsInput.setIsHidden(true);
|
||||
settingsInput.setShowAsAssetBadge(true);
|
||||
|
||||
CreateStructuredPropertyInput testInput =
|
||||
new CreateStructuredPropertyInput(
|
||||
null,
|
||||
"io.acryl.test",
|
||||
"Display Name",
|
||||
"description",
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new ArrayList<>(),
|
||||
settingsInput);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(testInput);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
|
||||
// Validate ingest is not called
|
||||
Mockito.verify(mockEntityClient, Mockito.times(0))
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSuccessWithSettings() throws Exception {
|
||||
EntityClient mockEntityClient = initMockEntityClient(true);
|
||||
CreateStructuredPropertyResolver resolver =
|
||||
new CreateStructuredPropertyResolver(mockEntityClient);
|
||||
|
||||
StructuredPropertySettingsInput settingsInput = new StructuredPropertySettingsInput();
|
||||
settingsInput.setShowAsAssetBadge(true);
|
||||
|
||||
CreateStructuredPropertyInput testInput =
|
||||
new CreateStructuredPropertyInput(
|
||||
null,
|
||||
"io.acryl.test",
|
||||
"Display Name",
|
||||
"description",
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new ArrayList<>(),
|
||||
settingsInput);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(testInput);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
StructuredPropertyEntity prop = resolver.get(mockEnv).get();
|
||||
|
||||
assertEquals(prop.getUrn(), TEST_STRUCTURED_PROPERTY_URN);
|
||||
|
||||
// Validate that we called ingest
|
||||
Mockito.verify(mockEntityClient, Mockito.times(1))
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
private EntityClient initMockEntityClient(boolean shouldSucceed) throws Exception {
|
||||
|
@ -0,0 +1,91 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.structuredproperties;
|
||||
|
||||
import static com.linkedin.datahub.graphql.TestUtils.getMockAllowContext;
|
||||
import static com.linkedin.datahub.graphql.TestUtils.getMockDenyContext;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import com.linkedin.common.urn.UrnUtils;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.generated.DeleteStructuredPropertyInput;
|
||||
import com.linkedin.entity.client.EntityClient;
|
||||
import com.linkedin.r2.RemoteInvocationException;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.mockito.Mockito;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class DeleteStructuredPropertyResolverTest {
|
||||
private static final String TEST_PROP_URN = "urn:li:structuredProperty:test";
|
||||
|
||||
private static final DeleteStructuredPropertyInput TEST_INPUT =
|
||||
new DeleteStructuredPropertyInput(TEST_PROP_URN);
|
||||
|
||||
@Test
|
||||
public void testGetSuccess() throws Exception {
|
||||
EntityClient mockEntityClient = initMockEntityClient(true);
|
||||
DeleteStructuredPropertyResolver resolver =
|
||||
new DeleteStructuredPropertyResolver(mockEntityClient);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(TEST_INPUT);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
Boolean success = resolver.get(mockEnv).get();
|
||||
assertTrue(success);
|
||||
|
||||
// Validate that we called delete
|
||||
Mockito.verify(mockEntityClient, Mockito.times(1))
|
||||
.deleteEntity(any(), Mockito.eq(UrnUtils.getUrn(TEST_PROP_URN)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnauthorized() throws Exception {
|
||||
EntityClient mockEntityClient = initMockEntityClient(true);
|
||||
DeleteStructuredPropertyResolver resolver =
|
||||
new DeleteStructuredPropertyResolver(mockEntityClient);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockDenyContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(TEST_INPUT);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
|
||||
// Validate that we did NOT call delete
|
||||
Mockito.verify(mockEntityClient, Mockito.times(0))
|
||||
.deleteEntity(any(), Mockito.eq(UrnUtils.getUrn(TEST_PROP_URN)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFailure() throws Exception {
|
||||
EntityClient mockEntityClient = initMockEntityClient(false);
|
||||
DeleteStructuredPropertyResolver resolver =
|
||||
new DeleteStructuredPropertyResolver(mockEntityClient);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(TEST_INPUT);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
|
||||
// Validate that deleteEntity was called, but since it's the thing that failed it was called
|
||||
// once still
|
||||
Mockito.verify(mockEntityClient, Mockito.times(1))
|
||||
.deleteEntity(any(), Mockito.eq(UrnUtils.getUrn(TEST_PROP_URN)));
|
||||
}
|
||||
|
||||
private EntityClient initMockEntityClient(boolean shouldSucceed) throws Exception {
|
||||
EntityClient client = Mockito.mock(EntityClient.class);
|
||||
if (!shouldSucceed) {
|
||||
Mockito.doThrow(new RemoteInvocationException()).when(client).deleteEntity(any(), any());
|
||||
}
|
||||
return client;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.linkedin.datahub.graphql.resolvers.structuredproperties;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
import com.linkedin.metadata.models.StructuredPropertyUtils;
|
||||
import java.util.UUID;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class StructuredPropertyUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testGetIdMismatchedInput() throws Exception {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> StructuredPropertyUtils.getPropertyId("test1", "test2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIdConsistentInput() throws Exception {
|
||||
assertEquals(StructuredPropertyUtils.getPropertyId("test1", "test1"), "test1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIdNullQualifiedName() throws Exception {
|
||||
assertEquals(StructuredPropertyUtils.getPropertyId("test1", null), "test1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIdNullId() throws Exception {
|
||||
assertEquals(StructuredPropertyUtils.getPropertyId(null, "test1"), "test1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIdNullForBoth() throws Exception {
|
||||
try {
|
||||
String id = StructuredPropertyUtils.getPropertyId(null, null);
|
||||
UUID.fromString(id);
|
||||
} catch (Exception e) {
|
||||
fail("ID produced is not a UUID");
|
||||
}
|
||||
}
|
||||
}
|
@ -2,20 +2,25 @@ package com.linkedin.datahub.graphql.resolvers.structuredproperties;
|
||||
|
||||
import static com.linkedin.datahub.graphql.TestUtils.getMockAllowContext;
|
||||
import static com.linkedin.datahub.graphql.TestUtils.getMockDenyContext;
|
||||
import static com.linkedin.metadata.Constants.STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
import com.linkedin.common.UrnArray;
|
||||
import com.linkedin.common.urn.UrnUtils;
|
||||
import com.linkedin.datahub.graphql.QueryContext;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertyEntity;
|
||||
import com.linkedin.datahub.graphql.generated.StructuredPropertySettingsInput;
|
||||
import com.linkedin.datahub.graphql.generated.UpdateStructuredPropertyInput;
|
||||
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.EntityClient;
|
||||
import com.linkedin.metadata.Constants;
|
||||
import com.linkedin.mxe.MetadataChangeProposal;
|
||||
import com.linkedin.r2.RemoteInvocationException;
|
||||
import com.linkedin.structured.StructuredPropertyDefinition;
|
||||
import graphql.schema.DataFetchingEnvironment;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import org.mockito.Mockito;
|
||||
@ -33,6 +38,7 @@ public class UpdateStructuredPropertyResolverTest {
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
@Test
|
||||
@ -53,7 +59,7 @@ public class UpdateStructuredPropertyResolverTest {
|
||||
|
||||
// Validate that we called ingest
|
||||
Mockito.verify(mockEntityClient, Mockito.times(1))
|
||||
.ingestProposal(any(), any(MetadataChangeProposal.class), Mockito.eq(false));
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -72,7 +78,7 @@ public class UpdateStructuredPropertyResolverTest {
|
||||
|
||||
// Validate that we did NOT call ingest
|
||||
Mockito.verify(mockEntityClient, Mockito.times(0))
|
||||
.ingestProposal(any(), any(MetadataChangeProposal.class), Mockito.eq(false));
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -91,7 +97,80 @@ public class UpdateStructuredPropertyResolverTest {
|
||||
|
||||
// Validate that ingest was not called since there was a get failure before ingesting
|
||||
Mockito.verify(mockEntityClient, Mockito.times(0))
|
||||
.ingestProposal(any(), any(MetadataChangeProposal.class), Mockito.eq(false));
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInvalidSettingsInput() throws Exception {
|
||||
EntityClient mockEntityClient = initMockEntityClient(true);
|
||||
UpdateStructuredPropertyResolver resolver =
|
||||
new UpdateStructuredPropertyResolver(mockEntityClient);
|
||||
|
||||
// if isHidden is true, other fields should not be true
|
||||
StructuredPropertySettingsInput settingsInput = new StructuredPropertySettingsInput();
|
||||
settingsInput.setIsHidden(true);
|
||||
settingsInput.setShowInSearchFilters(true);
|
||||
|
||||
final UpdateStructuredPropertyInput testInput =
|
||||
new UpdateStructuredPropertyInput(
|
||||
TEST_STRUCTURED_PROPERTY_URN,
|
||||
"New Display Name",
|
||||
"new description",
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
settingsInput);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(testInput);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
|
||||
|
||||
// Validate that ingest was not called since there was a get failure before ingesting
|
||||
Mockito.verify(mockEntityClient, Mockito.times(0))
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetValidSettingsInput() throws Exception {
|
||||
EntityClient mockEntityClient = initMockEntityClient(true);
|
||||
UpdateStructuredPropertyResolver resolver =
|
||||
new UpdateStructuredPropertyResolver(mockEntityClient);
|
||||
|
||||
// if isHidden is true, other fields should not be true
|
||||
StructuredPropertySettingsInput settingsInput = new StructuredPropertySettingsInput();
|
||||
settingsInput.setIsHidden(true);
|
||||
|
||||
final UpdateStructuredPropertyInput testInput =
|
||||
new UpdateStructuredPropertyInput(
|
||||
TEST_STRUCTURED_PROPERTY_URN,
|
||||
"New Display Name",
|
||||
"new description",
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
settingsInput);
|
||||
|
||||
// Execute resolver
|
||||
QueryContext mockContext = getMockAllowContext();
|
||||
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
|
||||
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(testInput);
|
||||
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
|
||||
|
||||
StructuredPropertyEntity prop = resolver.get(mockEnv).get();
|
||||
|
||||
assertEquals(prop.getUrn(), TEST_STRUCTURED_PROPERTY_URN);
|
||||
|
||||
// Validate that we called ingest
|
||||
Mockito.verify(mockEntityClient, Mockito.times(1))
|
||||
.batchIngestProposals(any(), Mockito.anyList(), Mockito.eq(false));
|
||||
}
|
||||
|
||||
private EntityClient initMockEntityClient(boolean shouldSucceed) throws Exception {
|
||||
@ -99,7 +178,11 @@ public class UpdateStructuredPropertyResolverTest {
|
||||
EntityResponse response = new EntityResponse();
|
||||
response.setEntityName(Constants.STRUCTURED_PROPERTY_ENTITY_NAME);
|
||||
response.setUrn(UrnUtils.getUrn(TEST_STRUCTURED_PROPERTY_URN));
|
||||
response.setAspects(new EnvelopedAspectMap());
|
||||
final EnvelopedAspectMap aspectMap = new EnvelopedAspectMap();
|
||||
aspectMap.put(
|
||||
STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME,
|
||||
new EnvelopedAspect().setValue(new Aspect(createDefinition().data())));
|
||||
response.setAspects(aspectMap);
|
||||
if (shouldSucceed) {
|
||||
Mockito.when(
|
||||
client.getV2(
|
||||
@ -120,4 +203,13 @@ public class UpdateStructuredPropertyResolverTest {
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private StructuredPropertyDefinition createDefinition() {
|
||||
StructuredPropertyDefinition definition = new StructuredPropertyDefinition();
|
||||
definition.setDisplayName("test");
|
||||
definition.setQualifiedName("test");
|
||||
definition.setValueType(UrnUtils.getUrn("urn:li:dataType:datahub.string"));
|
||||
definition.setEntityTypes(new UrnArray());
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import com.linkedin.metadata.query.filter.Filter;
|
||||
import com.linkedin.structured.PrimitivePropertyValue;
|
||||
import com.linkedin.structured.StructuredProperties;
|
||||
import com.linkedin.structured.StructuredPropertyDefinition;
|
||||
import com.linkedin.structured.StructuredPropertySettings;
|
||||
import com.linkedin.structured.StructuredPropertyValueAssignment;
|
||||
import com.linkedin.structured.StructuredPropertyValueAssignmentArray;
|
||||
import com.linkedin.util.Pair;
|
||||
@ -32,6 +33,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -45,6 +47,11 @@ public class StructuredPropertyUtils {
|
||||
static final Date MIN_DATE = Date.valueOf("1000-01-01");
|
||||
static final Date MAX_DATE = Date.valueOf("9999-12-31");
|
||||
|
||||
public static final String INVALID_SETTINGS_MESSAGE =
|
||||
"Cannot have property isHidden = true while other display location settings are also true.";
|
||||
public static final String ONLY_ONE_BADGE =
|
||||
"Cannot have more than one property set with show as badge. Property urns currently set: ";
|
||||
|
||||
public static LogicalValueType getLogicalValueType(
|
||||
StructuredPropertyDefinition structuredPropertyDefinition) {
|
||||
return getLogicalValueType(structuredPropertyDefinition.getValueType());
|
||||
@ -355,4 +362,47 @@ public class StructuredPropertyUtils {
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We accept both ID and qualifiedName as inputs when creating a structured property. However,
|
||||
* these two fields should ALWAYS be the same. If they don't provide either, use a UUID for both.
|
||||
* If they provide both, ensure they are the same otherwise throw. Otherwise, use what is provided.
|
||||
*/
|
||||
public static String getPropertyId(
|
||||
@Nullable final String inputId, @Nullable final String inputQualifiedName) {
|
||||
if (inputId != null && inputQualifiedName != null && !inputId.equals(inputQualifiedName)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Qualified name and the ID of a structured property must match");
|
||||
}
|
||||
|
||||
String id = UUID.randomUUID().toString();
|
||||
|
||||
if (inputQualifiedName != null) {
|
||||
id = inputQualifiedName;
|
||||
} else if (inputId != null) {
|
||||
id = inputId;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that a structured property settings aspect is valid by ensuring that if isHidden is true,
|
||||
* the other fields concerning display locations are false;
|
||||
*/
|
||||
public static boolean validatePropertySettings(
|
||||
StructuredPropertySettings settings, boolean shouldThrow) {
|
||||
if (settings.isIsHidden()) {
|
||||
if (settings.isShowInSearchFilters()
|
||||
|| settings.isShowInAssetSummary()
|
||||
|| settings.isShowAsAssetBadge()) {
|
||||
if (shouldThrow) {
|
||||
throw new IllegalArgumentException(INVALID_SETTINGS_MESSAGE);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -363,6 +363,8 @@ public class Constants {
|
||||
// Structured Property
|
||||
public static final String STRUCTURED_PROPERTY_DEFINITION_ASPECT_NAME = "propertyDefinition";
|
||||
public static final String STRUCTURED_PROPERTY_KEY_ASPECT_NAME = "structuredPropertyKey";
|
||||
public static final String STRUCTURED_PROPERTY_SETTINGS_ASPECT_NAME =
|
||||
"structuredPropertySettings";
|
||||
|
||||
// Form
|
||||
public static final String FORM_INFO_ASPECT_NAME = "formInfo";
|
||||
|
@ -118,11 +118,13 @@ class StructuredProperties(ConfigModel):
|
||||
@property
|
||||
def fqn(self) -> str:
|
||||
assert self.urn is not None
|
||||
return (
|
||||
self.qualified_name
|
||||
or self.id
|
||||
or Urn.from_string(self.urn).get_entity_id()[0]
|
||||
)
|
||||
id = Urn.create_from_string(self.urn).get_entity_id()[0]
|
||||
if self.qualified_name is not None:
|
||||
# ensure that qualified name and ID match
|
||||
assert (
|
||||
self.qualified_name == id
|
||||
), "ID in the urn and the qualified_name must match"
|
||||
return id
|
||||
|
||||
@validator("urn", pre=True, always=True)
|
||||
def urn_must_be_present(cls, v, values):
|
||||
|
@ -100,7 +100,7 @@ record StructuredPropertyDefinition {
|
||||
created: optional AuditStamp
|
||||
|
||||
/**
|
||||
* Created Audit stamp
|
||||
* Last Modified Audit stamp
|
||||
*/
|
||||
@Searchable = {
|
||||
"/time": {
|
||||
|
@ -0,0 +1,64 @@
|
||||
namespace com.linkedin.structured
|
||||
|
||||
import com.linkedin.common.AuditStamp
|
||||
|
||||
/**
|
||||
* Settings specific to a structured property entity
|
||||
*/
|
||||
@Aspect = {
|
||||
"name": "structuredPropertySettings"
|
||||
}
|
||||
record StructuredPropertySettings {
|
||||
/**
|
||||
* Whether or not this asset should be hidden in the main application
|
||||
*/
|
||||
@Searchable = {
|
||||
"fieldType": "BOOLEAN"
|
||||
}
|
||||
isHidden: boolean = false
|
||||
|
||||
/**
|
||||
* Whether or not this asset should be displayed as a search filter
|
||||
*/
|
||||
@Searchable = {
|
||||
"fieldType": "BOOLEAN"
|
||||
}
|
||||
showInSearchFilters: boolean = false
|
||||
|
||||
/**
|
||||
* Whether or not this asset should be displayed in the asset sidebar
|
||||
*/
|
||||
@Searchable = {
|
||||
"fieldType": "BOOLEAN"
|
||||
}
|
||||
showInAssetSummary: boolean = false
|
||||
|
||||
/**
|
||||
* Whether or not this asset should be displayed as an asset badge on other
|
||||
* asset's headers
|
||||
*/
|
||||
@Searchable = {
|
||||
"fieldType": "BOOLEAN"
|
||||
}
|
||||
showAsAssetBadge: boolean = false
|
||||
|
||||
/**
|
||||
* Whether or not this asset should be displayed as a column in the schema field table
|
||||
* in a Dataset's "Columns" tab.
|
||||
*/
|
||||
@Searchable = {
|
||||
"fieldType": "BOOLEAN"
|
||||
}
|
||||
showInColumnsTable: boolean = false
|
||||
|
||||
/**
|
||||
* Last Modified Audit stamp
|
||||
*/
|
||||
@Searchable = {
|
||||
"/time": {
|
||||
"fieldName": "lastModifiedSettings",
|
||||
"fieldType": "DATETIME"
|
||||
}
|
||||
}
|
||||
lastModified: optional AuditStamp
|
||||
}
|
@ -602,6 +602,7 @@ entities:
|
||||
keyAspect: structuredPropertyKey
|
||||
aspects:
|
||||
- propertyDefinition
|
||||
- structuredPropertySettings
|
||||
- institutionalMemory
|
||||
- status
|
||||
- name: form
|
||||
|
Loading…
x
Reference in New Issue
Block a user