-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(structuredProperties) Add CRUD graphql APIs for structured prope…
…rty entities (#10826)
- Loading branch information
1 parent
f067573
commit 65c7adb
Showing
11 changed files
with
948 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
136 changes: 136 additions & 0 deletions
136
...edin/datahub/graphql/resolvers/structuredproperties/CreateStructuredPropertyResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
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 com.linkedin.common.urn.Urn; | ||
import com.linkedin.data.template.SetMode; | ||
import com.linkedin.data.template.StringArray; | ||
import com.linkedin.data.template.StringArrayMap; | ||
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.CreateStructuredPropertyInput; | ||
import com.linkedin.datahub.graphql.generated.StructuredPropertyEntity; | ||
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.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 graphql.schema.DataFetcher; | ||
import graphql.schema.DataFetchingEnvironment; | ||
import java.util.Objects; | ||
import java.util.UUID; | ||
import java.util.concurrent.CompletableFuture; | ||
import javax.annotation.Nonnull; | ||
|
||
public class CreateStructuredPropertyResolver | ||
implements DataFetcher<CompletableFuture<StructuredPropertyEntity>> { | ||
|
||
private final EntityClient _entityClient; | ||
|
||
public CreateStructuredPropertyResolver(@Nonnull final EntityClient entityClient) { | ||
_entityClient = Objects.requireNonNull(entityClient, "entityClient must not be null"); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<StructuredPropertyEntity> get(final DataFetchingEnvironment environment) | ||
throws Exception { | ||
final QueryContext context = environment.getContext(); | ||
|
||
final CreateStructuredPropertyInput input = | ||
bindArgument(environment.getArgument("input"), CreateStructuredPropertyInput.class); | ||
|
||
return CompletableFuture.supplyAsync( | ||
() -> { | ||
try { | ||
if (!AuthorizationUtils.canManageStructuredProperties(context)) { | ||
throw new AuthorizationException( | ||
"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(); | ||
key.setId(id); | ||
final Urn propertyUrn = | ||
EntityKeyUtils.convertEntityKeyToUrn(key, STRUCTURED_PROPERTY_ENTITY_NAME); | ||
StructuredPropertyDefinitionPatchBuilder builder = | ||
new StructuredPropertyDefinitionPatchBuilder().urn(propertyUrn); | ||
|
||
builder.setQualifiedName(input.getQualifiedName()); | ||
builder.setValueType(input.getValueType()); | ||
input.getEntityTypes().forEach(builder::addEntityType); | ||
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); | ||
} | ||
if (input.getAllowedValues() != null) { | ||
buildAllowedValues(input, builder); | ||
} | ||
if (input.getCardinality() != null) { | ||
builder.setCardinality( | ||
PropertyCardinality.valueOf(input.getCardinality().toString())); | ||
} | ||
|
||
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); | ||
} | ||
}); | ||
} | ||
|
||
private void buildTypeQualifier( | ||
@Nonnull final CreateStructuredPropertyInput input, | ||
@Nonnull final StructuredPropertyDefinitionPatchBuilder builder) { | ||
if (input.getTypeQualifier().getAllowedTypes() != null) { | ||
final StringArrayMap typeQualifier = new StringArrayMap(); | ||
StringArray allowedTypes = new StringArray(); | ||
allowedTypes.addAll(input.getTypeQualifier().getAllowedTypes()); | ||
typeQualifier.put("allowedTypes", allowedTypes); | ||
builder.setTypeQualifier(typeQualifier); | ||
} | ||
} | ||
|
||
private void buildAllowedValues( | ||
@Nonnull final CreateStructuredPropertyInput input, | ||
@Nonnull final StructuredPropertyDefinitionPatchBuilder builder) { | ||
input | ||
.getAllowedValues() | ||
.forEach( | ||
allowedValueInput -> { | ||
PropertyValue value = new PropertyValue(); | ||
PrimitivePropertyValue primitiveValue = new PrimitivePropertyValue(); | ||
if (allowedValueInput.getStringValue() != null) { | ||
primitiveValue.setString(allowedValueInput.getStringValue()); | ||
} | ||
if (allowedValueInput.getNumberValue() != null) { | ||
primitiveValue.setDouble(allowedValueInput.getNumberValue().doubleValue()); | ||
} | ||
value.setValue(primitiveValue); | ||
value.setDescription(allowedValueInput.getDescription(), SetMode.IGNORE_NULL); | ||
builder.addAllowedValue(value); | ||
}); | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
...in/datahub/graphql/resolvers/structuredproperties/RemoveStructuredPropertiesResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package com.linkedin.datahub.graphql.resolvers.structuredproperties; | ||
|
||
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.bindArgument; | ||
|
||
import com.google.common.collect.ImmutableSet; | ||
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.exception.AuthorizationException; | ||
import com.linkedin.datahub.graphql.generated.RemoveStructuredPropertiesInput; | ||
import com.linkedin.datahub.graphql.types.structuredproperty.StructuredPropertiesMapper; | ||
import com.linkedin.entity.EntityResponse; | ||
import com.linkedin.entity.client.EntityClient; | ||
import com.linkedin.metadata.Constants; | ||
import com.linkedin.metadata.aspect.patch.builder.StructuredPropertiesPatchBuilder; | ||
import com.linkedin.mxe.MetadataChangeProposal; | ||
import com.linkedin.structured.StructuredProperties; | ||
import graphql.schema.DataFetcher; | ||
import graphql.schema.DataFetchingEnvironment; | ||
import java.util.Objects; | ||
import java.util.concurrent.CompletableFuture; | ||
import javax.annotation.Nonnull; | ||
|
||
public class RemoveStructuredPropertiesResolver | ||
implements DataFetcher< | ||
CompletableFuture<com.linkedin.datahub.graphql.generated.StructuredProperties>> { | ||
|
||
private final EntityClient _entityClient; | ||
|
||
public RemoveStructuredPropertiesResolver(@Nonnull final EntityClient entityClient) { | ||
_entityClient = Objects.requireNonNull(entityClient, "entityClient must not be null"); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<com.linkedin.datahub.graphql.generated.StructuredProperties> get( | ||
final DataFetchingEnvironment environment) throws Exception { | ||
final QueryContext context = environment.getContext(); | ||
|
||
final RemoveStructuredPropertiesInput input = | ||
bindArgument(environment.getArgument("input"), RemoveStructuredPropertiesInput.class); | ||
final Urn assetUrn = UrnUtils.getUrn(input.getAssetUrn()); | ||
|
||
return CompletableFuture.supplyAsync( | ||
() -> { | ||
try { | ||
// check authorization first | ||
if (!AuthorizationUtils.canEditProperties(assetUrn, context)) { | ||
throw new AuthorizationException( | ||
String.format( | ||
"Not authorized to update properties on the gives urn %s", assetUrn)); | ||
} | ||
|
||
if (!_entityClient.exists(context.getOperationContext(), assetUrn)) { | ||
throw new RuntimeException( | ||
String.format("Asset with provided urn %s does not exist", assetUrn)); | ||
} | ||
|
||
StructuredPropertiesPatchBuilder patchBuilder = | ||
new StructuredPropertiesPatchBuilder().urn(assetUrn); | ||
|
||
input | ||
.getStructuredPropertyUrns() | ||
.forEach( | ||
propertyUrn -> { | ||
patchBuilder.removeProperty(UrnUtils.getUrn(propertyUrn)); | ||
}); | ||
|
||
// ingest change proposal | ||
final MetadataChangeProposal structuredPropertiesProposal = patchBuilder.build(); | ||
|
||
_entityClient.ingestProposal( | ||
context.getOperationContext(), structuredPropertiesProposal, false); | ||
|
||
EntityResponse response = | ||
_entityClient.getV2( | ||
context.getOperationContext(), | ||
assetUrn.getEntityType(), | ||
assetUrn, | ||
ImmutableSet.of(Constants.STRUCTURED_PROPERTIES_ASPECT_NAME)); | ||
|
||
if (response == null | ||
|| response.getAspects().get(Constants.STRUCTURED_PROPERTIES_ASPECT_NAME) == null) { | ||
throw new RuntimeException( | ||
String.format("Failed to fetch structured properties from entity %s", assetUrn)); | ||
} | ||
|
||
StructuredProperties structuredProperties = | ||
new StructuredProperties( | ||
response | ||
.getAspects() | ||
.get(Constants.STRUCTURED_PROPERTIES_ASPECT_NAME) | ||
.getValue() | ||
.data()); | ||
|
||
return StructuredPropertiesMapper.map(context, structuredProperties); | ||
} catch (Exception e) { | ||
throw new RuntimeException( | ||
String.format("Failed to perform update against input %s", input), e); | ||
} | ||
}); | ||
} | ||
} |
Oops, something went wrong.