Skip to content

Commit

Permalink
feat(usergroup): implement corpgroup in graphql, refactor avatars and…
Browse files Browse the repository at this point in the history
… ownership in react (#2519)
  • Loading branch information
topwebtek7 committed May 12, 2021
1 parent 2811d23 commit d7d8870
Show file tree
Hide file tree
Showing 39 changed files with 999 additions and 267 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -34,3 +34,5 @@ MANIFEST

# Mac OS
**/.DS_Store

.vscode
Expand Up @@ -6,6 +6,7 @@
import com.linkedin.dataset.client.Datasets;
import com.linkedin.identity.client.CorpUsers;
import com.linkedin.lineage.client.Lineages;
import com.linkedin.identity.client.CorpGroups;
import com.linkedin.metadata.restli.DefaultRestliClientFactory;
import com.linkedin.ml.client.MLModels;
import com.linkedin.restli.client.Client;
Expand Down Expand Up @@ -38,6 +39,7 @@ public class GmsClientFactory {
Configuration.getEnvironmentVariable(GMS_SSL_PROTOCOL_VAR));

private static CorpUsers _corpUsers;
private static CorpGroups _corpGroups;
private static Datasets _datasets;
private static Dashboards _dashboards;
private static Charts _charts;
Expand All @@ -63,6 +65,17 @@ public static CorpUsers getCorpUsersClient() {
return _corpUsers;
}

public static CorpGroups getCorpGroupsClient() {
if (_corpGroups == null) {
synchronized (GmsClientFactory.class) {
if (_corpGroups == null) {
_corpGroups = new CorpGroups(REST_CLIENT);
}
}
}
return _corpGroups;
}

public static Datasets getDatasetsClient() {
if (_datasets == null) {
synchronized (GmsClientFactory.class) {
Expand Down
Expand Up @@ -21,13 +21,17 @@
import com.linkedin.datahub.graphql.types.SearchableEntityType;
import com.linkedin.datahub.graphql.types.chart.ChartType;
import com.linkedin.datahub.graphql.types.corpuser.CorpUserType;
import com.linkedin.datahub.graphql.types.corpgroup.CorpGroupType;
import com.linkedin.datahub.graphql.types.dashboard.DashboardType;
import com.linkedin.datahub.graphql.types.dataplatform.DataPlatformType;
import com.linkedin.datahub.graphql.types.dataset.DatasetType;
import com.linkedin.datahub.graphql.generated.CorpUser;
import com.linkedin.datahub.graphql.generated.CorpUserInfo;
import com.linkedin.datahub.graphql.generated.CorpGroupInfo;
import com.linkedin.datahub.graphql.generated.Owner;
import com.linkedin.datahub.graphql.resolvers.AuthenticatedResolver;
import com.linkedin.datahub.graphql.resolvers.load.LoadableTypeResolver;
import com.linkedin.datahub.graphql.resolvers.load.OwnerTypeResolver;
import com.linkedin.datahub.graphql.resolvers.browse.BrowsePathsResolver;
import com.linkedin.datahub.graphql.resolvers.browse.BrowseResolver;
import com.linkedin.datahub.graphql.resolvers.search.AutoCompleteResolver;
Expand Down Expand Up @@ -71,6 +75,7 @@ public class GmsGraphQLEngine {

public static final DatasetType DATASET_TYPE = new DatasetType(GmsClientFactory.getDatasetsClient());
public static final CorpUserType CORP_USER_TYPE = new CorpUserType(GmsClientFactory.getCorpUsersClient());
public static final CorpGroupType CORP_GROUP_TYPE = new CorpGroupType(GmsClientFactory.getCorpGroupsClient());
public static final ChartType CHART_TYPE = new ChartType(GmsClientFactory.getChartsClient());
public static final DashboardType DASHBOARD_TYPE = new DashboardType(GmsClientFactory.getDashboardsClient());
public static final DataPlatformType DATA_PLATFORM_TYPE = new DataPlatformType(GmsClientFactory.getDataPlatformsClient());
Expand All @@ -92,6 +97,7 @@ public class GmsGraphQLEngine {
public static final List<EntityType<?>> ENTITY_TYPES = ImmutableList.of(
DATASET_TYPE,
CORP_USER_TYPE,
CORP_GROUP_TYPE,
DATA_PLATFORM_TYPE,
CHART_TYPE,
DASHBOARD_TYPE,
Expand All @@ -115,7 +121,13 @@ public class GmsGraphQLEngine {
*/
public static final List<LoadableType<?>> LOADABLE_TYPES = Stream.concat(ENTITY_TYPES.stream(), RELATIONSHIP_TYPES.stream()).collect(Collectors.toList());


/**
* Configures the graph objects for owner
*/
public static final List<LoadableType<?>> OWNER_TYPES = ImmutableList.of(
CORP_USER_TYPE,
CORP_GROUP_TYPE
);

/**
* Configures the graph objects that can be searched.
Expand Down Expand Up @@ -163,6 +175,7 @@ public static void configureRuntimeWiring(final RuntimeWiring.Builder builder) {
configureMutationResolvers(builder);
configureDatasetResolvers(builder);
configureCorpUserResolvers(builder);
configureCorpGroupResolvers(builder);
configureDashboardResolvers(builder);
configureChartResolvers(builder);
configureTypeResolvers(builder);
Expand Down Expand Up @@ -207,6 +220,10 @@ private static void configureQueryResolvers(final RuntimeWiring.Builder builder)
new LoadableTypeResolver<>(
CORP_USER_TYPE,
(env) -> env.getArgument(URN_FIELD_NAME))))
.dataFetcher("corpGroup", new AuthenticatedResolver<>(
new LoadableTypeResolver<>(
CORP_GROUP_TYPE,
(env) -> env.getArgument(URN_FIELD_NAME))))
.dataFetcher("dashboard", new AuthenticatedResolver<>(
new LoadableTypeResolver<>(
DASHBOARD_TYPE,
Expand Down Expand Up @@ -269,9 +286,9 @@ private static void configureDatasetResolvers(final RuntimeWiring.Builder builde
)
.type("Owner", typeWiring -> typeWiring
.dataFetcher("owner", new AuthenticatedResolver<>(
new LoadableTypeResolver<>(
CORP_USER_TYPE,
(env) -> ((Owner) env.getSource()).getOwner().getUrn()))
new OwnerTypeResolver<>(
OWNER_TYPES,
(env) -> ((Owner) env.getSource()).getOwner()))
)
)
.type("RelatedDataset", typeWiring -> typeWiring
Expand All @@ -285,8 +302,7 @@ private static void configureDatasetResolvers(final RuntimeWiring.Builder builde
.dataFetcher("entity", new AuthenticatedResolver<>(
new EntityTypeResolver(
ENTITY_TYPES.stream().collect(Collectors.toList()),
(env) -> ((EntityRelationship) env.getSource()).getEntity())
)
(env) -> ((EntityRelationship) env.getSource()).getEntity()))
)
);
}
Expand All @@ -304,6 +320,28 @@ private static void configureCorpUserResolvers(final RuntimeWiring.Builder build
);
}

/**
* Configures resolvers responsible for resolving the {@link com.linkedin.datahub.graphql.generated.CorpGroup} type.
*/
private static void configureCorpGroupResolvers(final RuntimeWiring.Builder builder) {
builder.type("CorpGroupInfo", typeWiring -> typeWiring
.dataFetcher("admins", new AuthenticatedResolver<>(
new LoadableTypeBatchResolver<>(
CORP_USER_TYPE,
(env) -> ((CorpGroupInfo) env.getSource()).getAdmins().stream()
.map(CorpUser::getUrn)
.collect(Collectors.toList())))
)
.dataFetcher("members", new AuthenticatedResolver<>(
new LoadableTypeBatchResolver<>(
CORP_USER_TYPE,
(env) -> ((CorpGroupInfo) env.getSource()).getMembers().stream()
.map(CorpUser::getUrn)
.collect(Collectors.toList())))
)
);
}

private static void configureTagAssociationResolver(final RuntimeWiring.Builder builder) {
builder.type("TagAssociation", typeWiring -> typeWiring
.dataFetcher("tag", new AuthenticatedResolver<>(
Expand Down Expand Up @@ -379,12 +417,18 @@ private static void configureTypeResolvers(final RuntimeWiring.Builder builder)
.map(graphType -> (EntityType<?>) graphType)
.collect(Collectors.toList())
)))
.type("EntityWithRelationships", typeWiring -> typeWiring
.typeResolver(new EntityInterfaceTypeResolver(LOADABLE_TYPES.stream()
.filter(graphType -> graphType instanceof EntityType)
.map(graphType -> (EntityType<?>) graphType)
.collect(Collectors.toList())
)))
.type("EntityWithRelationships", typeWiring -> typeWiring
.typeResolver(new EntityInterfaceTypeResolver(LOADABLE_TYPES.stream()
.filter(graphType -> graphType instanceof EntityType)
.map(graphType -> (EntityType<?>) graphType)
.collect(Collectors.toList())
)))
.type("OwnerType", typeWiring -> typeWiring
.typeResolver(new EntityInterfaceTypeResolver(OWNER_TYPES.stream()
.filter(graphType -> graphType instanceof EntityType)
.map(graphType -> (EntityType<?>) graphType)
.collect(Collectors.toList())
)))
.type("PlatformSchema", typeWiring -> typeWiring
.typeResolver(new PlatformSchemaUnionTypeResolver())
)
Expand Down
@@ -0,0 +1,45 @@
package com.linkedin.datahub.graphql.resolvers.load;

import com.linkedin.datahub.graphql.generated.Entity;
import com.linkedin.datahub.graphql.generated.OwnerType;
import com.linkedin.datahub.graphql.types.LoadableType;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import org.dataloader.DataLoader;
import java.util.stream.Collectors;
import com.google.common.collect.Iterables;

/**
* Generic GraphQL resolver responsible for
*
* 1. Retrieving a single input urn.
* 2. Resolving a single {@link LoadableType}.
*
* Note that this resolver expects that {@link DataLoader}s were registered
* for the provided {@link LoadableType} under the name provided by {@link LoadableType#name()}
*
* @param <T> the generated GraphQL POJO corresponding to the resolved type.
*/
public class OwnerTypeResolver<T> implements DataFetcher<CompletableFuture<T>> {

private final List<LoadableType<?>> _loadableTypes;
private final Function<DataFetchingEnvironment, OwnerType> _urnProvider;

public OwnerTypeResolver(final List<LoadableType<?>> loadableTypes, final Function<DataFetchingEnvironment, OwnerType> urnProvider) {
_loadableTypes = loadableTypes;
_urnProvider = urnProvider;
}

@Override
public CompletableFuture<T> get(DataFetchingEnvironment environment) {
final OwnerType ownerType = _urnProvider.apply(environment);
final LoadableType<?> filteredEntity = Iterables.getOnlyElement(_loadableTypes.stream()
.filter(entity -> ownerType.getClass().isAssignableFrom(entity.objectClass()))
.collect(Collectors.toList()));
final DataLoader<String, T> loader = environment.getDataLoaderRegistry().getDataLoader(filteredEntity.name());
return loader.load(((Entity) ownerType).getUrn());
}
}
@@ -1,6 +1,7 @@
package com.linkedin.datahub.graphql.types.common.mappers;

import com.linkedin.datahub.graphql.generated.CorpUser;
import com.linkedin.datahub.graphql.generated.CorpGroup;
import com.linkedin.datahub.graphql.generated.Owner;
import com.linkedin.datahub.graphql.generated.OwnershipType;
import com.linkedin.datahub.graphql.types.mappers.ModelMapper;
Expand All @@ -24,9 +25,15 @@ public static Owner map(@Nonnull final com.linkedin.common.Owner owner) {
public Owner apply(@Nonnull final com.linkedin.common.Owner owner) {
final Owner result = new Owner();
result.setType(Enum.valueOf(OwnershipType.class, owner.getType().toString()));
CorpUser partialOwner = new CorpUser();
partialOwner.setUrn(owner.getOwner().toString());
result.setOwner(partialOwner);
if (owner.getOwner().getEntityType().equals("corpuser")) {
CorpUser partialOwner = new CorpUser();
partialOwner.setUrn(owner.getOwner().toString());
result.setOwner(partialOwner);
} else {
CorpGroup partialOwner = new CorpGroup();
partialOwner.setUrn(owner.getOwner().toString());
result.setOwner(partialOwner);
}
if (owner.hasSource()) {
result.setSource(OwnershipSourceMapper.map(owner.getSource()));
}
Expand Down
Expand Up @@ -8,7 +8,11 @@
import com.linkedin.common.OwnershipType;
import com.linkedin.datahub.graphql.generated.OwnerUpdate;
import com.linkedin.datahub.graphql.types.corpuser.CorpUserUtils;
import com.linkedin.datahub.graphql.types.corpgroup.CorpGroupUtils;
import com.linkedin.datahub.graphql.types.mappers.ModelMapper;
import com.linkedin.common.urn.Urn;

import java.net.URISyntaxException;

public class OwnerUpdateMapper implements ModelMapper<OwnerUpdate, Owner> {

Expand All @@ -21,7 +25,15 @@ public static Owner map(@Nonnull final OwnerUpdate input) {
@Override
public Owner apply(@Nonnull final OwnerUpdate input) {
final Owner owner = new Owner();
owner.setOwner(CorpUserUtils.getCorpUserUrn(input.getOwner()));
try {
if (Urn.createFromString(input.getOwner()).getEntityType().equals("corpuser")) {
owner.setOwner(CorpUserUtils.getCorpUserUrn(input.getOwner()));
} else if (Urn.createFromString(input.getOwner()).getEntityType().equals("corpGroup")) {
owner.setOwner(CorpGroupUtils.getCorpGroupUrn(input.getOwner()));
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
owner.setType(OwnershipType.valueOf(input.getType().toString()));
owner.setSource(new OwnershipSource().setType(OwnershipSourceType.SERVICE));
return owner;
Expand Down
@@ -0,0 +1,99 @@
package com.linkedin.datahub.graphql.types.corpgroup;

import com.linkedin.common.urn.CorpGroupUrn;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.generated.EntityType;
import com.linkedin.datahub.graphql.types.SearchableEntityType;
import com.linkedin.datahub.graphql.generated.AutoCompleteResults;
import com.linkedin.datahub.graphql.generated.CorpGroup;
import com.linkedin.datahub.graphql.generated.FacetFilterInput;
import com.linkedin.datahub.graphql.generated.SearchResults;
import com.linkedin.datahub.graphql.types.mappers.AutoCompleteResultsMapper;
import com.linkedin.datahub.graphql.types.corpgroup.mappers.CorpGroupMapper;
import com.linkedin.datahub.graphql.types.mappers.SearchResultsMapper;
import com.linkedin.identity.client.CorpGroups;
import com.linkedin.metadata.query.AutoCompleteResult;
import com.linkedin.restli.common.CollectionResponse;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CorpGroupType implements SearchableEntityType<CorpGroup> {

private static final String DEFAULT_AUTO_COMPLETE_FIELD = "name";

private final CorpGroups _corpGroupsClient;

public CorpGroupType(final CorpGroups corpGroupsClient) {
_corpGroupsClient = corpGroupsClient;
}

@Override
public Class<CorpGroup> objectClass() {
return CorpGroup.class;
}

@Override
public EntityType type() {
return EntityType.CORP_GROUP;
}

@Override
public List<CorpGroup> batchLoad(final List<String> urns, final QueryContext context) {
try {
final List<CorpGroupUrn> corpGroupUrns = urns
.stream()
.map(this::getCorpGroupUrn)
.collect(Collectors.toList());

final Map<CorpGroupUrn, com.linkedin.identity.CorpGroup> corpGroupMap = _corpGroupsClient
.batchGet(new HashSet<>(corpGroupUrns));

final List<com.linkedin.identity.CorpGroup> results = new ArrayList<>();
for (CorpGroupUrn urn : corpGroupUrns) {
results.add(corpGroupMap.getOrDefault(urn, null));
}
return results.stream()
.map(gmsCorpGroup -> gmsCorpGroup == null ? null : CorpGroupMapper.map(gmsCorpGroup))
.collect(Collectors.toList());
} catch (Exception e) {
throw new RuntimeException("Failed to batch load CorpGroup", e);
}
}

@Override
public SearchResults search(@Nonnull String query,
@Nullable List<FacetFilterInput> filters,
int start,
int count,
@Nonnull final QueryContext context) throws Exception {
final CollectionResponse<com.linkedin.identity.CorpGroup> searchResult = _corpGroupsClient.search(query, Collections.emptyMap(), start, count);
return SearchResultsMapper.map(searchResult, CorpGroupMapper::map);
}

@Override
public AutoCompleteResults autoComplete(@Nonnull String query,
@Nullable String field,
@Nullable List<FacetFilterInput> filters,
int limit,
@Nonnull final QueryContext context) throws Exception {
field = field != null ? field : DEFAULT_AUTO_COMPLETE_FIELD;
final AutoCompleteResult result = _corpGroupsClient.autocomplete(query, field, Collections.emptyMap(), limit);
return AutoCompleteResultsMapper.map(result);
}

private CorpGroupUrn getCorpGroupUrn(final String urnStr) {
try {
return CorpGroupUrn.createFromString(urnStr);
} catch (URISyntaxException e) {
throw new RuntimeException(String.format("Failed to retrieve CorpGroup with urn %s, invalid urn", urnStr));
}
}
}

0 comments on commit d7d8870

Please sign in to comment.