Skip to content

Commit

Permalink
Merge branch 'datahub-project:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
nadavgross authored Jul 9, 2024
2 parents 1a0501e + b6c7fe8 commit 036f1da
Show file tree
Hide file tree
Showing 156 changed files with 9,731 additions and 2,787 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/pr-labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ jobs:
"RyanHolstien",
"Kunal-kankriya",
"purnimagarg1",
"gaurav2733",
"dushayntAW",
"AvaniSiddhapuraAPT",
"akarsh991",
"shubhamjagtap639",
"mayurinehate"
"sagar-salvi-apptware",
"kushagra-apptware",
"Salman-Apptware",
"mayurinehate",
"noggi"
]'),
github.actor
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import com.linkedin.datahub.graphql.generated.DataQualityContract;
import com.linkedin.datahub.graphql.generated.Dataset;
import com.linkedin.datahub.graphql.generated.DatasetStatsSummary;
import com.linkedin.datahub.graphql.generated.Deprecation;
import com.linkedin.datahub.graphql.generated.Domain;
import com.linkedin.datahub.graphql.generated.ERModelRelationship;
import com.linkedin.datahub.graphql.generated.ERModelRelationshipProperties;
Expand Down Expand Up @@ -172,8 +173,11 @@
import com.linkedin.datahub.graphql.resolvers.form.BatchAssignFormResolver;
import com.linkedin.datahub.graphql.resolvers.form.BatchRemoveFormResolver;
import com.linkedin.datahub.graphql.resolvers.form.CreateDynamicFormAssignmentResolver;
import com.linkedin.datahub.graphql.resolvers.form.CreateFormResolver;
import com.linkedin.datahub.graphql.resolvers.form.DeleteFormResolver;
import com.linkedin.datahub.graphql.resolvers.form.IsFormAssignedToMeResolver;
import com.linkedin.datahub.graphql.resolvers.form.SubmitFormPromptResolver;
import com.linkedin.datahub.graphql.resolvers.form.UpdateFormResolver;
import com.linkedin.datahub.graphql.resolvers.form.VerifyFormResolver;
import com.linkedin.datahub.graphql.resolvers.glossary.AddRelatedTermsResolver;
import com.linkedin.datahub.graphql.resolvers.glossary.CreateGlossaryNodeResolver;
Expand Down Expand Up @@ -285,6 +289,9 @@
import com.linkedin.datahub.graphql.resolvers.settings.view.UpdateGlobalViewsSettingsResolver;
import com.linkedin.datahub.graphql.resolvers.step.BatchGetStepStatesResolver;
import com.linkedin.datahub.graphql.resolvers.step.BatchUpdateStepStatesResolver;
import com.linkedin.datahub.graphql.resolvers.structuredproperties.CreateStructuredPropertyResolver;
import com.linkedin.datahub.graphql.resolvers.structuredproperties.RemoveStructuredPropertiesResolver;
import com.linkedin.datahub.graphql.resolvers.structuredproperties.UpdateStructuredPropertyResolver;
import com.linkedin.datahub.graphql.resolvers.structuredproperties.UpsertStructuredPropertiesResolver;
import com.linkedin.datahub.graphql.resolvers.tag.CreateTagResolver;
import com.linkedin.datahub.graphql.resolvers.tag.DeleteTagResolver;
Expand Down Expand Up @@ -782,6 +789,7 @@ public void configureRuntimeWiring(final RuntimeWiring.Builder builder) {
configureBusinessAttributeResolver(builder);
configureBusinessAttributeAssociationResolver(builder);
configureConnectionResolvers(builder);
configureDeprecationResolvers(builder);
}

private void configureOrganisationRoleResolvers(RuntimeWiring.Builder builder) {
Expand Down Expand Up @@ -1316,10 +1324,23 @@ private void configureMutationResolvers(final RuntimeWiring.Builder builder) {
.dataFetcher(
"upsertStructuredProperties",
new UpsertStructuredPropertiesResolver(this.entityClient))
.dataFetcher(
"removeStructuredProperties",
new RemoveStructuredPropertiesResolver(this.entityClient))
.dataFetcher(
"createStructuredProperty",
new CreateStructuredPropertyResolver(this.entityClient))
.dataFetcher(
"updateStructuredProperty",
new UpdateStructuredPropertyResolver(this.entityClient))
.dataFetcher("raiseIncident", new RaiseIncidentResolver(this.entityClient))
.dataFetcher(
"updateIncidentStatus",
new UpdateIncidentStatusResolver(this.entityClient, this.entityService));
new UpdateIncidentStatusResolver(this.entityClient, this.entityService))
.dataFetcher(
"createForm", new CreateFormResolver(this.entityClient, this.formService))
.dataFetcher("deleteForm", new DeleteFormResolver(this.entityClient))
.dataFetcher("updateForm", new UpdateFormResolver(this.entityClient));
if (featureFlags.isBusinessAttributeEntityEnabled()) {
typeWiring
.dataFetcher(
Expand Down Expand Up @@ -3143,4 +3164,14 @@ private void configureConnectionResolvers(final RuntimeWiring.Builder builder) {
: null;
})));
}

