-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(structuredProperties) Add CRUD graphql APIs for structured property entities #10826
Changes from all commits
3f3744d
c59deaf
d6a2d34
4d00bd6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
}); | ||
} | ||
} |
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; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tip Codebase Verification Issue with Import Statement or Missing File The import statement
Analysis chainCheck for case sensitivity in import statements. Ensure that the import Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify the case sensitivity of the import statement.
# Test: Check if the file exists with the exact case.
fd --type f "RemoveStructuredPropertiesInput.java"
Length of output: 50 Script: #!/bin/bash
# Description: Verify the case sensitivity of the import statement by checking different case variations.
# Test: Check if the file exists with various case variations.
fd --type f -e java | grep -i "RemoveStructuredPropertiesInput"
# Test: Verify the directory structure for the package path.
fd --type d "generated" | grep "com/linkedin/datahub/graphql"
Length of output: 123 Script: #!/bin/bash
# Description: Perform a broader search for related files and directories.
# Test: Search for any files related to RemoveStructuredPropertiesInput with different case variations.
fd --type f -e java | grep -i "RemoveStructuredProperties"
# Test: Search for any directories related to the generated package path.
fd --type d | grep -i "generated"
Length of output: 1194 |
||||||
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(); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding null checks for While - final QueryContext context = environment.getContext();
+ final QueryContext context = Objects.requireNonNull(environment.getContext(), "context must not be null"); Committable suggestion
Suggested change
|
||||||
|
||||||
final RemoveStructuredPropertiesInput input = | ||||||
bindArgument(environment.getArgument("input"), RemoveStructuredPropertiesInput.class); | ||||||
final Urn assetUrn = UrnUtils.getUrn(input.getAssetUrn()); | ||||||
|
||||||
return CompletableFuture.supplyAsync( | ||||||
() -> { | ||||||
try { | ||||||
Comment on lines
+44
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider handling CompletableFuture exceptions. Using - () -> {
+ () -> {
+ try {
+ // existing code
+ } catch (Exception e) {
+ throw new CompletionException(e);
+ }
+ }
|
||||||
// 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); | ||||||
} | ||||||
}); | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add missing import for
UpsertStructuredPropertiesResolver
.The import statement for
UpsertStructuredPropertiesResolver
is missing, which might lead to compilation errors.+import com.linkedin.datahub.graphql.resolvers.structuredproperties.UpsertStructuredPropertiesResolver;
Committable suggestion