-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Fix #27107: soft-deleted users still appear in Experts/Reviewers across all entities #27120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
89122f6
f56057c
3264fb3
841775b
d4743a1
05b41cb
1a1888a
0c45006
639c950
97f328a
d9aa379
00e7896
37a014a
47def38
4af35c3
b3d7482
d5446b1
736b773
007976e
21c8e64
f4a875e
5e77478
8b443b1
85911a3
6807212
70a4b67
344bc44
7217773
135be1c
607743b
9e31dbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,14 +29,20 @@ | |
| import org.junit.jupiter.api.parallel.ExecutionMode; | ||
| import org.openmetadata.it.util.SdkClients; | ||
| import org.openmetadata.it.util.TestNamespace; | ||
| import org.openmetadata.schema.api.VoteRequest; | ||
| import org.openmetadata.schema.api.domains.CreateDomain; | ||
| import org.openmetadata.schema.api.domains.CreateDomain.DomainType; | ||
| import org.openmetadata.schema.api.teams.CreateUser; | ||
| import org.openmetadata.schema.entity.domains.Domain; | ||
| import org.openmetadata.schema.entity.teams.User; | ||
| import org.openmetadata.schema.type.ChangeEvent; | ||
| import org.openmetadata.schema.type.EntityHistory; | ||
| import org.openmetadata.schema.type.EntityReference; | ||
| import org.openmetadata.schema.type.Votes; | ||
| import org.openmetadata.sdk.client.OpenMetadataClient; | ||
| import org.openmetadata.sdk.models.ListParams; | ||
| import org.openmetadata.sdk.models.ListResponse; | ||
| import org.openmetadata.sdk.network.HttpMethod; | ||
|
|
||
| /** | ||
| * Integration tests for Domain entity operations. | ||
|
|
@@ -1153,4 +1159,204 @@ void test_renameDomainDoesNotAffectSimilarPrefixDomains(TestNamespace ns) throws | |
| // Verify old child FQN no longer works | ||
| assertThrows(Exception.class, () -> getEntityByName(oldChildFqn)); | ||
| } | ||
|
|
||
| @Test | ||
| void softDeletedExpert_notReturnedInSingleGet(TestNamespace ns) { | ||
| OpenMetadataClient client = SdkClients.adminClient(); | ||
|
|
||
| String userName = ns.shortPrefix("domain_expert"); | ||
| User expert = | ||
| client | ||
| .users() | ||
| .create( | ||
| new CreateUser() | ||
| .withName(userName) | ||
| .withEmail(userName + "@test.openmetadata.org") | ||
| .withDescription("Expert user for domain soft-delete test")); | ||
|
|
||
| CreateDomain create = | ||
| new CreateDomain() | ||
| .withName(ns.prefix("domain_softdel")) | ||
| .withDomainType(DomainType.AGGREGATE) | ||
| .withExperts(List.of(expert.getFullyQualifiedName())) | ||
| .withDescription("Domain for soft-delete expert test"); | ||
| Domain domain = createEntity(create); | ||
|
|
||
| client.users().delete(expert.getId().toString()); | ||
|
|
||
| Domain byId = client.domains().get(domain.getId().toString(), "experts"); | ||
| assertTrue( | ||
| byId.getExperts() == null || byId.getExperts().isEmpty(), | ||
| "Soft-deleted expert must not appear in single GET by ID"); | ||
|
|
||
| Domain byName = client.domains().getByName(domain.getFullyQualifiedName(), "experts"); | ||
| assertTrue( | ||
| byName.getExperts() == null || byName.getExperts().isEmpty(), | ||
| "Soft-deleted expert must not appear in single GET by name"); | ||
| } | ||
|
|
||
| @Test | ||
| void softDeletedExpert_notReturnedInListEndpoint(TestNamespace ns) { | ||
| OpenMetadataClient client = SdkClients.adminClient(); | ||
|
|
||
| String userName = ns.shortPrefix("domain_expert_list"); | ||
| User expert = | ||
| client | ||
| .users() | ||
| .create( | ||
| new CreateUser() | ||
| .withName(userName) | ||
| .withEmail(userName + "@test.openmetadata.org") | ||
| .withDescription("Expert user for domain list soft-delete test")); | ||
|
|
||
| CreateDomain create = | ||
| new CreateDomain() | ||
| .withName(ns.prefix("domain_softdel_list")) | ||
| .withDomainType(DomainType.AGGREGATE) | ||
| .withExperts(List.of(expert.getFullyQualifiedName())) | ||
| .withDescription("Domain for soft-delete expert list test"); | ||
| Domain domain = createEntity(create); | ||
|
|
||
| client.users().delete(expert.getId().toString()); | ||
|
|
||
| Domain listed = null; | ||
| ListParams params = new ListParams().setFields("experts").withLimit(100); | ||
| while (listed == null) { | ||
| ListResponse<Domain> page = listEntities(params); | ||
| listed = | ||
| page.getData().stream() | ||
| .filter(d -> d.getId().equals(domain.getId())) | ||
| .findFirst() | ||
| .orElse(null); | ||
| String after = page.getPaging() != null ? page.getPaging().getAfter() : null; | ||
| if (listed != null || after == null) break; | ||
| params = new ListParams().setFields("experts").withLimit(100).setAfter(after); | ||
| } | ||
| assertNotNull(listed, "Domain not found in list"); | ||
| assertTrue( | ||
| listed.getExperts() == null || listed.getExperts().isEmpty(), | ||
| "Soft-deleted expert must not appear in list endpoint"); | ||
| } | ||
|
|
||
| @Test | ||
| void softDeletedExpert_notReturnedInListWithIncludeAll(TestNamespace ns) { | ||
| OpenMetadataClient client = SdkClients.adminClient(); | ||
|
|
||
| String userName = ns.shortPrefix("domain_expert_all"); | ||
| User expert = | ||
| client | ||
| .users() | ||
| .create( | ||
| new CreateUser() | ||
| .withName(userName) | ||
| .withEmail(userName + "@test.openmetadata.org") | ||
| .withDescription("Expert user for domain include-all soft-delete test")); | ||
|
|
||
| CreateDomain create = | ||
| new CreateDomain() | ||
| .withName(ns.prefix("domain_softdel_all")) | ||
| .withDomainType(DomainType.AGGREGATE) | ||
| .withExperts(List.of(expert.getFullyQualifiedName())) | ||
| .withDescription("Domain for include-all soft-delete expert test"); | ||
| Domain domain = createEntity(create); | ||
|
|
||
| client.users().delete(expert.getId().toString()); | ||
|
|
||
| Domain listed = null; | ||
| ListParams params = | ||
| new ListParams().setFields("experts").withLimit(100).addFilter("include", "all"); | ||
| while (listed == null) { | ||
| ListResponse<Domain> page = listEntities(params); | ||
| listed = | ||
| page.getData().stream() | ||
| .filter(d -> d.getId().equals(domain.getId())) | ||
| .findFirst() | ||
| .orElse(null); | ||
| String after = page.getPaging() != null ? page.getPaging().getAfter() : null; | ||
| if (listed != null || after == null) break; | ||
| params = | ||
| new ListParams() | ||
| .setFields("experts") | ||
| .withLimit(100) | ||
| .addFilter("include", "all") | ||
| .setAfter(after); | ||
| } | ||
| assertNotNull(listed, "Domain not found in list with include=all"); | ||
| assertTrue( | ||
| listed.getExperts() == null || listed.getExperts().isEmpty(), | ||
| "Soft-deleted expert must not appear even when include=all (applies to top-level only)"); | ||
| } | ||
|
|
||
| @Test | ||
| void softDeletedFollower_notReturnedInListEndpoint(TestNamespace ns) { | ||
| OpenMetadataClient client = SdkClients.adminClient(); | ||
|
|
||
| String userName = ns.shortPrefix("follower_list"); | ||
| User follower = | ||
| client | ||
| .users() | ||
| .create( | ||
| new CreateUser().withName(userName).withEmail(userName + "@test.openmetadata.org")); | ||
|
|
||
| Domain domain = createEntity(createRequest(ns.prefix("dom_follower"), ns)); | ||
|
|
||
| client | ||
| .getHttpClient() | ||
| .execute( | ||
| HttpMethod.PUT, | ||
| "/v1/domains/" + domain.getId() + "/followers", | ||
| follower.getId(), | ||
| ChangeEvent.class); | ||
|
|
||
| client.users().delete(follower.getId().toString()); | ||
|
|
||
| ListParams params = new ListParams().setFields("followers").withLimit(1000000); | ||
| ListResponse<Domain> list = listEntities(params); | ||
|
Comment on lines
+1313
to
+1314
|
||
| Domain listed = | ||
| list.getData().stream() | ||
| .filter(d -> d.getId().equals(domain.getId())) | ||
| .findFirst() | ||
| .orElseThrow(() -> new AssertionError("Domain not found in list")); | ||
| assertTrue( | ||
| listed.getFollowers() == null || listed.getFollowers().isEmpty(), | ||
| "Soft-deleted follower must not appear in list endpoint"); | ||
| } | ||
|
|
||
| @Test | ||
| void softDeletedVoter_notReturnedInListEndpoint(TestNamespace ns) { | ||
| String userName = ns.shortPrefix("voter_list"); | ||
| String userEmail = userName + "@test.openmetadata.org"; | ||
|
|
||
| OpenMetadataClient adminClient = SdkClients.adminClient(); | ||
| User voter = | ||
| adminClient.users().create(new CreateUser().withName(userName).withEmail(userEmail)); | ||
|
|
||
| Domain domain = createEntity(createRequest(ns.prefix("dom_voter"), ns)); | ||
|
|
||
| OpenMetadataClient voterClient = SdkClients.createClient(userEmail, userEmail, new String[] {}); | ||
| voterClient | ||
| .getHttpClient() | ||
| .execute( | ||
| HttpMethod.PUT, | ||
| "/v1/domains/" + domain.getId() + "/vote", | ||
| new VoteRequest().withUpdatedVoteType(VoteRequest.VoteType.VOTED_UP), | ||
| ChangeEvent.class); | ||
|
|
||
| adminClient.users().delete(voter.getId().toString()); | ||
|
|
||
| ListParams params = new ListParams().setFields("votes").withLimit(1000000); | ||
| ListResponse<Domain> list = listEntities(params); | ||
|
Comment on lines
+1347
to
+1348
|
||
| Domain listed = | ||
| list.getData().stream() | ||
| .filter(d -> d.getId().equals(domain.getId())) | ||
| .findFirst() | ||
| .orElseThrow(() -> new AssertionError("Domain not found in list")); | ||
| Votes votes = listed.getVotes(); | ||
| boolean voterInUpVotes = | ||
| votes != null | ||
| && votes.getUpVoters() != null | ||
| && votes.getUpVoters().stream() | ||
| .anyMatch(ref -> ref != null && voter.getId().equals(ref.getId())); | ||
| assertFalse(voterInUpVotes, "Soft-deleted voter must not appear in list endpoint votes"); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This paging loop has no hard stop / cursor-repeat protection. If paging ever returns a non-null
afterthat doesn’t advance (or cycles), this test can hang indefinitely in CI. Consider adding a max-iterations bound and/or tracking previously seenaftercursors to fail fast with a clear assertion.