feat(graphql): Adding resolvers for adding multiple tags, terms, and owners (#4917)

This commit is contained in:
John Joyce 2022-05-16 14:47:59 -07:00 committed by GitHub
parent 3c32ac7916
commit 259b3453a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1128 additions and 25 deletions

View File

@ -104,8 +104,11 @@ import com.linkedin.datahub.graphql.resolvers.load.TimeSeriesAspectResolver;
import com.linkedin.datahub.graphql.resolvers.load.UsageTypeResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.AddLinkResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.AddOwnerResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.AddOwnersResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.AddTagResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.AddTagsResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.AddTermResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.AddTermsResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.MutableTypeResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.RemoveLinkResolver;
import com.linkedin.datahub.graphql.resolvers.mutate.RemoveOwnerResolver;
@ -584,14 +587,17 @@ public class GmsGraphQLEngine {
.dataFetcher("updateCorpUserProperties", new MutableTypeResolver<>(corpUserType))
.dataFetcher("updateCorpGroupProperties", new MutableTypeResolver<>(corpGroupType))
.dataFetcher("addTag", new AddTagResolver(entityService))
.dataFetcher("addTags", new AddTagsResolver(entityService))
.dataFetcher("removeTag", new RemoveTagResolver(entityService))
.dataFetcher("addTerm", new AddTermResolver(entityService))
.dataFetcher("addTerms", new AddTermsResolver(entityService))
.dataFetcher("removeTerm", new RemoveTermResolver(entityService))
.dataFetcher("createPolicy", new UpsertPolicyResolver(this.entityClient))
.dataFetcher("updatePolicy", new UpsertPolicyResolver(this.entityClient))
.dataFetcher("deletePolicy", new DeletePolicyResolver(this.entityClient))
.dataFetcher("updateDescription", new UpdateDescriptionResolver(entityService))
.dataFetcher("addOwner", new AddOwnerResolver(entityService))
.dataFetcher("addOwners", new AddOwnersResolver(entityService))
.dataFetcher("removeOwner", new RemoveOwnerResolver(entityService))
.dataFetcher("addLink", new AddLinkResolver(entityService))
.dataFetcher("removeLink", new RemoveLinkResolver(entityService))

View File

@ -47,7 +47,7 @@ public class AddOwnerResolver implements DataFetcher<CompletableFuture<Boolean>>
);
try {
log.debug("Adding Link. input: {}", input.toString());
log.debug("Adding Owner. input: {}", input.toString());
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
OwnerUtils.addOwner(

View File

@ -0,0 +1,63 @@
package com.linkedin.datahub.graphql.resolvers.mutate;
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.AddOwnersInput;
import com.linkedin.datahub.graphql.generated.OwnerInput;
import com.linkedin.datahub.graphql.resolvers.mutate.util.OwnerUtils;
import com.linkedin.metadata.entity.EntityService;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*;
@Slf4j
@RequiredArgsConstructor
public class AddOwnersResolver implements DataFetcher<CompletableFuture<Boolean>> {
private final EntityService _entityService;
@Override
public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throws Exception {
final AddOwnersInput input = bindArgument(environment.getArgument("input"), AddOwnersInput.class);
List<OwnerInput> owners = input.getOwners();
Urn targetUrn = Urn.createFromString(input.getResourceUrn());
return CompletableFuture.supplyAsync(() -> {
if (!OwnerUtils.isAuthorizedToUpdateOwners(environment.getContext(), targetUrn)) {
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
}
OwnerUtils.validateAddInput(
owners,
targetUrn,
_entityService
);
try {
log.debug("Adding Owners. input: {}", input.toString());
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
OwnerUtils.addOwners(
owners,
targetUrn,
actor,
_entityService
);
return true;
} catch (Exception e) {
log.error("Failed to add owners to resource with input {}, {}", input.toString(), e.getMessage());
throw new RuntimeException(String.format("Failed to add owners to resource with input %s", input.toString()), e);
}
});
}
}

View File

@ -1,5 +1,6 @@
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;
@ -51,8 +52,8 @@ public class AddTagResolver implements DataFetcher<CompletableFuture<Boolean>> {
log.info("Adding Tag. input: {}", input.toString());
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
LabelUtils.addTagToTarget(
tagUrn,
LabelUtils.addTagsToTarget(
ImmutableList.of(tagUrn),
targetUrn,
input.getSubResource(),
actor,

View File

@ -0,0 +1,69 @@
package com.linkedin.datahub.graphql.resolvers.mutate;
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.AddTagsInput;
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 AddTagsResolver implements DataFetcher<CompletableFuture<Boolean>> {
private final EntityService _entityService;
@Override
public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throws Exception {
final AddTagsInput input = bindArgument(environment.getArgument("input"), AddTagsInput.class);
List<Urn> tagUrns = input.getTagUrns().stream()
.map(UrnUtils::getUrn)
.collect(Collectors.toList());
Urn targetUrn = Urn.createFromString(input.getResourceUrn());
return CompletableFuture.supplyAsync(() -> {
if (!LabelUtils.isAuthorizedToUpdateTags(environment.getContext(), targetUrn, input.getSubResource())) {
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
}
LabelUtils.validateInput(
tagUrns,
targetUrn,
input.getSubResource(),
input.getSubResourceType(),
"tag",
_entityService,
false
);
try {
log.info("Adding Tags. input: {}", input.toString());
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
LabelUtils.addTagsToTarget(
tagUrns,
targetUrn,
input.getSubResource(),
actor,
_entityService
);
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);
}
});
}
}

View File

@ -1,5 +1,6 @@
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;
@ -44,8 +45,8 @@ public class AddTermResolver implements DataFetcher<CompletableFuture<Boolean>>
try {
log.info("Adding Term. input: {}", input);
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
LabelUtils.addTermToTarget(
termUrn,
LabelUtils.addTermsToTarget(
ImmutableList.of(termUrn),
targetUrn,
input.getSubResource(),
actor,

View File

@ -0,0 +1,67 @@
package com.linkedin.datahub.graphql.resolvers.mutate;
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.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 AddTermsResolver implements DataFetcher<CompletableFuture<Boolean>> {
private final EntityService _entityService;
@Override
public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throws Exception {
final AddTermsInput input = bindArgument(environment.getArgument("input"), AddTermsInput.class);
List<Urn> termUrns = input.getTermUrns().stream()
.map(UrnUtils::getUrn)
.collect(Collectors.toList());
Urn targetUrn = Urn.createFromString(input.getResourceUrn());
return CompletableFuture.supplyAsync(() -> {
if (!LabelUtils.isAuthorizedToUpdateTerms(environment.getContext(), targetUrn, input.getSubResource())) {
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
}
LabelUtils.validateInput(
termUrns,
targetUrn,
input.getSubResource(),
input.getSubResourceType(),
"glossaryTerm",
_entityService,
false
);
try {
log.info("Adding Term. input: {}", input);
Urn actor = CorpuserUrn.createFromString(((QueryContext) environment.getContext()).getActorUrn());
LabelUtils.addTermsToTarget(
termUrns,
targetUrn,
input.getSubResource(),
actor,
_entityService
);
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);
}
});
}
}

View File

@ -23,6 +23,8 @@ import com.linkedin.metadata.entity.EntityService;
import com.linkedin.schema.EditableSchemaFieldInfo;
import com.linkedin.schema.EditableSchemaMetadata;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
@ -100,8 +102,8 @@ public class LabelUtils {
}
}
public static void addTagToTarget(
Urn labelUrn,
public static void addTagsToTarget(
List<Urn> labelUrns,
Urn targetUrn,
String subResource,
Urn actor,
@ -114,7 +116,7 @@ public class LabelUtils {
if (!tags.hasTags()) {
tags.setTags(new TagAssociationArray());
}
addTagIfNotExists(tags, labelUrn);
addTagsIfNotExists(tags, labelUrns);
persistAspect(targetUrn, TAGS_ASPECT_NAME, tags, actor, entityService);
} else {
com.linkedin.schema.EditableSchemaMetadata editableSchemaMetadata =
@ -126,13 +128,13 @@ public class LabelUtils {
editableFieldInfo.setGlobalTags(new GlobalTags());
}
addTagIfNotExists(editableFieldInfo.getGlobalTags(), labelUrn);
addTagsIfNotExists(editableFieldInfo.getGlobalTags(), labelUrns);
persistAspect(targetUrn, EDITABLE_SCHEMA_METADATA, editableSchemaMetadata, actor, entityService);
}
}
public static void addTermToTarget(
Urn labelUrn,
public static void addTermsToTarget(
List<Urn> labelUrns,
Urn targetUrn,
String subResource,
Urn actor,
@ -147,7 +149,8 @@ public class LabelUtils {
terms.setTerms(new GlossaryTermAssociationArray());
}
addTermIfNotExistsToEntity(terms, labelUrn);
addTermsIfNotExistsToEntity(terms, labelUrns);
System.out.println(String.format("Persisting terms! %s", terms.toString()));
persistAspect(targetUrn, GLOSSARY_TERM_ASPECT_NAME, terms, actor, entityService);
} else {
com.linkedin.schema.EditableSchemaMetadata editableSchemaMetadata =
@ -161,12 +164,12 @@ public class LabelUtils {
editableFieldInfo.getGlossaryTerms().setAuditStamp(getAuditStamp(actor));
addTermIfNotExistsToEntity(editableFieldInfo.getGlossaryTerms(), labelUrn);
addTermsIfNotExistsToEntity(editableFieldInfo.getGlossaryTerms(), labelUrns);
persistAspect(targetUrn, EDITABLE_SCHEMA_METADATA, editableSchemaMetadata, actor, entityService);
}
}
private static void addTermIfNotExistsToEntity(GlossaryTerms terms, Urn termUrn)
private static void addTermsIfNotExistsToEntity(GlossaryTerms terms, List<Urn> termUrns)
throws URISyntaxException {
if (!terms.hasTerms()) {
terms.setTerms(new GlossaryTermAssociationArray());
@ -174,14 +177,24 @@ public class LabelUtils {
GlossaryTermAssociationArray termArray = terms.getTerms();
// if term exists, do not add it again
if (termArray.stream().anyMatch(association -> association.getUrn().equals(termUrn))) {
List<Urn> termsToAdd = new ArrayList<>();
for (Urn termUrn : termUrns) {
if (termArray.stream().anyMatch(association -> association.getUrn().equals(termUrn))) {
continue;
}
termsToAdd.add(termUrn);
}
// Check for no terms to add
if (termsToAdd.size() == 0) {
return;
}
GlossaryTermAssociation newAssociation = new GlossaryTermAssociation();
newAssociation.setUrn(GlossaryTermUrn.createFromUrn(termUrn));
termArray.add(newAssociation);
for (Urn termUrn : termsToAdd) {
GlossaryTermAssociation newAssociation = new GlossaryTermAssociation();
newAssociation.setUrn(GlossaryTermUrn.createFromUrn(termUrn));
termArray.add(newAssociation);
}
}
private static TagAssociationArray removeTagIfExists(GlobalTags tags, Urn tagUrn) {
@ -206,21 +219,31 @@ public class LabelUtils {
return termArray;
}
private static void addTagIfNotExists(GlobalTags tags, Urn tagUrn) throws URISyntaxException {
private static void addTagsIfNotExists(GlobalTags tags, List<Urn> tagUrns) throws URISyntaxException {
if (!tags.hasTags()) {
tags.setTags(new TagAssociationArray());
}
TagAssociationArray tagAssociationArray = tags.getTags();
// if tag exists, do not add it again
if (tagAssociationArray.stream().anyMatch(association -> association.getTag().equals(tagUrn))) {
List<Urn> tagsToAdd = new ArrayList<>();
for (Urn tagUrn : tagUrns) {
if (tagAssociationArray.stream().anyMatch(association -> association.getTag().equals(tagUrn))) {
continue;
}
tagsToAdd.add(tagUrn);
}
// Check for no tags to add
if (tagsToAdd.size() == 0) {
return;
}
TagAssociation newAssociation = new TagAssociation();
newAssociation.setTag(TagUrn.createFromUrn(tagUrn));
tagAssociationArray.add(newAssociation);
for (Urn tagUrn : tagsToAdd) {
TagAssociation newAssociation = new TagAssociation();
newAssociation.setTag(TagUrn.createFromUrn(tagUrn));
tagAssociationArray.add(newAssociation);
}
}
public static boolean isAuthorizedToUpdateTags(@Nonnull QueryContext context, Urn targetUrn, String subResource) {
@ -265,6 +288,24 @@ public class LabelUtils {
orPrivilegeGroups);
}
public static Boolean validateInput(
List<Urn> labelUrns,
Urn targetUrn,
String subResource,
SubResourceType subResourceType,
String labelEntityType,
EntityService entityService,
Boolean isRemoving
) {
for (Urn urn : labelUrns) {
boolean labelResult = validateInput(urn, targetUrn, subResource, subResourceType, labelEntityType, entityService, isRemoving);
if (!labelResult) {
return false;
}
}
return true;
}
public static Boolean validateInput(
Urn labelUrn,
Urn targetUrn,

View File

@ -9,15 +9,18 @@ import com.linkedin.common.OwnershipSource;
import com.linkedin.common.OwnershipSourceType;
import com.linkedin.common.OwnershipType;
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.authorization.ConjunctivePrivilegeGroup;
import com.linkedin.datahub.graphql.authorization.DisjunctivePrivilegeGroup;
import com.linkedin.datahub.graphql.generated.OwnerEntityType;
import com.linkedin.datahub.graphql.generated.OwnerInput;
import com.linkedin.datahub.graphql.resolvers.mutate.MutationUtils;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.authorization.PoliciesConfig;
import com.linkedin.metadata.entity.EntityService;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;
@ -49,6 +52,23 @@ public class OwnerUtils {
persistAspect(resourceUrn, Constants.OWNERSHIP_ASPECT_NAME, ownershipAspect, actor, entityService);
}
public static void addOwners(
List<OwnerInput> owners,
Urn resourceUrn,
Urn actor,
EntityService entityService
) {
Ownership ownershipAspect = (Ownership) getAspectFromEntity(
resourceUrn.toString(),
Constants.OWNERSHIP_ASPECT_NAME,
entityService,
new Ownership());
for (OwnerInput input : owners) {
addOwner(ownershipAspect, UrnUtils.getUrn(input.getOwnerUrn()), OwnershipType.valueOf(input.getType().toString()));
}
persistAspect(resourceUrn, Constants.OWNERSHIP_ASPECT_NAME, ownershipAspect, actor, entityService);
}
public static void removeOwner(
Urn ownerUrn,
Urn resourceUrn,
@ -106,6 +126,24 @@ public class OwnerUtils {
orPrivilegeGroups);
}
public static Boolean validateAddInput(
List<OwnerInput> owners,
Urn resourceUrn,
EntityService entityService
) {
for (OwnerInput owner : owners) {
boolean result = validateAddInput(
UrnUtils.getUrn(owner.getOwnerUrn()),
owner.getOwnerEntityType(),
resourceUrn,
entityService);
if (!result) {
return false;
}
}
return true;
}
public static Boolean validateAddInput(
Urn ownerUrn,
OwnerEntityType ownerEntityType,
@ -125,6 +163,10 @@ public class OwnerUtils {
throw new IllegalArgumentException(String.format("Failed to change ownership for resource %s. Resource does not exist.", resourceUrn));
}
if (!entityService.exists(ownerUrn)) {
throw new IllegalArgumentException(String.format("Failed to change ownership for resource %s. Owner does not exist.", resourceUrn));
}
return true;
}

View File

@ -210,6 +210,11 @@ type Mutation {
"""
addTag(input: TagAssociationInput!): Boolean
"""
Add multiple tags to a particular Entity or subresource
"""
addTags(input: AddTagsInput!): Boolean
"""
Remove a tag from a particular Entity or subresource
"""
@ -220,6 +225,11 @@ type Mutation {
"""
addTerm(input: TermAssociationInput!): Boolean
"""
Add multiple glossary terms to a particular Entity or subresource
"""
addTerms(input: AddTermsInput!): Boolean
"""
Remove a glossary term from a particular Entity or subresource
"""
@ -230,6 +240,11 @@ type Mutation {
"""
addOwner(input: AddOwnerInput!): Boolean
"""
Add multiple owners to a particular Entity
"""
addOwners(input: AddOwnersInput!): Boolean
"""
Remove an owner from a particular Entity
"""
@ -5782,6 +5797,31 @@ input TermAssociationInput {
subResource: String
}
"""
Input provided when adding Terms to an asset
"""
input AddTermsInput {
"""
The primary key of the Glossary Term to add or remove
"""
termUrns: [String!]!
"""
The target Metadata Entity to add or remove the Glossary Term from
"""
resourceUrn: String!
"""
An optional type of a sub resource to attach the Glossary Term to
"""
subResourceType: SubResourceType
"""
An optional sub resource identifier to attach the Glossary Term to
"""
subResource: String
}
"""
A type of Metadata Entity sub resource
"""
@ -5817,6 +5857,31 @@ input TagAssociationInput {
subResource: String
}
"""
Input provided when adding tags to an asset
"""
input AddTagsInput {
"""
The primary key of the Tags
"""
tagUrns: [String!]!
"""
The target Metadata Entity to add or remove the Tag to
"""
resourceUrn: String!
"""
An optional type of a sub resource to attach the Tag to
"""
subResourceType: SubResourceType
"""
An optional sub resource identifier to attach the Tag to
"""
subResource: String
}
"""
Entities that are able to own other entities
"""
@ -5857,6 +5922,41 @@ input AddOwnerInput {
resourceUrn: String!
}
"""
Input provided when adding an owner to an asset
"""
input OwnerInput {
"""
The primary key of the Owner to add or remove
"""
ownerUrn: String!
"""
The owner type, either a user or group
"""
ownerEntityType: OwnerEntityType!
"""
The ownership type for the new owner. If none is provided, then a new NONE will be added.
"""
type: OwnershipType
}
"""
Input provided when adding multiple associations between a Metadata Entity and an user or group owner
"""
input AddOwnersInput {
"""
The primary key of the Owner to add or remove
"""
owners: [OwnerInput!]!
"""
The urn of the resource or entity to attach or remove the owner from, for example a dataset urn
"""
resourceUrn: String!
}
"""
Input provided when removing the association between a Metadata Entity and an user or group owner
"""

View File

@ -0,0 +1,213 @@
package com.linkedin.datahub.graphql.resolvers.owner;
import com.google.common.collect.ImmutableList;
import com.linkedin.common.AuditStamp;
import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.generated.AddOwnersInput;
import com.linkedin.datahub.graphql.generated.OwnerEntityType;
import com.linkedin.datahub.graphql.generated.OwnerInput;
import com.linkedin.datahub.graphql.generated.OwnershipType;
import com.linkedin.datahub.graphql.resolvers.mutate.AddOwnersResolver;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.entity.EntityService;
import com.linkedin.mxe.MetadataChangeProposal;
import graphql.schema.DataFetchingEnvironment;
import java.util.concurrent.CompletionException;
import org.mockito.Mockito;
import org.testng.annotations.Test;
import static com.linkedin.datahub.graphql.TestUtils.*;
import static org.testng.Assert.*;
public class AddOwnersResolverTest {
private static final String TEST_ENTITY_URN = "urn:li:dataset:(urn:li:dataPlatform:mysql,my-test,PROD)";
private static final String TEST_OWNER_1_URN = "urn:li:corpuser:test-id-1";
private static final String TEST_OWNER_2_URN = "urn:li:corpuser:test-id-2";
@Test
public void testGetSuccessNoExistingOwners() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.OWNERSHIP_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_OWNER_1_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_OWNER_2_URN))).thenReturn(true);
AddOwnersResolver resolver = new AddOwnersResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddOwnersInput input = new AddOwnersInput(ImmutableList.of(
new OwnerInput(TEST_OWNER_1_URN, OwnerEntityType.CORP_USER, OwnershipType.TECHNICAL_OWNER),
new OwnerInput(TEST_OWNER_2_URN, OwnerEntityType.CORP_USER, OwnershipType.TECHNICAL_OWNER)
), TEST_ENTITY_URN);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertTrue(resolver.get(mockEnv).get());
// Unable to easily validate exact payload due to the injected timestamp
Mockito.verify(mockService, Mockito.times(1)).ingestProposal(
Mockito.any(MetadataChangeProposal.class),
Mockito.any(AuditStamp.class)
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_OWNER_1_URN))
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_OWNER_2_URN))
);
}
@Test
public void testGetSuccessExistingOwners() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.OWNERSHIP_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_OWNER_1_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_OWNER_2_URN))).thenReturn(true);
AddOwnersResolver resolver = new AddOwnersResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddOwnersInput input = new AddOwnersInput(ImmutableList.of(
new OwnerInput(TEST_OWNER_1_URN, OwnerEntityType.CORP_USER, OwnershipType.TECHNICAL_OWNER),
new OwnerInput(TEST_OWNER_2_URN, OwnerEntityType.CORP_USER, OwnershipType.TECHNICAL_OWNER)
), TEST_ENTITY_URN);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertTrue(resolver.get(mockEnv).get());
// Unable to easily validate exact payload due to the injected timestamp
Mockito.verify(mockService, Mockito.times(1)).ingestProposal(
Mockito.any(MetadataChangeProposal.class),
Mockito.any(AuditStamp.class)
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_OWNER_1_URN))
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_OWNER_2_URN))
);
}
@Test
public void testGetFailureOwnerDoesNotExist() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.OWNERSHIP_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_OWNER_1_URN))).thenReturn(false);
AddOwnersResolver resolver = new AddOwnersResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddOwnersInput input = new AddOwnersInput(ImmutableList.of(
new OwnerInput(TEST_OWNER_1_URN, OwnerEntityType.CORP_USER, OwnershipType.TECHNICAL_OWNER)), TEST_ENTITY_URN);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetFailureResourceDoesNotExist() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.OWNERSHIP_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(false);
Mockito.when(mockService.exists(Urn.createFromString(TEST_OWNER_1_URN))).thenReturn(true);
AddOwnersResolver resolver = new AddOwnersResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddOwnersInput input = new AddOwnersInput(ImmutableList.of(
new OwnerInput(TEST_OWNER_1_URN, OwnerEntityType.CORP_USER, OwnershipType.TECHNICAL_OWNER)), TEST_ENTITY_URN);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetUnauthorized() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
AddOwnersResolver resolver = new AddOwnersResolver(mockService);
// Execute resolver
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddOwnersInput input = new AddOwnersInput(ImmutableList.of(
new OwnerInput(TEST_OWNER_1_URN, OwnerEntityType.CORP_USER, OwnershipType.TECHNICAL_OWNER)), TEST_ENTITY_URN);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
QueryContext mockContext = getMockDenyContext();
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetEntityClientException() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.doThrow(RuntimeException.class).when(mockService).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
AddOwnersResolver resolver = new AddOwnersResolver(Mockito.mock(EntityService.class));
// Execute resolver
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
QueryContext mockContext = getMockAllowContext();
AddOwnersInput input = new AddOwnersInput(ImmutableList.of(
new OwnerInput(TEST_OWNER_1_URN, OwnerEntityType.CORP_USER, OwnershipType.TECHNICAL_OWNER)), TEST_ENTITY_URN);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
}
}

View File

@ -0,0 +1,246 @@
package com.linkedin.datahub.graphql.resolvers.tag;
import com.google.common.collect.ImmutableList;
import com.linkedin.common.AuditStamp;
import com.linkedin.common.GlobalTags;
import com.linkedin.common.TagAssociation;
import com.linkedin.common.TagAssociationArray;
import com.linkedin.common.urn.TagUrn;
import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.generated.AddTagsInput;
import com.linkedin.datahub.graphql.resolvers.mutate.AddTagsResolver;
import com.linkedin.events.metadata.ChangeType;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.entity.EntityService;
import com.linkedin.metadata.utils.GenericRecordUtils;
import com.linkedin.mxe.MetadataChangeProposal;
import graphql.schema.DataFetchingEnvironment;
import java.util.concurrent.CompletionException;
import org.mockito.Mockito;
import org.testng.annotations.Test;
import static com.linkedin.datahub.graphql.TestUtils.*;
import static org.testng.Assert.*;
public class AddTagsResolverTest {
private static final String TEST_ENTITY_URN = "urn:li:dataset:(urn:li:dataPlatform:mysql,my-test,PROD)";
private static final String TEST_TAG_1_URN = "urn:li:tag:test-id-1";
private static final String TEST_TAG_2_URN = "urn:li:tag:test-id-2";
@Test
public void testGetSuccessNoExistingTags() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.GLOBAL_TAGS_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TAG_1_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TAG_2_URN))).thenReturn(true);
AddTagsResolver resolver = new AddTagsResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTagsInput input = new AddTagsInput(ImmutableList.of(
TEST_TAG_1_URN,
TEST_TAG_2_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertTrue(resolver.get(mockEnv).get());
final GlobalTags newTags = new GlobalTags().setTags(new TagAssociationArray(ImmutableList.of(
new TagAssociation().setTag(TagUrn.createFromString(TEST_TAG_1_URN)),
new TagAssociation().setTag(TagUrn.createFromString(TEST_TAG_2_URN))
)));
final MetadataChangeProposal proposal = new MetadataChangeProposal();
proposal.setEntityUrn(Urn.createFromString(TEST_ENTITY_URN));
proposal.setEntityType(Constants.DATASET_ENTITY_NAME);
proposal.setAspectName(Constants.GLOBAL_TAGS_ASPECT_NAME);
proposal.setAspect(GenericRecordUtils.serializeAspect(newTags));
proposal.setChangeType(ChangeType.UPSERT);
Mockito.verify(mockService, Mockito.times(1)).ingestProposal(
Mockito.eq(proposal),
Mockito.any(AuditStamp.class)
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_TAG_1_URN))
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_TAG_2_URN))
);
}
@Test
public void testGetSuccessExistingTags() throws Exception {
GlobalTags originalTags = new GlobalTags().setTags(new TagAssociationArray(ImmutableList.of(
new TagAssociation().setTag(TagUrn.createFromString(TEST_TAG_1_URN))))
);
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.GLOBAL_TAGS_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(originalTags);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TAG_1_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TAG_2_URN))).thenReturn(true);
AddTagsResolver resolver = new AddTagsResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTagsInput input = new AddTagsInput(ImmutableList.of(
TEST_TAG_1_URN,
TEST_TAG_2_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertTrue(resolver.get(mockEnv).get());
final GlobalTags newTags = new GlobalTags().setTags(new TagAssociationArray(ImmutableList.of(
new TagAssociation().setTag(TagUrn.createFromString(TEST_TAG_1_URN)),
new TagAssociation().setTag(TagUrn.createFromString(TEST_TAG_2_URN))
)));
final MetadataChangeProposal proposal = new MetadataChangeProposal();
proposal.setEntityUrn(Urn.createFromString(TEST_ENTITY_URN));
proposal.setEntityType(Constants.DATASET_ENTITY_NAME);
proposal.setAspectName(Constants.GLOBAL_TAGS_ASPECT_NAME);
proposal.setAspect(GenericRecordUtils.serializeAspect(newTags));
proposal.setChangeType(ChangeType.UPSERT);
Mockito.verify(mockService, Mockito.times(1)).ingestProposal(
Mockito.eq(proposal),
Mockito.any(AuditStamp.class)
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_TAG_1_URN))
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_TAG_2_URN))
);
}
@Test
public void testGetFailureTagDoesNotExist() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.GLOBAL_TAGS_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TAG_1_URN))).thenReturn(false);
AddTagsResolver resolver = new AddTagsResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTagsInput input = new AddTagsInput(ImmutableList.of(
TEST_TAG_1_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetFailureResourceDoesNotExist() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.GLOBAL_TAGS_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(false);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TAG_1_URN))).thenReturn(true);
AddTagsResolver resolver = new AddTagsResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTagsInput input = new AddTagsInput(ImmutableList.of(
TEST_TAG_1_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetUnauthorized() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
AddTagsResolver resolver = new AddTagsResolver(mockService);
// Execute resolver
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTagsInput input = new AddTagsInput(ImmutableList.of(
TEST_TAG_1_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
QueryContext mockContext = getMockDenyContext();
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetEntityClientException() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.doThrow(RuntimeException.class).when(mockService).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
AddTagsResolver resolver = new AddTagsResolver(Mockito.mock(EntityService.class));
// Execute resolver
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
QueryContext mockContext = getMockAllowContext();
AddTagsInput input = new AddTagsInput(ImmutableList.of(
TEST_TAG_1_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
}
}

View File

@ -0,0 +1,222 @@
package com.linkedin.datahub.graphql.resolvers.term;
import com.google.common.collect.ImmutableList;
import com.linkedin.common.AuditStamp;
import com.linkedin.common.GlossaryTermAssociation;
import com.linkedin.common.GlossaryTermAssociationArray;
import com.linkedin.common.GlossaryTerms;
import com.linkedin.common.urn.GlossaryTermUrn;
import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.generated.AddTermsInput;
import com.linkedin.datahub.graphql.resolvers.mutate.AddTermsResolver;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.entity.EntityService;
import com.linkedin.mxe.MetadataChangeProposal;
import graphql.schema.DataFetchingEnvironment;
import java.util.concurrent.CompletionException;
import org.mockito.Mockito;
import org.testng.annotations.Test;
import static com.linkedin.datahub.graphql.TestUtils.*;
import static org.testng.Assert.*;
public class AddTermsResolverTest {
private static final String TEST_ENTITY_URN = "urn:li:dataset:(urn:li:dataPlatform:mysql,my-test,PROD)";
private static final String TEST_TERM_1_URN = "urn:li:glossaryTerm:test-id-1";
private static final String TEST_TERM_2_URN = "urn:li:glossaryTerm:test-id-2";
@Test
public void testGetSuccessNoExistingTerms() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.GLOSSARY_TERMS_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TERM_1_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TERM_2_URN))).thenReturn(true);
AddTermsResolver resolver = new AddTermsResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTermsInput input = new AddTermsInput(ImmutableList.of(
TEST_TERM_1_URN,
TEST_TERM_2_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertTrue(resolver.get(mockEnv).get());
// Unable to easily validate exact payload due to the injected timestamp
Mockito.verify(mockService, Mockito.times(1)).ingestProposal(
Mockito.any(MetadataChangeProposal.class),
Mockito.any(AuditStamp.class)
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_TERM_1_URN))
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_TERM_2_URN))
);
}
@Test
public void testGetSuccessExistingTerms() throws Exception {
GlossaryTerms originalTerms = new GlossaryTerms().setTerms(new GlossaryTermAssociationArray(ImmutableList.of(
new GlossaryTermAssociation().setUrn(GlossaryTermUrn.createFromString(TEST_TERM_1_URN))))
);
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.GLOSSARY_TERMS_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(originalTerms);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TERM_1_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TERM_2_URN))).thenReturn(true);
AddTermsResolver resolver = new AddTermsResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTermsInput input = new AddTermsInput(ImmutableList.of(
TEST_TERM_1_URN,
TEST_TERM_2_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertTrue(resolver.get(mockEnv).get());
// Unable to easily validate exact payload due to the injected timestamp
Mockito.verify(mockService, Mockito.times(1)).ingestProposal(
Mockito.any(MetadataChangeProposal.class),
Mockito.any(AuditStamp.class)
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_TERM_1_URN))
);
Mockito.verify(mockService, Mockito.times(1)).exists(
Mockito.eq(Urn.createFromString(TEST_TERM_2_URN))
);
}
@Test
public void testGetFailureTermDoesNotExist() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.GLOSSARY_TERMS_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(true);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TERM_1_URN))).thenReturn(false);
AddTermsResolver resolver = new AddTermsResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTermsInput input = new AddTermsInput(ImmutableList.of(
TEST_TERM_1_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetFailureResourceDoesNotExist() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.when(mockService.getAspect(
Mockito.eq(UrnUtils.getUrn(TEST_ENTITY_URN)),
Mockito.eq(Constants.GLOSSARY_TERMS_ASPECT_NAME),
Mockito.eq(0L)))
.thenReturn(null);
Mockito.when(mockService.exists(Urn.createFromString(TEST_ENTITY_URN))).thenReturn(false);
Mockito.when(mockService.exists(Urn.createFromString(TEST_TERM_1_URN))).thenReturn(true);
AddTermsResolver resolver = new AddTermsResolver(mockService);
// Execute resolver
QueryContext mockContext = getMockAllowContext();
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTermsInput input = new AddTermsInput(ImmutableList.of(
TEST_TERM_1_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetUnauthorized() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
AddTermsResolver resolver = new AddTermsResolver(mockService);
// Execute resolver
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
AddTermsInput input = new AddTermsInput(ImmutableList.of(
TEST_TERM_1_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
QueryContext mockContext = getMockDenyContext();
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
Mockito.verify(mockService, Mockito.times(0)).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
}
@Test
public void testGetEntityClientException() throws Exception {
EntityService mockService = Mockito.mock(EntityService.class);
Mockito.doThrow(RuntimeException.class).when(mockService).ingestProposal(
Mockito.any(),
Mockito.any(AuditStamp.class));
AddTermsResolver resolver = new AddTermsResolver(Mockito.mock(EntityService.class));
// Execute resolver
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
QueryContext mockContext = getMockAllowContext();
AddTermsInput input = new AddTermsInput(ImmutableList.of(
TEST_TERM_1_URN
), TEST_ENTITY_URN, null, null);
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(input);
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
}
}

View File

@ -130,5 +130,37 @@
"contentType":"application/json"
},
"systemMetadata":null
},
{
"auditHeader": null,
"proposedSnapshot": {
"com.linkedin.pegasus2avro.metadata.snapshot.CorpUserSnapshot": {
"urn": "urn:li:corpuser:jdoe",
"aspects": [
{
"com.linkedin.pegasus2avro.identity.CorpUserInfo": {
"active": true,
"displayName": {
"string": "John Doe"
},
"email": "jdoe@linkedin.com",
"title": {
"string": "Software Engineer"
},
"managerUrn": null,
"departmentId": null,
"departmentName": null,
"firstName": null,
"lastName": null,
"fullName": {
"string": "John Doe"
},
"countryCode": null
}
}
]
}
},
"proposedDelta": null
}
]