Simon Hunt

CORD Subscriber GUI -- More bundle wrangling.

Change-Id: I2fafdb281712d7747399d61611c3d4bb663a39b5
......@@ -17,9 +17,75 @@
package org.onosproject.cord.gui;
import com.google.common.collect.ImmutableList;
import org.onosproject.cord.gui.model.Bundle;
import org.onosproject.cord.gui.model.BundleDescriptor;
import org.onosproject.cord.gui.model.BundleFactory;
import org.onosproject.cord.gui.model.SubscriberUser;
import java.util.ArrayList;
import java.util.List;
/**
* In memory cache of the model of the subscriber's account.
*/
public class CordModelCache {
// faked for the demo
private static final int SUBSCRIBER_ID = 92;
private static final String MAC_1 = "010203040506";
private static final String MAC_2 = "010203040507";
private static final String MAC_3 = "010203040508";
private static final String MAC_4 = "010203040509";
private Bundle currentBundle;
private final List<SubscriberUser> users;
/**
* Constructs a model cache, initializing it with basic bundle.
*/
public CordModelCache() {
currentBundle = new Bundle(BundleFactory.BASIC_BUNDLE);
users = new ArrayList<SubscriberUser>();
initUsers();
}
/**
* Used to initialize users for the demo. These are currently fake.
*/
public void initUsers() {
users.add(new SubscriberUser(1, "Mom's MacBook", MAC_1));
users.add(new SubscriberUser(2, "Dad's iPad", MAC_2));
users.add(new SubscriberUser(3, "Dick's laptop", MAC_3));
users.add(new SubscriberUser(4, "Jane's laptop", MAC_4));
}
/**
* Returns the currently selected bundle.
*
* @return current bundle
*/
public Bundle getCurrentBundle() {
return currentBundle;
}
/**
* Sets a new bundle.
*
* @param bundleId bundle identifier
* @throws IllegalArgumentException if bundle ID is unknown
*/
public void setCurrentBundle(String bundleId) {
BundleDescriptor bdesc = BundleFactory.bundleFromId(bundleId);
currentBundle = new Bundle(bdesc);
}
/**
* Returns the list of current users for this subscriber account.
*
* @return the list of users
*/
public List<SubscriberUser> getUsers() {
return ImmutableList.copyOf(users);
}
}
......
/*
* Copyright 2015 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.cord.gui.model;
/**
* Encapsulates a bundle, including current state.
*/
public class Bundle {
private final BundleDescriptor bundleDescriptor;
/**
* Constructs a new bundle instance.
*
* @param bundleDescriptor the descriptor
*/
public Bundle(BundleDescriptor bundleDescriptor) {
this.bundleDescriptor = bundleDescriptor;
}
/**
* Returns the bundle descriptor.
*
* @return the descriptor
*/
public BundleDescriptor descriptor() {
return bundleDescriptor;
}
}
......@@ -17,14 +17,20 @@
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import java.util.List;
/**
* Utility factory for creating bundles and functions etc.
* Utility factory for creating and/or operating on bundles.
*/
public class BundleFactory {
public class BundleFactory extends JsonFactory {
private static final String BUNDLE = "bundle";
private static final String BUNDLES = "bundles";
private static final String FUNCTIONS = "functions";
private static final String BASIC_ID = "basic";
private static final String BASIC_DISPLAY_NAME = "Basic Bundle";
......@@ -35,23 +41,83 @@ public class BundleFactory {
// no instantiation
private BundleFactory() {}
private static final BundleDescriptor BASIC =
/**
* Designates the BASIC bundle.
*/
public static final BundleDescriptor BASIC_BUNDLE =
new DefaultBundleDescriptor(BASIC_ID, BASIC_DISPLAY_NAME,
XosFunctionDescriptor.INTERNET,
XosFunctionDescriptor.FIREWALL);
private static final BundleDescriptor FAMILY =
/**
* Designates the FAMILY bundle.
*/
public static final BundleDescriptor FAMILY_BUNDLE =
new DefaultBundleDescriptor(FAMILY_ID, FAMILY_DISPLAY_NAME,
XosFunctionDescriptor.INTERNET,
XosFunctionDescriptor.FIREWALL,
XosFunctionDescriptor.URL_FILTER);
// all bundles, in the order they should be listed in the GUI
private static final List<BundleDescriptor> ALL_BUNDLES = ImmutableList.of(
BASIC_BUNDLE,
FAMILY_BUNDLE
);
/**
* Returns the list of available bundles.
*
* @return available bundles
*/
public static List<BundleDescriptor> availableBundles() {
return ImmutableList.of(BASIC, FAMILY);
return ALL_BUNDLES;
}
/**
* Returns the bundle descriptor for the given identifier.
*
* @param bundleId bundle identifier
* @return bundle descriptor
* @throws IllegalArgumentException if bundle ID is unknown
*/
public static BundleDescriptor bundleFromId(String bundleId) {
for (BundleDescriptor bd : ALL_BUNDLES) {
if (bd.id().equals(bundleId)) {
return bd;
}
}
throw new IllegalArgumentException("unknown bundle: " + bundleId);
}
/**
* Returns a JSON string representation of the given bundle.
*
* @param bundle the bundle
* @return JSON string
*/
public static String toJson(Bundle bundle) {
ObjectNode root = objectNode();
ObjectNode bnode = objectNode()
.put(ID, bundle.descriptor().id())
.put(NAME, bundle.descriptor().displayName());
ArrayNode funcs = arrayNode();
for (XosFunctionDescriptor xfd: bundle.descriptor().functions()) {
funcs.add(XosFunctionFactory.toObjectNode(xfd));
}
bnode.set(FUNCTIONS, funcs);
root.set(BUNDLE, bnode);
ArrayNode bundles = arrayNode();
for (BundleDescriptor bd: BundleFactory.availableBundles()) {
ObjectNode bdnode = objectNode()
.put(ID, bd.id())
.put(NAME, bd.displayName());
bundles.add(bdnode);
}
root.set(BUNDLES, bundles);
return root.toString();
}
}
......
......@@ -57,4 +57,27 @@ public class DefaultBundleDescriptor implements BundleDescriptor {
public Set<XosFunctionDescriptor> functions() {
return functions;
}
@Override
public String toString() {
return "{BundleDescriptor: " + displayName + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DefaultBundleDescriptor that = (DefaultBundleDescriptor) o;
return id.equals(that.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}
......
/*
* Copyright 2015 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.cord.gui.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Base class for factories that convert objects to JSON.
*/
public abstract class JsonFactory {
private static final ObjectMapper mapper = new ObjectMapper();
protected static final String ID = "id";
protected static final String NAME = "name";
/**
* Returns a freshly minted object node.
*
* @return empty object node
*/
protected static ObjectNode objectNode() {
return mapper.createObjectNode();
}
/**
* Returns a freshly minted array node.
*
* @return empty array node
*/
protected static ArrayNode arrayNode() {
return mapper.createArrayNode();
}
}
/*
* Copyright 2015 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.cord.gui.model;
/**
* Designates a user of a subscriber's account.
*/
public class SubscriberUser {
private final int id;
private final String name;
private final String mac;
/**
* Constructs a subscriber user from the given parameters.
*
* @param id internal identifier
* @param name display name
* @param mac MAC address of the associated device
*/
public SubscriberUser(int id, String name, String mac) {
this.id = id;
this.name = name;
this.mac = mac;
}
/**
* Returns the internal identifier.
*
* @return the identifier
*/
public int id() {
return id;
}
/**
* Returns the display name.
*
* @return display name
*/
public String name() {
return name;
}
/**
* Returns the MAC address of the associated device.
*
* @return MAC address
*/
public String mac() {
return mac;
}
}
/*
* Copyright 2015 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.cord.gui.model;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Utility functions on users.
*/
public class UserFactory extends JsonFactory {
private static final String MAC = "mac";
private static final String PROFILE = "profile";
// no instantiation
private UserFactory() {}
/**
* Returns an object node representation of the given user.
*
* @param user the user
* @return object node
*/
public static ObjectNode toObjectNode(SubscriberUser user) {
ObjectNode root = objectNode()
.put(ID, user.id())
.put(NAME, user.name())
.put(MAC, user.mac());
// TODO: add profile data
return root;
}
}
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.HashMap;
import java.util.Map;
/**
* Utility factory for operating on XOS functions.
*/
public class XosFunctionFactory extends JsonFactory {
private static final String DESC = "desc";
private static final String PARAMS = "params";
private static final String LEVEL = "level";
private static final String LEVELS = "levels";
// URL Filtering Levels...
private static final String PG = "PG";
private static final String PG13 = "PG-13";
private static final String R = "R";
private static final String[] FILTER_LEVELS = { PG, PG13, R };
private static final String DEFAULT_FILTER_LEVEL = PG;
// no instantiation
private XosFunctionFactory() {}
/**
* Produces the JSON representation of the given XOS function descriptor.
*
* @param xfd function descriptor
* @return JSON encoding
*/
public static ObjectNode toObjectNode(XosFunctionDescriptor xfd) {
ObjectNode root = objectNode()
.put(ID, xfd.id())
.put(NAME, xfd.displayName())
.put(DESC, xfd.description());
root.set(PARAMS, paramsForXfd(xfd));
return root;
}
private static JsonNode paramsForXfd(XosFunctionDescriptor xfd) {
ParamStructFactory psf = PARAM_MAP.get(xfd);
if (psf == null) {
psf = DEF_PARAMS;
}
return psf.params();
}
// ==== handling different parameter structures...
private static final Map<XosFunctionDescriptor, ParamStructFactory>
PARAM_MAP = new HashMap<XosFunctionDescriptor, ParamStructFactory>();
private static final ParamStructFactory DEF_PARAMS = new ParamStructFactory();
static {
PARAM_MAP.put(XosFunctionDescriptor.URL_FILTER, new UrlFilterParams());
}
// private parameter structure creator
static class ParamStructFactory {
ObjectNode params() {
return objectNode();
}
}
static class UrlFilterParams extends ParamStructFactory {
@Override
ObjectNode params() {
ObjectNode result = objectNode();
result.put(LEVEL, DEFAULT_FILTER_LEVEL);
ArrayNode levels = arrayNode();
for (String lvl: FILTER_LEVELS) {
levels.add(lvl);
}
result.set(LEVELS, levels);
return result;
}
}
}
......@@ -6,13 +6,13 @@
{
"id": "internet",
"name": "Internet",
"desc": "Basic internet connectivity",
"desc": "Basic internet connectivity.",
"params": {}
},
{
"id": "firewall",
"name": "Firewall",
"desc": "Normal firewall protection",
"desc": "Normal firewall protection.",
"params": {}
}
]
......
......@@ -6,19 +6,19 @@
{
"id": "internet",
"name": "Internet",
"desc": "Basic internet connectivity",
"desc": "Basic internet connectivity.",
"params": {}
},
{
"id": "firewall",
"name": "Firewall",
"desc": "Normal firewall protection",
"desc": "Normal firewall protection.",
"params": {}
},
{
"id": "url_filter",
"name": "Parental Control",
"desc": "Variable levels of URL filtering",
"desc": "Variable levels of URL filtering.",
"params": {
"level": "PG",
"levels": [ "PG", "PG-13", "R" ]
......
/*
* Copyright 2015 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.cord.gui;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Before;
import org.junit.Test;
import org.onosproject.cord.gui.model.BundleFactory;
import org.onosproject.cord.gui.model.SubscriberUser;
import java.io.IOException;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Unit tests for {@link CordModelCache}.
*/
public class CoreModelCacheTest {
private CordModelCache cache;
@Before
public void setUp() {
cache = new CordModelCache();
}
@Test
public void basic() {
assertEquals("wrong bundle", BundleFactory.BASIC_BUNDLE,
cache.getCurrentBundle().descriptor());
}
@Test
public void basicBundleJson() {
String json = BundleFactory.toJson(cache.getCurrentBundle());
assertTrue("bad basic json", sameJson(BASIC_BUNDLE_JSON, json));
}
@Test
public void chooseFamilyBundle() {
cache.setCurrentBundle("family");
assertEquals("wrong bundle", BundleFactory.FAMILY_BUNDLE,
cache.getCurrentBundle().descriptor());
}
@Test
public void familyBundleJson() {
cache.setCurrentBundle("family");
String json = BundleFactory.toJson(cache.getCurrentBundle());
System.out.println(json);
assertTrue("bad family json", sameJson(FAMILY_BUNDLE_JSON, json));
}
@Test
public void checkUsers() {
List<SubscriberUser> users = cache.getUsers();
assertEquals("wrong # users", 4, users.size());
}
// =============
private boolean sameJson(String s1, String s2) {
try {
JsonNode tree1 = MAPPER.readTree(s1);
JsonNode tree2 = MAPPER.readTree(s2);
return tree1.equals(tree2);
} catch (IOException e) {
System.out.println("Exception: " + e);
}
return false;
}
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String BASIC_BUNDLE_JSON = "{\n" +
" \"bundle\": {\n" +
" \"id\": \"basic\",\n" +
" \"name\": \"Basic Bundle\",\n" +
" \"functions\": [\n" +
" {\n" +
" \"id\": \"internet\",\n" +
" \"name\": \"Internet\",\n" +
" \"desc\": \"Basic internet connectivity.\",\n" +
" \"params\": {}\n" +
" },\n" +
" {\n" +
" \"id\": \"firewall\",\n" +
" \"name\": \"Firewall\",\n" +
" \"desc\": \"Normal firewall protection.\",\n" +
" \"params\": {}\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"bundles\": [\n" +
" { \"id\": \"basic\", \"name\": \"Basic Bundle\" },\n" +
" { \"id\": \"family\", \"name\": \"Family Bundle\" }\n" +
" ]\n" +
"}\n";
private static final String FAMILY_BUNDLE_JSON = "{\n" +
" \"bundle\": {\n" +
" \"id\": \"family\",\n" +
" \"name\": \"Family Bundle\",\n" +
" \"functions\": [\n" +
" {\n" +
" \"id\": \"internet\",\n" +
" \"name\": \"Internet\",\n" +
" \"desc\": \"Basic internet connectivity.\",\n" +
" \"params\": {}\n" +
" },\n" +
" {\n" +
" \"id\": \"firewall\",\n" +
" \"name\": \"Firewall\",\n" +
" \"desc\": \"Normal firewall protection.\",\n" +
" \"params\": {}\n" +
" },\n" +
" {\n" +
" \"id\": \"url_filter\",\n" +
" \"name\": \"Parental Control\",\n" +
" \"desc\": \"Variable levels of URL filtering.\",\n" +
" \"params\": {\n" +
" \"level\": \"PG\",\n" +
" \"levels\": [ \"PG\", \"PG-13\", \"R\" ]\n" +
" }\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"bundles\": [\n" +
" { \"id\": \"basic\", \"name\": \"Basic Bundle\" },\n" +
" { \"id\": \"family\", \"name\": \"Family Bundle\" }\n" +
" ]\n" +
"}\n";
}
......@@ -22,7 +22,7 @@ import org.junit.Test;
import java.util.Set;
import static org.junit.Assert.*;
import static org.onosproject.cord.gui.model.BundleFactory.availableBundles;
import static org.onosproject.cord.gui.model.BundleFactory.*;
import static org.onosproject.cord.gui.model.XosFunctionDescriptor.*;
/**
......@@ -33,11 +33,13 @@ public class BundleFactoryTest {
@Test
public void bundleCount() {
assertEquals("wrong count", 2, availableBundles().size());
assertTrue("missing basic", availableBundles().contains(BASIC_BUNDLE));
assertTrue("missing family", availableBundles().contains(FAMILY_BUNDLE));
}
@Test
public void basicBundle() {
BundleDescriptor bundle = availableBundles().get(0);
BundleDescriptor bundle = BundleFactory.BASIC_BUNDLE;
assertEquals("wrong id", "basic", bundle.id());
assertEquals("wrong id", "Basic Bundle", bundle.displayName());
Set<XosFunctionDescriptor> funcs = bundle.functions();
......@@ -48,7 +50,7 @@ public class BundleFactoryTest {
@Test
public void familyBundle() {
BundleDescriptor bundle = availableBundles().get(1);
BundleDescriptor bundle = BundleFactory.FAMILY_BUNDLE;
assertEquals("wrong id", "family", bundle.id());
assertEquals("wrong id", "Family Bundle", bundle.displayName());
Set<XosFunctionDescriptor> funcs = bundle.functions();
......@@ -57,5 +59,19 @@ public class BundleFactoryTest {
assertTrue("missing url-f", funcs.contains(URL_FILTER));
}
@Test
public void bundleFromIdBasic() {
assertEquals("wrong bundle", BASIC_BUNDLE, bundleFromId("basic"));
}
@Test
public void bundleFromIdFamily() {
assertEquals("wrong bundle", FAMILY_BUNDLE, bundleFromId("family"));
}
@Test(expected = IllegalArgumentException.class)
public void bundleFromIdUnknown() {
bundleFromId("unknown");
}
}
......
......@@ -49,7 +49,7 @@ public class XosFunctionDescriptorTest {
@Test
public void urlFiltering() {
assertEquals("wrong id", "url_filtering", URL_FILTER.id());
assertEquals("wrong id", "url_filter", URL_FILTER.id());
assertEquals("wrong display", "Parental Control", URL_FILTER.displayName());
assertTrue("wrong desc", URL_FILTER.description().startsWith("Variable"));
}
......