From 1f06ab778ee0d1ff2f64c41844542c1a9d5a641d Mon Sep 17 00:00:00 2001 From: Mateus Molina Date: Wed, 28 Aug 2024 13:35:23 +0200 Subject: [PATCH] Fix inoutvariables not being processed in SubmodelService/Repository Controllers :: invokeOperation (#396) * Fix inoutvariables not being processed in controller invokeOperation(...) * Apply the fix for the SubmodelRepositoryApiHttpController --- .../SubmodelRepositoryApiHTTPController.java | 22 ++++++++++++++---- .../SubmodelServiceHelper.java | 18 ++++++++++++++- .../SubmodelServiceHTTPApiController.java | 23 +++++++++++++++---- ...lServiceSubmodelElementsTestSuiteHTTP.java | 11 +++++++++ .../src/test/resources/Submodel.json | 22 ++++++++++++++++++ .../src/test/resources/SubmodelElements.json | 22 ++++++++++++++++++ .../resources/operation/parameters-inout.json | 22 ++++++++++++++++++ .../resources/operation/result-inout.json | 12 ++++++++++ 8 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 basyx.submodelservice/basyx.submodelservice-http/src/test/resources/operation/parameters-inout.json create mode 100644 basyx.submodelservice/basyx.submodelservice-http/src/test/resources/operation/result-inout.json diff --git a/basyx.submodelrepository/basyx.submodelrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/http/SubmodelRepositoryApiHTTPController.java b/basyx.submodelrepository/basyx.submodelrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/http/SubmodelRepositoryApiHTTPController.java index 3d111664d..c6039bdb5 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/http/SubmodelRepositoryApiHTTPController.java +++ b/basyx.submodelrepository/basyx.submodelrepository-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/http/SubmodelRepositoryApiHTTPController.java @@ -272,14 +272,28 @@ private ResponseEntity handleSubmodelElementValueNormalGetReque @Override public ResponseEntity invokeOperationSubmodelRepo(Base64UrlEncodedIdentifier submodelIdentifier, String idShortPath, @Valid OperationRequest body, @Valid Boolean async) { - OperationVariable[] result = repository.invokeOperation(submodelIdentifier.getIdentifier(), idShortPath, body.getInputArguments().toArray(new OperationVariable[0])); + List inVars = new ArrayList<>(); + inVars.addAll(body.getInputArguments()); + inVars.addAll(body.getInoutputArguments()); - return new ResponseEntity(createOperationResult(result), HttpStatus.OK); + List result = Arrays.asList(repository.invokeOperation(submodelIdentifier.getIdentifier(), idShortPath, inVars.toArray(new OperationVariable[0]))); + List outVars = new ArrayList<>(result); + List inoutputVars = new ArrayList<>(); + + if (!body.getInoutputArguments().isEmpty()) { + List inoutputVarsIdShorts = body.getInoutputArguments().stream().map(OperationVariable::getValue).map(SubmodelElement::getIdShort).toList(); + + inoutputVars = result.stream().filter(opVar -> inoutputVarsIdShorts.contains(opVar.getValue().getIdShort())).toList(); + + outVars.removeAll(inoutputVars); + } + + return ResponseEntity.ok(createOperationResult(outVars, inoutputVars)); } - private OperationResult createOperationResult(OperationVariable[] result) { - return new DefaultOperationResult.Builder().outputArguments(Arrays.asList(result)).build(); + private OperationResult createOperationResult(List outputVars, List inoutputVars) { + return new DefaultOperationResult.Builder().outputArguments(outputVars).inoutputArguments(inoutputVars).build(); } private String getEncodedCursorFromCursorResult(CursorResult cursorResult) { diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceHelper.java b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceHelper.java index ab3bcae87..8c6ef3f31 100644 --- a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceHelper.java +++ b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceHelper.java @@ -173,6 +173,7 @@ public class SubmodelServiceHelper { public static final String SUBMODEL_TECHNICAL_DATA_SUBMODEL_ELEMENT_LIST_CATEGORY = "PARAMETER"; public static final String SUBMODEL_TECHNICAL_DATA_OPERATION_ID = "square"; + public static final String SUBMODEL_TECHNICAL_DATA_OPERATIONINOUT_ID = "sum"; public static SubmodelElement getDummySubmodelElement(Submodel technicalData, String idShort) { return technicalData.getSubmodelElements() @@ -333,7 +334,7 @@ public static List getAllSubmodelElements() { Lists.newArrayList(createPropertySubmodelElement(), createRangeSubmodelElement(), createMultiLanguagePropertySubmodelElement(), createFileSubmodelElement(), createEntitySubmodelElement(), createReferenceElementSubmodelElement(), createRelationshipElementSubmodelElement(), createAnnotatedRelationshipElementSubmodelElement(), createBlobSubmodelElement(), createSubmodelElementCollection(), createSubmodelElementList(), - createInvokableOperation())); + createInvokableOperation(), createInvokableInOutOperation())); return list; } @@ -370,6 +371,11 @@ private static Operation createInvokableOperation() { .invokable(SubmodelServiceHelper::square).build(); } + private static Operation createInvokableInOutOperation() { + return new InvokableOperation.Builder().idShort(SUBMODEL_TECHNICAL_DATA_OPERATIONINOUT_ID).inputVariables(createIntOperationVariable("input")).inoutputVariables(createIntOperationVariable("stack")) + .invokable(SubmodelServiceHelper::sum).build(); + } + private static Operation createOperation() { return new DefaultOperation.Builder().idShort(SUBMODEL_TECHNICAL_DATA_OPERATION_ID).inputVariables(createIntOperationVariable("input")).outputVariables(createIntOperationVariable("result")) .build(); @@ -385,6 +391,16 @@ private static OperationVariable[] square(OperationVariable[] inputs) { return new OperationVariable[] { createOperationVariable(in) }; } + private static OperationVariable[] sum(OperationVariable[] inputs) { + Property in = (Property) inputs[0].getValue(); + Property stack = (Property) inputs[1].getValue(); + Integer inVal = Integer.valueOf(in.getValue()); + Integer stackVal = Integer.valueOf(stack.getValue()); + Integer sumResult = inVal + stackVal; + stack.setValue(sumResult.toString()); + return new OperationVariable[] { createOperationVariable(stack) }; + } + private static DefaultOperationVariable createIntOperationVariable(String idShort) { return new DefaultOperationVariable.Builder().value(new DefaultProperty.Builder().idShort(idShort).valueType(DataTypeDefXsd.INT).build()).build(); } diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApiController.java b/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApiController.java index 2d309dfe9..7248c6904 100644 --- a/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApiController.java +++ b/basyx.submodelservice/basyx.submodelservice-http/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceHTTPApiController.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -236,15 +237,29 @@ public ResponseEntity patchSubmodelValueOnly(@Parameter(in = ParameterIn.D public ResponseEntity invokeOperation( @Parameter(in = ParameterIn.PATH, description = "IdShort path to the submodel element (dot-separated)", required = true, schema = @Schema()) @PathVariable("idShortPath") String idShortPath, @Parameter(in = ParameterIn.DEFAULT, description = "Operation request object", required = true, schema = @Schema()) @Valid @RequestBody OperationRequest body) { - OperationVariable[] result = service.invokeOperation(idShortPath, body.getInputArguments().toArray(new OperationVariable[0])); + List inVars = new ArrayList<>(); + inVars.addAll(body.getInputArguments()); + inVars.addAll(body.getInoutputArguments()); - return new ResponseEntity(createOperationResult(result), HttpStatus.OK); + List result = Arrays.asList(service.invokeOperation(idShortPath, inVars.toArray(new OperationVariable[0]))); + List outVars = new ArrayList<>(result); + List inoutputVars = new ArrayList<>(); + + if (!body.getInoutputArguments().isEmpty()) { + List inoutputVarsIdShorts = body.getInoutputArguments().stream().map(OperationVariable::getValue).map(SubmodelElement::getIdShort).toList(); + + inoutputVars = result.stream().filter(opVar -> inoutputVarsIdShorts.contains(opVar.getValue().getIdShort())).toList(); + + outVars.removeAll(inoutputVars); + } + + return ResponseEntity.ok(createOperationResult(outVars, inoutputVars)); } - private OperationResult createOperationResult(OperationVariable[] result) { + private OperationResult createOperationResult(List outputVars, List inoutputVars) { return new DefaultOperationResult.Builder() - .outputArguments(Arrays.asList(result)) + .outputArguments(outputVars).inoutputArguments(inoutputVars) .build(); } diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceSubmodelElementsTestSuiteHTTP.java b/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceSubmodelElementsTestSuiteHTTP.java index 23bcd0ec9..f8c873a40 100644 --- a/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceSubmodelElementsTestSuiteHTTP.java +++ b/basyx.submodelservice/basyx.submodelservice-http/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/http/SubmodelServiceSubmodelElementsTestSuiteHTTP.java @@ -518,6 +518,17 @@ public void invokeOperation() throws FileNotFoundException, IOException, ParseEx } + @Test + public void invokeInOutOperation() throws IOException, ParseException { + String parameters = getJSONValueAsString("operation/parameters-inout.json"); + CloseableHttpResponse response = requestOperationInvocation(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_OPERATIONINOUT_ID, parameters); + + assertEquals(HttpStatus.OK.value(), response.getCode()); + String expectedValue = getJSONValueAsString("operation/result-inout.json"); + BaSyxHttpTestUtils.assertSameJSONContent(expectedValue, BaSyxHttpTestUtils.getResponseAsString(response)); + + } + @Test public void updateFileSMEWithNonFileSME() throws FileNotFoundException, IOException, ParseException { String element = getJSONValueAsString("PropertySubmodelElementUpdateWithNewIdShort.json"); diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/Submodel.json b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/Submodel.json index c83974bcb..b21604a9f 100644 --- a/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/Submodel.json +++ b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/Submodel.json @@ -392,6 +392,28 @@ } ] }, + { + "modelType": "Operation", + "idShort": "sum", + "inputVariables": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "idShort": "input" + } + } + ], + "inoutputVariables": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "idShort": "stack" + } + } + ] + }, { "modelType": "Property", "value": "4370", diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/SubmodelElements.json b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/SubmodelElements.json index 1efab173e..de79867e1 100644 --- a/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/SubmodelElements.json +++ b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/SubmodelElements.json @@ -438,6 +438,28 @@ } } ] + }, + { + "modelType": "Operation", + "idShort": "sum", + "inputVariables": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "idShort": "input" + } + } + ], + "inoutputVariables": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "idShort": "stack" + } + } + ] } ] } \ No newline at end of file diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/operation/parameters-inout.json b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/operation/parameters-inout.json new file mode 100644 index 000000000..98ce64fa5 --- /dev/null +++ b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/operation/parameters-inout.json @@ -0,0 +1,22 @@ +{ + "inputArguments": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "value": "5", + "idShort": "input" + } + } + ], + "inoutputArguments": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "idShort": "stack", + "value": "3" + } + } + ] +} \ No newline at end of file diff --git a/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/operation/result-inout.json b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/operation/result-inout.json new file mode 100644 index 000000000..3aa563302 --- /dev/null +++ b/basyx.submodelservice/basyx.submodelservice-http/src/test/resources/operation/result-inout.json @@ -0,0 +1,12 @@ +{ + "inoutputArguments": [ + { + "value": { + "modelType": "Property", + "valueType": "xs:int", + "idShort": "stack", + "value": "8" + } + } + ] +} \ No newline at end of file