|
1 | 1 | package com.linkedin.metadata.service;
|
2 | 2 |
|
| 3 | +import static com.linkedin.metadata.Constants.CONTAINER_ENTITY_NAME; |
| 4 | +import static com.linkedin.metadata.search.utils.QueryUtils.createRelationshipFilter; |
| 5 | +import static com.linkedin.metadata.utils.CriterionUtils.buildCriterion; |
3 | 6 | import static org.mockito.ArgumentMatchers.any;
|
| 7 | +import static org.mockito.ArgumentMatchers.eq; |
| 8 | +import static org.mockito.ArgumentMatchers.nullable; |
4 | 9 | import static org.mockito.Mockito.mock;
|
5 | 10 | import static org.mockito.Mockito.reset;
|
6 | 11 | import static org.mockito.Mockito.times;
|
7 | 12 | import static org.mockito.Mockito.verify;
|
8 | 13 | import static org.mockito.Mockito.verifyNoInteractions;
|
9 | 14 | import static org.testng.Assert.assertEquals;
|
10 | 15 |
|
| 16 | +import com.google.common.collect.ImmutableList; |
11 | 17 | import com.linkedin.common.Status;
|
12 | 18 | import com.linkedin.common.urn.Urn;
|
13 | 19 | import com.linkedin.common.urn.UrnUtils;
|
| 20 | +import com.linkedin.container.Container; |
14 | 21 | import com.linkedin.dataset.DatasetProperties;
|
15 | 22 | import com.linkedin.events.metadata.ChangeType;
|
16 | 23 | import com.linkedin.metadata.Constants;
|
|
21 | 28 | import com.linkedin.metadata.graph.elastic.ElasticSearchGraphService;
|
22 | 29 | import com.linkedin.metadata.models.registry.EntityRegistry;
|
23 | 30 | import com.linkedin.metadata.models.registry.LineageRegistry;
|
| 31 | +import com.linkedin.metadata.query.filter.Condition; |
| 32 | +import com.linkedin.metadata.query.filter.ConjunctiveCriterion; |
| 33 | +import com.linkedin.metadata.query.filter.ConjunctiveCriterionArray; |
| 34 | +import com.linkedin.metadata.query.filter.Criterion; |
| 35 | +import com.linkedin.metadata.query.filter.CriterionArray; |
| 36 | +import com.linkedin.metadata.query.filter.Filter; |
| 37 | +import com.linkedin.metadata.query.filter.RelationshipDirection; |
| 38 | +import com.linkedin.metadata.query.filter.RelationshipFilter; |
24 | 39 | import com.linkedin.metadata.search.elasticsearch.indexbuilder.ESIndexBuilder;
|
25 | 40 | import com.linkedin.metadata.search.elasticsearch.update.ESBulkProcessor;
|
26 | 41 | import com.linkedin.metadata.utils.GenericRecordUtils;
|
|
29 | 44 | import com.linkedin.mxe.MetadataChangeLog;
|
30 | 45 | import io.datahubproject.metadata.context.OperationContext;
|
31 | 46 | import io.datahubproject.test.metadata.context.TestOperationContexts;
|
| 47 | +import java.util.List; |
| 48 | +import javax.annotation.Nonnull; |
32 | 49 | import org.mockito.ArgumentCaptor;
|
33 | 50 | import org.opensearch.index.query.QueryBuilder;
|
34 | 51 | import org.opensearch.script.Script;
|
@@ -180,4 +197,109 @@ public void testStatusNoOpEvent() {
|
180 | 197 |
|
181 | 198 | verifyNoInteractions(mockWriteDAO);
|
182 | 199 | }
|
| 200 | + |
| 201 | + @Test |
| 202 | + public void testMissingAspectGraphDelete() { |
| 203 | + // Test deleting a null aspect |
| 204 | + test.handleChangeEvent( |
| 205 | + TEST_OP_CONTEXT, |
| 206 | + new MetadataChangeLog() |
| 207 | + .setChangeType(ChangeType.DELETE) |
| 208 | + .setEntityType(TEST_URN.getEntityType()) |
| 209 | + .setEntityUrn(TEST_URN) |
| 210 | + .setAspectName(Constants.CONTAINER_ASPECT_NAME)); |
| 211 | + |
| 212 | + // For missing aspects, verify no writes |
| 213 | + verifyNoInteractions(mockWriteDAO); |
| 214 | + } |
| 215 | + |
| 216 | + @Test |
| 217 | + public void testNodeGraphDelete() { |
| 218 | + Urn containerUrn = UrnUtils.getUrn("urn:li:container:foo"); |
| 219 | + |
| 220 | + // Test deleting container entity |
| 221 | + test.handleChangeEvent( |
| 222 | + TEST_OP_CONTEXT, |
| 223 | + new MetadataChangeLog() |
| 224 | + .setChangeType(ChangeType.DELETE) |
| 225 | + .setEntityType(CONTAINER_ENTITY_NAME) |
| 226 | + .setEntityUrn(containerUrn) |
| 227 | + .setAspectName(Constants.CONTAINER_KEY_ASPECT_NAME)); |
| 228 | + |
| 229 | + // Delete all outgoing edges of this entity |
| 230 | + verify(mockWriteDAO, times(1)) |
| 231 | + .deleteByQuery( |
| 232 | + eq(TEST_OP_CONTEXT), |
| 233 | + nullable(String.class), |
| 234 | + eq(createUrnFilter(containerUrn)), |
| 235 | + nullable(String.class), |
| 236 | + eq(new Filter().setOr(new ConjunctiveCriterionArray())), |
| 237 | + eq(List.of()), |
| 238 | + eq(new RelationshipFilter().setDirection(RelationshipDirection.OUTGOING))); |
| 239 | + |
| 240 | + // Delete all incoming edges of this entity |
| 241 | + verify(mockWriteDAO, times(1)) |
| 242 | + .deleteByQuery( |
| 243 | + eq(TEST_OP_CONTEXT), |
| 244 | + nullable(String.class), |
| 245 | + eq(createUrnFilter(containerUrn)), |
| 246 | + nullable(String.class), |
| 247 | + eq(new Filter().setOr(new ConjunctiveCriterionArray())), |
| 248 | + eq(List.of()), |
| 249 | + eq(new RelationshipFilter().setDirection(RelationshipDirection.INCOMING))); |
| 250 | + |
| 251 | + // Delete all edges where this entity is a lifecycle owner |
| 252 | + verify(mockWriteDAO, times(1)) |
| 253 | + .deleteByQuery( |
| 254 | + eq(TEST_OP_CONTEXT), |
| 255 | + nullable(String.class), |
| 256 | + eq(new Filter().setOr(new ConjunctiveCriterionArray())), |
| 257 | + nullable(String.class), |
| 258 | + eq(new Filter().setOr(new ConjunctiveCriterionArray())), |
| 259 | + eq(List.of()), |
| 260 | + eq(new RelationshipFilter().setDirection(RelationshipDirection.INCOMING)), |
| 261 | + eq(containerUrn.toString())); |
| 262 | + } |
| 263 | + |
| 264 | + @Test |
| 265 | + public void testContainerDelete() { |
| 266 | + Urn containerUrn = UrnUtils.getUrn("urn:li:container:foo"); |
| 267 | + |
| 268 | + // Test deleting a container aspect |
| 269 | + test.handleChangeEvent( |
| 270 | + TEST_OP_CONTEXT, |
| 271 | + new MetadataChangeLog() |
| 272 | + .setChangeType(ChangeType.DELETE) |
| 273 | + .setEntityType(TEST_URN.getEntityType()) |
| 274 | + .setEntityUrn(TEST_URN) |
| 275 | + .setAspectName(Constants.CONTAINER_ASPECT_NAME) |
| 276 | + .setPreviousAspectValue( |
| 277 | + GenericRecordUtils.serializeAspect(new Container().setContainer(containerUrn)))); |
| 278 | + |
| 279 | + // For container aspects, verify that only edges are removed in both cases |
| 280 | + verify(mockWriteDAO, times(1)) |
| 281 | + .deleteByQuery( |
| 282 | + eq(TEST_OP_CONTEXT), |
| 283 | + nullable(String.class), |
| 284 | + eq(createUrnFilter(TEST_URN)), |
| 285 | + nullable(String.class), |
| 286 | + eq(new Filter().setOr(new ConjunctiveCriterionArray())), |
| 287 | + eq(List.of("IsPartOf")), |
| 288 | + eq( |
| 289 | + createRelationshipFilter( |
| 290 | + new Filter().setOr(new ConjunctiveCriterionArray()), |
| 291 | + RelationshipDirection.OUTGOING))); |
| 292 | + } |
| 293 | + |
| 294 | + private static Filter createUrnFilter(@Nonnull final Urn urn) { |
| 295 | + Filter filter = new Filter(); |
| 296 | + CriterionArray criterionArray = new CriterionArray(); |
| 297 | + Criterion criterion = buildCriterion("urn", Condition.EQUAL, urn.toString()); |
| 298 | + criterionArray.add(criterion); |
| 299 | + filter.setOr( |
| 300 | + new ConjunctiveCriterionArray( |
| 301 | + ImmutableList.of(new ConjunctiveCriterion().setAnd(criterionArray)))); |
| 302 | + |
| 303 | + return filter; |
| 304 | + } |
183 | 305 | }
|
0 commit comments