private void configureDeprecationResolvers(final RuntimeWiring.Builder builder) {
builder.type(
"Deprecation",
typeWiring ->
typeWiring.dataFetcher(
"actorEntity",
new EntityTypeResolver(
entityTypes, (env) -> ((Deprecation) env.getSource()).getActorEntity())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,20 @@ public static <T> T restrictEntity(@Nonnull Object entity, Class<T> clazz) {
}
}

public static boolean canManageStructuredProperties(@Nonnull QueryContext context) {
return AuthUtil.isAuthorized(
context.getAuthorizer(),
context.getActorUrn(),
PoliciesConfig.MANAGE_STRUCTURED_PROPERTIES_PRIVILEGE);
}

public static boolean canManageForms(@Nonnull QueryContext context) {
return AuthUtil.isAuthorized(
context.getAuthorizer(),
context.getActorUrn(),
PoliciesConfig.MANAGE_DOCUMENTATION_FORMS_PRIVILEGE);
}

public static boolean isAuthorized(
@Nonnull Authorizer authorizer,
@Nonnull String actor,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.linkedin.datahub.graphql.resolvers.form;

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

import com.linkedin.common.urn.Urn;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.CreateFormInput;
import com.linkedin.datahub.graphql.generated.CreatePromptInput;
import com.linkedin.datahub.graphql.generated.Form;
import com.linkedin.datahub.graphql.generated.FormPromptType;
import com.linkedin.datahub.graphql.resolvers.mutate.util.FormUtils;
import com.linkedin.datahub.graphql.types.form.FormMapper;
import com.linkedin.entity.EntityResponse;
import com.linkedin.entity.client.EntityClient;
import com.linkedin.form.FormInfo;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.service.FormService;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class CreateFormResolver implements DataFetcher<CompletableFuture<Form>> {

private final EntityClient _entityClient;
private final FormService _formService;

public CreateFormResolver(
@Nonnull final EntityClient entityClient, @Nonnull final FormService formService) {
_entityClient = Objects.requireNonNull(entityClient, "entityClient must not be null");
_formService = Objects.requireNonNull(formService, "formService must not be null");
}

@Override
public CompletableFuture<Form> get(final DataFetchingEnvironment environment) throws Exception {
final QueryContext context = environment.getContext();

final CreateFormInput input =
bindArgument(environment.getArgument("input"), CreateFormInput.class);
final FormInfo formInfo = FormUtils.mapFormInfo(input);

return CompletableFuture.supplyAsync(
() -> {
try {
if (!AuthorizationUtils.canManageForms(context)) {
throw new AuthorizationException("Unable to create form. Please contact your admin.");
}
validatePrompts(input.getPrompts());

Urn formUrn =
_formService.createForm(context.getOperationContext(), formInfo, input.getId());
EntityResponse response =
_entityClient.getV2(
context.getOperationContext(), Constants.FORM_ENTITY_NAME, formUrn, null);
return FormMapper.map(context, response);
} catch (Exception e) {
throw new RuntimeException(
String.format("Failed to perform update against input %s", input), e);
}
});
}

private void validatePrompts(@Nullable List<CreatePromptInput> prompts) {
if (prompts == null) {
return;
}
prompts.forEach(
prompt -> {
if (prompt.getType().equals(FormPromptType.STRUCTURED_PROPERTY)
|| prompt.getType().equals(FormPromptType.FIELDS_STRUCTURED_PROPERTY)) {
if (prompt.getStructuredPropertyParams() == null) {
throw new IllegalArgumentException(
"Provided prompt with type STRUCTURED_PROPERTY or FIELDS_STRUCTURED_PROPERTY and no structured property params");
}
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.linkedin.datahub.graphql.resolvers.form;

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

import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.DeleteFormInput;
import com.linkedin.entity.client.EntityClient;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DeleteFormResolver implements DataFetcher<CompletableFuture<Boolean>> {

private final EntityClient _entityClient;

public DeleteFormResolver(@Nonnull final EntityClient entityClient) {
_entityClient = Objects.requireNonNull(entityClient, "entityClient must not be null");
}

@Override
public CompletableFuture<Boolean> get(final DataFetchingEnvironment environment)
throws Exception {
final QueryContext context = environment.getContext();

final DeleteFormInput input =
bindArgument(environment.getArgument("input"), DeleteFormInput.class);
final Urn formUrn = UrnUtils.getUrn(input.getUrn());

return CompletableFuture.supplyAsync(
() -> {
try {
if (!AuthorizationUtils.canManageForms(context)) {
throw new AuthorizationException("Unable to delete form. Please contact your admin.");
}
_entityClient.deleteEntity(context.getOperationContext(), formUrn);
// Asynchronously Delete all references to the entity (to return quickly)
CompletableFuture.runAsync(
() -> {
try {
_entityClient.deleteEntityReferences(context.getOperationContext(), formUrn);
} catch (Exception e) {
log.error(
String.format(
"Caught exception while attempting to clear all entity references for Form with urn %s",
formUrn),
e);
}
});

return true;
} catch (Exception e) {
throw new RuntimeException(
String.format("Failed to perform update against input %s", input), e);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.linkedin.datahub.graphql.resolvers.form;

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

import com.linkedin.common.urn.Urn;
import com.linkedin.common.urn.UrnUtils;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.Form;
import com.linkedin.datahub.graphql.generated.UpdateFormInput;
import com.linkedin.datahub.graphql.resolvers.mutate.util.FormUtils;
import com.linkedin.datahub.graphql.types.form.FormMapper;
import com.linkedin.entity.EntityResponse;
import com.linkedin.entity.client.EntityClient;
import com.linkedin.form.FormType;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.aspect.patch.builder.FormInfoPatchBuilder;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;

public class UpdateFormResolver implements DataFetcher<CompletableFuture<Form>> {

private final EntityClient _entityClient;

public UpdateFormResolver(@Nonnull final EntityClient entityClient) {
_entityClient = Objects.requireNonNull(entityClient, "entityClient must not be null");
}

@Override
public CompletableFuture<Form> get(final DataFetchingEnvironment environment) throws Exception {
final QueryContext context = environment.getContext();

final UpdateFormInput input =
bindArgument(environment.getArgument("input"), UpdateFormInput.class);
final Urn formUrn = UrnUtils.getUrn(input.getUrn());

return CompletableFuture.supplyAsync(
() -> {
try {
if (!AuthorizationUtils.canManageForms(context)) {
throw new AuthorizationException("Unable to update form. Please contact your admin.");
}
if (!_entityClient.exists(context.getOperationContext(), formUrn)) {
throw new IllegalArgumentException(
String.format("Form with urn %s does not exist", formUrn));
}

FormInfoPatchBuilder patchBuilder = new FormInfoPatchBuilder().urn(formUrn);
if (input.getName() != null) {
patchBuilder.setName(input.getName());
}
if (input.getDescription() != null) {
patchBuilder.setDescription(input.getDescription());
}
if (input.getType() != null) {
patchBuilder.setType(FormType.valueOf(input.getType().toString()));
}
if (input.getPromptsToAdd() != null) {
patchBuilder.addPrompts(FormUtils.mapPromptsToAdd(input.getPromptsToAdd()));
}
if (input.getPromptsToRemove() != null) {
patchBuilder.removePrompts(input.getPromptsToRemove());
}
if (input.getActors() != null) {
if (input.getActors().getOwners() != null) {
patchBuilder.setOwnershipForm(input.getActors().getOwners());
}
if (input.getActors().getUsersToAdd() != null) {
input.getActors().getUsersToAdd().forEach(patchBuilder::addAssignedUser);
}
if (input.getActors().getUsersToRemove() != null) {
input.getActors().getUsersToRemove().forEach(patchBuilder::removeAssignedUser);
}
if (input.getActors().getGroupsToAdd() != null) {
input.getActors().getGroupsToAdd().forEach(patchBuilder::addAssignedGroup);
}
if (input.getActors().getGroupsToRemove() != null) {
input.getActors().getGroupsToRemove().forEach(patchBuilder::removeAssignedGroup);
}
}
_entityClient.ingestProposal(
context.getOperationContext(), patchBuilder.build(), false);

EntityResponse response =
_entityClient.getV2(
context.getOperationContext(), Constants.FORM_ENTITY_NAME, formUrn, null);
return FormMapper.map(context, response);
} catch (Exception e) {
throw new RuntimeException(
String.format("Failed to perform update against input %s", input), e);
}
});
}
}
Loading

0 comments on commit 036f1da

Please sign in to comment.