Skip to content

Commit

Permalink
[Bugfix] HTTP PATCH on /$value not working (#1013)
Browse files Browse the repository at this point in the history
* Fix HTTP PATCH for valueOnly not working
* renames SetSubmodelElementValueByPath... to PatchSubmodelElementValueByPath...
* include PatchSubmodelElementValueByPathRequest in service profiles so it is available via HTTP
* update changelog
  • Loading branch information
mjacoby authored Jan 16, 2025
1 parent c73d5ff commit 11d326b
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import de.fraunhofer.iosb.ilt.faaast.service.exception.ConfigurationException;
import de.fraunhofer.iosb.ilt.faaast.service.exception.InvalidConfigurationException;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.Response;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.SetSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.PatchSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.value.DataElementValue;
import de.fraunhofer.iosb.ilt.faaast.service.model.value.ElementValue;
import de.fraunhofer.iosb.ilt.faaast.service.util.ElementValueHelper;
Expand Down Expand Up @@ -221,7 +221,7 @@ private void setupSubscription(Reference reference, AssetSubscriptionProvider pr
}
try {
provider.addNewDataListener((DataElementValue data) -> {
Response response = service.execute(SetSubmodelElementValueByPathRequest.builder()
Response response = service.execute(PatchSubmodelElementValueByPathRequest.builder()
.submodelId(ReferenceHelper.findFirstKeyType(reference, KeyTypes.SUBMODEL))
.path(ReferenceHelper.toPath(reference))
.disableSyncWithAsset()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import de.fraunhofer.iosb.ilt.faaast.service.model.api.StatusCode;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.modifier.Extent;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.modifier.OutputModifier;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.SetSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.SetSubmodelElementValueByPathResponse;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.PatchSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.PatchSubmodelElementValueByPathResponse;
import de.fraunhofer.iosb.ilt.faaast.service.model.messagebus.event.change.ValueChangeEventMessage;
import de.fraunhofer.iosb.ilt.faaast.service.model.value.ElementValue;
import de.fraunhofer.iosb.ilt.faaast.service.model.value.mapper.ElementValueMapper;
Expand All @@ -31,20 +31,20 @@

/**
* Class to handle a
* {@link de.fraunhofer.iosb.ilt.faaast.service.model.api.request.SetSubmodelElementValueByPathRequest} in the service
* {@link de.fraunhofer.iosb.ilt.faaast.service.model.api.request.PatchSubmodelElementValueByPathRequest} in the service
* and to send the corresponding response
* {@link de.fraunhofer.iosb.ilt.faaast.service.model.api.response.SetSubmodelElementValueByPathResponse}. Is
* {@link de.fraunhofer.iosb.ilt.faaast.service.model.api.response.PatchSubmodelElementValueByPathResponse}. Is
* responsible for communication with the persistence and sends the corresponding events to the message bus.
*/
public class SetSubmodelElementValueByPathRequestHandler
extends AbstractSubmodelInterfaceRequestHandler<SetSubmodelElementValueByPathRequest<?>, SetSubmodelElementValueByPathResponse> {
public class PatchSubmodelElementValueByPathRequestHandler
extends AbstractSubmodelInterfaceRequestHandler<PatchSubmodelElementValueByPathRequest<?>, PatchSubmodelElementValueByPathResponse> {

@Override
public SetSubmodelElementValueByPathResponse doProcess(SetSubmodelElementValueByPathRequest request, RequestExecutionContext context) throws Exception {
public PatchSubmodelElementValueByPathResponse doProcess(PatchSubmodelElementValueByPathRequest request, RequestExecutionContext context) throws Exception {
if (request == null || request.getValueParser() == null) {
throw new IllegalArgumentException("value parser of request must be non-null");
}
SetSubmodelElementValueByPathResponse response = new SetSubmodelElementValueByPathResponse();
PatchSubmodelElementValueByPathResponse response = new PatchSubmodelElementValueByPathResponse();
Reference reference = new ReferenceBuilder()
.submodel(request.getSubmodelId())
.idShortPath(request.getPath())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import de.fraunhofer.iosb.ilt.faaast.service.model.api.modifier.QueryModifier;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.operation.OperationHandle;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.paging.Page;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.SetSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.PatchSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.DeleteSubmodelReferenceRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.DeleteThumbnailRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.GetAllSubmodelReferencesRequest;
Expand Down Expand Up @@ -94,7 +94,7 @@
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.submodelrepository.GetSubmodelByIdRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.submodelrepository.PostSubmodelRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.submodelrepository.PutSubmodelByIdRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.SetSubmodelElementValueByPathResponse;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.PatchSubmodelElementValueByPathResponse;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.aas.DeleteSubmodelReferenceResponse;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.aas.DeleteThumbnailResponse;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.aas.GetAllSubmodelReferencesResponse;
Expand Down Expand Up @@ -1054,14 +1054,14 @@ public void testPutSubmodelElementByPathRequest() throws ResourceNotFoundExcepti


@Test
public void testSetSubmodelElementValueByPathRequest() throws ResourceNotFoundException, AssetConnectionException, Exception {
public void testPatchSubmodelElementValueByPathRequest() throws ResourceNotFoundException, AssetConnectionException, Exception {
when(persistence.getSubmodelElement((SubmodelElementIdentifier) any(), any()))
.thenReturn(environment.getSubmodels().get(0).getSubmodelElements().get(0));
when(assetConnectionManager.hasValueProvider(any())).thenReturn(true);
PropertyValue propertyValue = new PropertyValue.Builder()
.value(new StringValue("Test"))
.build();
SetSubmodelElementValueByPathRequest request = new SetSubmodelElementValueByPathRequest.Builder<ElementValue>()
PatchSubmodelElementValueByPathRequest request = new PatchSubmodelElementValueByPathRequest.Builder<ElementValue>()
.submodelId(environment.getSubmodels().get(0).getId())
.value(propertyValue)
.valueParser(new ElementValueParser<ElementValue>() {
Expand All @@ -1074,7 +1074,7 @@ public <U extends ElementValue> U parse(ElementValue raw, Class<U> type) {
.build();

Response actual = manager.execute(request, context);
SetSubmodelElementValueByPathResponse expected = new SetSubmodelElementValueByPathResponse.Builder()
PatchSubmodelElementValueByPathResponse expected = new PatchSubmodelElementValueByPathResponse.Builder()
.statusCode(StatusCode.SUCCESS_NO_CONTENT)
.build();
Assert.assertTrue(ResponseHelper.equalsIgnoringTime(expected, actual));
Expand Down
1 change: 1 addition & 0 deletions docs/source/other/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- HTTP
- URL query parameters are now correctly URL-decoded
- Enabled `level` query parameter for calls to /submodels/{submodelIdentifier}/$reference as this is not explicitely forbidden in the specification although the parameter does not have any actual effect
- fixed bug that disabled any HTTP PATCH request to /$value

## 1.2.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
import de.fraunhofer.iosb.ilt.faaast.service.endpoint.http.model.HttpRequest;
import de.fraunhofer.iosb.ilt.faaast.service.endpoint.http.request.mapper.AbstractSubmodelInterfaceRequestMapper;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.modifier.OutputModifier;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.SetSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.SetSubmodelElementValueByPathResponse;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.PatchSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.PatchSubmodelElementValueByPathResponse;
import de.fraunhofer.iosb.ilt.faaast.service.model.exception.InvalidRequestException;
import de.fraunhofer.iosb.ilt.faaast.service.model.exception.PersistenceException;
import de.fraunhofer.iosb.ilt.faaast.service.model.exception.ResourceNotFoundException;
Expand All @@ -37,25 +37,26 @@


/**
* class to map HTTP-PUT-Request path: submodels/{submodelIdentifier}/submodel-elements/{idShortPath},
* class to map HTTP-PATCH-Request path: submodels/{submodelIdentifier}/submodel-elements/{idShortPath},
* shells/{aasIdentifier}/submodels/{submodelIdentifier}/submodel-elements/{idShortPath}.
*/
public class SetSubmodelElementValueByPathRequestMapper
extends AbstractSubmodelInterfaceRequestMapper<SetSubmodelElementValueByPathRequest<?>, SetSubmodelElementValueByPathResponse> {
public class PatchSubmodelElementValueByPathRequestMapper
extends AbstractSubmodelInterfaceRequestMapper<PatchSubmodelElementValueByPathRequest<?>, PatchSubmodelElementValueByPathResponse> {

private static final String SUBMODEL_ELEMENT_PATH = RegExHelper.uniqueGroupName();
private static final String PATTERN = String.format("submodel-elements/%s/\\$value", pathElement(SUBMODEL_ELEMENT_PATH));

public SetSubmodelElementValueByPathRequestMapper(ServiceContext serviceContext) {
public PatchSubmodelElementValueByPathRequestMapper(ServiceContext serviceContext) {
super(serviceContext, HttpMethod.PATCH, PATTERN);
}


@Override
public SetSubmodelElementValueByPathRequest doParse(HttpRequest httpRequest, Map<String, String> urlParameters, OutputModifier outputModifier) throws InvalidRequestException {
public PatchSubmodelElementValueByPathRequest doParse(HttpRequest httpRequest, Map<String, String> urlParameters, OutputModifier outputModifier)
throws InvalidRequestException {
final String path = EncodingHelper.urlDecode(urlParameters.get(SUBMODEL_ELEMENT_PATH));
final String identifier = getParameterBase64UrlEncoded(urlParameters, SUBMODEL_ID);
return SetSubmodelElementValueByPathRequest.builder()
return PatchSubmodelElementValueByPathRequest.builder()
.path(path)
.value(httpRequest.getBodyAsString())
.valueParser(new ElementValueParser<Object>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import de.fraunhofer.iosb.ilt.faaast.service.model.api.modifier.Level;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.modifier.OutputModifier;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.operation.OperationHandle;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.SetSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.PatchSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.DeleteSubmodelReferenceRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.DeleteThumbnailRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.GetAllSubmodelReferencesRequest;
Expand Down Expand Up @@ -1109,8 +1109,8 @@ public void testPutThumbnail() throws SerializationException, InvalidRequestExce


@Test
public void testSetSubmodelElementValueByPathContentNormal() throws Exception {
SetSubmodelElementValueByPathRequest expected = SetSubmodelElementValueByPathRequest.<String> builder()
public void testPatchSubmodelElementValueByPathContentNormal() throws Exception {
PatchSubmodelElementValueByPathRequest expected = PatchSubmodelElementValueByPathRequest.<String> builder()
.submodelId(SUBMODEL.getId())
.path(ReferenceHelper.toPath(SUBMODEL_ELEMENT_REF))
.build();
Expand All @@ -1122,16 +1122,16 @@ public void testSetSubmodelElementValueByPathContentNormal() throws Exception {
+ "/$value")
.body(serializer.write(SUBMODEL_ELEMENT).getBytes())
.build());
SetSubmodelElementValueByPathRequest actual = (SetSubmodelElementValueByPathRequest) temp;
PatchSubmodelElementValueByPathRequest actual = (PatchSubmodelElementValueByPathRequest) temp;
Assert.assertEquals(expected.getSubmodelId(), actual.getSubmodelId());
Assert.assertEquals(expected.getPath(), actual.getPath());
Assert.assertEquals(ElementValueMapper.toValue(SUBMODEL_ELEMENT), actual.getValueParser().parse(actual.getRawValue(), ElementValue.class));
}


@Test
public void testSetSubmodelElementValueByPathContentValue() throws Exception {
SetSubmodelElementValueByPathRequest expected = SetSubmodelElementValueByPathRequest.<String> builder()
public void testPatchSubmodelElementValueByPathContentValue() throws Exception {
PatchSubmodelElementValueByPathRequest expected = PatchSubmodelElementValueByPathRequest.<String> builder()
.submodelId(SUBMODEL.getId())
.path(ReferenceHelper.toPath(SUBMODEL_ELEMENT_REF))
.build();
Expand All @@ -1143,7 +1143,7 @@ public void testSetSubmodelElementValueByPathContentValue() throws Exception {
+ "/$value")
.body(serializer.write(ElementValueMapper.toValue(SUBMODEL_ELEMENT)).getBytes())
.build());
SetSubmodelElementValueByPathRequest actual = (SetSubmodelElementValueByPathRequest) temp;
PatchSubmodelElementValueByPathRequest actual = (PatchSubmodelElementValueByPathRequest) temp;
Assert.assertEquals(expected.getSubmodelId(), actual.getSubmodelId());
Assert.assertEquals(expected.getPath(), actual.getPath());
Assert.assertEquals(ElementValueMapper.toValue(SUBMODEL_ELEMENT), actual.getValueParser().parse(actual.getRawValue(), ElementValue.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import de.fraunhofer.iosb.ilt.faaast.service.messagebus.MessageBus;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.Response;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.StatusCode;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.SetSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.PatchSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.submodel.GetSubmodelElementByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.submodel.InvokeOperationSyncRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.response.submodel.GetSubmodelElementByPathResponse;
Expand Down Expand Up @@ -135,7 +135,7 @@ public boolean writeValue(SubmodelElement element, Submodel submodel, Reference
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("writeValue: Reference {}; Path {}", ReferenceHelper.toString(refElement), path);
}
SetSubmodelElementValueByPathRequest request = new SetSubmodelElementValueByPathRequest();
PatchSubmodelElementValueByPathRequest request = new PatchSubmodelElementValueByPathRequest();

request.setSubmodelId(submodel.getId());
request.setPath(path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package de.fraunhofer.iosb.ilt.faaast.service.model;

import de.fraunhofer.iosb.ilt.faaast.service.model.api.Request;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.PatchSubmodelElementValueByPathRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.DeleteSubmodelReferenceRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.DeleteThumbnailRequest;
import de.fraunhofer.iosb.ilt.faaast.service.model.api.request.aas.GetAllSubmodelReferencesRequest;
Expand Down Expand Up @@ -130,6 +131,7 @@ public enum ServiceSpecificationProfile {
InvokeOperationRequest.class,
InvokeOperationSyncRequest.class,
PatchSubmodelElementByPathRequest.class,
PatchSubmodelElementValueByPathRequest.class,
PatchSubmodelRequest.class,
PostSubmodelElementByPathRequest.class,
PostSubmodelElementRequest.class,
Expand Down Expand Up @@ -184,6 +186,7 @@ public enum ServiceSpecificationProfile {
InvokeOperationRequest.class,
InvokeOperationSyncRequest.class,
PatchSubmodelElementByPathRequest.class,
PatchSubmodelElementValueByPathRequest.class,
PatchSubmodelRequest.class,
PostSubmodelElementByPathRequest.class,
PostSubmodelElementRequest.class,
Expand Down Expand Up @@ -312,6 +315,7 @@ public enum ServiceSpecificationProfile {
InvokeOperationRequest.class,
InvokeOperationSyncRequest.class,
PatchSubmodelElementByPathRequest.class,
PatchSubmodelElementValueByPathRequest.class,
PatchSubmodelRequest.class,
PostSubmodelElementByPathRequest.class,
PostSubmodelElementRequest.class,
Expand Down Expand Up @@ -395,6 +399,7 @@ public enum ServiceSpecificationProfile {
InvokeOperationRequest.class,
InvokeOperationSyncRequest.class,
PatchSubmodelElementByPathRequest.class,
PatchSubmodelElementValueByPathRequest.class,
PatchSubmodelRequest.class,
PostSubmodelElementByPathRequest.class,
PostSubmodelElementRequest.class,
Expand Down
Loading

0 comments on commit 11d326b

Please sign in to comment.