Implementation of REST POST API for creating intents
- codec for constraint decode
- codec for intent decode
- POST method for intents
- unit tests for codecs and POST method

Change-Id: Ibc0ef8f99a0c0664710a733985424c77010c49b5
diff --git a/core/api/src/main/java/org/onosproject/codec/JsonCodec.java b/core/api/src/main/java/org/onosproject/codec/JsonCodec.java
index 815f1e4..6df8f11 100644
--- a/core/api/src/main/java/org/onosproject/codec/JsonCodec.java
+++ b/core/api/src/main/java/org/onosproject/codec/JsonCodec.java
@@ -87,4 +87,29 @@
         return result;
     }
 
+    /**
+     * Gets a child Object Node from a parent by name. If the child is not found
+     * or does nor represent an object, null is returned.
+     *
+     * @param parent parent object
+     * @param childName name of child to query
+     * @return child object if found, null if not found or if not an object
+     */
+    protected static ObjectNode get(ObjectNode parent, String childName) {
+        JsonNode node = parent.path(childName);
+        return node.isObject() && !node.isNull() ? (ObjectNode) node : null;
+    }
+
+    /**
+     * Gets a child Object Node from a parent by index. If the child is not found
+     * or does nor represent an object, null is returned.
+     *
+     * @param parent parent object
+     * @param childIndex index of child to query
+     * @return child object if found, null if not found or if not an object
+     */
+    protected static ObjectNode get(JsonNode parent, int childIndex) {
+        JsonNode node = parent.path(childIndex);
+        return node.isObject() && !node.isNull() ? (ObjectNode) node : null;
+    }
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
index 7e145b4..8c71462 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/AnnotatedCodec.java
@@ -55,7 +55,7 @@
 
         JsonCodec<Annotations> codec = context.codec(Annotations.class);
         if (objNode.has("annotations") && objNode.isObject()) {
-            return codec.decode((ObjectNode) objNode.get("annotations"), context);
+            return codec.decode(get(objNode, "annotations"), context);
         } else {
             return DefaultAnnotations.EMPTY;
         }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/ConnectivityIntentCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/ConnectivityIntentCodec.java
index dbd824c..9e8cd86 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/ConnectivityIntentCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/ConnectivityIntentCodec.java
@@ -15,6 +15,9 @@
  */
 package org.onosproject.codec.impl;
 
+import java.util.ArrayList;
+import java.util.stream.IntStream;
+
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.net.flow.TrafficSelector;
@@ -23,6 +26,7 @@
 import org.onosproject.net.intent.Constraint;
 import org.onosproject.net.intent.Intent;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
