Integrating A1 client with service supervision 36/2036/2
authorPatrikBuhr <patrik.buhr@est.tech>
Mon, 16 Dec 2019 15:01:13 +0000 (16:01 +0100)
committerPatrikBuhr <patrik.buhr@est.tech>
Mon, 16 Dec 2019 15:15:44 +0000 (16:15 +0100)
Introducing a "mock" mode.

Added som test schemas that I forgot to add in the
previous commit.

Code formatting,

Change-Id: Ide38969ae8513d0d86d798696306191b9bc2f5b4
Issue-ID: NONRTRIC-84
Signed-off-by: PatrikBuhr <patrik.buhr@est.tech>
17 files changed:
dashboard/webapp-backend/src/main/java/org/oransc/ric/portal/dashboard/policyagentapi/PolicyAgentApiImpl.java
dashboard/webapp-backend/src/test/java/org/oransc/ric/portal/dashboard/config/PolicyControllerMockConfiguration.java
dashboard/webapp-frontend/src/app/footer/footer.component.scss
policy-agent/src/main/java/org/oransc/policyagent/BeanFactory.java
policy-agent/src/main/java/org/oransc/policyagent/clients/A1Client.java
policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientImpl.java [new file with mode: 0644]
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyInfo.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/PolicyTypeInfo.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/RicInfo.java
policy-agent/src/main/java/org/oransc/policyagent/controllers/ServiceRegistrationInfo.java
policy-agent/src/main/java/org/oransc/policyagent/repository/Ric.java
policy-agent/src/main/java/org/oransc/policyagent/tasks/ServiceSupervision.java
policy-agent/src/test/java/org/oransc/policyagent/MockPolicyAgent.java
policy-agent/src/test/resources/policy_types/anr-policy-schema.json [new file with mode: 0644]
policy-agent/src/test/resources/policy_types/demo-policy-schema-1.json [new file with mode: 0644]
policy-agent/src/test/resources/policy_types/demo-policy-schema-2.json [new file with mode: 0644]
policy-agent/src/test/resources/policy_types/demo-policy-schema-3.json [new file with mode: 0644]

index 4213488..0736064 100644 (file)
@@ -90,7 +90,6 @@ public class PolicyControllerMockConfiguration {
                        result.addAll(inst);
                        return result;
                }
-
        }
 
        class Database {
index c06dc01..fb87ba5 100644 (file)
@@ -21,6 +21,7 @@
   color: gray;
   letter-spacing: 0.1rem;
   font-size: 10px;
+  margin-left: 10px;
 }
 
 .copyright__text-dark {
index 637eac1..06b66ca 100644 (file)
@@ -20,6 +20,8 @@
 
 package org.oransc.policyagent;
 
+import org.oransc.policyagent.clients.A1Client;
+import org.oransc.policyagent.clients.A1ClientImpl;
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.repository.Policies;
 import org.oransc.policyagent.repository.PolicyTypes;
@@ -55,4 +57,9 @@ class BeanFactory {
         return new Services();
     }
 
+    @Bean
+    A1Client getA1Client() {
+        return new A1ClientImpl();
+    }
+
 }
