diff --git a/src/main/java/com/commercetools/sync/services/impl/UnresolvedReferencesServiceImpl.java b/src/main/java/com/commercetools/sync/services/impl/UnresolvedReferencesServiceImpl.java index 8336c6b101..04251d6974 100644 --- a/src/main/java/com/commercetools/sync/services/impl/UnresolvedReferencesServiceImpl.java +++ b/src/main/java/com/commercetools/sync/services/impl/UnresolvedReferencesServiceImpl.java @@ -15,6 +15,7 @@ import com.commercetools.sync.commons.utils.ChunkUtils; import com.commercetools.sync.services.UnresolvedReferencesService; import com.fasterxml.jackson.databind.ObjectMapper; +import io.vrap.rmf.base.client.error.NotFoundException; import io.vrap.rmf.base.client.utils.json.JsonUtils; import java.util.Collections; import java.util.List; @@ -154,6 +155,12 @@ public CompletionStage> delete( return Optional.of( OBJECT_MAPPER.convertValue(resource.getBody().getValue(), clazz)); } else { + if (exception instanceof NotFoundException) { + // if we try to delete and the custom object is already gone, then everything is + // fine + // and there is no need to invoke the error callback + return Optional.empty(); + } syncOptions.applyErrorCallback( new SyncException(format(DELETE_FAILED, hash(key), key), exception)); return Optional.empty(); diff --git a/src/test/java/com/commercetools/sync/services/impl/UnresolvedReferencesServiceImplTest.java b/src/test/java/com/commercetools/sync/services/impl/UnresolvedReferencesServiceImplTest.java index d5f583c8a9..7f94907fb3 100644 --- a/src/test/java/com/commercetools/sync/services/impl/UnresolvedReferencesServiceImplTest.java +++ b/src/test/java/com/commercetools/sync/services/impl/UnresolvedReferencesServiceImplTest.java @@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import io.vrap.rmf.base.client.ApiHttpResponse; +import io.vrap.rmf.base.client.error.NotFoundException; import io.vrap.rmf.base.client.utils.CompletableFutureUtils; import java.nio.charset.StandardCharsets; import java.util.*; @@ -405,6 +406,64 @@ void delete_WithUnsuccessfulMockCtpResponse_ShouldReturnProperException() .hasCauseExactlyInstanceOf(BadRequestException.class); } + @Test + void delete_With404NotFoundResponse_ShouldConsiderAsDeleted() throws JsonProcessingException { + // preparation + final String key = "product-draft-key"; + final ProductDraft productDraftMock = + ProductDraftBuilder.of() + .productType(ProductTypeResourceIdentifierBuilder.of().key("product-type").build()) + .key(key) + .name(LocalizedString.ofEnglish("product-name")) + .slug(LocalizedString.ofEnglish("product-slug")) + .build(); + + final ApiHttpResponse apiHttpResponse = mock(ApiHttpResponse.class); + when(apiHttpResponse.getBody()).thenReturn(mock()); + final ErrorResponse errorResponse = + ErrorResponseBuilder.of() + .statusCode(404) + .errors(Collections.emptyList()) + .message("test") + .build(); + + final ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + final String json = ow.writeValueAsString(errorResponse); + + final ByProjectKeyCustomObjectsByContainerByKeyDelete customObjectsDelete = + mock(ByProjectKeyCustomObjectsByContainerByKeyDelete.class); + when(customObjectsDelete.execute()) + .thenReturn( + CompletableFutureUtils.failed( + new NotFoundException( + 404, + "", + null, + "not found", + new ApiHttpResponse<>(404, null, json.getBytes(StandardCharsets.UTF_8))))); + when(productSyncOptions + .getCtpClient() + .customObjects() + .withContainerAndKey(anyString(), anyString()) + .delete()) + .thenReturn(customObjectsDelete); + + // test + final Optional toBeResolvedOptional = + service + .delete( + "product-draft-key", + UnresolvedReferencesServiceImpl.CUSTOM_OBJECT_PRODUCT_CONTAINER_KEY, + WaitingToBeResolvedProducts.class) + .toCompletableFuture() + .join(); + + // assertions + assertThat(toBeResolvedOptional).isEmpty(); + assertThat(errorMessages).hasSize(0); + assertThat(errorExceptions).hasSize(0); + } + @SuppressWarnings("unchecked") @Test void delete_OnSuccess_ShouldRemoveTheResourceObject() {