Skip to content

Commit

Permalink
feat(ui): Support Batch adding and removing Glossary Terms (Batch Act…
Browse files Browse the repository at this point in the history
…ions 3/7) (#5544)
  • Loading branch information
jjoyce0510 authored Aug 2, 2022
1 parent 4e0da5d commit 5c7975b
Show file tree
Hide file tree
Showing 19 changed files with 983 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,9 @@
import com.linkedin.datahub.graphql.resolvers.mutate.AddTermResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.AddTermsResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.BatchAddTagsResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.BatchAddTermsResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.BatchRemoveTagsResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.BatchRemoveTermsResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.MutableTypeResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.RemoveLinkResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.RemoveOwnerResolver;
Expand Down Expand Up @@ -692,8 +694,10 @@ private void configureMutationResolvers(final RuntimeWiring.Builder builder) {
.dataFetcher("removeTag", new RemoveTagResolver(entityService))
.dataFetcher("batchRemoveTags", new BatchRemoveTagsResolver(entityService))
.dataFetcher("addTerm", new AddTermResolver(entityService))
.dataFetcher("batchAddTerms", new BatchAddTermsResolver(entityService))
.dataFetcher("addTerms", new AddTermsResolver(entityService))
.dataFetcher("removeTerm", new RemoveTermResolver(entityService))
.dataFetcher("batchRemoveTerms", new BatchRemoveTermsResolver(entityService))
.dataFetcher("createPolicy", new UpsertPolicyResolver(this.entityClient))
.dataFetcher("updatePolicy", new UpsertPolicyResolver(this.entityClient))
.dataFetcher("deletePolicy", new DeletePolicyResolver(this.entityClient))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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.ResourceRefInput;
import com.linkedin.datahub.graphql.generated.TermAssociationInput;
import com.linkedin.datahub.graphql.resolvers.mutate.util.LabelUtils;
import com.linkedin.metadata.Constants;
Expand Down Expand Up @@ -46,10 +47,9 @@ public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throw
try {
log.info("Adding Term. input: {}", input);
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
LabelUtils.addTermsToResource(
LabelUtils.addTermsToResources(
ImmutableList.of(termUrn),
targetUrn,
input.getSubResource(),
ImmutableList.of(new ResourceRefInput(input.getResourceUrn(), input.getSubResourceType(), input.getSubResource())),
actor,
_entityService
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.linkedin.datahub.graphql.resolvers.mutate;

import com.google.common.collect.ImmutableList;
import com.linkedin.common.urn.CorpuserUrn;
import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.AddTermsInput;
import com.linkedin.datahub.graphql.generated.ResourceRefInput;
import com.linkedin.datahub.graphql.resolvers.mutate.util.LabelUtils;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.entity.EntityService;
Expand Down Expand Up @@ -51,10 +53,9 @@ public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throw
try {
log.info("Adding Term. input: {}", input);
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
LabelUtils.addTermsToResource(
LabelUtils.addTermsToResources(
termUrns,
targetUrn,
input.getSubResource(),
ImmutableList.of(new ResourceRefInput(input.getResourceUrn(), input.getSubResourceType(), input.getSubResource())),
actor,
_entityService
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.linkedin.datahub.graphql.resolvers.mutate;

import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.BatchAddTermsInput;
import com.linkedin.datahub.graphql.generated.ResourceRefInput;
import com.linkedin.datahub.graphql.resolvers.mutate.util.LabelUtils;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.entity.EntityService;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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


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

private final EntityService _entityService;

@Override
public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throws Exception {
final QueryContext context = environment.getContext();
final BatchAddTermsInput input = bindArgument(environment.getArgument("input"), BatchAddTermsInput.class);
final List<Urn> termUrns = input.getTermUrns().stream()
.map(UrnUtils::getUrn)
.collect(Collectors.toList());
final List<ResourceRefInput> resources = input.getResources();

return CompletableFuture.supplyAsync(() -> {

// First, validate the batch
validateTerms(termUrns);
validateInputResources(resources, context);

try {
// Then execute the bulk add
batchAddTerms(termUrns, resources, context);
return true;
} catch (Exception e) {
log.error("Failed to perform update against input {}, {}", input.toString(), e.getMessage());
throw new RuntimeException(String.format("Failed to perform update against input %s", input.toString()), e);
}
});
}

private void validateTerms(List<Urn> termUrns) {
for (Urn termUrn : termUrns) {
LabelUtils.validateLabel(termUrn, Constants.GLOSSARY_TERM_ENTITY_NAME, _entityService);
}
}

private void validateInputResources(List<ResourceRefInput> resources, QueryContext context) {
for (ResourceRefInput resource : resources) {
validateInputResource(resource, context);
}
}

private void validateInputResource(ResourceRefInput resource, QueryContext context) {
final Urn resourceUrn = UrnUtils.getUrn(resource.getResourceUrn());
if (!LabelUtils.isAuthorizedToUpdateTerms(context, resourceUrn, resource.getSubResource())) {
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
}
LabelUtils.validateResource(resourceUrn, resource.getSubResource(), resource.getSubResourceType(), _entityService);
}

private void batchAddTerms(List<Urn> termUrns, List<ResourceRefInput> resources, QueryContext context) {
log.debug("Batch adding Terms. terms: {}, resources: {}", resources, termUrns);
try {
LabelUtils.addTermsToResources(termUrns, resources, UrnUtils.getUrn(context.getActorUrn()), _entityService);
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to batch add Terms %s to resources with urns %s!",
termUrns,
resources.stream().map(ResourceRefInput::getResourceUrn).collect(Collectors.toList())),
e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.linkedin.datahub.graphql.resolvers.mutate;

import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.BatchRemoveTermsInput;
import com.linkedin.datahub.graphql.generated.ResourceRefInput;
import com.linkedin.datahub.graphql.resolvers.mutate.util.LabelUtils;
import com.linkedin.metadata.entity.EntityService;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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


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

private final EntityService _entityService;

@Override
public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throws Exception {
final QueryContext context = environment.getContext();
final BatchRemoveTermsInput input = bindArgument(environment.getArgument("input"), BatchRemoveTermsInput.class);
final List<Urn> termUrns = input.getTermUrns().stream()
.map(UrnUtils::getUrn)
.collect(Collectors.toList());
final List<ResourceRefInput> resources = input.getResources();

return CompletableFuture.supplyAsync(() -> {

// First, validate the batch
validateInputResources(resources, context);

try {
// Then execute the bulk add
batchRemoveTerms(termUrns, resources, context);
return true;
} catch (Exception e) {
log.error("Failed to perform update against input {}, {}", input.toString(), e.getMessage());
throw new RuntimeException(String.format("Failed to perform update against input %s", input.toString()), e);
}
});
}

private void validateInputResources(List<ResourceRefInput> resources, QueryContext context) {
for (ResourceRefInput resource : resources) {
validateInputResource(resource, context);
}
}

private void validateInputResource(ResourceRefInput resource, QueryContext context) {
final Urn resourceUrn = UrnUtils.getUrn(resource.getResourceUrn());
if (!LabelUtils.isAuthorizedToUpdateTerms(context, resourceUrn, resource.getSubResource())) {
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
}
LabelUtils.validateResource(resourceUrn, resource.getSubResource(), resource.getSubResourceType(), _entityService);
}

private void batchRemoveTerms(List<Urn> termUrns, List<ResourceRefInput> resources, QueryContext context) {
log.debug("Batch removing Terms. terms: {}, resources: {}", resources, termUrns);
try {
LabelUtils.removeTermsFromResources(termUrns, resources, UrnUtils.getUrn(context.getActorUrn()), _entityService);
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to remove Terms %s to resources with urns %s!",
termUrns,
resources.stream().map(ResourceRefInput::getResourceUrn).collect(Collectors.toList())),
e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.linkedin.datahub.graphql.resolvers.mutate;

import com.google.common.collect.ImmutableList;
import com.linkedin.common.urn.CorpuserUrn;
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.ResourceRefInput;
import com.linkedin.datahub.graphql.generated.TermAssociationInput;
import com.linkedin.datahub.graphql.resolvers.mutate.util.LabelUtils;
import com.linkedin.metadata.Constants;
Expand Down Expand Up @@ -52,10 +54,9 @@ public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throw

log.info(String.format("Removing Term. input: {}", input));
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
LabelUtils.removeTermFromResource(
termUrn,
targetUrn,
input.getSubResource(),
LabelUtils.removeTermsFromResources(
ImmutableList.of(termUrn),
ImmutableList.of(new ResourceRefInput(input.getResourceUrn(), input.getSubResourceType(), input.getSubResource())),
actor,
_entityService
);
Expand Down
Loading

0 comments on commit 5c7975b

Please sign in to comment.