Simon Hunt

CORD Subscriber GUI - XosFunction mementos now stored inside each SubscriberUser…

… to capture the parameter state per user.

Change-Id: I678249f63a68172db66a5d3faa0b1747c670bf6e
......@@ -26,6 +26,9 @@ import org.onosproject.cord.gui.model.BundleFactory;
import org.onosproject.cord.gui.model.JsonFactory;
import org.onosproject.cord.gui.model.SubscriberUser;
import org.onosproject.cord.gui.model.UserFactory;
import org.onosproject.cord.gui.model.XosFunction;
import org.onosproject.cord.gui.model.XosFunctionDescriptor;
import org.onosproject.cord.gui.model.XosFunctionFactory;
import java.util.ArrayList;
import java.util.List;
......@@ -62,12 +65,21 @@ public class CordModelCache extends JsonFactory {
* 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));
users.add(createUser(1, "Mom's MacBook", MAC_1));
users.add(createUser(2, "Dad's iPad", MAC_2));
users.add(createUser(3, "Dick's laptop", MAC_3));
users.add(createUser(4, "Jane's laptop", MAC_4));
}
private SubscriberUser createUser(int uid, String name, String mac) {
SubscriberUser user = new SubscriberUser(uid, name, mac);
for (XosFunction f: currentBundle.functions()) {
user.setMemento(f.descriptor(), f.createMemento());
}
return user;
}
/**
* Returns the currently selected bundle.
*
......@@ -84,10 +96,20 @@ public class CordModelCache extends JsonFactory {
* @throws IllegalArgumentException if bundle ID is unknown
*/
public void setCurrentBundle(String bundleId) {
BundleDescriptor bdesc = BundleFactory.bundleFromId(bundleId);
currentBundle = new Bundle(bdesc);
BundleDescriptor bd = BundleFactory.bundleFromId(bundleId);
currentBundle = new Bundle(bd);
// update the user mementos
for (SubscriberUser user: users) {
user.clearMementos();
for (XosFunction f: currentBundle.functions()) {
user.setMemento(f.descriptor(), f.createMemento());
}
}
// TODO: tell XOS which functions are enabled / disabled
}
/**
* Returns the list of current users for this subscriber account.
*
......@@ -97,6 +119,25 @@ public class CordModelCache extends JsonFactory {
return ImmutableList.copyOf(users);
}
/**
* Applies a function parameter change for a user.
*
* @param userId user identifier
* @param funcId function identifier
* @param param function parameter to change
* @param value new value for function parameter
*/
public void applyPerUserParam(String userId, String funcId,
String param, String value) {
// FIXME: this is not right yet...
int uid = Integer.parseInt(userId);
XosFunctionDescriptor xfd =
XosFunctionDescriptor.valueOf(funcId.toUpperCase());
XosFunctionFactory.apply(xfd, uid, param, value);
}
// =============
private ArrayNode userJsonArray() {
ArrayNode userList = arrayNode();
for (SubscriberUser user: users) {
......
......@@ -55,9 +55,19 @@ public class CordWebResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("bundle/{id}")
@Deprecated
public Response bundle(@PathParam("id") String bundleId) {
CordModelCache.INSTANCE.setCurrentBundle(bundleId);
return bundle();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("users/{id}/apply/{func}/{param}/{value}")
public Response bundle(@PathParam("id") String userId,
@PathParam("func") String funcId,
@PathParam("param") String param,
@PathParam("value") String value) {
CordModelCache.INSTANCE.applyPerUserParam(userId, funcId, param, value);
return users();
}
}
......
......@@ -17,11 +17,17 @@
package org.onosproject.cord.gui.model;
import com.google.common.collect.ImmutableSet;
import java.util.HashSet;
import java.util.Set;
/**
* Encapsulates a bundle, including current state.
*/
public class Bundle {
private final BundleDescriptor bundleDescriptor;
private final Set<XosFunction> functions;
/**
* Constructs a new bundle instance.
......@@ -30,6 +36,7 @@ public class Bundle {
*/
public Bundle(BundleDescriptor bundleDescriptor) {
this.bundleDescriptor = bundleDescriptor;
this.functions = initFunctions();
}
/**
......@@ -41,4 +48,39 @@ public class Bundle {
return bundleDescriptor;
}
/**
* Returns the set of function instances for this bundle.
*
* @return the functions
*/
public Set<XosFunction> functions() {
return ImmutableSet.copyOf(functions);
}
/**
* Creates an initial set of function instances.
*
* @return initial function instances
*/
private Set<XosFunction> initFunctions() {
Set<XosFunction> funcs = new HashSet<XosFunction>();
for (XosFunctionDescriptor xfd: bundleDescriptor.functions()) {
funcs.add(createFunction(xfd));
}
return funcs;
}
private XosFunction createFunction(XosFunctionDescriptor xfd) {
XosFunction func;
switch (xfd) {
case URL_FILTER:
func = new UrlFilterFunction(xfd);
break;
default:
func = new DefaultXosFunction(xfd);
break;
}
return func;
}
}
......
/*
* 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;
/**
* Default XOS function implementation, that does not have any parameters
* to tweak.
*/
public class DefaultXosFunction implements XosFunction {
protected static final ObjectMapper MAPPER = new ObjectMapper();
private final XosFunctionDescriptor xfd;
public DefaultXosFunction(XosFunctionDescriptor xfd) {
this.xfd = xfd;
}
public XosFunctionDescriptor descriptor() {
return xfd;
}
/**
* {@inheritDoc}
* <p>
* This default implementation throws an exception.
*
* @param user user to apply the change to
* @param param parameter name
* @param value new parameter value
* @throws UnsupportedOperationException if invoked
*/
public void applyParam(SubscriberUser user, String param, String value) {
throw new UnsupportedOperationException();
}
public Memento createMemento() {
return null;
}
}
......@@ -26,7 +26,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
*/
public abstract class JsonFactory {
private static final ObjectMapper mapper = new ObjectMapper();
private static final ObjectMapper MAPPER = new ObjectMapper();
protected static final String ID = "id";
protected static final String NAME = "name";
......@@ -38,7 +38,7 @@ public abstract class JsonFactory {
* @return empty object node
*/
protected static ObjectNode objectNode() {
return mapper.createObjectNode();
return MAPPER.createObjectNode();
}
/**
......@@ -47,6 +47,6 @@ public abstract class JsonFactory {
* @return empty array node
*/
protected static ArrayNode arrayNode() {
return mapper.createArrayNode();
return MAPPER.createArrayNode();
}
}
......
......@@ -17,6 +17,9 @@
package org.onosproject.cord.gui.model;
import java.util.HashMap;
import java.util.Map;
/**
* Designates a user of a subscriber's account.
*/
......@@ -25,6 +28,9 @@ public class SubscriberUser {
private final String name;
private final String mac;
private final Map<XosFunctionDescriptor, XosFunction.Memento> mementos =
new HashMap<XosFunctionDescriptor, XosFunction.Memento>();
/**
* Constructs a subscriber user from the given parameters.
*
......@@ -64,4 +70,33 @@ public class SubscriberUser {
public String mac() {
return mac;
}
/**
* Stores a memento for the given XOS function.
*
* @param f XOS function
* @param m memento
*/
public void setMemento(XosFunctionDescriptor f, XosFunction.Memento m) {
if (m != null) {
mementos.put(f, m);
}
}
/**
* Returns the memento stored on this user, for the given XOS function.
*
* @param f XOS function
* @return memento
*/
public XosFunction.Memento getMemento(XosFunctionDescriptor f) {
return mementos.get(f);
}
/**
* Clears the memento map.
*/
public void clearMementos() {
mementos.clear();
}
}
......
/*
* 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;
/**
* Specialization of XosFunction for URL filtering.
*/
public class UrlFilterFunction extends DefaultXosFunction {
private static final String LEVEL = "level";
/**
* Denotes the URL filtering levels available.
*/
public enum Level { PG, PG_13, R }
/**
* The default URL filtering level
*/
public static final Level DEFAULT_LEVEL = Level.PG;
public UrlFilterFunction(XosFunctionDescriptor xfd) {
super(xfd);
}
@Override
public Memento createMemento() {
return new UrlFilterMemento();
}
class UrlFilterMemento implements Memento {
private Level level = DEFAULT_LEVEL;
public ObjectNode toObjectNode() {
ObjectNode node = MAPPER.createObjectNode();
node.put(LEVEL, level.name());
return node;
}
public void setLevel(Level level) {
this.level = level;
}
}
}
......@@ -41,7 +41,7 @@ public class UserFactory extends JsonFactory {
.put(ID, user.id())
.put(NAME, user.name())
.put(MAC, user.mac());
// TODO: add profile data
root.set(PROFILE, XosFunctionFactory.profileForUser(user));
return root;
}
......
......@@ -28,16 +28,37 @@ public interface XosFunction {
/**
* Returns the descriptor for this function.
*
* @return function identifier
* @return function descriptor
*/
XosFunctionDescriptor descriptor();
/**
* Returns the current state of this function, encapsulated
* as a JSON node.
* Applies a parameter change for the given user.
*
* @return parameters for the function
* @param user user to apply change to
* @param param parameter name
* @param value new parameter value
*/
ObjectNode params();
void applyParam(SubscriberUser user, String param, String value);
/**
* Create an initialized memento.
* If the function maintains no state per user, return null.
*
* @return a new memento
*/
Memento createMemento();
/**
* Internal state memento.
*/
interface Memento {
/**
* Returns a JSON representation of this memento.
*
* @return memento state as object node
*/
ObjectNode toObjectNode();
}
}
......
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;
import static org.onosproject.cord.gui.model.XosFunctionDescriptor.URL_FILTER;
/**
* Utility factory for operating on XOS functions.
*/
public class XosFunctionFactory extends JsonFactory {
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;
private static final UrlFilterFunction.Level DEFAULT_FILTER_LEVEL =
UrlFilterFunction.Level.PG;
// no instantiation
......@@ -45,38 +39,76 @@ public class XosFunctionFactory extends JsonFactory {
return root;
}
private static JsonNode paramsForXfd(XosFunctionDescriptor xfd) {
ParamStructFactory psf = PARAM_MAP.get(xfd);
private static ObjectNode paramsForXfd(XosFunctionDescriptor xfd) {
ParamsFactory psf = PARAM_MAP.get(xfd);
if (psf == null) {
psf = DEF_PARAMS;
psf = DEF_PARAMS_FACTORY;
}
return psf.params();
}
// ==== handling different parameter structures...
private static final Map<XosFunctionDescriptor, ParamStructFactory>
PARAM_MAP = new HashMap<XosFunctionDescriptor, ParamStructFactory>();
private static final Map<XosFunctionDescriptor, ParamsFactory>
PARAM_MAP = new HashMap<XosFunctionDescriptor, ParamsFactory>();
private static final ParamStructFactory DEF_PARAMS = new ParamStructFactory();
private static final ParamsFactory DEF_PARAMS_FACTORY = new ParamsFactory();
static {
PARAM_MAP.put(XosFunctionDescriptor.URL_FILTER, new UrlFilterParams());
PARAM_MAP.put(URL_FILTER, new UrlFilterParamsFactory());
}
/**
* Applies a parameter change for the given function, in the context of
* the specified user.
*
* @param xfd function context
* @param userId user identifier
* @param param parameter name
* @param value value to apply
*/
public static void apply(XosFunctionDescriptor xfd, int userId,
String param, String value) {
// TODO:
}
/**
* Creates an object node representation of the profile for the
* specified user.
*
* @param user the user
* @return object node profile
*/
public static ObjectNode profileForUser(SubscriberUser user) {
ObjectNode root = objectNode();
for (XosFunctionDescriptor xfd: XosFunctionDescriptor.values()) {
XosFunction.Memento mem = user.getMemento(xfd);
if (mem != null) {
root.set(xfd.id(), mem.toObjectNode());
}
}
return root;
}
// ===================================================================
// === factories for creating parameter structures, both default
// and from a memento...
// private parameter structure creator
static class ParamStructFactory {
static class ParamsFactory {
ObjectNode params() {
return objectNode();
}
}
static class UrlFilterParams extends ParamStructFactory {
static class UrlFilterParamsFactory extends ParamsFactory {
@Override
ObjectNode params() {
ObjectNode result = objectNode();
result.put(LEVEL, DEFAULT_FILTER_LEVEL);
result.put(LEVEL, DEFAULT_FILTER_LEVEL.name());
ArrayNode levels = arrayNode();
for (String lvl: FILTER_LEVELS) {
levels.add(lvl);
for (UrlFilterFunction.Level lvl: UrlFilterFunction.Level.values()) {
levels.add(lvl.name());
}
result.set(LEVELS, levels);
return result;
......
......@@ -3,7 +3,7 @@
{
"id": 1,
"name": "Mom's MacBook",
"mac": "01:02:03:04:05:06",
"mac": "010203040506",
"profile": {
"url_filter": {
"level": "R"
......@@ -13,7 +13,7 @@
{
"id": 2,
"name": "Dad's iPad",
"mac": "01:02:03:04:05:77",
"mac": "010203040507",
"profile": {
"url_filter": {
"level": "R"
......@@ -23,17 +23,17 @@
{
"id": 3,
"name": "Dick's laptop",
"mac": "01:02:03:04:05:88",
"mac": "010203040508",
"profile": {
"url_filter": {
"level": "PG-13"
"level": "PG_13"
}
}
},
{
"id": 4,
"name": "Jane's laptop",
"mac": "01:02:03:04:05:99",
"mac": "010203040509",
"profile": {
"url_filter": {
"level": "PG"
......
......@@ -51,6 +51,7 @@ public class CoreModelCacheTest {
@Test
public void basicBundleJson() {
String json = BundleFactory.toJson(cache.getCurrentBundle());
System.out.println(json);
assertTrue("bad basic json", sameJson(BASIC_BUNDLE_JSON, json));
}
......@@ -75,6 +76,21 @@ public class CoreModelCacheTest {
assertEquals("wrong # users", 4, users.size());
}
@Test
public void usersBasicJson() {
String json = cache.jsonUsers();
System.out.println(json);
assertTrue("bad users basic json", sameJson(USERS_BASIC, json));
}
@Test
public void usersFamilyJson() {
cache.setCurrentBundle("family");
String json = cache.jsonUsers();
System.out.println(json);
assertTrue("bad users family json", sameJson(USERS_FAMILY, json));
}
// =============
private boolean sameJson(String s1, String s2) {
......@@ -91,61 +107,157 @@ public class CoreModelCacheTest {
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" +
" \"bundle\": {\n" +
" \"id\": \"basic\",\n" +
" \"name\": \"Basic Bundle\",\n" +
" \"desc\": \"Provides basic internet and firewall functions.\",\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" +
" {\n" +
" \"id\": \"basic\",\n" +
" \"name\": \"Basic Bundle\",\n" +
" \"desc\": \"Provides basic internet and firewall functions.\"\n" +
" },\n" +
" {\n" +
" \"id\": \"family\",\n" +
" \"name\": \"Family Bundle\",\n" +
" \"desc\": \"Provides internet, firewall and parental control functions.\"\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" +
" \"bundle\": {\n" +
" \"id\": \"family\",\n" +
" \"name\": \"Family Bundle\",\n" +
" \"desc\": \"Provides internet, firewall and parental control functions.\",\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\": [\n" +
" \"PG\",\n" +
" \"PG_13\",\n" +
" \"R\"\n" +
" ]\n" +
" }\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"bundles\": [\n" +
" {\n" +
" \"id\": \"basic\",\n" +
" \"name\": \"Basic Bundle\",\n" +
" \"desc\": \"Provides basic internet and firewall functions.\"\n" +
" },\n" +
" {\n" +
" \"id\": \"family\",\n" +
" \"name\": \"Family Bundle\",\n" +
" \"desc\": \"Provides internet, firewall and parental control functions.\"\n" +
" }\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"bundles\": [\n" +
" { \"id\": \"basic\", \"name\": \"Basic Bundle\" },\n" +
" { \"id\": \"family\", \"name\": \"Family Bundle\" }\n" +
"}\n";
private static final String USERS_BASIC = "{\n" +
" \"users\": [\n" +
" {\n" +
" \"id\": 1,\n" +
" \"name\": \"Mom's MacBook\",\n" +
" \"mac\": \"010203040506\",\n" +
" \"profile\": { }\n" +
" },\n" +
" {\n" +
" \"id\": 2,\n" +
" \"name\": \"Dad's iPad\",\n" +
" \"mac\": \"010203040507\",\n" +
" \"profile\": { }\n" +
" },\n" +
" {\n" +
" \"id\": 3,\n" +
" \"name\": \"Dick's laptop\",\n" +
" \"mac\": \"010203040508\",\n" +
" \"profile\": { }\n" +
" },\n" +
" {\n" +
" \"id\": 4,\n" +
" \"name\": \"Jane's laptop\",\n" +
" \"mac\": \"010203040509\",\n" +
" \"profile\": { }\n" +
" }\n" +
" ]\n" +
"}\n";
private static final String USERS_FAMILY = "{\n" +
" \"users\": [\n" +
" {\n" +
" \"id\": 1,\n" +
" \"name\": \"Mom's MacBook\",\n" +
" \"mac\": \"010203040506\",\n" +
" \"profile\": {\n" +
" \"url_filter\": {\n" +
" \"level\": \"PG\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"id\": 2,\n" +
" \"name\": \"Dad's iPad\",\n" +
" \"mac\": \"010203040507\",\n" +
" \"profile\": {\n" +
" \"url_filter\": {\n" +
" \"level\": \"PG\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"id\": 3,\n" +
" \"name\": \"Dick's laptop\",\n" +
" \"mac\": \"010203040508\",\n" +
" \"profile\": {\n" +
" \"url_filter\": {\n" +
" \"level\": \"PG\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"id\": 4,\n" +
" \"name\": \"Jane's laptop\",\n" +
" \"mac\": \"010203040509\",\n" +
" \"profile\": {\n" +
" \"url_filter\": {\n" +
" \"level\": \"PG\"\n" +
" }\n" +
" }\n" +
" }\n" +
" ]\n" +
"}\n";
}
......