Skip to content

Commit 6a06770

Browse files
feat(forms) Add batchRemoveForm graphql endpoint (#9840)
1 parent 66871cb commit 6a06770

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java

+2
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@
141141
import com.linkedin.datahub.graphql.resolvers.entity.EntityExistsResolver;
142142
import com.linkedin.datahub.graphql.resolvers.entity.EntityPrivilegesResolver;
143143
import com.linkedin.datahub.graphql.resolvers.form.BatchAssignFormResolver;
144+
import com.linkedin.datahub.graphql.resolvers.form.BatchRemoveFormResolver;
144145
import com.linkedin.datahub.graphql.resolvers.form.CreateDynamicFormAssignmentResolver;
145146
import com.linkedin.datahub.graphql.resolvers.form.IsFormAssignedToMeResolver;
146147
import com.linkedin.datahub.graphql.resolvers.form.SubmitFormPromptResolver;
@@ -1214,6 +1215,7 @@ private void configureMutationResolvers(final RuntimeWiring.Builder builder) {
12141215
new CreateDynamicFormAssignmentResolver(this.formService))
12151216
.dataFetcher(
12161217
"verifyForm", new VerifyFormResolver(this.formService, this.groupService))
1218+
.dataFetcher("batchRemoveForm", new BatchRemoveFormResolver(this.formService))
12171219
.dataFetcher("raiseIncident", new RaiseIncidentResolver(this.entityClient))
12181220
.dataFetcher(
12191221
"updateIncidentStatus",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.linkedin.datahub.graphql.resolvers.form;
2+
3+
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.bindArgument;
4+
5+
import com.datahub.authentication.Authentication;
6+
import com.linkedin.common.urn.Urn;
7+
import com.linkedin.common.urn.UrnUtils;
8+
import com.linkedin.datahub.graphql.QueryContext;
9+
import com.linkedin.datahub.graphql.generated.BatchAssignFormInput;
10+
import com.linkedin.metadata.service.FormService;
11+
import graphql.schema.DataFetcher;
12+
import graphql.schema.DataFetchingEnvironment;
13+
import java.util.List;
14+
import java.util.Objects;
15+
import java.util.concurrent.CompletableFuture;
16+
import java.util.stream.Collectors;
17+
import javax.annotation.Nonnull;
18+
19+
public class BatchRemoveFormResolver implements DataFetcher<CompletableFuture<Boolean>> {
20+
21+
private final FormService _formService;
22+
23+
public BatchRemoveFormResolver(@Nonnull final FormService formService) {
24+
_formService = Objects.requireNonNull(formService, "formService must not be null");
25+
}
26+
27+
@Override
28+
public CompletableFuture<Boolean> get(final DataFetchingEnvironment environment)
29+
throws Exception {
30+
final QueryContext context = environment.getContext();
31+
32+
final BatchAssignFormInput input =
33+
bindArgument(environment.getArgument("input"), BatchAssignFormInput.class);
34+
final Urn formUrn = UrnUtils.getUrn(input.getFormUrn());
35+
final List<String> entityUrns = input.getEntityUrns();
36+
final Authentication authentication = context.getAuthentication();
37+
38+
// TODO: (PRD-1062) Add permission check once permission exists
39+
40+
return CompletableFuture.supplyAsync(
41+
() -> {
42+
try {
43+
_formService.batchUnassignFormForEntities(
44+
entityUrns.stream().map(UrnUtils::getUrn).collect(Collectors.toList()),
45+
formUrn,
46+
authentication);
47+
return true;
48+
} catch (Exception e) {
49+
throw new RuntimeException(
50+
String.format("Failed to perform update against input %s", input), e);
51+
}
52+
});
53+
}
54+
}

datahub-graphql-core/src/main/resources/forms.graphql

+22
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
extend type Mutation {
2+
"""
3+
Remove a form from a given list of entities.
4+
"""
5+
batchRemoveForm(input: BatchRemoveFormInput!): Boolean!
6+
}
7+
18
"""
29
Requirements forms that are assigned to an entity.
310
"""
@@ -376,3 +383,18 @@ input VerifyFormInput {
376383
"""
377384
entityUrn: String!
378385
}
386+
387+
"""
388+
Input for batch removing a form from different entities
389+
"""
390+
input BatchRemoveFormInput {
391+
"""
392+
The urn of the form being removed from entities
393+
"""
394+
formUrn: String!
395+
396+
"""
397+
The entities that this form is being removed from
398+
"""
399+
entityUrns: [String!]!
400+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.linkedin.datahub.graphql.resolvers.form;
2+
3+
import static com.linkedin.datahub.graphql.TestUtils.getMockAllowContext;
4+
import static org.testng.Assert.assertThrows;
5+
import static org.testng.Assert.assertTrue;
6+
7+
import com.datahub.authentication.Authentication;
8+
import com.linkedin.common.urn.UrnUtils;
9+
import com.linkedin.datahub.graphql.QueryContext;
10+
import com.linkedin.datahub.graphql.generated.BatchAssignFormInput;
11+
import com.linkedin.metadata.service.FormService;
12+
import graphql.com.google.common.collect.ImmutableList;
13+
import graphql.schema.DataFetchingEnvironment;
14+
import java.util.concurrent.CompletionException;
15+
import org.mockito.Mockito;
16+
import org.testng.annotations.Test;
17+
18+
public class BatchRemoveFormResolverTest {
19+
20+
private static final String TEST_DATASET_URN =
21+
"urn:li:dataset:(urn:li:dataPlatform:hive,name,PROD)";
22+
private static final String TEST_FORM_URN = "urn:li:form:1";
23+
24+
private static final BatchAssignFormInput TEST_INPUT =
25+
new BatchAssignFormInput(TEST_FORM_URN, ImmutableList.of(TEST_DATASET_URN));
26+
27+
@Test
28+
public void testGetSuccess() throws Exception {
29+
FormService mockFormService = initMockFormService(true);
30+
BatchRemoveFormResolver resolver = new BatchRemoveFormResolver(mockFormService);
31+
32+
// Execute resolver
33+
QueryContext mockContext = getMockAllowContext();
34+
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
35+
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(TEST_INPUT);
36+
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
37+
38+
boolean success = resolver.get(mockEnv).get();
39+
40+
assertTrue(success);
41+
42+
// Validate that we called unassign on the service
43+
Mockito.verify(mockFormService, Mockito.times(1))
44+
.batchUnassignFormForEntities(
45+
Mockito.eq(ImmutableList.of(UrnUtils.getUrn(TEST_DATASET_URN))),
46+
Mockito.eq(UrnUtils.getUrn(TEST_FORM_URN)),
47+
Mockito.any(Authentication.class));
48+
}
49+
50+
@Test
51+
public void testThrowsError() throws Exception {
52+
FormService mockFormService = initMockFormService(false);
53+
BatchRemoveFormResolver resolver = new BatchRemoveFormResolver(mockFormService);
54+
55+
// Execute resolver
56+
QueryContext mockContext = getMockAllowContext();
57+
DataFetchingEnvironment mockEnv = Mockito.mock(DataFetchingEnvironment.class);
58+
Mockito.when(mockEnv.getArgument(Mockito.eq("input"))).thenReturn(TEST_INPUT);
59+
Mockito.when(mockEnv.getContext()).thenReturn(mockContext);
60+
61+
assertThrows(CompletionException.class, () -> resolver.get(mockEnv).join());
62+
63+
// Validate that we called unassign on the service - but it throws an error
64+
Mockito.verify(mockFormService, Mockito.times(1))
65+
.batchUnassignFormForEntities(
66+
Mockito.eq(ImmutableList.of(UrnUtils.getUrn(TEST_DATASET_URN))),
67+
Mockito.eq(UrnUtils.getUrn(TEST_FORM_URN)),
68+
Mockito.any(Authentication.class));
69+
}
70+
71+
private FormService initMockFormService(final boolean shouldSucceed) throws Exception {
72+
FormService service = Mockito.mock(FormService.class);
73+
74+
if (!shouldSucceed) {
75+
Mockito.doThrow(new RuntimeException())
76+
.when(service)
77+
.batchUnassignFormForEntities(
78+
Mockito.any(), Mockito.any(), Mockito.any(Authentication.class));
79+
}
80+
81+
return service;
82+
}
83+
}

0 commit comments

Comments
 (0)