index db048fb..9ad5dd7 100644 (file)
  */
 package org.oransc.policyagent.clients;
 
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.List;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
-public class A1Client {
-    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+public interface A1Client {
 
-    public String getBaseUrl(final String nearRtRicUrl) {
-        return nearRtRicUrl + "/A1-P/v1";
-    }
+    public Flux<String> getAllPolicyTypes(String nearRtRicUrl);
 
-    public Flux<String> getAllPolicyTypes(String nearRtRicUrl) {
-        logger.debug("getAllPolicyTypes nearRtRicUrl = {}", nearRtRicUrl);
-        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
-        Mono<String> response = client.get("/policytypes");
-        return response.flatMapMany(this::createPolicyTypesFlux);
-    }
+    public Flux<String> getPoliciesForType(String nearRtRicUrl, String policyTypeId);
 
-    public Flux<String> getPoliciesForType(String nearRtRicUrl, String policyTypeId) {
-        logger.debug("getPoliciesForType nearRtRicUrl = {}, policyTypeId = {}", nearRtRicUrl, policyTypeId);
-        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
-        Mono<String> response = client.get("/policies");
-        return response.flatMapMany(policiesString -> createPoliciesFlux(policiesString, policyTypeId));
-    }
+    public Mono<String> getPolicy(String nearRtRicUrl, String policyId);
 
-    public Mono<String> getPolicy(String nearRtRicUrl, String policyId) {
-        logger.debug("getPolicy nearRtRicUrl = {}, policyId = {}", nearRtRicUrl, policyId);
-        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
-        Mono<String> response = client.get("/policies/" + policyId);
-        return response.flatMap(this::createPolicyMono);
-    }
+    public Mono<String> putPolicy(String nearRtRicUrl, String policyId, String policyString);
 
-    public Mono<String> putPolicy(String nearRtRicUrl, String policyId, String policyString) {
-        logger.debug("putPolicy nearRtRicUrl = {}, policyId = {}, policyString = {}", nearRtRicUrl, policyId,
-            policyString);
-        try {
-            new JSONObject(policyString);
-        } catch (JSONException ex) { // invalid json
-            return Mono.error(ex);
-        }
-        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
-        Mono<String> response = client.put("/policies/" + policyId, policyString);
-        return response.flatMap(this::createPolicyMono);
-    }
+    public Mono<Void> deletePolicy(String nearRtRicUrl, String policyId);
 
-    public Mono<Void> deletePolicy(String nearRtRicUrl, String policyId) {
-        logger.debug("deletePolicy nearRtRicUrl = {}, policyId = {}", nearRtRicUrl, policyId);
-        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
-        return client.delete("/policies/" + policyId);
-    }
-
-    private Flux<String> createPolicyTypesFlux(String policyTypesString) {
-        try {
-            List<String> policyTypesList = new ArrayList<>();
-            JSONArray policyTypesArray = new JSONArray(policyTypesString);
-            for (int i = 0; i < policyTypesArray.length(); i++) {
-                policyTypesList.add(policyTypesArray.getJSONObject(i).toString());
-            }
-            logger.debug("A1 client: policyTypes = {}", policyTypesList);
-            return Flux.fromIterable(policyTypesList);
-        } catch (JSONException ex) { // invalid json
-            return Flux.error(ex);
-        }
-    }
-
-    private Flux<String> createPoliciesFlux(String policiesString, String policyTypeId) {
-        try {
-            List<String> policiesList = new ArrayList<>();
-            JSONArray policiesArray = new JSONArray(policiesString);
-            for (int i = 0; i < policiesArray.length(); i++) {
-                JSONObject policyObject = policiesArray.getJSONObject(i);
-                if (policyObject.get("policyTypeId").equals(policyTypeId)) {
-                    policiesList.add(policyObject.toString());
-                }
-            }
-            logger.debug("A1 client: policies = {}", policiesList);
-            return Flux.fromIterable(policiesList);
-        } catch (JSONException ex) { // invalid json
-            return Flux.error(ex);
-        }
-    }
-
-    private Mono<String> createPolicyMono(String policyString) {
-        try {
-            JSONObject policyObject = new JSONObject(policyString);
-            String policy = policyObject.toString();
-            logger.debug("A1 client: policy = {}", policy);
-            return Mono.just(policy);
-        } catch (JSONException ex) { // invalid json
-            return Mono.error(ex);
-        }
-    }
 }
diff --git a/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientImpl.java b/policy-agent/src/main/java/org/oransc/policyagent/clients/A1ClientImpl.java
new file mode 100644 (file)
index 0000000..4f7a9dd
--- /dev/null
@@ -0,0 +1,124 @@
+/*-
+ * ========================LICENSE_START=================================
+ * O-RAN-SC
+ * %%
+ * Copyright (C) 2019 Nordix Foundation
+ * %%
+ * 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.
+ * ========================LICENSE_END===================================
+ */
+package org.oransc.policyagent.clients;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class A1ClientImpl implements A1Client {
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    private static String getBaseUrl(final String nearRtRicUrl) {
+        return nearRtRicUrl + "/A1-P/v1";
+    }
+
+    @Override
+    public Flux<String> getAllPolicyTypes(String nearRtRicUrl) {
+        logger.debug("getAllPolicyTypes nearRtRicUrl = {}", nearRtRicUrl);
+        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
+        Mono<String> response = client.get("/policytypes");
+        return response.flatMapMany(this::createPolicyTypesFlux);
+    }
+
+    @Override
+    public Flux<String> getPoliciesForType(String nearRtRicUrl, String policyTypeId) {
+        logger.debug("getPoliciesForType nearRtRicUrl = {}, policyTypeId = {}", nearRtRicUrl, policyTypeId);
+        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
+        return client.get("/policies") //
+            .flatMapMany(policiesString -> createPoliciesFlux(policiesString, policyTypeId));
+    }
+
+    @Override
+    public Mono<String> getPolicy(String nearRtRicUrl, String policyId) {
+        logger.debug("getPolicy nearRtRicUrl = {}, policyId = {}", nearRtRicUrl, policyId);
+        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
+        Mono<String> response = client.get("/policies/" + policyId);
+        return response.flatMap(this::createPolicyMono);
+    }
+
+    @Override
+    public Mono<String> putPolicy(String nearRtRicUrl, String policyId, String policyString) {
+        logger.debug("putPolicy nearRtRicUrl = {}, policyId = {}, policyString = {}", nearRtRicUrl, policyId,
+            policyString);
+        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
+        Mono<String> response = client.put("/policies/" + policyId, policyString);
+        return response.flatMap(this::createPolicyMono);
+    }
+
+    @Override
+    public Mono<Void> deletePolicy(String nearRtRicUrl, String policyId) {
+        logger.debug("deletePolicy nearRtRicUrl = {}, policyId = {}", nearRtRicUrl, policyId);
+        AsyncRestClient client = new AsyncRestClient(getBaseUrl(nearRtRicUrl));
+        return client.delete("/policies/" + policyId);
+    }
+
+    private Flux<String> createPolicyTypesFlux(String policyTypesString) {
+        try {
+            List<String> policyTypesList = new ArrayList<>();
+            JSONArray policyTypesArray = new JSONArray(policyTypesString);
+            for (int i = 0; i < policyTypesArray.length(); i++) {
+                policyTypesList.add(policyTypesArray.getJSONObject(i).toString());
+            }
+            logger.debug("A1 client: policyTypes = {}", policyTypesList);
+            return Flux.fromIterable(policyTypesList);
+        } catch (JSONException ex) { // invalid json
+            return Flux.error(ex);
+        }
+    }
+
+    private Flux<String> createPoliciesFlux(String policiesString, String policyTypeId) {
+        try {
+            List<String> policiesList = new ArrayList<>();
+            JSONArray policiesArray = new JSONArray(policiesString);
+            for (int i = 0; i < policiesArray.length(); i++) {
+                JSONObject policyObject = policiesArray.getJSONObject(i);
+                if (policyObject.get("policyTypeId").equals(policyTypeId)) {
+                    policiesList.add(policyObject.toString());
+                }
+            }
+            logger.debug("A1 client: policies = {}", policiesList);
+            return Flux.fromIterable(policiesList);
+        } catch (JSONException ex) { // invalid json
+            return Flux.error(ex);
+        }
+    }
+
+    private Mono<String> createPolicyMono(String policyString) {
+        try {
+            JSONObject policyObject = new JSONObject(policyString);
+            String policy = policyObject.toString();
+            logger.debug("A1 client: policy = {}", policy);
+            return Mono.just(policy);
+        } catch (JSONException ex) { // invalid json
+            return Mono.error(ex);
+
+        }
+    }
+}
index 3caadae..df612dc 100644 (file)
@@ -25,7 +25,6 @@ import java.util.List;
 import java.util.Vector;
 
 import org.oransc.policyagent.configuration.RicConfig;
-import org.oransc.policyagent.repository.Ric.RicState;
 
 /**
  * Represents the dynamic information about a NearRealtime-RIC.
@@ -56,6 +55,10 @@ public class Ric {
         state = newState;
     }
 
+    public RicConfig getConfig() {
+        return this.ricConfig;
+    }
+
     /**
      * Gets the nodes managed by this Ric.
      *
index acb546b..f2de7cf 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.oransc.policyagent.tasks;
 
+import org.oransc.policyagent.clients.A1Client;
 import org.oransc.policyagent.repository.Policies;
 import org.oransc.policyagent.repository.Policy;
 import org.oransc.policyagent.repository.Service;
@@ -30,7 +31,9 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
+
 import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
 
 @Component
 @EnableScheduling
@@ -38,11 +41,13 @@ public class ServiceSupervision {
     private static final Logger logger = LoggerFactory.getLogger(ServiceSupervision.class);
     private final Services services;
     private final Policies policies;
+    private A1Client a1Client;
 
     @Autowired
-    public ServiceSupervision(Services services, Policies policies) {
+    public ServiceSupervision(Services services, Policies policies, A1Client a1Client) {
         this.services = services;
         this.policies = policies;
+        this.a1Client = a1Client;
     }
 
     @Scheduled(fixedRate = 1000 * 60)
@@ -63,16 +68,27 @@ public class ServiceSupervision {
         logger.debug("Checking services completed");
     }
 
-    Flux<Policy> createTask() {
+    private Flux<Policy> createTask() {
         return Flux.fromIterable(services.getAll()) //
             .filter(service -> service.isExpired()) //
             .doOnNext(service -> logger.info("Service is expired:" + service.getName()))
             .flatMap(service -> getAllPolicies(service)) //
-            .doOnNext(policy -> this.policies.remove(policy));
+            .doOnNext(policy -> this.policies.remove(policy)) //
+            .flatMap(policy -> deletePolicyInRic(policy));
     }
 
-    Flux<Policy> getAllPolicies(Service service) {
+    private Flux<Policy> getAllPolicies(Service service) {
         return Flux.fromIterable(policies.getForService(service.getName()));
     }
 
+    private Mono<Policy> deletePolicyInRic(Policy policy) {
+        return a1Client.deletePolicy(policy.ric().getConfig().baseUrl(), policy.id()) //
+            .onErrorResume(exception -> handleDeleteFromRicFailure(policy, exception)) //
+            .flatMap((nothing) -> Mono.just(policy));
+    }
+
+    private Mono<Void> handleDeleteFromRicFailure(Policy policy, Throwable e) {
+        logger.warn("Could not delete policy: {} from ric: {}", policy.id(), policy.ric().name(), e);
+        return Mono.empty();
+    }
 }
index d02522c..b7ea7dd 100644 (file)
@@ -26,9 +26,11 @@ import java.io.File;
 import java.io.IOException;
 import java.net.URL;
 import java.nio.file.Files;
+import java.util.Vector;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.oransc.policyagent.clients.A1Client;
 import org.oransc.policyagent.configuration.ApplicationConfig;
 import org.oransc.policyagent.repository.ImmutablePolicyType;
 import org.oransc.policyagent.repository.Policies;
@@ -43,6 +45,9 @@ import org.springframework.boot.web.server.LocalServerPort;
 import org.springframework.context.annotation.Bean;
 import org.springframework.test.context.junit4.SpringRunner;
 
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
 @RunWith(SpringRunner.class)
 @SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
 public class MockPolicyAgent {
@@ -64,16 +69,85 @@ public class MockPolicyAgent {
         }
     }
 
+    static class A1ClientMock implements A1Client {
+        private final Policies policies;
+        private final PolicyTypes policyTypes;
+
+        A1ClientMock(Policies policies, PolicyTypes policyTypes) {
+            this.policies = policies;
+            this.policyTypes = policyTypes;
+        }
+
+        @Override
+        public Flux<String> getAllPolicyTypes(String nearRtRicUrl) {
+            Vector<String> result = new Vector<>();
+            for (PolicyType p : this.policyTypes.getAll()) {
+                result.add(p.name());
+            }
+            return Flux.fromIterable(result);
+        }
+
+        @Override
+        public Flux<String> getPoliciesForType(String nearRtRicUrl, String policyTypeId) {
+            return Flux.empty();
+        }
+
+        @Override
+        public Mono<String> getPolicy(String nearRtRicUrl, String policyId) {
+            try {
+                return Mono.just(this.policies.get(policyId).json());
+            } catch (Exception e) {
+                return Mono.error(e);
+            }
+        }
+
+        @Override
+        public Mono<String> putPolicy(String nearRtRicUrl, String policyId, String policyString) {
+            return Mono.just("OK");
+        }
+
+        @Override
+        public Mono<Void> deletePolicy(String nearRtRicUrl, String policyId) {
+            return Mono.error(new Exception("TODO We cannot use Void like this")); // TODO We cannot use Void like this
+        }
+
+    }
+
     /**
      * overrides the BeanFactory
      */
     @TestConfiguration
     static class TestBeanFactory {
 
+        private final Rics rics = new Rics();
+        private final Policies policies = new Policies();
+        private final PolicyTypes policyTypes = new PolicyTypes();
+
         @Bean
         public ApplicationConfig getApplicationConfig() {
             return new MockApplicationConfig();
         }
+
+        @Bean
+        A1Client getA1Client() {
+            return new A1ClientMock(this.policies, this.policyTypes);
+        }
+
+        @Bean
+        public Policies getPolicies() {
+            return this.policies;
+        }
+
+        @Bean
+        public PolicyTypes getPolicyTypes() {
+            return this.policyTypes;
+        }
+
+        @Bean
+        public Rics getRics() {
+            return this.rics;
+        }
+
     }
 
     @LocalServerPort
diff --git a/policy-agent/src/test/resources/policy_types/anr-policy-schema.json b/policy-agent/src/test/resources/policy_types/anr-policy-schema.json
new file mode 100644 (file)
index 0000000..6e0263d
--- /dev/null
@@ -0,0 +1,40 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "ANR",
+  "description": "ANR Neighbour Cell Relation Policy",
+  "type": "object",
+  "properties": {
+    "servingCellNrcgi": {
+      "type": "string",
+      "description": "Serving Cell Identifier (NR CGI)"
+    },
+    "neighborCellNrpci": {
+      "type": "string",
+      "description": "Neighbor Cell Identifier (NR PCI)"
+    },
+    "neighborCellNrcgi": {
+      "type": "string",
+      "description": "Neighbor Cell Identifier (NR CGI)"
+    },
+    "flagNoHo": {
+      "type": "boolean",
+      "description": "Flag for HANDOVER NOT ALLOWED"
+    },
+    "flagNoXn": {
+      "type": "boolean",
+      "description": "Flag for Xn CONNECTION NOT ALLOWED"
+    },
+    "flagNoRemove": {
+      "type": "boolean",
+      "description": "Flag for DELETION NOT ALLOWED"
+    }
+  },
+  "required": [
+    "servingCellNrcgi",
+    "neighborCellNrpci",
+    "neighborCellNrcgi",
+    "flagNoHo",
+    "flagNoXn",
+    "flagNoRemove"
+  ]
+}
\ No newline at end of file
diff --git a/policy-agent/src/test/resources/policy_types/demo-policy-schema-1.json b/policy-agent/src/test/resources/policy_types/demo-policy-schema-1.json
new file mode 100644 (file)
index 0000000..fa7410f
--- /dev/null
@@ -0,0 +1,42 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "Example_QoSTarget_1.0.0",
+  "description": "Example QoS Target policy type",
+  "type": "object",
+  "properties": {
+    "scope": {
+      "type": "object",
+      "properties": {
+        "qosId": {
+          "type": "string"
+        },
+        "cellId": {
+          "type": "string"
+        }
+      },
+      "additionalProperties": false,
+      "required": [
+        "qosId"
+      ]
+    },
+    "statement": {
+      "type": "object",
+      "properties": {
+        "gfbr": {
+          "type": "number"
+        },
+        "mfbr": {
+          "type": "number"
+        },
+        "priorityLevel": {
+          "type": "number"
+        },
+        "pdb": {
+          "type": "number"
+        }
+      },
+      "minProperties": 1,
+      "additionalProperties": false
+    }
+  }
+}
\ No newline at end of file
diff --git a/policy-agent/src/test/resources/policy_types/demo-policy-schema-2.json b/policy-agent/src/test/resources/policy_types/demo-policy-schema-2.json
new file mode 100644 (file)
index 0000000..f3eb28f
--- /dev/null
@@ -0,0 +1,49 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "Example_QoETarget_1.0.0",
+  "description": "Example QoE Target policy type",
+  "type": "object",
+  "properties": {
+    "scope": {
+      "type": "object",
+      "properties": {
+        "ueId": {
+          "type": "string"
+        },
+        "sliceId": {
+          "type": "string"
+        },
+        "qosId": {
+          "type": "string"
+        },
+        "cellId": {
+          "type": "string"
+        }
+      },
+      "additionalProperties": false,
+      "required": [
+        "ueId",
+        "sliceId"
+      ]
+    },
+    "statement": {
+      "type": "object",
+      "properties": {
+        "qoeScore": {
+          "type": "number"
+        },
+        "initialBuffering": {
+          "type": "number"
+        },
+        "reBuffFreq": {
+          "type": "number"
+        },
+        "stallRatio": {
+          "type": "number"
+        }
+      },
+      "minProperties": 1,
+      "additionalProperties": false
+    }
+  }
+}
\ No newline at end of file
diff --git a/policy-agent/src/test/resources/policy_types/demo-policy-schema-3.json b/policy-agent/src/test/resources/policy_types/demo-policy-schema-3.json
new file mode 100644 (file)
index 0000000..695514c
--- /dev/null
@@ -0,0 +1,59 @@
+{
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "title": "Example_TrafficSteeringPreference_1.0.0",
+  "description": "Example QoE Target policy type",
+  "type": "object",
+  "properties": {
+    "scope": {
+      "type": "object",
+      "properties": {
+        "ueId": {
+          "type": "string"
+        },
+        "sliceId": {
+          "type": "string"
+        },
+        "qosId": {
+          "type": "string"
+        },
+        "cellId": {
+          "type": "string"
+        }
+      },
+      "additionalProperties": false,
+      "required": [
+        "ueId"
+      ]
+    },
+    "statement": {
+      "type": "object",
+      "properties": {
+        "cellIdList": {
+          "type": "array",
+          "minItems": 1,
+          "uniqueItems": true,
+          "items": {
+            "type": "string"
+          }
+        },
+        "preference": {
+          "type": "string",
+          "enum": [
+            "SHALL",
+            "PREFER",
+            "AVOID",
+            "FORBID"
+          ]
+        },
+        "primary": {
+          "type": "boolean"
+        }
+      },
+      "required": [
+        "cellIdList",
+        "preference"
+      ],
+      "additionalProperties": false
+    }
+  }
+}
\ No newline at end of file