@@ -33,6 +37,10 @@
  */
 public final class ConnectivityIntentCodec extends JsonCodec<ConnectivityIntent> {
 
+    private static final String CONSTRAINTS = "constraints";
+    private static final String SELECTOR = "selector";
+    private static final String TREATMENT = "treatment";
+
     @Override
     public ObjectNode encode(ConnectivityIntent intent, CodecContext context) {
         checkNotNull(intent, "Connectivity intent cannot be null");
@@ -43,19 +51,19 @@
         if (intent.selector() != null) {
             final JsonCodec<TrafficSelector> selectorCodec =
                     context.codec(TrafficSelector.class);
-            result.set("selector", selectorCodec.encode(intent.selector(), context));
+            result.set(SELECTOR, selectorCodec.encode(intent.selector(), context));
         }
 
         if (intent.treatment() != null) {
             final JsonCodec<TrafficTreatment> treatmentCodec =
                     context.codec(TrafficTreatment.class);
-            result.set("treatment", treatmentCodec.encode(intent.treatment(), context));
+            result.set(TREATMENT, treatmentCodec.encode(intent.treatment(), context));
         }
 
-        result.put("priority", intent.priority());
+        result.put(IntentCodec.PRIORITY, intent.priority());
 
         if (intent.constraints() != null) {
-            final ArrayNode jsonConstraints = result.putArray("constraints");
+            final ArrayNode jsonConstraints = result.putArray(CONSTRAINTS);
 
             if (intent.constraints() != null) {
                 final JsonCodec<Constraint> constraintCodec =
@@ -70,4 +78,41 @@
 
         return result;
     }
+
+    /**
+     * Extracts connectivity intent specific attributes from a JSON object
+     * and adds them to a builder.
+     *
+     * @param json root JSON object
+     * @param context code context
+     * @param builder builder to use for storing the attributes. Constraints,
+     *                selector and treatment are modified by this call.
+     */
+    public static void intentAttributes(ObjectNode json, CodecContext context,
+                                        ConnectivityIntent.Builder builder) {
+        JsonNode constraintsJson = json.get(CONSTRAINTS);
+        if (constraintsJson != null) {
+            JsonCodec<Constraint> constraintsCodec = context.codec(Constraint.class);
+            ArrayList<Constraint> constraints = new ArrayList<>(constraintsJson.size());
+            IntStream.range(0, constraintsJson.size())
+                    .forEach(i -> constraints.add(
+                            constraintsCodec.decode(get(constraintsJson, i),
+                                    context)));
+            builder.constraints(constraints);
+        }
+
+        ObjectNode selectorJson = get(json, SELECTOR);
+        if (selectorJson != null) {
+            JsonCodec<TrafficSelector> selectorCodec = context.codec(TrafficSelector.class);
+            TrafficSelector selector = selectorCodec.decode(selectorJson, context);
+            builder.selector(selector);
+        }
+
+        ObjectNode treatmentJson = get(json, TREATMENT);
+        if (treatmentJson != null) {
+            JsonCodec<TrafficTreatment> treatmentCodec = context.codec(TrafficTreatment.class);
+            TrafficTreatment treatment = treatmentCodec.decode(treatmentJson, context);
+            builder.treatment(treatment);
+        }
+    }
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/ConstraintCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/ConstraintCodec.java
index dd9563c..45728c5 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/ConstraintCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/ConstraintCodec.java
@@ -17,18 +17,8 @@
 
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
 import org.onosproject.net.intent.Constraint;
-import org.onosproject.net.intent.constraint.AnnotationConstraint;
-import org.onosproject.net.intent.constraint.BandwidthConstraint;
-import org.onosproject.net.intent.constraint.LambdaConstraint;
-import org.onosproject.net.intent.constraint.LatencyConstraint;
-import org.onosproject.net.intent.constraint.LinkTypeConstraint;
-import org.onosproject.net.intent.constraint.ObstacleConstraint;
-import org.onosproject.net.intent.constraint.WaypointConstraint;
 
-import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -38,170 +28,36 @@
  */
 public final class ConstraintCodec extends JsonCodec<Constraint> {
 
-    /**
-     * Encodes a latency constraint.
-     *
-     * @param constraint latency constraint to encode
-     * @param context code context
-     * @return JSON ObjectNode representing the constraint
-     */
-    private ObjectNode encodeLatencyConstraint(Constraint constraint,
-                                                CodecContext context) {
-        checkNotNull(constraint, "Duration constraint cannot be null");
-        final LatencyConstraint latencyConstraint =
-                (LatencyConstraint) constraint;
-        return context.mapper().createObjectNode()
-                .put("latencyMillis", latencyConstraint.latency().toMillis());
-    }
-
-    /**
-     * Encodes an obstacle constraint.
-     *
-     * @param constraint obstacle constraint to encode
-     * @param context code context
-     * @return JSON ObjectNode representing the constraint
-     */
-    private ObjectNode encodeObstacleConstraint(Constraint constraint,
-                                                CodecContext context) {
-        checkNotNull(constraint, "Obstacle constraint cannot be null");
-        final ObstacleConstraint obstacleConstraint =
-                (ObstacleConstraint) constraint;
-
-        final ObjectNode result = context.mapper().createObjectNode();
-        final ArrayNode jsonObstacles = result.putArray("obstacles");
-
-        for (DeviceId did : obstacleConstraint.obstacles()) {
-            jsonObstacles.add(did.toString());
-        }
-
-        return result;
-    }
-
-    /**
-     * Encodes a waypoint constraint.
-     *
-     * @param constraint waypoint constraint to encode
-     * @param context code context
-     * @return JSON ObjectNode representing the constraint
-     */
-    private ObjectNode encodeWaypointConstraint(Constraint constraint,
-                                                CodecContext context) {
-        checkNotNull(constraint, "Waypoint constraint cannot be null");
-        final WaypointConstraint waypointConstraint =
-                (WaypointConstraint) constraint;
-
-        final ObjectNode result = context.mapper().createObjectNode();
-        final ArrayNode jsonWaypoints = result.putArray("waypoints");
-
-        for (DeviceId did : waypointConstraint.waypoints()) {
-            jsonWaypoints.add(did.toString());
-        }
-
-        return result;
-    }
-
-    /**
-     * Encodes a annotation constraint.
-     *
-     * @param constraint annotation constraint to encode
-     * @param context code context
-     * @return JSON ObjectNode representing the constraint
-     */
-    private ObjectNode encodeAnnotationConstraint(Constraint constraint,
-                                                  CodecContext context) {
-        checkNotNull(constraint, "Annotation constraint cannot be null");
-        final AnnotationConstraint annotationConstraint =
-                (AnnotationConstraint) constraint;
-        return context.mapper().createObjectNode()
-                .put("key", annotationConstraint.key())
-                .put("threshold", annotationConstraint.threshold());
-    }
-
-    /**
-     * Encodes a bandwidth constraint.
-     *
-     * @param constraint bandwidth constraint to encode
-     * @param context code context
-     * @return JSON ObjectNode representing the constraint
-     */
-    private ObjectNode encodeBandwidthConstraint(Constraint constraint,
-                                                 CodecContext context) {
-        checkNotNull(constraint, "Bandwidth constraint cannot be null");
-        final BandwidthConstraint bandwidthConstraint =
-                (BandwidthConstraint) constraint;
-        return context.mapper().createObjectNode()
-                .put("bandwidth", bandwidthConstraint.bandwidth().toDouble());
-    }
-
-    /**
-     * Encodes a lambda constraint.
-     *
-     * @param constraint lambda constraint to encode
-     * @param context code context
-     * @return JSON ObjectNode representing the constraint
-     */
-    private ObjectNode encodeLambdaConstraint(Constraint constraint,
-                                              CodecContext context) {
-        checkNotNull(constraint, "Lambda constraint cannot be null");
-        final LambdaConstraint lambdaConstraint =
-                (LambdaConstraint) constraint;
-
-        return context.mapper().createObjectNode()
-                .put("lambda", lambdaConstraint.lambda().toInt());
-    }
-
-    /**
-     * Encodes a link type constraint.
-     *
-     * @param constraint link type constraint to encode
-     * @param context code context
-     * @return JSON ObjectNode representing the constraint
-     */
-    private ObjectNode encodeLinkTypeConstraint(Constraint constraint,
-                                                        CodecContext context) {
-        checkNotNull(constraint, "Link type constraint cannot be null");
-
-        final LinkTypeConstraint linkTypeConstraint =
-                (LinkTypeConstraint) constraint;
-
-        final ObjectNode result = context.mapper().createObjectNode()
-                .put("inclusive", linkTypeConstraint.isInclusive());
-
-        final ArrayNode jsonTypes = result.putArray("types");
-
-        if (linkTypeConstraint.types() != null) {
-            for (Link.Type type : linkTypeConstraint.types()) {
-                jsonTypes.add(type.name());
-            }
-        }
-
-        return result;
-    }
+    protected static final String MISSING_MEMBER_MESSAGE =
+            " member is required in Constraint";
+    protected static final String TYPE = "type";
+    protected static final String TYPES = "types";
+    protected static final String INCLUSIVE = "inclusive";
+    protected static final String KEY = "key";
+    protected static final String THRESHOLD = "threshold";
+    protected static final String BANDWIDTH = "bandwidth";
+    protected static final String LAMBDA = "lambda";
+    protected static final String LATENCY_MILLIS = "latencyMillis";
+    protected static final String OBSTACLES = "obstacles";
+    protected static final String WAYPOINTS = "waypoints";
 
     @Override
     public ObjectNode encode(Constraint constraint, CodecContext context) {
         checkNotNull(constraint, "Constraint cannot be null");
 
-        final ObjectNode result;
-        if (constraint instanceof BandwidthConstraint) {
-            result = encodeBandwidthConstraint(constraint, context);
-        } else if (constraint instanceof LambdaConstraint) {
-            result = encodeLambdaConstraint(constraint, context);
-        } else if (constraint instanceof LinkTypeConstraint) {
-            result = encodeLinkTypeConstraint(constraint, context);
-        } else if (constraint instanceof AnnotationConstraint) {
-            result = encodeAnnotationConstraint(constraint, context);
-        } else if (constraint instanceof LatencyConstraint) {
-            result = encodeLatencyConstraint(constraint, context);
-        } else if (constraint instanceof ObstacleConstraint) {
-            result = encodeObstacleConstraint(constraint, context);
-        } else if (constraint instanceof WaypointConstraint) {
-            result = encodeWaypointConstraint(constraint, context);
-        } else {
-            result = context.mapper().createObjectNode();
-        }
+        final EncodeConstraintCodec encodeCodec =
+                new EncodeConstraintCodec(constraint, context);
 
-        result.put("type", constraint.getClass().getSimpleName());
-        return result;
+        return encodeCodec.encode();
+    }
+
+    @Override
+    public Constraint decode(ObjectNode json, CodecContext context) {
+        checkNotNull(json, "JSON cannot be null");
+
+        final DecodeConstraintCodec decodeCodec =
+                new DecodeConstraintCodec(json);
+
+        return decodeCodec.decode();
     }
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/DecodeConstraintCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/DecodeConstraintCodec.java
new file mode 100644
index 0000000..3d63090
--- /dev/null
+++ b/core/common/src/main/java/org/onosproject/codec/impl/DecodeConstraintCodec.java
@@ -0,0 +1,225 @@
+/*
+ * 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.codec.impl;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.stream.IntStream;
+
+import org.onlab.util.Bandwidth;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.IndexedLambda;
+import org.onosproject.net.Link;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.constraint.AnnotationConstraint;
+import org.onosproject.net.intent.constraint.AsymmetricPathConstraint;
+import org.onosproject.net.intent.constraint.BandwidthConstraint;
+import org.onosproject.net.intent.constraint.LambdaConstraint;
+import org.onosproject.net.intent.constraint.LatencyConstraint;
+import org.onosproject.net.intent.constraint.LinkTypeConstraint;
+import org.onosproject.net.intent.constraint.ObstacleConstraint;
+import org.onosproject.net.intent.constraint.WaypointConstraint;
+import org.onosproject.net.resource.link.BandwidthResource;
+import org.onosproject.net.resource.link.LambdaResource;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * Constraint JSON decoder.
+ */
+public final class DecodeConstraintCodec {
+    private final ObjectNode json;
+
+    /**
+     * Constructs a constraint decoder.
+     *
+     * @param json object node to decode
+     */
+    public DecodeConstraintCodec(ObjectNode json) {
+        this.json = json;
+    }
+
+    /**
+     * Decodes a link type constraint.
+     *
+     * @return link type constraint object.
+     */
+    private Constraint decodeLinkTypeConstraint() {
+        boolean inclusive = nullIsIllegal(json.get(ConstraintCodec.INCLUSIVE),
+                ConstraintCodec.INCLUSIVE + ConstraintCodec.MISSING_MEMBER_MESSAGE).asBoolean();
+
+        JsonNode types = nullIsIllegal(json.get(ConstraintCodec.TYPES),
+                ConstraintCodec.TYPES + ConstraintCodec.MISSING_MEMBER_MESSAGE);
+        if (types.size() < 1) {
+            throw new IllegalArgumentException(
+                    "types array in link constraint must have at least one value");
+        }
+
+        ArrayList<Link.Type> typesEntries = new ArrayList<>(types.size());
+        IntStream.range(0, types.size())
+                .forEach(index ->
+                        typesEntries.add(Link.Type.valueOf(types.get(index).asText())));
+
+        return new LinkTypeConstraint(inclusive,
+                typesEntries.toArray(new Link.Type[types.size()]));
+    }
+
+    /**
+     * Decodes an annotation constraint.
+     *
+     * @return annotation constraint object.
+     */
+    private Constraint decodeAnnotationConstraint() {
+        String key = nullIsIllegal(json.get(ConstraintCodec.KEY),
+                ConstraintCodec.KEY + ConstraintCodec.MISSING_MEMBER_MESSAGE)
+                .asText();
+        double threshold = nullIsIllegal(json.get(ConstraintCodec.THRESHOLD),
+                ConstraintCodec.THRESHOLD + ConstraintCodec.MISSING_MEMBER_MESSAGE)
+                .asDouble();
+
+        return new AnnotationConstraint(key, threshold);
+    }
+
+    /**
+     * Decodes a lambda constraint.
+     *
+     * @return lambda constraint object.
+     */
+    private Constraint decodeLambdaConstraint() {
+        long lambda = nullIsIllegal(json.get(ConstraintCodec.LAMBDA),
+                ConstraintCodec.LAMBDA + ConstraintCodec.MISSING_MEMBER_MESSAGE)
+                .asLong();
+
+        return new LambdaConstraint(LambdaResource.valueOf(new IndexedLambda(lambda)));
+    }
+
+    /**
+     * Decodes a latency constraint.
+     *
+     * @return latency constraint object.
+     */
+    private Constraint decodeLatencyConstraint() {
+        long latencyMillis = nullIsIllegal(json.get(ConstraintCodec.LATENCY_MILLIS),
+                ConstraintCodec.LATENCY_MILLIS + ConstraintCodec.MISSING_MEMBER_MESSAGE)
+                .asLong();
+
+        return new LatencyConstraint(Duration.ofMillis(latencyMillis));
+    }
+
+    /**
+     * Decodes an obstacle constraint.
+     *
+     * @return obstacle constraint object.
+     */
+    private Constraint decodeObstacleConstraint() {
+        JsonNode obstacles = nullIsIllegal(json.get(ConstraintCodec.OBSTACLES),
+                ConstraintCodec.OBSTACLES + ConstraintCodec.MISSING_MEMBER_MESSAGE);
+        if (obstacles.size() < 1) {
+            throw new IllegalArgumentException(
+                    "obstacles array in obstacles constraint must have at least one value");
+        }
+
+        ArrayList<DeviceId> obstacleEntries = new ArrayList<>(obstacles.size());
+        IntStream.range(0, obstacles.size())
+                .forEach(index ->
+                        obstacleEntries.add(DeviceId.deviceId(obstacles.get(index).asText())));
+
+        return new ObstacleConstraint(
+                obstacleEntries.toArray(new DeviceId[obstacles.size()]));
+    }
+
+    /**
+     * Decodes a waypoint constraint.
+     *
+     * @return waypoint constraint object.
+     */
+    private Constraint decodeWaypointConstraint() {
+        JsonNode waypoints = nullIsIllegal(json.get(ConstraintCodec.WAYPOINTS),
+                ConstraintCodec.WAYPOINTS + ConstraintCodec.MISSING_MEMBER_MESSAGE);
+        if (waypoints.size() < 1) {
+            throw new IllegalArgumentException(
+                    "obstacles array in obstacles constraint must have at least one value");
+        }
+
+        ArrayList<DeviceId> waypointEntries = new ArrayList<>(waypoints.size());
+        IntStream.range(0, waypoints.size())
+                .forEach(index ->
+                        waypointEntries.add(DeviceId.deviceId(waypoints.get(index).asText())));
+
+        return new WaypointConstraint(
+                waypointEntries.toArray(new DeviceId[waypoints.size()]));
+    }
+
+    /**
+     * Decodes an asymmetric path constraint.
+     *
+     * @return asymmetric path constraint object.
+     */
+    private Constraint decodeAsymmetricPathConstraint() {
+        return new AsymmetricPathConstraint();
+    }
+
+    /**
+     * Decodes a bandwidth constraint.
+     *
+     * @return bandwidth constraint object.
+     */
+    private Constraint decodeBandwidthConstraint() {
+        double bandwidth = nullIsIllegal(json.get(ConstraintCodec.BANDWIDTH),
+                ConstraintCodec.BANDWIDTH + ConstraintCodec.MISSING_MEMBER_MESSAGE)
+                .asDouble();
+
+        return new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(bandwidth)));
+    }
+
+    /**
+     * Decodes the given constraint.
+     *
+     * @return constraint object.
+     */
+    public Constraint decode() {
+        final String type = nullIsIllegal(json.get(ConstraintCodec.TYPE),
+                ConstraintCodec.TYPE + ConstraintCodec.MISSING_MEMBER_MESSAGE)
+                .asText();
+
+        if (type.equals(BandwidthConstraint.class.getSimpleName())) {
+            return decodeBandwidthConstraint();
+        } else if (type.equals(LambdaConstraint.class.getSimpleName())) {
+            return decodeLambdaConstraint();
+        } else if (type.equals(LinkTypeConstraint.class.getSimpleName())) {
+            return decodeLinkTypeConstraint();
+        } else if (type.equals(AnnotationConstraint.class.getSimpleName())) {
+            return decodeAnnotationConstraint();
+        } else if (type.equals(LatencyConstraint.class.getSimpleName())) {
+            return decodeLatencyConstraint();
+        } else if (type.equals(ObstacleConstraint.class.getSimpleName())) {
+            return decodeObstacleConstraint();
+        } else if (type.equals(WaypointConstraint.class.getSimpleName())) {
+            return decodeWaypointConstraint();
+        } else if (type.equals(AsymmetricPathConstraint.class.getSimpleName())) {
+            return decodeAsymmetricPathConstraint();
+        } else if (type.equals(LinkTypeConstraint.class.getSimpleName())) {
+            return decodeLinkTypeConstraint();
+        } else if (type.equals(AnnotationConstraint.class.getSimpleName())) {
+            return decodeAnnotationConstraint();
+        }
+        throw new IllegalArgumentException("Instruction type "
+                + type + " is not supported");
+    }
+}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/EncodeConstraintCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/EncodeConstraintCodec.java
new file mode 100644
index 0000000..7e18cac
--- /dev/null
+++ b/core/common/src/main/java/org/onosproject/codec/impl/EncodeConstraintCodec.java
@@ -0,0 +1,201 @@
+/*
+ * 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.codec.impl;
+
+import org.onosproject.codec.CodecContext;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.constraint.AnnotationConstraint;
+import org.onosproject.net.intent.constraint.BandwidthConstraint;
+import org.onosproject.net.intent.constraint.LambdaConstraint;
+import org.onosproject.net.intent.constraint.LatencyConstraint;
+import org.onosproject.net.intent.constraint.LinkTypeConstraint;
+import org.onosproject.net.intent.constraint.ObstacleConstraint;
+import org.onosproject.net.intent.constraint.WaypointConstraint;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of encoder for constraint JSON codec.
+ */
+public final class EncodeConstraintCodec {
+
+    private final Constraint constraint;
+    private final CodecContext context;
+
+    /**
+     * Constructs a constraint encoder.
+     *
+     * @param constraint constraint to encode
+     * @param context to use for look ups
+     */
+    public EncodeConstraintCodec(Constraint constraint, CodecContext context) {
+        this.constraint = constraint;
+        this.context = context;
+    }
+
+    /**
+     * Encodes a latency constraint.
+     *
+     * @return JSON ObjectNode representing the constraint
+     */
+    private ObjectNode encodeLatencyConstraint() {
+        checkNotNull(constraint, "Duration constraint cannot be null");
+        final LatencyConstraint latencyConstraint =
+                (LatencyConstraint) constraint;
+        return context.mapper().createObjectNode()
+                .put("latencyMillis", latencyConstraint.latency().toMillis());
+    }
+
+    /**
+     * Encodes an obstacle constraint.
+     *
+     * @return JSON ObjectNode representing the constraint
+     */
+    private ObjectNode encodeObstacleConstraint() {
+        checkNotNull(constraint, "Obstacle constraint cannot be null");
+        final ObstacleConstraint obstacleConstraint =
+                (ObstacleConstraint) constraint;
+
+        final ObjectNode result = context.mapper().createObjectNode();
+        final ArrayNode jsonObstacles = result.putArray("obstacles");
+
+        for (DeviceId did : obstacleConstraint.obstacles()) {
+            jsonObstacles.add(did.toString());
+        }
+
+        return result;
+    }
+
+    /**
+     * Encodes a waypoint constraint.
+     *
+     * @return JSON ObjectNode representing the constraint
+     */
+    private ObjectNode encodeWaypointConstraint() {
+        checkNotNull(constraint, "Waypoint constraint cannot be null");
+        final WaypointConstraint waypointConstraint =
+                (WaypointConstraint) constraint;
+
+        final ObjectNode result = context.mapper().createObjectNode();
+        final ArrayNode jsonWaypoints = result.putArray("waypoints");
+
+        for (DeviceId did : waypointConstraint.waypoints()) {
+            jsonWaypoints.add(did.toString());
+        }
+
+        return result;
+    }
+
+    /**
+     * Encodes a annotation constraint.
+     *
+     * @return JSON ObjectNode representing the constraint
+     */
+    private ObjectNode encodeAnnotationConstraint() {
+        checkNotNull(constraint, "Annotation constraint cannot be null");
+        final AnnotationConstraint annotationConstraint =
+                (AnnotationConstraint) constraint;
+        return context.mapper().createObjectNode()
+                .put("key", annotationConstraint.key())
+                .put("threshold", annotationConstraint.threshold());
+    }
+
+    /**
+     * Encodes a bandwidth constraint.
+     *
+     * @return JSON ObjectNode representing the constraint
+     */
+    private ObjectNode encodeBandwidthConstraint() {
+        checkNotNull(constraint, "Bandwidth constraint cannot be null");
+        final BandwidthConstraint bandwidthConstraint =
+                (BandwidthConstraint) constraint;
+        return context.mapper().createObjectNode()
+                .put("bandwidth", bandwidthConstraint.bandwidth().toDouble());
+    }
+
+    /**
+     * Encodes a lambda constraint.
+     *
+     * @return JSON ObjectNode representing the constraint
+     */
+    private ObjectNode encodeLambdaConstraint() {
+        checkNotNull(constraint, "Lambda constraint cannot be null");
+        final LambdaConstraint lambdaConstraint =
+                (LambdaConstraint) constraint;
+
+        return context.mapper().createObjectNode()
+                .put("lambda", lambdaConstraint.lambda().toInt());
+    }
+
+    /**
+     * Encodes a link type constraint.
+     *
+     * @return JSON ObjectNode representing the constraint
+     */
+    private ObjectNode encodeLinkTypeConstraint() {
+        checkNotNull(constraint, "Link type constraint cannot be null");
+
+        final LinkTypeConstraint linkTypeConstraint =
+                (LinkTypeConstraint) constraint;
+
+        final ObjectNode result = context.mapper().createObjectNode()
+                .put(ConstraintCodec.INCLUSIVE, linkTypeConstraint.isInclusive());
+
+        final ArrayNode jsonTypes = result.putArray(ConstraintCodec.TYPES);
+
+        if (linkTypeConstraint.types() != null) {
+            for (Link.Type type : linkTypeConstraint.types()) {
+                jsonTypes.add(type.name());
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Encodes the constraint in JSON.
+     *
+     * @return JSON node
+     */
+    public ObjectNode encode() {
+        final ObjectNode result;
+        if (constraint instanceof BandwidthConstraint) {
+            result = encodeBandwidthConstraint();
+        } else if (constraint instanceof LambdaConstraint) {
+            result = encodeLambdaConstraint();
+        } else if (constraint instanceof LinkTypeConstraint) {
+            result = encodeLinkTypeConstraint();
+        } else if (constraint instanceof AnnotationConstraint) {
+            result = encodeAnnotationConstraint();
+        } else if (constraint instanceof LatencyConstraint) {
+            result = encodeLatencyConstraint();
+        } else if (constraint instanceof ObstacleConstraint) {
+            result = encodeObstacleConstraint();
+        } else if (constraint instanceof WaypointConstraint) {
+            result = encodeWaypointConstraint();
+        } else {
+            result = context.mapper().createObjectNode();
+        }
+
+        result.put(ConstraintCodec.TYPE, constraint.getClass().getSimpleName());
+        return result;
+    }
+}
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java
index bd81ee0..6c02841 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/FlowRuleCodec.java
@@ -75,14 +75,14 @@
                 DEVICE_ID + MISSING_MEMBER_MESSAGE).asText());
         resultBuilder.forDevice(deviceId);
 
-        ObjectNode treatmentJson = (ObjectNode) json.get(TREATMENT);
+        ObjectNode treatmentJson = get(json, TREATMENT);
         if (treatmentJson != null) {
             JsonCodec<TrafficTreatment> treatmentCodec =
                     context.codec(TrafficTreatment.class);
             resultBuilder.withTreatment(treatmentCodec.decode(treatmentJson, context));
         }
 
-        ObjectNode selectorJson = (ObjectNode) json.get(SELECTOR);
+        ObjectNode selectorJson = get(json, SELECTOR);
         if (selectorJson != null) {
             JsonCodec<TrafficSelector> selectorCodec =
                     context.codec(TrafficSelector.class);
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/HostToHostIntentCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/HostToHostIntentCodec.java
index 59ceb2b..597ab55 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/HostToHostIntentCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/HostToHostIntentCodec.java
@@ -17,18 +17,23 @@
 
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.net.HostId;
 import org.onosproject.net.intent.ConnectivityIntent;
 import org.onosproject.net.intent.HostToHostIntent;
 
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
 
 /**
  * Host to host intent codec.
  */
 public final class HostToHostIntentCodec extends JsonCodec<HostToHostIntent> {
 
+    private static final String ONE = "one";
+    private static final String TWO = "two";
+
     @Override
     public ObjectNode encode(HostToHostIntent intent, CodecContext context) {
         checkNotNull(intent, "Host to host intent cannot be null");
@@ -39,9 +44,27 @@
 
         final String one = intent.one().toString();
         final String two = intent.two().toString();
-        result.put("one", one);
-        result.put("two", two);
+        result.put(ONE, one);
+        result.put(TWO, two);
 
         return result;
     }
+
+    @Override
+    public HostToHostIntent decode(ObjectNode json, CodecContext context) {
+        HostToHostIntent.Builder builder = HostToHostIntent.builder();
+
+        IntentCodec.intentAttributes(json, context, builder);
+        ConnectivityIntentCodec.intentAttributes(json, context, builder);
+
+        String one = nullIsIllegal(json.get(ONE),
+                ONE + IntentCodec.MISSING_MEMBER_MESSAGE).asText();
+        builder.one(HostId.hostId(one));
+
+        String two = nullIsIllegal(json.get(TWO),
+                TWO + IntentCodec.MISSING_MEMBER_MESSAGE).asText();
+        builder.two(HostId.hostId(two));
+
+        return builder.build();
+    }
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/IntentCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/IntentCodec.java
index a45ae87..36ff8fa 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/IntentCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/IntentCodec.java
@@ -17,31 +17,46 @@
 
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.CoreService;
 import org.onosproject.net.NetworkResource;
+import org.onosproject.net.intent.HostToHostIntent;
 import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.PointToPointIntent;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
 
 /**
  * Intent JSON codec.
  */
 public final class IntentCodec extends JsonCodec<Intent> {
 
+    protected static final String TYPE = "type";
+    protected static final String ID = "id";
+    protected static final String APP_ID = "appId";
+    protected static final String DETAILS = "details";
+    protected static final String STATE = "state";
+    protected static final String PRIORITY = "priority";
+    protected static final String RESOURCES = "resources";
+    protected static final String MISSING_MEMBER_MESSAGE =
+            " member is required in Intent";
+
     @Override
     public ObjectNode encode(Intent intent, CodecContext context) {
         checkNotNull(intent, "Intent cannot be null");
         final ObjectNode result = context.mapper().createObjectNode()
-                .put("type", intent.getClass().getSimpleName())
-                .put("id", intent.id().toString())
-                .put("appId", intent.appId().toString())
-                .put("details", intent.toString());
+                .put(TYPE, intent.getClass().getSimpleName())
+                .put(ID, intent.id().toString())
+                .put(APP_ID, intent.appId().toString())
+                .put(DETAILS, intent.toString());
 
-        final ArrayNode jsonResources = result.putArray("resources");
+        final ArrayNode jsonResources = result.putArray(RESOURCES);
 
         for (final NetworkResource resource : intent.resources()) {
             jsonResources.add(resource.toString());
@@ -50,9 +65,47 @@
         IntentService service = context.getService(IntentService.class);
         IntentState state = service.getIntentState(intent.key());
         if (state != null) {
-            result.put("state", state.toString());
+            result.put(STATE, state.toString());
         }
 
         return result;
     }
+
+    @Override
+    public Intent decode(ObjectNode json, CodecContext context) {
+        checkNotNull(json, "JSON cannot be null");
+
+        String type = nullIsIllegal(json.get(TYPE),
+                TYPE + MISSING_MEMBER_MESSAGE).asText();
+
+        if (type.equals(PointToPointIntent.class.getSimpleName())) {
+            return context.codec(PointToPointIntent.class).decode(json, context);
+        } else if (type.equals(HostToHostIntent.class.getSimpleName())) {
+            return context.codec(HostToHostIntent.class).decode(json, context);
+        }
+
+        throw new IllegalArgumentException("Intent type "
+                + type + " is not supported");
+    }
+
+    /**
+     * Extracts base intent specific attributes from a JSON object
+     * and adds them to a builder.
+     *
+     * @param json root JSON object
+     * @param context code context
+     * @param builder builder to use for storing the attributes
+     */
+    public static void intentAttributes(ObjectNode json, CodecContext context,
+                                    Intent.Builder builder) {
+        short appId = (short) nullIsIllegal(json.get(IntentCodec.APP_ID),
+                IntentCodec.TYPE + IntentCodec.MISSING_MEMBER_MESSAGE).asInt();
+        CoreService service = context.getService(CoreService.class);
+        builder.appId(service.getAppId(appId));
+
+        JsonNode priorityJson = json.get(IntentCodec.PRIORITY);
+        if (priorityJson != null) {
+            builder.priority(priorityJson.asInt());
+        }
+    }
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/LinkCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/LinkCodec.java
index 491d751..14ee9b7 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/LinkCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/LinkCodec.java
@@ -70,8 +70,8 @@
         // TODO: add providerId to JSON if we need to recover them.
         ProviderId pid = new ProviderId("json", "LinkCodec");
 
-        ConnectPoint src = codec.decode((ObjectNode) json.get(SRC), context);
-        ConnectPoint dst = codec.decode((ObjectNode) json.get(DST), context);
+        ConnectPoint src = codec.decode(get(json, SRC), context);
+        ConnectPoint dst = codec.decode(get(json, DST), context);
         Type type = Type.valueOf(json.get(TYPE).asText());
         Annotations annotations = extractAnnotations(json, context);
 
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/PointToPointIntentCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/PointToPointIntentCodec.java
index 45f2f3d..20df489 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/PointToPointIntentCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/PointToPointIntentCodec.java
@@ -24,12 +24,16 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
 
 /**
  * Point to point intent codec.
  */
 public final class PointToPointIntentCodec extends JsonCodec<PointToPointIntent> {
 
+    private static final String INGRESS_POINT = "ingressPoint";
+    private static final String EGRESS_POINT = "egressPoint";
+
     @Override
     public ObjectNode encode(PointToPointIntent intent, CodecContext context) {
         checkNotNull(intent, "Point to point intent cannot be null");
@@ -45,9 +49,32 @@
         final ObjectNode egress =
                 connectPointCodec.encode(intent.egressPoint(), context);
 
-        result.set("ingressPoint", ingress);
-        result.set("egressPoint", egress);
+        result.set(INGRESS_POINT, ingress);
+        result.set(EGRESS_POINT, egress);
 
         return result;
     }
+
+
+    @Override
+    public PointToPointIntent decode(ObjectNode json, CodecContext context) {
+        PointToPointIntent.Builder builder = PointToPointIntent.builder();
+
+        IntentCodec.intentAttributes(json, context, builder);
+        ConnectivityIntentCodec.intentAttributes(json, context, builder);
+
+        ObjectNode ingressJson = nullIsIllegal(get(json, INGRESS_POINT),
+                INGRESS_POINT + IntentCodec.MISSING_MEMBER_MESSAGE);
+        ConnectPoint ingress = context.codec(ConnectPoint.class)
+                .decode(ingressJson, context);
+        builder.ingressPoint(ingress);
+
+        ObjectNode egressJson = nullIsIllegal(get(json, EGRESS_POINT),
+                EGRESS_POINT + IntentCodec.MISSING_MEMBER_MESSAGE);
+        ConnectPoint egress = context.codec(ConnectPoint.class)
+                .decode(egressJson, context);
+        builder.egressPoint(egress);
+
+        return builder.build();
+    }
 }
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/TrafficSelectorCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/TrafficSelectorCodec.java
index eea9fae..24ebef1 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/TrafficSelectorCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/TrafficSelectorCodec.java
@@ -63,7 +63,7 @@
         if (criteriaJson != null) {
             IntStream.range(0, criteriaJson.size())
                     .forEach(i -> builder.add(
-                            criterionCodec.decode((ObjectNode) criteriaJson.get(i),
+                            criterionCodec.decode(get(criteriaJson, i),
                                     context)));
         }
         return builder.build();
diff --git a/core/common/src/main/java/org/onosproject/codec/impl/TrafficTreatmentCodec.java b/core/common/src/main/java/org/onosproject/codec/impl/TrafficTreatmentCodec.java
index edfe10c..0d7fb42 100644
--- a/core/common/src/main/java/org/onosproject/codec/impl/TrafficTreatmentCodec.java
+++ b/core/common/src/main/java/org/onosproject/codec/impl/TrafficTreatmentCodec.java
@@ -68,7 +68,7 @@
         if (instructionsJson != null) {
             IntStream.range(0, instructionsJson.size())
                     .forEach(i -> builder.add(
-                            instructionsCodec.decode((ObjectNode) instructionsJson.get(i),
+                            instructionsCodec.decode(get(instructionsJson, i),
                                     context)));
         }
         return builder.build();
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/ConstraintCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/ConstraintCodecTest.java
new file mode 100644
index 0000000..2a47d11
--- /dev/null
+++ b/core/common/src/test/java/org/onosproject/codec/impl/ConstraintCodecTest.java
@@ -0,0 +1,202 @@
+/*
+ * 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.codec.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.Link;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.constraint.AnnotationConstraint;
+import org.onosproject.net.intent.constraint.AsymmetricPathConstraint;
+import org.onosproject.net.intent.constraint.BandwidthConstraint;
+import org.onosproject.net.intent.constraint.LambdaConstraint;
+import org.onosproject.net.intent.constraint.LatencyConstraint;
+import org.onosproject.net.intent.constraint.LinkTypeConstraint;
+import org.onosproject.net.intent.constraint.ObstacleConstraint;
+import org.onosproject.net.intent.constraint.WaypointConstraint;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onosproject.net.NetTestTools.APP_ID;
+import static org.onosproject.net.NetTestTools.did;
+
+/**
+ * Unit tests for Constraint codec.
+ */
+public class ConstraintCodecTest {
+
+    MockCodecContext context;
+    JsonCodec<Constraint> constraintCodec;
+    final CoreService mockCoreService = createMock(CoreService.class);
+
+    /**
+     * Sets up for each test.  Creates a context and fetches the flow rule
+     * codec.
+     */
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        constraintCodec = context.codec(Constraint.class);
+        assertThat(constraintCodec, notNullValue());
+
+        expect(mockCoreService.registerApplication(FlowRuleCodec.REST_APP_ID))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Reads in a constraint from the given resource and decodes it.
+     *
+     * @param resourceName resource to use to read the JSON for the constraint
+     * @return decoded constraint
+     */
+    private Constraint getConstraint(String resourceName) {
+        InputStream jsonStream = ConstraintCodecTest.class
+                .getResourceAsStream(resourceName);
+        try {
+            JsonNode json = context.mapper().readTree(jsonStream);
+            assertThat(json, notNullValue());
+            Constraint constraint = constraintCodec.decode((ObjectNode) json, context);
+            assertThat(constraint, notNullValue());
+            return checkNotNull(constraint);
+        } catch (IOException ioe) {
+            Assert.fail(ioe.getMessage());
+            throw new IllegalStateException("cannot happen");
+        }
+    }
+
+
+    /**
+     * Tests link type constraint.
+     */
+    @Test
+    public void linkTypeConstraint() {
+        Constraint constraint = getConstraint("LinkTypeConstraint.json");
+        assertThat(constraint, instanceOf(LinkTypeConstraint.class));
+
+        LinkTypeConstraint linkTypeConstraint = (LinkTypeConstraint) constraint;
+        assertThat(linkTypeConstraint.isInclusive(), is(false));
+        assertThat(linkTypeConstraint.types(), hasSize(2));
+        assertThat(linkTypeConstraint.types(), hasItem(Link.Type.OPTICAL));
+        assertThat(linkTypeConstraint.types(), hasItem(Link.Type.DIRECT));
+    }
+
+    /**
+     * Tests annotation constraint.
+     */
+    @Test
+    public void annotationConstraint() {
+        Constraint constraint = getConstraint("AnnotationConstraint.json");
+        assertThat(constraint, instanceOf(AnnotationConstraint.class));
+
+        AnnotationConstraint annotationConstraint = (AnnotationConstraint) constraint;
+        assertThat(annotationConstraint.key(), is("key"));
+        assertThat(annotationConstraint.threshold(), is(123.0D));
+    }
+
+    /**
+     * Tests bandwidth constraint.
+     */
+    @Test
+    public void bandwidthConstraint() {
+        Constraint constraint = getConstraint("BandwidthConstraint.json");
+        assertThat(constraint, instanceOf(BandwidthConstraint.class));
+
+        BandwidthConstraint bandwidthConstraint = (BandwidthConstraint) constraint;
+        assertThat(bandwidthConstraint.bandwidth().toDouble(), is(345.678D));
+    }
+
+    /**
+     * Tests lambda constraint.
+     */
+    @Test
+    public void lambdaConstraint() {
+        Constraint constraint = getConstraint("LambdaConstraint.json");
+        assertThat(constraint, instanceOf(LambdaConstraint.class));
+
+        LambdaConstraint lambdaConstraint = (LambdaConstraint) constraint;
+        assertThat(lambdaConstraint.lambda().toInt(), is(444));
+    }
+
+    /**
+     * Tests latency constraint.
+     */
+    @Test
+    public void latencyConstraint() {
+        Constraint constraint = getConstraint("LatencyConstraint.json");
+        assertThat(constraint, instanceOf(LatencyConstraint.class));
+
+        LatencyConstraint latencyConstraint = (LatencyConstraint) constraint;
+        assertThat(latencyConstraint.latency().toMillis(), is(111L));
+    }
+
+    /**
+     * Tests obstacle constraint.
+     */
+    @Test
+    public void obstacleConstraint() {
+        Constraint constraint = getConstraint("ObstacleConstraint.json");
+        assertThat(constraint, instanceOf(ObstacleConstraint.class));
+
+        ObstacleConstraint obstacleConstraint = (ObstacleConstraint) constraint;
+
+        assertThat(obstacleConstraint.obstacles(), hasItem(did("dev1")));
+        assertThat(obstacleConstraint.obstacles(), hasItem(did("dev2")));
+        assertThat(obstacleConstraint.obstacles(), hasItem(did("dev3")));
+    }
+
+    /**
+     * Tests waypoint constaint.
+     */
+    @Test
+    public void waypointConstraint() {
+        Constraint constraint = getConstraint("WaypointConstraint.json");
+        assertThat(constraint, instanceOf(WaypointConstraint.class));
+
+        WaypointConstraint waypointConstraint = (WaypointConstraint) constraint;
+
+        assertThat(waypointConstraint.waypoints(), hasItem(did("devA")));
+        assertThat(waypointConstraint.waypoints(), hasItem(did("devB")));
+        assertThat(waypointConstraint.waypoints(), hasItem(did("devC")));
+    }
+
+    /**
+     * Tests asymmetric path constraint.
+     */
+    @Test
+    public void asymmetricPathConstraint() {
+        Constraint constraint = getConstraint("AsymmetricPathConstraint.json");
+        assertThat(constraint, instanceOf(AsymmetricPathConstraint.class));
+    }
+}
diff --git a/core/common/src/test/java/org/onosproject/codec/impl/IntentCodecTest.java b/core/common/src/test/java/org/onosproject/codec/impl/IntentCodecTest.java
index cdbd533..0824bd5 100644
--- a/core/common/src/test/java/org/onosproject/codec/impl/IntentCodecTest.java
+++ b/core/common/src/test/java/org/onosproject/codec/impl/IntentCodecTest.java
@@ -15,6 +15,8 @@
  */
 package org.onosproject.codec.impl;
 
+import java.io.IOException;
+import java.io.InputStream;
 import java.time.Duration;
 import java.util.List;
 
@@ -26,6 +28,7 @@
 import org.onlab.util.Bandwidth;
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
 import org.onosproject.core.DefaultApplicationId;
 import org.onosproject.net.ChannelSpacing;
 import org.onosproject.net.ConnectPoint;
@@ -43,9 +46,13 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.intent.AbstractIntentTest;
 import org.onosproject.net.intent.Constraint;
 import org.onosproject.net.intent.HostToHostIntent;
+import org.onosproject.net.intent.Intent;
 import org.onosproject.net.intent.IntentService;
 import org.onosproject.net.intent.IntentServiceAdapter;
 import org.onosproject.net.intent.PointToPointIntent;
@@ -59,14 +66,22 @@
 import org.onosproject.net.resource.link.BandwidthResource;
 import org.onosproject.net.resource.link.LambdaResource;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.ImmutableList;
 
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.onosproject.codec.impl.IntentJsonMatcher.matchesIntent;
 import static org.onosproject.net.NetTestTools.did;
 import static org.onosproject.net.NetTestTools.hid;
+import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
 
 /**
  * Unit tests for the host to host intent class codec.
@@ -81,11 +96,16 @@
     final TrafficTreatment emptyTreatment =
             DefaultTrafficTreatment.emptyTreatment();
     private final MockCodecContext context = new MockCodecContext();
+    final CoreService mockCoreService = createMock(CoreService.class);
 
     @Before
     public void setUpIntentService() {
         final IntentService mockIntentService = new IntentServiceAdapter();
         context.registerService(IntentService.class, mockIntentService);
+        context.registerService(CoreService.class, mockCoreService);
+        expect(mockCoreService.getAppId((short) 2))
+                .andReturn(new DefaultApplicationId(2, "app"));
+        replay(mockCoreService);
     }
 
     /**
@@ -161,13 +181,13 @@
 
         final List<Constraint> constraints =
                 ImmutableList.of(
-                    new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(1.0))),
-                    new LambdaConstraint(LambdaResource.valueOf(3)),
-                    new AnnotationConstraint("key", 33.0),
-                    new AsymmetricPathConstraint(),
-                    new LatencyConstraint(Duration.ofSeconds(2)),
-                    new ObstacleConstraint(did1, did2),
-                    new WaypointConstraint(did3));
+                        new BandwidthConstraint(new BandwidthResource(Bandwidth.bps(1.0))),
+                        new LambdaConstraint(LambdaResource.valueOf(3)),
+                        new AnnotationConstraint("key", 33.0),
+                        new AsymmetricPathConstraint(),
+                        new LatencyConstraint(Duration.ofSeconds(2)),
+                        new ObstacleConstraint(did1, did2),
+                        new WaypointConstraint(did3));
 
         final PointToPointIntent intent =
                 PointToPointIntent.builder()
@@ -188,4 +208,81 @@
         assertThat(intentJson, matchesIntent(intent));
 
     }
+
+    /**
+     * Reads in a rule from the given resource and decodes it.
+     *
+     * @param resourceName resource to use to read the JSON for the rule
+     * @return decoded flow rule
+     * @throws IOException if processing the resource fails
+     */
+    private Intent getIntent(String resourceName, JsonCodec intentCodec) throws IOException {
+        InputStream jsonStream = FlowRuleCodecTest.class
+                .getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        assertThat(json, notNullValue());
+        Intent intent = (Intent) intentCodec.decode((ObjectNode) json, context);
+        assertThat(intent, notNullValue());
+        return intent;
+    }
+
+    /**
+     * Tests the point to point intent JSON codec.
+     *
+     * @throws IOException if JSON processing fails
+     */
+    @Test
+    public void decodePointToPointIntent() throws IOException {
+        JsonCodec<Intent> intentCodec = context.codec(Intent.class);
+        assertThat(intentCodec, notNullValue());
+
+        Intent intent = getIntent("PointToPointIntent.json", intentCodec);
+        assertThat(intent, notNullValue());
+        assertThat(intent, instanceOf(PointToPointIntent.class));
+
+        PointToPointIntent pointIntent = (PointToPointIntent) intent;
+        assertThat(pointIntent.priority(), is(55));
+        assertThat(pointIntent.ingressPoint().deviceId(), is(did("0000000000000001")));
+        assertThat(pointIntent.ingressPoint().port(), is(PortNumber.portNumber(1)));
+        assertThat(pointIntent.egressPoint().deviceId(), is(did("0000000000000007")));
+        assertThat(pointIntent.egressPoint().port(), is(PortNumber.portNumber(2)));
+
+        assertThat(pointIntent.constraints(), hasSize(1));
+
+        assertThat(pointIntent.selector(), notNullValue());
+        assertThat(pointIntent.selector().criteria(), hasSize(1));
+        Criterion criterion1 = pointIntent.selector().criteria().iterator().next();
+        assertThat(criterion1, instanceOf(EthCriterion.class));
+        EthCriterion ethCriterion = (EthCriterion) criterion1;
+        assertThat(ethCriterion.mac().toString(), is("11:22:33:44:55:66"));
+        assertThat(ethCriterion.type().name(), is("ETH_DST"));
+
+        assertThat(pointIntent.treatment(), notNullValue());
+        assertThat(pointIntent.treatment().allInstructions(), hasSize(1));
+        Instruction instruction1 = pointIntent.treatment().allInstructions().iterator().next();
+        assertThat(instruction1, instanceOf(ModEtherInstruction.class));
+        ModEtherInstruction ethInstruction = (ModEtherInstruction) instruction1;
+        assertThat(ethInstruction.mac().toString(), is("22:33:44:55:66:77"));
+        assertThat(ethInstruction.type().toString(), is("L2MODIFICATION"));
+        assertThat(ethInstruction.subtype().toString(), is("ETH_SRC"));
+    }
+
+    /**
+     * Tests the host to host intent JSON codec.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void decodeHostToHostIntent() throws IOException {
+        JsonCodec<Intent> intentCodec = context.codec(Intent.class);
+        assertThat(intentCodec, notNullValue());
+
+        Intent intent = getIntent("HostToHostIntent.json", intentCodec);
+        assertThat(intent, notNullValue());
+        assertThat(intent, instanceOf(HostToHostIntent.class));
+
+        HostToHostIntent hostIntent = (HostToHostIntent) intent;
+        assertThat(hostIntent.priority(), is(7));
+        assertThat(hostIntent.constraints(), hasSize(1));
+    }
 }
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/AnnotationConstraint.json b/core/common/src/test/resources/org/onosproject/codec/impl/AnnotationConstraint.json
new file mode 100644
index 0000000..aaa72a7
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/AnnotationConstraint.json
@@ -0,0 +1,5 @@
+{
+  "type":"AnnotationConstraint",
+  "key":"key",
+  "threshold":123.0
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/AsymmetricPathConstraint.json b/core/common/src/test/resources/org/onosproject/codec/impl/AsymmetricPathConstraint.json
new file mode 100644
index 0000000..340bdba
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/AsymmetricPathConstraint.json
@@ -0,0 +1,3 @@
+{
+  "type":"AsymmetricPathConstraint"
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/BandwidthConstraint.json b/core/common/src/test/resources/org/onosproject/codec/impl/BandwidthConstraint.json
new file mode 100644
index 0000000..3475006
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/BandwidthConstraint.json
@@ -0,0 +1,4 @@
+{
+  "type":"BandwidthConstraint",
+  "bandwidth":345.678
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/HostToHostIntent.json b/core/common/src/test/resources/org/onosproject/codec/impl/HostToHostIntent.json
new file mode 100644
index 0000000..5010c48
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/HostToHostIntent.json
@@ -0,0 +1,19 @@
+{
+  "type": "HostToHostIntent",
+  "appId": 2,
+  "selector": {"criteria": []},
+  "treatment": {
+    "instructions": [],
+    "deferred": []
+  },
+  "priority": 7,
+  "constraints": [
+    {
+      "inclusive": false,
+      "types": ["OPTICAL"],
+      "type": "LinkTypeConstraint"
+    }
+  ],
+  "one": "00:00:00:00:00:02/-1",
+  "two": "00:00:00:00:00:05/-1"
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/LambdaConstraint.json b/core/common/src/test/resources/org/onosproject/codec/impl/LambdaConstraint.json
new file mode 100644
index 0000000..4ac3763
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/LambdaConstraint.json
@@ -0,0 +1,4 @@
+{
+  "type":"LambdaConstraint",
+  "lambda":444
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/LatencyConstraint.json b/core/common/src/test/resources/org/onosproject/codec/impl/LatencyConstraint.json
new file mode 100644
index 0000000..1c46e5e
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/LatencyConstraint.json
@@ -0,0 +1,4 @@
+{
+  "type":"LatencyConstraint",
+  "latencyMillis":111
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/LinkTypeConstraint.json b/core/common/src/test/resources/org/onosproject/codec/impl/LinkTypeConstraint.json
new file mode 100644
index 0000000..8b766da
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/LinkTypeConstraint.json
@@ -0,0 +1,5 @@
+{
+  "inclusive":false,
+  "types":["DIRECT","OPTICAL"],
+  "type":"LinkTypeConstraint"
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/ObstacleConstraint.json b/core/common/src/test/resources/org/onosproject/codec/impl/ObstacleConstraint.json
new file mode 100644
index 0000000..35dcb0f
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/ObstacleConstraint.json
@@ -0,0 +1,4 @@
+{
+  "type":"ObstacleConstraint",
+  "obstacles":["of:dev1","of:dev2","of:dev3"]
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/PointToPointIntent.json b/core/common/src/test/resources/org/onosproject/codec/impl/PointToPointIntent.json
new file mode 100644
index 0000000..4c6c4b8
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/PointToPointIntent.json
@@ -0,0 +1,38 @@
+{
+  "type": "PointToPointIntent",
+  "appId": 2,
+  "selector": {
+    "criteria": [
+      {
+        "type": "ETH_DST",
+        "mac": "11:22:33:44:55:66"
+      }
+    ]
+  },
+  "treatment": {
+    "instructions": [
+      {
+        "type": "L2MODIFICATION",
+        "subtype": "ETH_SRC",
+        "mac": "22:33:44:55:66:77"
+      }
+    ],
+    "deferred": []
+  },
+  "priority": 55,
+  "constraints": [
+    {
+      "inclusive": false,
+      "types": ["OPTICAL"],
+      "type": "LinkTypeConstraint"
+    }
+  ],
+  "ingressPoint": {
+    "port": "1",
+    "device": "of:0000000000000001"
+  },
+  "egressPoint": {
+    "port": "2",
+    "device": "of:0000000000000007"
+  }
+}
diff --git a/core/common/src/test/resources/org/onosproject/codec/impl/WaypointConstraint.json b/core/common/src/test/resources/org/onosproject/codec/impl/WaypointConstraint.json
new file mode 100644
index 0000000..7009cf9
--- /dev/null
+++ b/core/common/src/test/resources/org/onosproject/codec/impl/WaypointConstraint.json
@@ -0,0 +1,4 @@
+{
+  "type":"WaypointConstraint",
+  "waypoints":["of:devA","of:devB","of:devC"]
+}
diff --git a/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java b/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
index f49e6be..967d477 100644
--- a/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
+++ b/web/api/src/main/java/org/onosproject/rest/resources/IntentsWebResource.java
@@ -15,12 +15,18 @@
  */
 package org.onosproject.rest.resources;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -177,4 +183,31 @@
         }
     }
 
+    /**
+     * Creates an intent from a POST of a JSON string and attempts to apply it.
+     *
+     * @param stream input JSON
+     * @return status of the request - CREATED if the JSON is correct,
+     * BAD_REQUEST if the JSON is invalid
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createIntent(InputStream stream) {
+        URI location;
+        try {
+            IntentService service = get(IntentService.class);
+            ObjectNode root = (ObjectNode) mapper().readTree(stream);
+            Intent intent = codec(Intent.class).decode(root, this);
+            service.submit(intent);
+            location = new URI(Short.toString(intent.appId().id()) + "/"
+                    + Long.toString(intent.id().fingerprint()));
+        } catch (IOException | URISyntaxException ex) {
+            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
+        }
+        return Response
+                .created(location)
+                .build();
+    }
+
 }
diff --git a/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
index 63e9644..192c417 100644
--- a/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/FlowsResourceTest.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.rest;
 
+import java.io.InputStream;
 import java.net.HttpURLConnection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -107,10 +108,6 @@
     final MockFlowEntry flow5 = new MockFlowEntry(deviceId2, 5);
     final MockFlowEntry flow6 = new MockFlowEntry(deviceId2, 6);
 
-    private static final String FLOW_JSON = "{\"priority\":1,\"isPermanent\":true,"
-            + "\"treatment\":{\"instructions\":[ {\"type\":\"OUTPUT\",\"port\":2}]},"
-            + "\"selector\":{\"criteria\":[ {\"type\":\"ETH_TYPE\",\"ethType\":2054}]}}";
-
     /**
      * Mock class for a flow entry.
      */
@@ -582,11 +579,12 @@
         replay(mockFlowService);
 
         WebResource rs = resource();
-
+        InputStream jsonStream = IntentsResourceTest.class
+                .getResourceAsStream("post-flow.json");
 
         ClientResponse response = rs.path("flows/of:0000000000000001")
                 .type(MediaType.APPLICATION_JSON_TYPE)
-                .post(ClientResponse.class, FLOW_JSON);
+                .post(ClientResponse.class, jsonStream);
         assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
         String location = response.getLocation().getPath();
         assertThat(location, Matchers.startsWith("/flows/of:0000000000000001/"));
diff --git a/web/api/src/test/java/org/onosproject/rest/IntentsResourceTest.java b/web/api/src/test/java/org/onosproject/rest/IntentsResourceTest.java
index 08a6ce5..6572b68 100644
--- a/web/api/src/test/java/org/onosproject/rest/IntentsResourceTest.java
+++ b/web/api/src/test/java/org/onosproject/rest/IntentsResourceTest.java
@@ -15,10 +15,15 @@
  */
 package org.onosproject.rest;
 
+import java.io.InputStream;
+import java.net.HttpURLConnection;
 import java.util.Collections;
 import java.util.HashSet;
 
+import javax.ws.rs.core.MediaType;
+
 import org.hamcrest.Description;
+import org.hamcrest.Matchers;
 import org.hamcrest.TypeSafeMatcher;
 import org.junit.After;
 import org.junit.Before;
@@ -42,12 +47,14 @@
 import com.eclipsesource.json.JsonArray;
 import com.eclipsesource.json.JsonObject;
 import com.eclipsesource.json.JsonValue;
+import com.sun.jersey.api.client.ClientResponse;
 import com.sun.jersey.api.client.UniformInterfaceException;
 import com.sun.jersey.api.client.WebResource;
 
 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.containsString;
@@ -358,4 +365,29 @@
                     containsString("returned a response status of"));
         }
     }
+
+    /**
+     * Tests creating an intent with POST.
+     */
+    @Test
+    public void testPost() {
+        expect(mockCoreService.getAppId((short) 2))
+                .andReturn(new DefaultApplicationId(2, "app"));
+        replay(mockCoreService);
+
+        mockIntentService.submit(anyObject());
+        expectLastCall();
+        replay(mockIntentService);
+
+        InputStream jsonStream = IntentsResourceTest.class
+                .getResourceAsStream("post-intent.json");
+        WebResource rs = resource();
+
+        ClientResponse response = rs.path("intents")
+                .type(MediaType.APPLICATION_JSON_TYPE)
+                .post(ClientResponse.class, jsonStream);
+        assertThat(response.getStatus(), is(HttpURLConnection.HTTP_CREATED));
+        String location = response.getLocation().getPath();
+        assertThat(location, Matchers.startsWith("/intents/2/"));
+    }
 }
diff --git a/web/api/src/test/resources/org/onosproject/rest/post-flow.json b/web/api/src/test/resources/org/onosproject/rest/post-flow.json
new file mode 100644
index 0000000..939b447
--- /dev/null
+++ b/web/api/src/test/resources/org/onosproject/rest/post-flow.json
@@ -0,0 +1,20 @@
+{
+  "priority": 1,
+  "isPermanent": true,
+  "treatment": {
+    "instructions": [
+      {
+        "type": "OUTPUT",
+        "port": 2
+      }
+    ]
+  },
+  "selector": {
+    "criteria": [
+      {
+        "type": "ETH_TYPE",
+        "ethType": 2054
+      }
+    ]
+  }
+}
diff --git a/web/api/src/test/resources/org/onosproject/rest/post-intent.json b/web/api/src/test/resources/org/onosproject/rest/post-intent.json
new file mode 100644
index 0000000..4c6c4b8
--- /dev/null
+++ b/web/api/src/test/resources/org/onosproject/rest/post-intent.json
@@ -0,0 +1,38 @@
+{
+  "type": "PointToPointIntent",
+  "appId": 2,
+  "selector": {
+    "criteria": [
+      {
+        "type": "ETH_DST",
+        "mac": "11:22:33:44:55:66"
+      }
+    ]
+  },
+  "treatment": {
+    "instructions": [
+      {
+        "type": "L2MODIFICATION",
+        "subtype": "ETH_SRC",
+        "mac": "22:33:44:55:66:77"
+      }
+    ],
+    "deferred": []
+  },
+  "priority": 55,
+  "constraints": [
+    {
+      "inclusive": false,
+      "types": ["OPTICAL"],
+      "type": "LinkTypeConstraint"
+    }
+  ],
+  "ingressPoint": {
+    "port": "1",
+    "device": "of:0000000000000001"
+  },
+  "egressPoint": {
+    "port": "2",
+    "device": "of:0000000000000007"
+  }
+}