Skip to content

Commit

Permalink
Merge pull request datahub-project#10 from poorvi767/pl-join-ui-pr
Browse files Browse the repository at this point in the history
Pl join UI pr
  • Loading branch information
poorvi767 authored Oct 3, 2023
2 parents d784cad + e7916f2 commit 9ce4607
Show file tree
Hide file tree
Showing 32 changed files with 439 additions and 227 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@
import com.linkedin.metadata.service.QueryService;
import com.linkedin.metadata.service.SettingsService;
import com.linkedin.metadata.service.ViewService;
import com.linkedin.metadata.service.JoinService;
import com.linkedin.metadata.timeline.TimelineService;
import com.linkedin.metadata.timeseries.TimeseriesAspectService;
import com.linkedin.metadata.version.GitVersion;
Expand Down Expand Up @@ -439,6 +440,7 @@ public class GmsGraphQLEngine {
private final QueryType queryType;
private final DataProductType dataProductType;
private final OwnershipType ownershipType;
private final JoinService joinService;

/**
* A list of GraphQL Plugins that extend the core engine
Expand Down Expand Up @@ -503,6 +505,7 @@ public GmsGraphQLEngine(final GmsGraphQLEngineArgs args) {
this.settingsService = args.settingsService;
this.lineageService = args.lineageService;
this.queryService = args.queryService;
this.joinService = args.joinService;
this.dataProductService = args.dataProductService;

this.ingestionConfiguration = Objects.requireNonNull(args.ingestionConfiguration);
Expand Down Expand Up @@ -544,7 +547,7 @@ public GmsGraphQLEngine(final GmsGraphQLEngineArgs args) {
this.dataHubPolicyType = new DataHubPolicyType(entityClient);
this.dataHubRoleType = new DataHubRoleType(entityClient);
this.schemaFieldType = new SchemaFieldType();
this.joinType = new JoinType(entityClient);
this.joinType = new JoinType(entityClient, featureFlags);
this.dataHubViewType = new DataHubViewType(entityClient);
this.queryType = new QueryType(entityClient);
this.dataProductType = new DataProductType(entityClient);
Expand Down Expand Up @@ -927,7 +930,7 @@ private void configureMutationResolvers(final RuntimeWiring.Builder builder) {
.dataFetcher("updateCorpUserProperties", new MutableTypeResolver<>(corpUserType))
.dataFetcher("updateCorpGroupProperties", new MutableTypeResolver<>(corpGroupType))
.dataFetcher("updateJoin", new UpdateJoinResolver(this.entityClient))
.dataFetcher("createJoin", new CreateJoinResolver(this.entityClient))
.dataFetcher("createJoin", new CreateJoinResolver(this.entityClient, this.joinService))
.dataFetcher("addTag", new AddTagResolver(entityService))
.dataFetcher("addTags", new AddTagsResolver(entityService))
.dataFetcher("batchAddTags", new BatchAddTagsResolver(entityService))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.linkedin.metadata.service.LineageService;
import com.linkedin.metadata.service.OwnershipTypeService;
import com.linkedin.metadata.service.QueryService;
import com.linkedin.metadata.service.JoinService;
import com.linkedin.metadata.service.SettingsService;
import com.linkedin.metadata.service.ViewService;
import com.linkedin.metadata.timeline.TimelineService;
Expand Down Expand Up @@ -71,6 +72,6 @@ public class GmsGraphQLEngineArgs {
QueryService queryService;
FeatureFlags featureFlags;
DataProductService dataProductService;

JoinService joinService;
//any fork specific args should go below this line
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public class FeatureFlags {
private boolean showBrowseV2 = false;
private PreProcessHooks preProcessHooks;
private boolean showAcrylInfo = false;
private boolean joinFeatureEnabled = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ public CompletableFuture<AppConfig> get(final DataFetchingEnvironment environmen
.setReadOnlyModeEnabled(_featureFlags.isReadOnlyModeEnabled())
.setShowBrowseV2(_featureFlags.isShowBrowseV2())
.setShowAcrylInfo(_featureFlags.isShowAcrylInfo())
.setJoinFeatureEnabled(_featureFlags.isJoinFeatureEnabled())
.build();

appConfig.setFeatureFlags(featureFlagsConfig);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,71 @@
package com.linkedin.datahub.graphql.types.join;

import com.datahub.authentication.Authentication;
import com.linkedin.common.urn.CorpuserUrn;
import com.linkedin.common.urn.JoinUrn;
import com.linkedin.common.urn.Urn;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.JoinPropertiesInput;
import com.linkedin.datahub.graphql.generated.JoinUpdateInput;
import com.linkedin.datahub.graphql.types.join.mappers.JoinMapper;
import com.linkedin.datahub.graphql.types.join.mappers.JoinUpdateInputMapper;
import com.linkedin.entity.client.EntityClient;
import com.linkedin.metadata.service.JoinService;
import com.linkedin.mxe.MetadataChangeProposal;
import com.linkedin.r2.RemoteInvocationException;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.linkedin.datahub.graphql.generated.Join;
import org.apache.commons.codec.digest.DigestUtils;

import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.bindArgument;


@Slf4j
@RequiredArgsConstructor
public class CreateJoinResolver implements DataFetcher<CompletableFuture<Boolean>> {
public class CreateJoinResolver implements DataFetcher<CompletableFuture<Join>> {

private final EntityClient _entityClient;
private final JoinService _joinService;

@Override
public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throws Exception {
public CompletableFuture<Join> get(DataFetchingEnvironment environment) throws Exception {
final JoinUpdateInput input = bindArgument(environment.getArgument("input"), JoinUpdateInput.class);
JoinUrn inputUrn = new JoinUrn(UUID.randomUUID().toString());

final JoinPropertiesInput joinPropertiesInput = input.getProperties();
String joinName = joinPropertiesInput.getName();
String datasetA = joinPropertiesInput.getDataSetA();
String datasetB = joinPropertiesInput.getDatasetB();

String lowDataset = datasetA;
String highDataset = datasetB;
if (datasetA.compareTo(datasetB) > 0) {
lowDataset = datasetB;
highDataset = datasetA;
}
// The following sequence mimics datahub.emitter.mce_builder.datahub_guid

String joinKey =
"{\"DatasetA\":\"" + lowDataset + "\",\"DatasetB\":\"" + highDataset + "\",\"JoinName\":\"" + joinName
+ "\"}";

byte[] mybytes = joinKey.getBytes(StandardCharsets.UTF_8);

String joinKeyEncoded = new String(mybytes, StandardCharsets.UTF_8);
String joinGuid = DigestUtils.md5Hex(joinKeyEncoded);
log.info("joinkey {}, joinGuid {}", joinKeyEncoded, joinGuid);

JoinUrn inputUrn = new JoinUrn(joinGuid);
QueryContext context = environment.getContext();
final Authentication authentication = context.getAuthentication();
final CorpuserUrn actor = CorpuserUrn.createFromString(context.getActorUrn());
if (!JoinType.isAuthorizedToCreateJoin(context, Urn.createFromString(input.getProperties().getDataSetA()),
if (!JoinType.canCreateJoin(context, Urn.createFromString(input.getProperties().getDataSetA()),
Urn.createFromString(input.getProperties().getDatasetB()))) {
throw new AuthorizationException("Unauthorized to create join. Please contact your DataHub administrator.");
}
Expand All @@ -42,19 +74,16 @@ public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throw
log.debug("Create Join input: {}", input);
final Collection<MetadataChangeProposal> proposals = JoinUpdateInputMapper.map(input, actor);
proposals.forEach(proposal -> proposal.setEntityUrn(inputUrn));

try {
_entityClient.batchIngestProposals(proposals, context.getAuthentication(), false);
} catch (RemoteInvocationException e) {
throw new RuntimeException(String.format("Failed to create join entity"), e);
throw new RuntimeException("Failed to create join entity", e);
}
return true;
return JoinMapper.map(_joinService.getJoinResponse(Urn.createFromString(inputUrn.toString()), authentication));
} catch (Exception e) {
log.error("Failed to create Join to resource with input {}, {}", input, e.getMessage());
throw new RuntimeException(String.format("Failed to create join to resource with input %s", input), e);
}
});
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.linkedin.data.template.StringArray;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
import com.linkedin.datahub.graphql.featureflags.FeatureFlags;
import com.linkedin.datahub.graphql.generated.AutoCompleteResults;
import com.linkedin.datahub.graphql.generated.BrowsePath;
import com.linkedin.datahub.graphql.generated.BrowseResults;
Expand All @@ -36,7 +37,6 @@
import com.linkedin.metadata.query.filter.Filter;
import com.linkedin.metadata.search.SearchResult;
import graphql.execution.DataFetcherResult;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
Expand All @@ -62,20 +62,19 @@ public class JoinType implements com.linkedin.datahub.graphql.types.EntityType<J
INSTITUTIONAL_MEMORY_ASPECT_NAME,
OWNERSHIP_ASPECT_NAME,
STATUS_ASPECT_NAME,
CONTAINER_ASPECT_NAME,
GLOBAL_TAGS_ASPECT_NAME,
GLOSSARY_TERMS_ASPECT_NAME,
BROWSE_PATHS_ASPECT_NAME
GLOSSARY_TERMS_ASPECT_NAME
);

private static final Set<String> FACET_FIELDS = ImmutableSet.of("name");
private static final String ENTITY_NAME = "join";

private final EntityClient _entityClient;
private final FeatureFlags _featureFlags;


public JoinType(final EntityClient entityClient) {
public JoinType(final EntityClient entityClient, final FeatureFlags featureFlags) {
_entityClient = entityClient;
_featureFlags = featureFlags; // TODO: check if Join Feture is Enabled and throw error when called
}

@Override
Expand Down Expand Up @@ -142,18 +141,10 @@ public BrowseResults browse(@Nonnull List<String> path, @Nullable List<FacetFilt
@Nonnull
@Override
public List<BrowsePath> browsePaths(@Nonnull String urn, @Nonnull QueryContext context) throws Exception {
final StringArray result = _entityClient.getBrowsePaths(getJoinUrn(urn), context.getAuthentication());
final StringArray result = _entityClient.getBrowsePaths(UrnUtils.getUrn(urn), context.getAuthentication());
return BrowsePathsMapper.map(result);
}

private com.linkedin.common.urn.JoinUrn getJoinUrn(String urnStr) {
try {
return JoinUrn.createFromString(urnStr);
} catch (URISyntaxException e) {
throw new RuntimeException(String.format("Failed to retrieve data product with urn %s, invalid urn", urnStr));
}
}

@Override
public SearchResults search(@Nonnull String query, @Nullable List<FacetFilterInput> filters,
int start, int count, @Nonnull QueryContext context) throws Exception {
Expand All @@ -171,25 +162,19 @@ public AutoCompleteResults autoComplete(@Nonnull String query, @Nullable String
return AutoCompleteResultsMapper.map(result);
}

public static boolean isAuthorizedToUpdateJoin(@Nonnull QueryContext context, JoinUrn resourceUrn, JoinUpdateInput updateInput) {
final ConjunctivePrivilegeGroup allPrivilegesGroup = new ConjunctivePrivilegeGroup(ImmutableList.of(
public static boolean canUpdateJoin(@Nonnull QueryContext context, JoinUrn resourceUrn, JoinUpdateInput updateInput) {
final ConjunctivePrivilegeGroup editPrivilegesGroup = new ConjunctivePrivilegeGroup(ImmutableList.of(
PoliciesConfig.EDIT_ENTITY_PRIVILEGE.getType()
));
List<String> specificPrivileges = new ArrayList<>();
if (updateInput.getOwnership() != null) {
specificPrivileges.add(PoliciesConfig.EDIT_ENTITY_OWNERS_PRIVILEGE.getType());
}
if (updateInput.getEditableProperties() != null) {
specificPrivileges.add(PoliciesConfig.EDIT_ENTITY_DOCS_PRIVILEGE.getType());
}
if (updateInput.getTags() != null) {
specificPrivileges.add(PoliciesConfig.EDIT_ENTITY_TAGS_PRIVILEGE.getType());
}
final ConjunctivePrivilegeGroup specificPrivilegeGroup = new ConjunctivePrivilegeGroup(specificPrivileges);

// If you either have all entity privileges, or have the specific privileges required, you are authorized.
DisjunctivePrivilegeGroup orPrivilegeGroups = new DisjunctivePrivilegeGroup(ImmutableList.of(
allPrivilegesGroup,
editPrivilegesGroup,
specificPrivilegeGroup
));
return AuthorizationUtils.isAuthorized(
Expand All @@ -199,9 +184,17 @@ public static boolean isAuthorizedToUpdateJoin(@Nonnull QueryContext context, Jo
resourceUrn.toString(),
orPrivilegeGroups);
}
public static boolean isAuthorizedToCreateJoin(@Nonnull QueryContext context, Urn datasetAUrn, Urn datasetBUrn) {
final DisjunctivePrivilegeGroup orPrivilegeGroups = new DisjunctivePrivilegeGroup(ImmutableList.of(
new ConjunctivePrivilegeGroup(ImmutableList.of(PoliciesConfig.CREATE_JOIN_PRIVILEGE.getType()))
public static boolean canCreateJoin(@Nonnull QueryContext context, Urn datasetAUrn, Urn datasetBUrn) {
final ConjunctivePrivilegeGroup editPrivilegesGroup = new ConjunctivePrivilegeGroup(ImmutableList.of(
PoliciesConfig.EDIT_ENTITY_PRIVILEGE.getType()
));
final ConjunctivePrivilegeGroup createPrivilegesGroup = new ConjunctivePrivilegeGroup(ImmutableList.of(
PoliciesConfig.CREATE_JOIN_PRIVILEGE.getType()
));
// If you either have all entity privileges, or have the specific privileges required, you are authorized.
DisjunctivePrivilegeGroup orPrivilegeGroups = new DisjunctivePrivilegeGroup(ImmutableList.of(
editPrivilegesGroup,
createPrivilegesGroup
));
boolean datasetAPrivilege = AuthorizationUtils.isAuthorized(
context.getAuthorizer(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throw
JoinUrn inputUrn = JoinUrn.createFromString(urn);
QueryContext context = environment.getContext();
final CorpuserUrn actor = CorpuserUrn.createFromString(context.getActorUrn());
if (!JoinType.isAuthorizedToUpdateJoin(context, inputUrn, input)) {
if (!JoinType.canUpdateJoin(context, inputUrn, input)) {
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
}
return CompletableFuture.supplyAsync(() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,28 @@ public Join apply(final EntityResponse entityResponse) {
MappingHelper<Join> mappingHelper = new MappingHelper<>(aspectMap, result);
mappingHelper.mapToResult(JOIN_KEY_ASPECT_NAME, this::mapJoinKey);
mappingHelper.mapToResult(JOIN_PROPERTIES_ASPECT_NAME, this::mapProperties);
mappingHelper.mapToResult(EDITABLE_JOIN_PROPERTIES_ASPECT_NAME, this::mapEditableProperties);
mappingHelper.mapToResult(INSTITUTIONAL_MEMORY_ASPECT_NAME, (join, dataMap) ->
join.setInstitutionalMemory(InstitutionalMemoryMapper.map(new InstitutionalMemory(dataMap), entityUrn)));
mappingHelper.mapToResult(OWNERSHIP_ASPECT_NAME, (join, dataMap) ->
join.setOwnership(OwnershipMapper.map(new Ownership(dataMap), entityUrn)));
mappingHelper.mapToResult(STATUS_ASPECT_NAME, (join, dataMap) ->
join.setStatus(StatusMapper.map(new Status(dataMap))));
mappingHelper.mapToResult(GLOBAL_TAGS_ASPECT_NAME, (join, dataMap) -> this.mapGlobalTags(join, dataMap, entityUrn));
mappingHelper.mapToResult(GLOSSARY_TERMS_ASPECT_NAME, (join, dataMap) ->
join.setGlossaryTerms(GlossaryTermsMapper.map(new GlossaryTerms(dataMap), entityUrn)));
if (aspectMap != null && aspectMap.containsKey(EDITABLE_JOIN_PROPERTIES_ASPECT_NAME)) {
mappingHelper.mapToResult(EDITABLE_JOIN_PROPERTIES_ASPECT_NAME, this::mapEditableProperties);
}
if (aspectMap != null && aspectMap.containsKey(INSTITUTIONAL_MEMORY_ASPECT_NAME)) {
mappingHelper.mapToResult(INSTITUTIONAL_MEMORY_ASPECT_NAME, (join, dataMap) ->
join.setInstitutionalMemory(InstitutionalMemoryMapper.map(new InstitutionalMemory(dataMap), entityUrn)));
}
if (aspectMap != null && aspectMap.containsKey(OWNERSHIP_ASPECT_NAME)) {
mappingHelper.mapToResult(OWNERSHIP_ASPECT_NAME, (join, dataMap) ->
join.setOwnership(OwnershipMapper.map(new Ownership(dataMap), entityUrn)));
}
if (aspectMap != null && aspectMap.containsKey(STATUS_ASPECT_NAME)) {
mappingHelper.mapToResult(STATUS_ASPECT_NAME, (join, dataMap) ->
join.setStatus(StatusMapper.map(new Status(dataMap))));
}
if (aspectMap != null && aspectMap.containsKey(GLOBAL_TAGS_ASPECT_NAME)) {
mappingHelper.mapToResult(GLOBAL_TAGS_ASPECT_NAME, (join, dataMap) -> this.mapGlobalTags(join, dataMap, entityUrn));
}
if (aspectMap != null && aspectMap.containsKey(GLOSSARY_TERMS_ASPECT_NAME)) {
mappingHelper.mapToResult(GLOSSARY_TERMS_ASPECT_NAME, (join, dataMap) ->
join.setGlossaryTerms(GlossaryTermsMapper.map(new GlossaryTerms(dataMap), entityUrn)));
}
return mappingHelper.getResult();
}

Expand Down Expand Up @@ -107,7 +119,6 @@ private Dataset createPartialDataset(@Nonnull Urn datasetUrn) {
}
private com.linkedin.datahub.graphql.generated.JoinFieldMapping mapJoinFieldMappings(JoinProperties joinProperties) {
return com.linkedin.datahub.graphql.generated.JoinFieldMapping.builder()
.setDetails(joinProperties.getJoinFieldMapping().getDetails())
.setFieldMappings(joinProperties.getJoinFieldMapping()
.getFieldMappings()
.stream()
Expand Down
Loading

0 comments on commit 9ce4607

Please sign in to comment.