Jian Li
Committed by Gerrit Code Review

[ONOS-4016] Implement Region administration REST API

- Impelent Region management REST API
- Add unit test for Region management REST API
- Add swagger docs for Region management REST API
- Add SCR Component and Service annotation for RegionManager

Change-Id: I042e92ed7144d596659b779a59239afba832ca62
......@@ -17,9 +17,11 @@
package org.onosproject.net.region.impl;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onosproject.cluster.NodeId;
import org.onosproject.event.AbstractListenerManager;
import org.onosproject.net.DeviceId;
......@@ -45,6 +47,8 @@ import static org.slf4j.LoggerFactory.getLogger;
/**
* Provides implementation of the region service APIs.
*/
@Component(immediate = true)
@Service
public class RegionManager extends AbstractListenerManager<RegionEvent, RegionListener>
implements RegionAdminService, RegionService {
......
......@@ -46,7 +46,8 @@ public class CoreWebApplication extends AbstractWebApplication {
MetricsWebResource.class,
FlowObjectiveWebResource.class,
MulticastRouteWebResource.class,
DeviceKeyWebResource.class
DeviceKeyWebResource.class,
RegionsWebResource.class
);
}
}
......
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.rest.resources;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Sets;
import org.onosproject.net.DeviceId;
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionAdminService;
import org.onosproject.net.region.RegionId;
import org.onosproject.net.region.RegionService;
import org.onosproject.rest.AbstractWebResource;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Set;
import static org.onlab.util.Tools.nullIsNotFound;
/**
* Manages region and device membership.
*/
@Path("regions")
public class RegionsWebResource extends AbstractWebResource {
private final RegionService regionService = get(RegionService.class);
private final RegionAdminService regionAdminService = get(RegionAdminService.class);
private static final String REGION_NOT_FOUND = "Region is not found for ";
private static final String REGION_INVALID = "Invalid regionId in region update request";
private static final String DEVICE_IDS_INVALID = "Invalid device identifiers";
/**
* Returns set of all regions.
*
* @return 200 OK
* @onos.rsModel Regions
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getRegions() {
final Iterable<Region> regions = regionService.getRegions();
return ok(encodeArray(Region.class, "regions", regions)).build();
}
/**
* Returns the region with the specified identifier.
*
* @param regionId region identifier
* @return 200 OK, 404 not found
* @onos.rsModel Region
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{regionId}")
public Response getRegionById(@PathParam("regionId") String regionId) {
final RegionId rid = RegionId.regionId(regionId);
final Region region = nullIsNotFound(regionService.getRegion(rid),
REGION_NOT_FOUND + rid.toString());
return ok(codec(Region.class).encode(region, this)).build();
}
/**
* Returns the set of devices that belong to the specified region.
*
* @param regionId region identifier
* @return 200 OK
* @onos.rsModel RegionDeviceIds
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("{regionId}/devices")
public Response getRegionDevices(@PathParam("regionId") String regionId) {
final RegionId rid = RegionId.regionId(regionId);
final Iterable<DeviceId> deviceIds = regionService.getRegionDevices(rid);
final ObjectNode root = mapper().createObjectNode();
final ArrayNode deviceIdsNode = root.putArray("deviceIds");
deviceIds.forEach(did -> deviceIdsNode.add(did.toString()));
return ok(root).build();
}
/**
* Creates a new region using the supplied JSON input stream.
*
* @param stream region JSON stream
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel RegionPost
*/
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createRegion(InputStream stream) {
URI location;
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
final Region region = codec(Region.class).decode(jsonTree, this);
final Region resultRegion = regionAdminService.createRegion(region.id(),
region.name(), region.type(), region.masters());
location = new URI(resultRegion.id().id());
} catch (IOException | URISyntaxException e) {
throw new IllegalArgumentException(e);
}
return Response.created(location).build();
}
/**
* Updates the specified region using the supplied JSON input stream.
*
* @param regionId region identifier
* @param stream region JSON stream
* @return status of the request - UPDATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel RegionPost
*/
@PUT
@Path("{regionId}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateRegion(@PathParam("regionId") String regionId,
InputStream stream) {
try {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
JsonNode specifiedRegionId = jsonTree.get("id");
if (specifiedRegionId != null &&
!specifiedRegionId.asText().equals(regionId)) {
throw new IllegalArgumentException(REGION_INVALID);
}
final Region region = codec(Region.class).decode(jsonTree, this);
regionAdminService.updateRegion(region.id(),
region.name(), region.type(), region.masters());
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return Response.ok().build();
}
/**
* Removes the specified region using the given region identifier.
*
* @param regionId region identifier
* @return 200 OK, 404 not found
*/
@DELETE
@Path("{regionId}")
@Produces(MediaType.APPLICATION_JSON)
public Response removeRegion(@PathParam("regionId") String regionId) {
final RegionId rid = RegionId.regionId(regionId);
regionAdminService.removeRegion(rid);
return Response.ok().build();
}
/**
* Adds the specified collection of devices to the region.
*
* @param regionId region identifier
* @param stream deviceIds JSON stream
* @return status of the request - CREATED if the JSON is correct,
* BAD_REQUEST if the JSON is invalid
* @onos.rsModel RegionDeviceIds
*/
@POST
@Path("{regionId}/devices")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response addDevices(@PathParam("regionId") String regionId,
InputStream stream) {
final RegionId rid = RegionId.regionId(regionId);
URI location;
try {
regionAdminService.addDevices(rid, extractDeviceIds(stream));
location = new URI(rid.id());
} catch (IOException | URISyntaxException e) {
throw new IllegalArgumentException(e);
}
return Response.created(location).build();
}
/**
* Removes the specified collection of devices from the region.
*
* @param regionId region identifier
* @param stream deviceIds JSON stream
* @return 200 OK, 404 not found
* @onos.rsModel RegionDeviceIds
*/
@DELETE
@Path("{regionId}/devices")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response removeDevices(@PathParam("regionId") String regionId,
InputStream stream) {
final RegionId rid = RegionId.regionId(regionId);
try {
regionAdminService.removeDevices(rid, extractDeviceIds(stream));
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return Response.ok().build();
}
/**
* Extracts device ids from a given JSON string.
*
* @param stream deviceIds JSON stream
* @return a set of device identifiers
* @throws IOException
*/
private Set<DeviceId> extractDeviceIds(InputStream stream) throws IOException {
ObjectNode jsonTree = (ObjectNode) mapper().readTree(stream);
JsonNode deviceIdsJson = jsonTree.get("deviceIds");
if (deviceIdsJson == null || deviceIdsJson.size() == 0) {
throw new IllegalArgumentException(DEVICE_IDS_INVALID);
}
Set<DeviceId> deviceIds = Sets.newHashSet();
deviceIdsJson.forEach(did -> deviceIds.add(DeviceId.deviceId(did.asText())));
return deviceIds;
}
}
{
"type": "object",
"title": "region",
"required": [
"id",
"name",
"type",
"masters"
],
"properties": {
"id": {
"type": "string",
"example": "1"
},
"name": {
"type": "string",
"example": "region"
},
"type": {
"type": "string",
"example": "ROOM"
},
"masters": {
"type": "array",
"xml": {
"name": "masters",
"wrapped": true
},
"items": {
"type": "array",
"xml": {
"name": "masters",
"wrapped": true
},
"items": {
"type": "string",
"example": "1"
}
}
}
}
}
\ No newline at end of file
{
"type": "object",
"title": "deviceIds",
"required": [
"deviceIds"
],
"properties": {
"deviceIds": {
"type": "array",
"xml": {
"name": "deviceIds",
"wrapped": true
},
"items": {
"type": "string",
"example": "of:0000000000000001"
}
}
}
}
\ No newline at end of file
{
"type": "object",
"title": "region",
"required": [
"id",
"name",
"type",
"masters"
],
"properties": {
"id": {
"type": "string",
"example": "1"
},
"name": {
"type": "string",
"example": "region"
},
"type": {
"type": "string",
"example": "ROOM"
},
"masters": {
"type": "array",
"xml": {
"name": "masters",
"wrapped": true
},
"items": {
"type": "array",
"xml": {
"name": "masters",
"wrapped": true
},
"items": {
"type": "string",
"example": "1"
}
}
}
}
}
{
"type": "object",
"title": "regions",
"required": [
"regions"
],
"properties": {
"regions": {
"type": "array",
"xml": {
"name": "regions",
"wrapped": true
},
"items": {
"type": "object",
"title": "region",
"required": [
"id",
"name",
"type",
"masters"
],
"properties": {
"id": {
"type": "string",
"example": "1"
},
"name": {
"type": "string",
"example": "region"
},
"type": {
"type": "string",
"example": "ROOM"
},
"masters": {
"type": "array",
"xml": {
"name": "masters",
"wrapped": true
},
"items": {
"type": "array",
"xml": {
"name": "masters",
"wrapped": true
},
"items": {
"type": "string",
"example": "1"
}
}
}
}
}
}
}
}
\ No newline at end of file
/*
* Copyright 2016 Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.rest;
import com.eclipsesource.json.Json;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.Test;
import org.onlab.osgi.ServiceDirectory;
import org.onlab.osgi.TestServiceDirectory;
import org.onlab.rest.BaseResource;
import org.onosproject.cluster.NodeId;
import org.onosproject.codec.CodecService;
import org.onosproject.codec.impl.CodecManager;
import org.onosproject.net.DeviceId;
import org.onosproject.net.region.Region;
import org.onosproject.net.region.RegionAdminService;
import org.onosproject.net.region.RegionId;
import org.onosproject.net.region.RegionService;
import org.onosproject.rest.resources.CoreWebApplication;
import javax.ws.rs.core.MediaType;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Set;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
/**
* Unit tests for region REST APIs.
*/
public class RegionsResourceTest extends ResourceTest {
final RegionService mockRegionService = createMock(RegionService.class);
final RegionAdminService mockRegionAdminService = createMock(RegionAdminService.class);
final RegionId regionId1 = RegionId.regionId("1");
final RegionId regionId2 = RegionId.regionId("2");
final RegionId regionId3 = RegionId.regionId("3");
final MockRegion region1 = new MockRegion(regionId1, "r1", Region.Type.RACK);
final MockRegion region2 = new MockRegion(regionId2, "r2", Region.Type.ROOM);
final MockRegion region3 = new MockRegion(regionId3, "r3", Region.Type.CAMPUS);
public RegionsResourceTest() {
super(CoreWebApplication.class);
}
/**
* Mock class for a region.
*/
private static class MockRegion implements Region {
private final RegionId id;
private final String name;
private final Type type;
private final List<Set<NodeId>> masters;
public MockRegion(RegionId id, String name, Type type) {
this.id = id;
this.name = name;
this.type = type;
final NodeId nodeId1 = NodeId.nodeId("1");
final NodeId nodeId2 = NodeId.nodeId("2");
final NodeId nodeId3 = NodeId.nodeId("3");
final NodeId nodeId4 = NodeId.nodeId("4");
Set<NodeId> nodeIds1 = ImmutableSet.of(nodeId1);
Set<NodeId> nodeIds2 = ImmutableSet.of(nodeId1, nodeId2);
Set<NodeId> nodeIds3 = ImmutableSet.of(nodeId1, nodeId2, nodeId3);
Set<NodeId> nodeIds4 = ImmutableSet.of(nodeId1, nodeId2, nodeId3, nodeId4);
this.masters = ImmutableList.of(nodeIds1, nodeIds2, nodeIds3, nodeIds4);
}
@Override
public RegionId id() {
return this.id;
}
@Override
public String name() {
return this.name;
}
@Override
public Type type() {
return this.type;
}
@Override
public List<Set<NodeId>> masters() {
return this.masters;
}
}
/**
* Sets up the global values for all the tests.
*/
@Before
public void setupTest() {
final CodecManager codecService = new CodecManager();
codecService.activate();
ServiceDirectory testDirectory =
new TestServiceDirectory()
.add(RegionService.class, mockRegionService)
.add(RegionAdminService.class, mockRegionAdminService)
.add(CodecService.class, codecService);
BaseResource.setServiceDirectory(testDirectory);
}
/**
* Hamcrest matcher to check that a meter representation in JSON matches
* the actual meter.
*/
public static class RegionJsonMatcher extends TypeSafeMatcher<JsonObject> {
private final Region region;
private String reason = "";
public RegionJsonMatcher(Region regionValue) {
this.region = regionValue;
}
@Override
protected boolean matchesSafely(JsonObject jsonRegion) {
// check id
String jsonRegionId = jsonRegion.get("id").asString();
String regionId = region.id().toString();
if (!jsonRegionId.equals(regionId)) {
reason = "region id was " + jsonRegionId;
return false;
}
// check type
String jsonType = jsonRegion.get("type").asString();
String type = region.type().toString();
if (!jsonType.equals(type)) {
reason = "type was " + jsonType;
return false;
}
// check name
String jsonName = jsonRegion.get("name").asString();
String name = region.name();
if (!jsonName.equals(name)) {
reason = "name was " + jsonName;
return false;
}
// check size of master array
JsonArray jsonMasters = jsonRegion.get("masters").asArray();
if (jsonMasters.size() != region.masters().size()) {
reason = "masters size was " + jsonMasters.size();
return false;
}
// check master
for (Set<NodeId> set : region.masters()) {
boolean masterFound = false;
for (int masterIndex = 0; masterIndex < jsonMasters.size(); masterIndex++) {
masterFound = checkEquality(jsonMasters.get(masterIndex).asArray(), set);
}
if (!masterFound) {
reason = "master not found " + set.toString();
return false;
}
}
return true;
}
@Override
public void describeTo(Description description) {
description.appendText(reason);
}
private Set<NodeId> jsonToSet(JsonArray nodes) {
final Set<NodeId> nodeIds = Sets.newHashSet();
nodes.forEach(node -> nodeIds.add(NodeId.nodeId(node.asString())));
return nodeIds;
}
private boolean checkEquality(JsonArray nodes, Set<NodeId> nodeIds) {
Set<NodeId> jsonSet = jsonToSet(nodes);
if (jsonSet.size() == nodes.size()) {
return jsonSet.containsAll(nodeIds);
}
return false;
}
}
private static RegionJsonMatcher matchesRegion(Region region) {
return new RegionJsonMatcher(region);
}
/**
* Hamcrest matcher to check that a region is represented properly in a JSON
* array of regions.
*/
public static class RegionJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
private final Region region;
private String reason = "";
public RegionJsonArrayMatcher(Region regionValue) {
this.region = regionValue;
}
@Override
protected boolean matchesSafely(JsonArray json) {
boolean regionFound = false;
for (int jsonRegionIndex = 0; jsonRegionIndex < json.size(); jsonRegionIndex++) {
final JsonObject jsonRegion = json.get(jsonRegionIndex).asObject();
final String regionId = region.id().toString();
final String jsonRegionId = jsonRegion.get("id").asString();
if (jsonRegionId.equals(regionId)) {
regionFound = true;
assertThat(jsonRegion, matchesRegion(region));
}
}
if (!regionFound) {
reason = "Region with id " + region.id().toString() + " not found";
return false;
} else {
return true;
}
}
@Override
public void describeTo(Description description) {
description.appendText(reason);
}
}
/**
* Factory to allocate a region array matcher.
*
* @param region region object we are looking for
* @return matcher
*/
private static RegionJsonArrayMatcher hasRegion(Region region) {
return new RegionJsonArrayMatcher(region);
}
@Test
public void testRegionEmptyArray() {
expect(mockRegionService.getRegions()).andReturn(ImmutableSet.of()).anyTimes();
replay((mockRegionService));
final WebResource rs = resource();
final String response = rs.path("regions").get(String.class);
assertThat(response, is("{\"regions\":[]}"));
verify(mockRegionService);
}
/**
* Tests the results of the REST API GET when there are active regions.
*/
@Test
public void testRegionsPopulatedArray() {
final Set<Region> regions = ImmutableSet.of(region1, region2, region3);
expect(mockRegionService.getRegions()).andReturn(regions).anyTimes();
replay(mockRegionService);
final WebResource rs = resource();
final String response = rs.path("regions").get(String.class);
final JsonObject result = Json.parse(response).asObject();
assertThat(result, notNullValue());
assertThat(result.names(), hasSize(1));
assertThat(result.names().get(0), is("regions"));
final JsonArray jsonRegions = result.get("regions").asArray();
assertThat(jsonRegions, notNullValue());
assertThat(jsonRegions, hasRegion(region1));
assertThat(jsonRegions, hasRegion(region2));
assertThat(jsonRegions, hasRegion(region3));
verify(mockRegionService);
}
/**
* Tests the result of a REST API GET for a region with region id.
*/
@Test
public void testGetRegionById() {
expect(mockRegionService.getRegion(anyObject())).andReturn(region1).anyTimes();
replay(mockRegionService);
final WebResource rs = resource();
final String response = rs.path("regions/" + regionId1.toString()).get(String.class);
final JsonObject result = Json.parse(response).asObject();
assertThat(result, notNullValue());
assertThat(result, matchesRegion(region1));
verify(mockRegionService);
}
/**
* Tests creating a region with POST.
*/
@Test
public void testRegionPost() {
mockRegionAdminService.createRegion(anyObject(), anyObject(),
anyObject(), anyObject());
expectLastCall().andReturn(region2).anyTimes();
replay(mockRegionAdminService);
WebResource rs = resource();
InputStream jsonStream = MetersResourceTest.class
.getResourceAsStream("post-region.json");
ClientResponse response = rs.path("regions")
.type(MediaType.APPLICATION_JSON_TYPE)
.post(ClientResponse.class, jsonStream);
assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
verify(mockRegionAdminService);
}
/**
* Tests updating a region with PUT.
*/
@Test
public void testRegionPut() {
mockRegionAdminService.updateRegion(anyObject(), anyObject(),
anyObject(), anyObject());
expectLastCall().andReturn(region1).anyTimes();
replay(mockRegionAdminService);
WebResource rs = resource();
InputStream jsonStream = MetersResourceTest.class
.getResourceAsStream("post-region.json");
ClientResponse response = rs.path("regions/" + region1.id().toString())
.type(MediaType.APPLICATION_JSON_TYPE)
.put(ClientResponse.class, jsonStream);
assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
verify(mockRegionAdminService);
}
/**
* Tests deleting a region with DELETE.
*/
@Test
public void testRegionDelete() {
mockRegionAdminService.removeRegion(anyObject());
expectLastCall();
replay(mockRegionAdminService);
WebResource rs = resource();
ClientResponse response = rs.path("regions/" + region1.id().toString())
.delete(ClientResponse.class);
assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
verify(mockRegionAdminService);
}
/**
* Tests retrieving device ids that are associated with the given region.
*/
@Test
public void testGetRegionDevices() {
final DeviceId deviceId1 = DeviceId.deviceId("1");
final DeviceId deviceId2 = DeviceId.deviceId("2");
final DeviceId deviceId3 = DeviceId.deviceId("3");
final Set<DeviceId> deviceIds = ImmutableSet.of(deviceId1, deviceId2, deviceId3);
expect(mockRegionService.getRegionDevices(anyObject()))
.andReturn(deviceIds).anyTimes();
replay(mockRegionService);
final WebResource rs = resource();
final String response = rs.path("regions/" +
region1.id().toString() + "/devices").get(String.class);
final JsonObject result = Json.parse(response).asObject();
assertThat(result, notNullValue());
assertThat(result.names(), hasSize(1));
assertThat(result.names().get(0), is("deviceIds"));
final JsonArray jsonDeviceIds = result.get("deviceIds").asArray();
assertThat(jsonDeviceIds.size(), is(3));
assertThat(jsonDeviceIds.get(0).asString(), is("1"));
assertThat(jsonDeviceIds.get(1).asString(), is("2"));
assertThat(jsonDeviceIds.get(2).asString(), is("3"));
verify(mockRegionService);
}
/**
* Tests adding a set of devices in region with POST.
*/
@Test
public void testAddDevicesPost() {
mockRegionAdminService.addDevices(anyObject(), anyObject());
expectLastCall();
replay(mockRegionAdminService);
WebResource rs = resource();
InputStream jsonStream = MetersResourceTest.class
.getResourceAsStream("region-deviceIds.json");
ClientResponse response = rs.path("regions/" +
region1.id().toString() + "/devices")
.type(MediaType.APPLICATION_JSON_TYPE)
.post(ClientResponse.class, jsonStream);
assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
verify(mockRegionAdminService);
}
/**
* Tests deleting a set of devices contained in the given region with DELETE.
*/
@Test
public void testRemoveDevicesDelete() {
mockRegionAdminService.removeDevices(anyObject(), anyObject());
expectLastCall();
replay(mockRegionAdminService);
WebResource rs = resource();
InputStream jsonStream = MetersResourceTest.class
.getResourceAsStream("region-deviceIds.json");
ClientResponse response = rs.path("regions/" +
region1.id().toString() + "/devices")
.type(MediaType.APPLICATION_JSON_TYPE)
.delete(ClientResponse.class, jsonStream);
assertThat(response.getStatus(), is(HttpURLConnection.HTTP_OK));
verify(mockRegionAdminService);
}
}
{
"id": 1,
"type": "ROOM",
"name": "foo",
"masters": [
[
"1"
],
[
"1", "2"
]
]
}
\ No newline at end of file
{
"deviceIds": [
"of:0000000000000001",
"of:0000000000000002",
"of:0000000000000003"
]
}
\ No newline at end of file