Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support of Partial Update to WRITE on multi-instance resources #1037

Merged
merged 1 commit into from
Jul 1, 2021
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 @@ -289,15 +289,20 @@ public void handlePOST(CoapExchange exchange) {

// Manage Execute Request
if (path.isResource()) {
byte[] payload = exchange.getRequestPayload();
ExecuteResponse response = nodeEnabler.execute(identity,
new ExecuteRequest(URI, payload != null ? new String(payload) : null, coapRequest));
if (response.getCode().isError()) {
exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
} else {
exchange.respond(toCoapResponseCode(response.getCode()));
// execute request has no content format at all or a TEXT concent format for parameters.
if (!exchange.getRequestOptions().hasContentFormat()
|| ContentFormat.fromCode(exchange.getRequestOptions().getContentFormat()) == ContentFormat.TEXT) {
byte[] payload = exchange.getRequestPayload();

ExecuteResponse response = nodeEnabler.execute(identity,
new ExecuteRequest(URI, payload != null ? new String(payload) : null, coapRequest));
if (response.getCode().isError()) {
exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
} else {
exchange.respond(toCoapResponseCode(response.getCode()));
}
return;
}
return;
}

// handle content format for Write (Update) and Create request
Expand All @@ -313,6 +318,22 @@ public void handlePOST(CoapExchange exchange) {
}
LwM2mModel model = new StaticModel(nodeEnabler.getObjectModel());

// manage partial update of multi-instance resource
if (path.isResource()) {
try {
LwM2mNode lwM2mNode = decoder.decode(exchange.getRequestPayload(), contentFormat, path, model);
WriteResponse response = nodeEnabler.write(identity,
new WriteRequest(Mode.UPDATE, contentFormat, URI, lwM2mNode, coapRequest));
if (response.getCode().isError()) {
exchange.respond(toCoapResponseCode(response.getCode()), response.getErrorMessage());
} else {
exchange.respond(toCoapResponseCode(response.getCode()));
}
} catch (CodecException e) {
handleInvalidRequest(exchange.advanced(), "Unable to decode payload on WRITE", e);
}
return;
}
// Manage Update Instance
if (path.isObjectInstance()) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,8 @@ protected WriteResponse doWrite(ServerIdentity identity, WriteRequest request) {

// Manage Resource case
if (path.getResourceInstanceId() == null) {
return instance.write(identity, true, path.getResourceId(), (LwM2mResource) request.getNode());
return instance.write(identity, request.isReplaceRequest(), path.getResourceId(),
(LwM2mResource) request.getNode());
}

// Manage Resource Instance case
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,23 +299,25 @@ public WriteRequest(int objectId, int objectInstanceId, int resourceId, Map<Inte
}

/**
* Request to write a specific resource instance from a client.
* Request to write a <b>Multi-Instance Resource</b> using the TLV content format.
*
* @param mode the mode of the request : replace or update.
* @param contentFormat Format of the payload (TLV or JSON).
* @param objectId the object ID of the resource
* @param objectInstanceId the object instance ID
* @param resourceId the (individual) resource's ID
* @param resourceInstanceId the resource instance's ID
* @param value the resource instance (id-&gt;value) to write.
* @param objectId the id of the object to write.
* @param objectInstanceId the id of the object instance to write.
* @param resourceId the id of the resource to write.
* @param values the list of resource instance (id-&gt;value) to write.
* @param type the data type of the resource.
* @exception InvalidRequestException if parameters are invalid.
*/
public WriteRequest(Mode mode, ContentFormat contentFormat, int objectId, int objectInstanceId, int resourceId,
int resourceInstanceId, Object value, Type type) {
this(mode, contentFormat, new LwM2mPath(objectId, objectInstanceId, resourceId, resourceInstanceId),
LwM2mResourceInstance.newInstance(resourceInstanceId, value, type), null);
Map<Integer, ?> values, Type type) throws InvalidRequestException {
this(mode, ContentFormat.TLV, new LwM2mPath(objectId, objectInstanceId, resourceId),
LwM2mMultipleResource.newResource(resourceId, values, type), null);
}

// ***************** write resource instance****************** //

/**
* Request to write a specific resource instance from a client, MODE = REPLACE.
*
Expand Down Expand Up @@ -387,7 +389,8 @@ public WriteRequest(Mode mode, ContentFormat format, LwM2mPath target, LwM2mNode
throw new InvalidRequestException("new node value is mandatory for %s", target);

// Validate Mode
if (getPath().isResource() && mode == Mode.UPDATE)
if (getPath().isResource() && mode == Mode.UPDATE
&& (node instanceof LwM2mSingleResource || node instanceof LwM2mResourceInstance))
throw new InvalidRequestException("Invalid mode for '%s': update is not allowed on resource", target);

// Validate node and path coherence
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,15 @@ public void can_write_multi_instance_objlnk_resource_in_tlv() throws Interrupted
@Test
public void can_write_object_resource_instance() throws InterruptedException {

// --------------------------------
// write (replace) multi instance
// --------------------------------
Map<Integer, String> values = new HashMap<>();
values.put(10, "value10");
values.put(20, "value20");

// write multi instance
WriteResponse response = helper.server.send(helper.getCurrentRegistration(),
new WriteRequest(contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
new WriteRequest(Mode.REPLACE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, values, Type.STRING));

// Verify Write result
Expand All @@ -239,6 +241,59 @@ public void can_write_object_resource_instance() throws InterruptedException {
LwM2mMultipleResource resourceInstance = (LwM2mMultipleResource) readResponse.getContent();
assertEquals("value10", resourceInstance.getInstance(10).getValue());
assertEquals("value20", resourceInstance.getInstance(20).getValue());
}

// --------------------------------
// write (update) multi instance
// --------------------------------
Map<Integer, String> newValues = new HashMap<>();
newValues.put(20, "value200");
newValues.put(30, "value30");
response = helper.server.send(helper.getCurrentRegistration(),
new WriteRequest(Mode.UPDATE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, newValues, Type.STRING));

// Verify Write result
assertEquals(ResponseCode.CHANGED, response.getCode());
assertNotNull(response.getCoapResponse());
assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));

// read multi instance
readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(contentFormat,
IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID));

// verify result
assertEquals(CONTENT, readResponse.getCode());
assertContentFormat(contentFormat, readResponse);

resourceInstance = (LwM2mMultipleResource) readResponse.getContent();
assertEquals("value200", resourceInstance.getInstance(20).getValue());
assertEquals("value30", resourceInstance.getInstance(30).getValue());
assertEquals("value10", resourceInstance.getInstance(10).getValue());

// --------------------------------
// write (replace) multi instance
// --------------------------------
newValues = new HashMap<>();
newValues.put(1, "value1");
response = helper.server.send(helper.getCurrentRegistration(),
new WriteRequest(Mode.REPLACE, contentFormat, IntegrationTestHelper.TEST_OBJECT_ID, 0,
IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID, newValues, Type.STRING));

// Verify Write result
assertEquals(ResponseCode.CHANGED, response.getCode());
assertNotNull(response.getCoapResponse());
assertThat(response.getCoapResponse(), is(instanceOf(Response.class)));

// read multi instance
readResponse = helper.server.send(helper.getCurrentRegistration(), new ReadRequest(contentFormat,
IntegrationTestHelper.TEST_OBJECT_ID, 0, IntegrationTestHelper.STRING_RESOURCE_INSTANCE_ID));

// verify result
assertEquals(CONTENT, readResponse.getCode());
assertContentFormat(contentFormat, readResponse);

resourceInstance = (LwM2mMultipleResource) readResponse.getContent();
assertEquals(1, resourceInstance.getInstances().size());
assertEquals("value1", resourceInstance.getInstance(1).getValue());
}
}