Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,19 @@ public Changeset addUpdate( @Nonnull final ODataRequestUpdate request )
{
final String versionIdentifier = request.getVersionIdentifier();
request.addVersionIdentifierToHeaderIfPresent(versionIdentifier);
final String httpMethod = request.getUpdateStrategy() == UpdateStrategy.MODIFY_WITH_PATCH ? "PATCH" : "PUT";

final String httpMethod;
switch( request.getUpdateStrategy() ) {
case MODIFY_WITH_PATCH, MODIFY_WITH_PATCH_RECURSIVE_DELTA, MODIFY_WITH_PATCH_RECURSIVE_FULL:
httpMethod = "PATCH";
break;
case REPLACE_WITH_PUT:
httpMethod = "PUT";
break;
default:
throw new IllegalStateException("Unexpected update strategy: " + request.getUpdateStrategy());
}

final BatchItemSingle item =
new BatchItemSingle(originalRequest, request, httpMethod, request::getSerializedEntity);
queries.add(item);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,12 +174,13 @@ public ODataRequestResultGeneric execute( @Nonnull final HttpClient httpClient )
final ODataHttpRequest request = ODataHttpRequest.forHttpEntity(this, httpClient, requestHttpEntity);
addVersionIdentifierToHeaderIfPresent(versionIdentifier);

if( updateStrategy == UpdateStrategy.MODIFY_WITH_PATCH ) {
return tryExecuteWithCsrfToken(httpClient, request::requestPatch).get();
} else if( updateStrategy == UpdateStrategy.REPLACE_WITH_PUT ) {
return tryExecuteWithCsrfToken(httpClient, request::requestPut).get();
} else {
throw new IllegalStateException("Unexpected update Strategy: " + updateStrategy);
switch( updateStrategy ) {
case MODIFY_WITH_PATCH, MODIFY_WITH_PATCH_RECURSIVE_DELTA, MODIFY_WITH_PATCH_RECURSIVE_FULL:
return tryExecuteWithCsrfToken(httpClient, request::requestPatch).get();
case REPLACE_WITH_PUT:
return tryExecuteWithCsrfToken(httpClient, request::requestPut).get();
default:
throw new IllegalStateException("Unexpected update Strategy: " + updateStrategy);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*/
package com.sap.cloud.sdk.datamodel.odata.client.request;

import com.google.common.annotations.Beta;

/**
* The strategy to use when updating existing entities.
*/
Expand All @@ -17,5 +19,28 @@ public enum UpdateStrategy
/**
* Request to update the entity is sent with the HTTP method PATCH and its payload contains the changed fields only.
*/
MODIFY_WITH_PATCH;
MODIFY_WITH_PATCH,

/**
* Request to update the entity is sent with the HTTP method PATCH and its payload contains the changed fields
* including the changes in nested non-entity type fields.
*
* The request payload contains only the changed fields. Navigation properties are not supported.
*
* @since 5.16.0
*/
@Beta
MODIFY_WITH_PATCH_RECURSIVE_DELTA,

/**
* Request to update the entity is sent with the HTTP method PATCH and its payload contains the changed fields
* including the changes in nested non-entity type fields.
*
* The request payload contains the full value of complex fields for changes in any nested field. Navigation
* properties are not supported.
*
* @since 5.16.0
*/
@Beta
MODIFY_WITH_PATCH_RECURSIVE_FULL;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package com.sap.cloud.sdk.datamodel.odata.helper;

import static com.sap.cloud.sdk.datamodel.odata.helper.ModifyPatchStrategy.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
Expand All @@ -19,7 +20,6 @@
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.message.BasicHttpResponse;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import com.sap.cloud.sdk.cloudplatform.connectivity.DefaultHttpDestination;
Expand Down Expand Up @@ -211,21 +211,41 @@ void testUpdateBPatchUpdateNull()
}

@Test
@Disabled( " Test is failing as the getChangedFields() method on Complex Type is not working as expected." )
void testUpdatePatchComplexProperty()
void testUpdatePatchComplexPropertyDelta()
{
final ProductCount count1 = ProductCount.builder().productId(123).quantity(10).build();
final Receipt receipt = Receipt.builder().id(1001).customerId(9001).productCount1(count1).build();

final String expectedSerializedEntity = "{\"ProductCount1\":{\"Quantity\":\"20\"}}";
final String expectedSerializedEntity = "{\"ProductCount1\":{\"Quantity\":20}}";

count1.setQuantity(20);

final ODataRequestUpdate receiptUpdate =
FluentHelperFactory
.withServicePath(ODATA_ENDPOINT_URL)
.update(ENTITY_COLLECTION, receipt)
.modifyingEntity()
.modifyingEntity(RECURSIVE_DELTA)
.toRequest();

assertThat(receiptUpdate).isNotNull();
assertThat(receiptUpdate.getSerializedEntity()).isEqualTo(expectedSerializedEntity);
}

@Test
void testUpdatePatchComplexPropertyFull()
{
final ProductCount count1 = ProductCount.builder().productId(123).quantity(10).build();
final Receipt receipt = Receipt.builder().id(1001).customerId(9001).productCount1(count1).build();

final String expectedSerializedEntity = "{\"ProductCount1\":{\"ProductId\":123,\"Quantity\":20}}";

count1.setQuantity(20);

final ODataRequestUpdate receiptUpdate =
FluentHelperFactory
.withServicePath(ODATA_ENDPOINT_URL)
.update(ENTITY_COLLECTION, receipt)
.modifyingEntity(RECURSIVE_FULL)
.toRequest();

assertThat(receiptUpdate).isNotNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.apache.http.client.HttpClient;

import com.google.common.annotations.Beta;
import com.sap.cloud.sdk.cloudplatform.connectivity.Destination;
import com.sap.cloud.sdk.cloudplatform.connectivity.HttpClientAccessor;
import com.sap.cloud.sdk.datamodel.odata.client.ODataProtocol;
Expand Down Expand Up @@ -144,23 +145,31 @@ private String getSerializedEntity()
{
final EntityT entity = getEntity();
try {
final List<FieldReference> fieldsToExcludeUpdate =
excludedFields
.stream()
.map(EntitySelectable::getFieldName)
.map(FieldReference::of)
.collect(Collectors.toList());

final List<FieldReference> fieldsToIncludeInUpdate =
includedFields
.stream()
.map(EntitySelectable::getFieldName)
.map(FieldReference::of)
.collect(Collectors.toList());

switch( updateStrategy ) {
case REPLACE_WITH_PUT:
final List<FieldReference> fieldsToExcludeUpdate =
excludedFields
.stream()
.map(EntitySelectable::getFieldName)
.map(FieldReference::of)
.collect(Collectors.toList());
return ODataEntitySerializer.serializeEntityForUpdatePut(entity, fieldsToExcludeUpdate);
case MODIFY_WITH_PATCH:
final List<FieldReference> fieldsToIncludeInUpdate =
includedFields
.stream()
.map(EntitySelectable::getFieldName)
.map(FieldReference::of)
.collect(Collectors.toList());
return ODataEntitySerializer.serializeEntityForUpdatePatch(entity, fieldsToIncludeInUpdate);
return ODataEntitySerializer.serializeEntityForUpdatePatchShallow(entity, fieldsToIncludeInUpdate);
case MODIFY_WITH_PATCH_RECURSIVE_DELTA:
return ODataEntitySerializer
.serializeEntityForUpdatePatchRecursiveDelta(entity, fieldsToIncludeInUpdate);
case MODIFY_WITH_PATCH_RECURSIVE_FULL:
return ODataEntitySerializer
.serializeEntityForUpdatePatchRecursiveFull(entity, fieldsToIncludeInUpdate);
default:
throw new IllegalStateException("Unexpected update strategy:" + updateStrategy);
}
Expand Down Expand Up @@ -193,7 +202,6 @@ private String getSerializedEntity()
*
* @param fields
* The fields to be included in the update execution.
*
* @return The same fluent helper which will include the specified fields in an update request.
*/
@Nonnull
Expand All @@ -212,7 +220,6 @@ public final FluentHelperT includingFields( @Nonnull final EntitySelectable<Enti
*
* @param fields
* The fields to be excluded in the update execution.
*
* @return The same fluent helper which will exclude the specified fields in an update request.
*/
@Nonnull
Expand Down Expand Up @@ -255,4 +262,35 @@ public final FluentHelperT modifyingEntity()
updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH;
return getThis();
}

/**
* Allows to control that the request to update the entity is sent with the HTTP method PATCH and its payload
* contains the changed fields only, with different strategies for handling nested fields.
*
* @param strategy
* The strategy to use for the PATCH update.
* @return The same fluent helper which will modify the entity in the remote system.
* @throws IllegalArgumentException
* If an unknown ModifyPatchStrategy is provided.
* @since 5.16.0
*/
@Beta
@Nonnull
public final FluentHelperT modifyingEntity( @Nonnull final ModifyPatchStrategy strategy )
{
switch( strategy ) {
case SHALLOW:
return modifyingEntity();
case RECURSIVE_DELTA:
updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH_RECURSIVE_DELTA;
break;
case RECURSIVE_FULL:
updateStrategy = UpdateStrategy.MODIFY_WITH_PATCH_RECURSIVE_FULL;
break;
default:
throw new IllegalArgumentException("Unknown ModifyPatchStrategy: " + strategy);
}
return getThis();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.sap.cloud.sdk.datamodel.odata.helper;

import com.google.common.annotations.Beta;

/**
* Strategy to determine how a patch operation should be applied to an entity.
*
* @since 5.16.0
*/
@Beta
public enum ModifyPatchStrategy
{
/** Only the top level fields can be patched */
SHALLOW,

/** All top level and nested fields can be patched, resulting in JSON containing only the changed fields */
RECURSIVE_DELTA,

/**
* All top level and nested fields can be patched, resulting in JSON containing the full value of complex object.
*/
RECURSIVE_FULL
}
Loading
Loading