Skip to content

Commit

Permalink
Ensure compatibility with old style parameter objects in SET/ACT
Browse files Browse the repository at this point in the history
The legacy sensiNact northbound REST provider expected parameters to be encapsulated in a JSON object, not a raw array. This should continue to work.

Signed-off-by: Tim Ward <timothyjward@apache.org>
  • Loading branch information
timothyjward committed Apr 24, 2023
1 parent f529ebd commit d3a4f31
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.eclipse.sensinact.northbound.query.api.AbstractQueryDTO;
import org.eclipse.sensinact.northbound.query.api.EQueryType;
import org.eclipse.sensinact.northbound.query.dto.query.jackson.ActParametersDeserializer;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*********************************************************************
* Copyright (c) 2023 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Kentyou - initial implementation
**********************************************************************/
package org.eclipse.sensinact.northbound.query.dto.query;

import java.util.List;

import org.eclipse.sensinact.northbound.query.dto.query.jackson.WrappedAccessMethodCallParametersDeserializer;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize(using = WrappedAccessMethodCallParametersDeserializer.class)
public class WrappedAccessMethodCallParametersDTO {

public List<AccessMethodCallParameterDTO> parameters;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
* Contributors:
* Kentyou - initial implementation
**********************************************************************/
package org.eclipse.sensinact.northbound.query.dto.query;
package org.eclipse.sensinact.northbound.query.dto.query.jackson;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.eclipse.sensinact.northbound.query.dto.query.AccessMethodCallParameterDTO;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*********************************************************************
* Copyright (c) 2023 Contributors to the Eclipse Foundation.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Kentyou - initial implementation
**********************************************************************/
package org.eclipse.sensinact.northbound.query.dto.query.jackson;

import java.io.IOException;
import java.util.List;

import org.eclipse.sensinact.northbound.query.dto.query.AccessMethodCallParameterDTO;
import org.eclipse.sensinact.northbound.query.dto.query.WrappedAccessMethodCallParametersDTO;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

/**
* Handles the deserialization of method parameters
*/
public class WrappedAccessMethodCallParametersDeserializer
extends StdDeserializer<WrappedAccessMethodCallParametersDTO> {

private static final long serialVersionUID = 1L;

public WrappedAccessMethodCallParametersDeserializer() {
this(null);
}

protected WrappedAccessMethodCallParametersDeserializer(final Class<?> vc) {
super(vc);
}

@Override
public WrappedAccessMethodCallParametersDTO deserialize(final JsonParser parser, final DeserializationContext ctxt)
throws IOException, JacksonException {

WrappedAccessMethodCallParametersDTO dto = new WrappedAccessMethodCallParametersDTO();
if (parser.currentToken() == JsonToken.START_OBJECT) {
if ("parameters".equals(parser.nextFieldName())) {
// Look for the array
parser.nextToken();
} else {
throw new JsonMappingException(parser, "Expected the field name to be \"parameters\"",
parser.currentTokenLocation());
}
}
if (parser.currentToken() == JsonToken.START_ARRAY) {
dto.parameters = parser.readValueAs(new TypeReference<List<AccessMethodCallParameterDTO>>() {
});
} else {
throw new JsonMappingException(parser, "Invalid token " + parser.currentToken() + " expected ARRAY_START",
parser.currentTokenLocation());
}
return dto;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.eclipse.sensinact.northbound.query.dto.query.QueryDescribeDTO;
import org.eclipse.sensinact.northbound.query.dto.query.QueryGetDTO;
import org.eclipse.sensinact.northbound.query.dto.query.QuerySetDTO;
import org.eclipse.sensinact.northbound.query.dto.query.WrappedAccessMethodCallParametersDTO;
import org.eclipse.sensinact.northbound.query.dto.result.AccessMethodDTO;
import org.eclipse.sensinact.northbound.query.dto.result.AccessMethodParameterDTO;
import org.eclipse.sensinact.northbound.query.dto.result.CompleteProviderDescriptionDTO;
Expand All @@ -47,6 +48,7 @@
import org.eclipse.sensinact.prototype.model.ResourceType;
import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
Expand Down Expand Up @@ -440,4 +442,33 @@ void testSet() throws Exception {
assertEquals(setRc.response.type, parsedResult.response.type);
assertEquals(setRc.response.value, parsedResult.response.value);
}

@Test
void testWrappedAccessMethodCallParametesrDTO() throws JsonProcessingException {

final AccessMethodCallParameterDTO arg1 = new AccessMethodCallParameterDTO();
arg1.name = "arg1";
arg1.type = Integer.class.getName();
arg1.value = 42;
final AccessMethodCallParameterDTO arg2 = new AccessMethodCallParameterDTO();
arg2.name = "arg2";
arg2.type = String.class.getName();
arg2.value = "abc";

List<AccessMethodCallParameterDTO> parameters = List.of(arg1, arg2);

final WrappedAccessMethodCallParametersDTO dto = new WrappedAccessMethodCallParametersDTO();
dto.parameters = parameters;

String s = mapper.writeValueAsString(dto);

WrappedAccessMethodCallParametersDTO read = mapper.readValue(s, WrappedAccessMethodCallParametersDTO.class);

assertEquals(dto.parameters.get(0).name, read.parameters.get(0).name);

s = mapper.writeValueAsString(parameters);
WrappedAccessMethodCallParametersDTO read2 = mapper.readValue(s, WrappedAccessMethodCallParametersDTO.class);
assertEquals(dto.parameters.get(0).name, read2.parameters.get(0).name);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@

import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON;

import java.util.List;

import org.eclipse.sensinact.northbound.query.api.AbstractResultDTO;
import org.eclipse.sensinact.northbound.query.dto.query.AccessMethodCallParameterDTO;
import org.eclipse.sensinact.northbound.query.dto.query.WrappedAccessMethodCallParametersDTO;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
Expand Down Expand Up @@ -72,13 +70,13 @@ AbstractResultDTO resourceGet(@PathParam("providerId") String providerId,
@POST
AbstractResultDTO resourceSet(@PathParam("providerId") String providerId,
@PathParam("serviceName") String serviceName, @PathParam("rcName") String rcName,
List<AccessMethodCallParameterDTO> parameters);
WrappedAccessMethodCallParametersDTO parameters);

@Path("providers/{providerId}/services/{serviceName}/resources/{rcName}/ACT")
@POST
AbstractResultDTO resourceAct(@PathParam("providerId") String providerId,
@PathParam("serviceName") String serviceName, @PathParam("rcName") String rcName,
List<AccessMethodCallParameterDTO> parameters);
WrappedAccessMethodCallParametersDTO parameters);

@Path("providers/{providerId}/services/{serviceName}/resources/{rcName}/SUBSCRIBE")
@GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.eclipse.sensinact.northbound.query.dto.query.QueryGetDTO;
import org.eclipse.sensinact.northbound.query.dto.query.QueryListDTO;
import org.eclipse.sensinact.northbound.query.dto.query.QuerySetDTO;
import org.eclipse.sensinact.northbound.query.dto.query.WrappedAccessMethodCallParametersDTO;
import org.eclipse.sensinact.northbound.rest.api.IRestNorthbound;
import org.eclipse.sensinact.prototype.SensiNactSession;
import org.eclipse.sensinact.prototype.notification.ClientDataListener;
Expand Down Expand Up @@ -222,11 +223,11 @@ private Object extractSetValue(final List<AccessMethodCallParameterDTO> paramete

@Override
public AbstractResultDTO resourceSet(final String providerId, final String serviceName, final String rcName,
final List<AccessMethodCallParameterDTO> parameters) {
final WrappedAccessMethodCallParametersDTO parameters) {

final QuerySetDTO query = new QuerySetDTO();
query.uri = new SensinactPath(providerId, serviceName, rcName);
query.value = extractSetValue(parameters);
query.value = extractSetValue(parameters.parameters);
return handleQuery(query);
}

Expand Down Expand Up @@ -267,14 +268,14 @@ private Map<String, Object> extractActParams(final List<Entry<String, Class<?>>>

@Override
public AbstractResultDTO resourceAct(final String providerId, final String serviceName, final String rcName,
final List<AccessMethodCallParameterDTO> parameters) {
final WrappedAccessMethodCallParametersDTO parameters) {

final List<Entry<String, Class<?>>> actMethodArgumentsTypes = getSession().describeResourceShort(providerId,
serviceName, rcName).actMethodArgumentsTypes;

final QueryActDTO query = new QueryActDTO();
query.uri = new SensinactPath(providerId, serviceName, rcName);
query.parameters = extractActParams(actMethodArgumentsTypes, parameters);
query.parameters = extractActParams(actMethodArgumentsTypes, parameters.parameters);
return handleQuery(query);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.eclipse.sensinact.gateway.geojson.GeoJsonObject;
import org.eclipse.sensinact.northbound.query.api.EResultType;
import org.eclipse.sensinact.northbound.query.dto.query.AccessMethodCallParameterDTO;
import org.eclipse.sensinact.northbound.query.dto.query.WrappedAccessMethodCallParametersDTO;
import org.eclipse.sensinact.northbound.query.dto.result.ResponseGetDTO;
import org.eclipse.sensinact.northbound.query.dto.result.ResultActDTO;
import org.eclipse.sensinact.northbound.query.dto.result.TypedResponse;
Expand All @@ -45,6 +46,8 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.opentest4j.AssertionFailedError;
import org.osgi.framework.BundleContext;
import org.osgi.service.cm.Configuration;
Expand Down Expand Up @@ -174,8 +177,9 @@ void resourceUpdate() throws Exception {
/**
* Update the resource value from the REST endpoint
*/
@Test
void resourceSet() throws Exception {
@ParameterizedTest
@ValueSource(booleans = { true, false })
void resourceSet(boolean wrapParams) throws Exception {
// Register the resource
GenericDto dto = utils.makeDto(PROVIDER, SERVICE, RESOURCE, VALUE, Integer.class);
Instant firstUpdateTime = Instant.now().truncatedTo(ChronoUnit.MILLIS);
Expand All @@ -202,7 +206,7 @@ void resourceSet() throws Exception {
param.value = VALUE_2;
result = utils.queryJson(
String.join("/", "providers", PROVIDER, "services", SERVICE, "resources", RESOURCE, "SET"),
List.of(param), TypedResponse.class);
wrapParams(wrapParams, List.of(param)), TypedResponse.class);
utils.assertResultSuccess(result, EResultType.SET_RESPONSE, PROVIDER, SERVICE, RESOURCE);
response = utils.convert(result, ResponseGetDTO.class);
assertEquals(RESOURCE, response.name);
Expand All @@ -222,6 +226,15 @@ void resourceSet() throws Exception {
assertTrue(firstTimestamp.isBefore(Instant.ofEpochMilli(response.timestamp)), "Timestamp wasn't updated");
}

private Object wrapParams(boolean wrap, List<AccessMethodCallParameterDTO> params) {
if (wrap) {
WrappedAccessMethodCallParametersDTO dto = new WrappedAccessMethodCallParametersDTO();
dto.parameters = params;
return dto;
}
return params;
}

/**
* Get a resource value from the admin service
*/
Expand Down Expand Up @@ -257,8 +270,9 @@ public Double toDoubleDouble(@ActParam(name = "input") Long longValue) {
}
}

@Test
void resourceAct(@InjectBundleContext BundleContext context) throws Exception {
@ParameterizedTest
@ValueSource(booleans = { true, false })
void resourceAct(boolean wrapParams, @InjectBundleContext BundleContext context) throws Exception {

context.registerService(TestAction.class, new TestAction(),
new Hashtable<>(Map.of("sensiNact.whiteboard.resource", true)));
Expand All @@ -273,7 +287,7 @@ void resourceAct(@InjectBundleContext BundleContext context) throws Exception {

ResultActDTO response = utils.queryJson(
String.join("/", "providers", PROVIDER, "services", SERVICE, "resources", "action", "ACT"),
List.of(param), ResultActDTO.class);
wrapParams(wrapParams, List.of(param)), ResultActDTO.class);

assertNotNull(response);
assertEquals(200, response.statusCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

import java.io.IOException;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
Expand Down

0 comments on commit d3a4f31

Please sign in to comment.