UNOMI-598 : add keyword to validate scopes (#449)

* UNOMI-598 : add keyword to validate scopes
diff --git a/api/src/main/java/org/apache/unomi/api/services/ScopeService.java b/api/src/main/java/org/apache/unomi/api/services/ScopeService.java
index 5dddf63..91980a4 100644
--- a/api/src/main/java/org/apache/unomi/api/services/ScopeService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/ScopeService.java
@@ -17,7 +17,6 @@
 
 package org.apache.unomi.api.services;
 
-import org.apache.unomi.api.Metadata;
 import org.apache.unomi.api.Scope;
 
 import java.util.List;
diff --git a/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/impl/SchemaServiceImpl.java b/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/impl/SchemaServiceImpl.java
index d3fac54..ebc431f 100644
--- a/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/impl/SchemaServiceImpl.java
+++ b/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/impl/SchemaServiceImpl.java
@@ -27,9 +27,11 @@
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.unomi.api.Item;
+import org.apache.unomi.api.services.ScopeService;
 import org.apache.unomi.persistence.spi.PersistenceService;
 import org.apache.unomi.schema.api.JsonSchemaWrapper;
 import org.apache.unomi.schema.api.SchemaService;
+import org.apache.unomi.schema.keyword.ScopeKeyword;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,6 +68,8 @@
     private ScheduledFuture<?> scheduledFuture;
 
     private PersistenceService persistenceService;
+    private ScopeService scopeService;
+
     private JsonSchemaFactory jsonSchemaFactory;
 
     // TODO UNOMI-572: when fixing UNOMI-572 please remove the usage of the custom ScheduledExecutorService and re-introduce the Unomi Scheduler Service
@@ -317,6 +321,7 @@
     private void initJsonSchemaFactory() {
         jsonSchemaFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909))
                 .addMetaSchema(JsonMetaSchema.builder(URI, JsonMetaSchema.getV201909())
+                        .addKeyword(new ScopeKeyword(scopeService))
                         .addKeyword(new NonValidationKeyword("self"))
                         .build())
                 .defaultMetaSchemaURI(URI)
@@ -358,6 +363,10 @@
         this.persistenceService = persistenceService;
     }
 
+    public void setScopeService(ScopeService scopeService) {
+        this.scopeService = scopeService;
+    }
+
     public void setJsonSchemaRefreshInterval(Integer jsonSchemaRefreshInterval) {
         this.jsonSchemaRefreshInterval = jsonSchemaRefreshInterval;
     }
diff --git a/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/keyword/ScopeKeyword.java b/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/keyword/ScopeKeyword.java
new file mode 100644
index 0000000..471a475
--- /dev/null
+++ b/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/keyword/ScopeKeyword.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.unomi.schema.keyword;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.AbstractKeyword;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonValidator;
+import com.networknt.schema.ValidationContext;
+import org.apache.unomi.api.services.ScopeService;
+
+public class ScopeKeyword extends AbstractKeyword {
+
+    private ScopeService scopeService;
+
+    public ScopeKeyword(ScopeService scopeService) {
+        super("validateScope");
+        this.scopeService = scopeService;
+    }
+
+    @Override
+    public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema,
+            ValidationContext validationContext) {
+        return new ScopeValidator(scopeService);
+    }
+}
diff --git a/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/keyword/ScopeValidator.java b/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/keyword/ScopeValidator.java
new file mode 100644
index 0000000..63bc65b
--- /dev/null
+++ b/extensions/json-schema/services/src/main/java/org/apache/unomi/schema/keyword/ScopeValidator.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.unomi.schema.keyword;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.networknt.schema.AbstractJsonValidator;
+import com.networknt.schema.CustomErrorMessageType;
+import com.networknt.schema.ValidationMessage;
+import org.apache.unomi.api.services.ScopeService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.MessageFormat;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class ScopeValidator extends AbstractJsonValidator {
+    private static final Logger logger = LoggerFactory.getLogger(ScopeValidator.class);
+
+    private ScopeService scopeService;
+
+    public ScopeValidator(ScopeService scopeService) {
+        super("validateScope");
+        this.scopeService = scopeService;
+    }
+
+    @Override
+    public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("validate( {}, {}, {})", node, rootNode, at);
+        }
+        Set<ValidationMessage> errors = new LinkedHashSet();
+        if (scopeService.getScope(node.textValue()) == null) {
+            errors.add(this.buildValidationMessage(
+                    CustomErrorMessageType.of("1100", new MessageFormat("Unknown scope value at \"{0}\" for value {1}")), at,
+                    node.textValue()));
+        }
+        return errors;
+    }
+}
diff --git a/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/events/event.json b/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/events/event.json
index 29fa59c..b4673a7 100644
--- a/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/events/event.json
+++ b/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/events/event.json
@@ -25,6 +25,7 @@
     },
     "scope" : {
       "type" : [ "string"],
+      "validateScope": true,
       "pattern" : "^(\\w|[-_@\\.]){0,60}$"
     },
     "sourceId" : {
@@ -35,4 +36,4 @@
       "type" : "boolean"
     }
   }
-}
\ No newline at end of file
+}
diff --git a/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/items/consent/consent.json b/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/items/consent/consent.json
index 557bb25..40b09d6 100644
--- a/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/items/consent/consent.json
+++ b/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/items/consent/consent.json
@@ -11,7 +11,8 @@
   "type": "object",
   "properties" : {
     "scope" : {
-      "type" : "string"
+      "type" : "string",
+      "validateScope" : true
     },
     "typeIdentifier" : {
       "type" : "string"
@@ -29,4 +30,4 @@
     }
   },
   "unevaluatedProperties": false
-}
\ No newline at end of file
+}
diff --git a/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/items/item.json b/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/items/item.json
index 5c71db4..d4a9f77 100644
--- a/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/items/item.json
+++ b/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas/items/item.json
@@ -21,6 +21,7 @@
     },
     "scope" : {
       "type" : ["null","string"],
+      "validateScope": true,
       "description" : "The item's scope"
     },
     "version" : {
diff --git a/extensions/json-schema/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/extensions/json-schema/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 88485b8..9998ad9 100644
--- a/extensions/json-schema/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/extensions/json-schema/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -27,13 +27,14 @@
         </cm:default-properties>
     </cm:property-placeholder>
 
-    <reference id="profileService" interface="org.apache.unomi.api.services.ProfileService"/>
+    <reference id="scopeService" interface="org.apache.unomi.api.services.ScopeService"/>
     <reference id="persistenceService" interface="org.apache.unomi.persistence.spi.PersistenceService"/>
     <!--reference id="schedulerService" interface="org.apache.unomi.api.services.SchedulerService"/-->
 
     <bean id="schemaServiceImpl" class="org.apache.unomi.schema.impl.SchemaServiceImpl" init-method="init"
           destroy-method="destroy">
         <property name="persistenceService" ref="persistenceService"/>
+        <property name="scopeService" ref="scopeService"/>
         <!--property name="schedulerService" ref="schedulerService"/-->
         <property name="jsonSchemaRefreshInterval" value="${json.schema.refresh.interval}"/>
     </bean>
diff --git a/itests/src/test/java/org/apache/unomi/itests/BasicIT.java b/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
index 941072c..819ce44 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BasicIT.java
@@ -28,14 +28,19 @@
 import org.apache.unomi.api.ContextRequest;
 import org.apache.unomi.api.CustomItem;
 import org.apache.unomi.api.Event;
+import org.apache.unomi.api.Metadata;
 import org.apache.unomi.api.Profile;
+import org.apache.unomi.api.Scope;
 import org.apache.unomi.api.conditions.ConditionType;
 import org.apache.unomi.api.rules.Rule;
 import org.apache.unomi.api.services.DefinitionsService;
 import org.apache.unomi.api.services.ProfileService;
+import org.apache.unomi.api.services.ScopeService;
 import org.apache.unomi.itests.tools.httpclient.HttpClientThatWaitsForUnomi;
 import org.apache.unomi.persistence.spi.CustomObjectMapper;
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.junit.PaxExam;
@@ -64,7 +69,6 @@
     private static final String SESSION_ID_4 = "aa3b04bd-8f4d-4a07-8e96-d33ffa04d3d4";
 
     private static final String EVENT_TYPE_LOGIN = "login";
-    private static final String EVENT_TYPE_LOGIN_SCHEMA = "schemas/events/login.json";
     private static final String EVENT_TYPE_VIEW = "view";
     private static final String TEST_SCOPE = "testScope";
 
@@ -93,6 +97,21 @@
     @Inject @Filter(timeout = 600000)
     protected DefinitionsService definitionsService;
 
+    @Inject @Filter(timeout = 600000)
+    protected ScopeService scopeService;
+
+    @Before
+    public void setUp() throws InterruptedException {
+        TestUtils.createScope(TEST_SCOPE, "Test scope", scopeService);
+        keepTrying("Scope "+ TEST_SCOPE +" not found in the required time", () -> scopeService.getScope(TEST_SCOPE),
+                Objects::nonNull, DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+    }
+
+    @After
+    public void tearDown() {
+        scopeService.delete(TEST_SCOPE);
+    }
+
     @Test
     public void testContextJS() throws IOException {
         LOGGER.info("Start test testContextJS");
diff --git a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
index fe1d1c9..830e995 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
@@ -26,6 +26,7 @@
 import org.apache.unomi.api.Event;
 import org.apache.unomi.api.Metadata;
 import org.apache.unomi.api.Profile;
+import org.apache.unomi.api.Scope;
 import org.apache.unomi.api.Session;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.segments.Scoring;
@@ -33,6 +34,7 @@
 import org.apache.unomi.api.services.DefinitionsService;
 import org.apache.unomi.api.services.EventService;
 import org.apache.unomi.api.services.ProfileService;
+import org.apache.unomi.api.services.ScopeService;
 import org.apache.unomi.api.services.SegmentService;
 import org.apache.unomi.persistence.spi.CustomObjectMapper;
 import org.apache.unomi.persistence.spi.PersistenceService;
@@ -89,6 +91,7 @@
 
     private static final int DEFAULT_TRYING_TIMEOUT = 2000;
     private static final int DEFAULT_TRYING_TRIES = 30;
+    public static final String TEST_SCOPE = "test-scope";
 
     @Inject
     @Filter(timeout = 600000)
@@ -114,6 +117,10 @@
     @Filter(timeout = 600000)
     protected SchemaService schemaService;
 
+    @Inject
+    @Filter(timeout = 600000)
+    protected ScopeService scopeService;
+
     private Profile profile;
 
     @Before
@@ -145,6 +152,10 @@
                 (schemaIds) -> (schemaIds.contains("https://unomi.apache.org/schemas/json/events/floatPropertyType/1-0-0") &&
                         schemaIds.contains("https://unomi.apache.org/schemas/json/events/testEventType/1-0-0")),
                 DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+
+        TestUtils.createScope(TEST_SCOPE, "Test scope", scopeService);
+        keepTrying("Scope test-scope not found in the required time", () -> scopeService.getScope(TEST_SCOPE),
+                Objects::nonNull, DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
     }
 
     @After
@@ -163,6 +174,8 @@
                 (schemaIds) -> (!schemaIds.contains("https://unomi.apache.org/schemas/json/events/floatPropertyType/1-0-0") &&
                         !schemaIds.contains("https://unomi.apache.org/schemas/json/events/testEventType/1-0-0")),
                 DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+
+        scopeService.delete(TEST_SCOPE);
     }
 
     @Test
@@ -170,7 +183,7 @@
         //Arrange
         String eventId = "test-event-id-" + System.currentTimeMillis();
         String sessionId = "test-session-id";
-        String scope = "test-scope";
+        String scope = TEST_SCOPE;
         String eventTypeOriginal = "test-event-type-original";
         Profile profile = new Profile(TEST_PROFILE_ID);
         Session session = new Session(sessionId, profile, new Date(), scope);
@@ -208,7 +221,7 @@
         //Arrange
         String eventId = "test-event-id-" + System.currentTimeMillis();
         String sessionId = "test-session-id";
-        String scope = "test-scope";
+        String scope = TEST_SCOPE;
         String eventTypeOriginal = "test-event-type-original";
         String eventTypeUpdated = TEST_EVENT_TYPE;
         Profile profile = new Profile(TEST_PROFILE_ID);
@@ -246,7 +259,7 @@
         //Arrange
         String eventId = "test-event-id-" + System.currentTimeMillis();
         String sessionId = "test-session-id";
-        String scope = "test-scope";
+        String scope = TEST_SCOPE;
         String eventTypeOriginal = "test-event-type-original";
         String eventTypeUpdated = TEST_EVENT_TYPE;
         Session session = new Session(sessionId, profile, new Date(), scope);
@@ -278,7 +291,7 @@
     public void testCreateEventsWithNoTimestampParam_profileAddedToSegment() throws IOException, InterruptedException {
         //Arrange
         String sessionId = "test-session-id";
-        String scope = "test-scope";
+        String scope = TEST_SCOPE;
         Event event = new Event();
         event.setEventType(TEST_EVENT_TYPE);
         event.setScope(scope);
@@ -310,7 +323,7 @@
     public void testCreateEventWithTimestampParam_pastEvent_profileIsNotAddedToSegment() throws IOException, InterruptedException {
         //Arrange
         String sessionId = "test-session-id";
-        String scope = "test-scope";
+        String scope = TEST_SCOPE;
         Event event = new Event();
         event.setEventType(TEST_EVENT_TYPE);
         event.setScope(scope);
@@ -343,7 +356,7 @@
     public void testCreateEventWithTimestampParam_futureEvent_profileIsNotAddedToSegment() throws IOException, InterruptedException {
         //Arrange
         String sessionId = "test-session-id";
-        String scope = "test-scope";
+        String scope = TEST_SCOPE;
         Event event = new Event();
         event.setEventType(TEST_EVENT_TYPE);
         event.setScope(scope);
diff --git a/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java b/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
index 0723d58..3ca1887 100644
--- a/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/InputValidationIT.java
@@ -24,6 +24,9 @@
 import org.apache.http.entity.StringEntity;
 import org.apache.http.util.EntityUtils;
 import org.apache.unomi.api.Event;
+import org.apache.unomi.api.Metadata;
+import org.apache.unomi.api.Scope;
+import org.apache.unomi.api.services.ScopeService;
 import org.apache.unomi.itests.tools.httpclient.HttpClientThatWaitsForUnomi;
 import org.apache.unomi.schema.api.JsonSchemaWrapper;
 import org.apache.unomi.schema.api.SchemaService;
@@ -39,7 +42,9 @@
 import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
@@ -54,11 +59,28 @@
 
     private final static String ERROR_MESSAGE_REQUEST_SIZE_LIMIT_EXCEEDED = "Request rejected by the server because: Request size exceed the limit";
     private final static String ERROR_MESSAGE_INVALID_DATA_RECEIVED = "Request rejected by the server because: Invalid received data";
+    public static final String DUMMY_SCOPE = "dummy_scope";
 
     @Inject
     @Filter(timeout = 600000)
     protected SchemaService schemaService;
 
+    @Inject
+    @Filter(timeout = 600000)
+    protected ScopeService scopeService;
+
+    @Before
+    public void setUp() throws InterruptedException {
+        TestUtils.createScope(DUMMY_SCOPE, "Dummy scope", scopeService);
+        keepTrying("Scope "+ DUMMY_SCOPE +" not found in the required time", () -> scopeService.getScope(DUMMY_SCOPE),
+                Objects::nonNull, DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+    }
+
+    @After
+    public void tearDown() throws InterruptedException {
+        removeItems(Scope.class);
+    }
+
     @Test
     public void test_param_EventsCollectorRequestNotNull() throws IOException {
         doPOSTRequestTest(EVENT_COLLECTOR_URL, null, null, 400, ERROR_MESSAGE_INVALID_DATA_RECEIVED);
diff --git a/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java b/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java
index 5640f21..75f7d13 100644
--- a/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/JSONSchemaIT.java
@@ -23,7 +23,10 @@
 import org.apache.http.entity.StringEntity;
 import org.apache.http.util.EntityUtils;
 import org.apache.unomi.api.Event;
+import org.apache.unomi.api.Metadata;
+import org.apache.unomi.api.Scope;
 import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.api.services.ScopeService;
 import org.apache.unomi.itests.tools.httpclient.HttpClientThatWaitsForUnomi;
 import org.apache.unomi.schema.api.JsonSchemaWrapper;
 import org.apache.unomi.schema.api.SchemaService;
@@ -56,20 +59,29 @@
     private final static String JSONSCHEMA_URL = "/cxs/jsonSchema";
     private static final int DEFAULT_TRYING_TIMEOUT = 2000;
     private static final int DEFAULT_TRYING_TRIES = 30;
+    public static final String DUMMY_SCOPE = "dummy_scope";
 
     @Inject
     @Filter(timeout = 600000)
     protected SchemaService schemaService;
 
+    @Inject
+    @Filter(timeout = 6000000)
+    protected ScopeService scopeService;
+
     @Before
     public void setUp() throws InterruptedException {
         keepTrying("Couldn't find json schema endpoint", () -> get(JSONSCHEMA_URL, List.class), Objects::nonNull, DEFAULT_TRYING_TIMEOUT,
                 DEFAULT_TRYING_TRIES);
+
+        TestUtils.createScope(DUMMY_SCOPE, "Dummy scope", scopeService);
+        keepTrying("Scope "+ DUMMY_SCOPE +" not found in the required time", () -> scopeService.getScope(DUMMY_SCOPE),
+                Objects::nonNull, DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
     }
 
     @After
     public void tearDown() throws InterruptedException {
-        removeItems(JsonSchemaWrapper.class, Event.class);
+        removeItems(JsonSchemaWrapper.class, Event.class, Scope.class);
         // ensure all schemas have been cleaned from schemaService.
         keepTrying("Should not find json schemas anymore",
                 () -> schemaService.getInstalledJsonSchemaIds(),
@@ -102,6 +114,10 @@
         assertFalse(schemaService.isEventValid(resourceAsString("schemas/event-dummy-invalid-3.json"),
                 "dummy"));
 
+        // Event with unexisting scope:
+        assertFalse(schemaService.isEventValid(resourceAsString("schemas/event-dummy-invalid-4.json"),
+                "dummy"));
+
         // remove one of the schema:
         assertTrue(schemaService.deleteSchema("https://vendor.test.com/schemas/json/events/dummy/properties/1-0-0"));
         keepTrying("Event should be invalid since of the schema have been deleted", () -> schemaService
diff --git a/itests/src/test/java/org/apache/unomi/itests/TestUtils.java b/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
index 2767a38..c52d920 100644
--- a/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
+++ b/itests/src/test/java/org/apache/unomi/itests/TestUtils.java
@@ -37,10 +37,13 @@
 import org.apache.http.util.EntityUtils;
 import org.apache.unomi.api.ContextResponse;
 import org.apache.unomi.api.Event;
+import org.apache.unomi.api.Metadata;
 import org.apache.unomi.api.Profile;
+import org.apache.unomi.api.Scope;
 import org.apache.unomi.api.Session;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.services.DefinitionsService;
+import org.apache.unomi.api.services.ScopeService;
 import org.apache.unomi.itests.tools.httpclient.HttpClientThatWaitsForUnomi;
 import org.apache.unomi.persistence.spi.CustomObjectMapper;
 import org.apache.unomi.persistence.spi.PersistenceService;
@@ -131,6 +134,16 @@
 		return persistenceService.removeByQuery(condition, Session.class);
 	}
 
+	public static void createScope(String scopeId, String scopeName, ScopeService scopeService) {
+		Scope scope = new Scope();
+		scope.setItemId(scopeId);
+		Metadata metadata = new Metadata();
+		metadata.setName(scopeName);
+		metadata.setId(scopeId);
+		scope.setMetadata(metadata);
+		scopeService.save(scope);
+	}
+
 	public static class RequestResponse {
 		private ContextResponse contextResponse;
 		private String cookieHeaderValue;
diff --git a/itests/src/test/resources/schemas/event-dummy-invalid-4.json b/itests/src/test/resources/schemas/event-dummy-invalid-4.json
new file mode 100644
index 0000000..3bd4cab
--- /dev/null
+++ b/itests/src/test/resources/schemas/event-dummy-invalid-4.json
@@ -0,0 +1,8 @@
+{
+    "eventType": "dummy",
+    "scope": "unknown_scope",
+    "properties": {
+        "workspace": "dummy_workspace",
+        "path": "dummy/path"
+    }
+}
diff --git a/itests/src/test/resources/schemas/event-flattened-valid.json b/itests/src/test/resources/schemas/event-flattened-valid.json
index 0a9a802..7c8e380 100644
--- a/itests/src/test/resources/schemas/event-flattened-valid.json
+++ b/itests/src/test/resources/schemas/event-flattened-valid.json
@@ -1,13 +1,13 @@
 {
-  "eventType":"flattened",
-  "scope":"scope",
-  "flattenedProperties": {
-    "interests": {
-      "cars": 15,
-      "football": 59
+    "eventType": "flattened",
+    "scope": "dummy_scope",
+    "flattenedProperties": {
+        "interests": {
+            "cars": 15,
+            "football": 59
+        }
+    },
+    "properties": {
+        "marker": "###EVENT_MARKER###"
     }
-  },
-  "properties": {
-    "marker": "###EVENT_MARKER###"
-  }
-}
\ No newline at end of file
+}
diff --git a/itests/src/test/resources/schemas/schema-dummy-properties.json b/itests/src/test/resources/schemas/schema-dummy-properties.json
index 8a1965d..e497791 100644
--- a/itests/src/test/resources/schemas/schema-dummy-properties.json
+++ b/itests/src/test/resources/schemas/schema-dummy-properties.json
@@ -13,6 +13,10 @@
     "workspace": {
       "type": "string"
     },
+    "scope": {
+      "type": "string",
+      "validateScope": true
+    },
     "path": {
       "type": "string",
       "maxLength": 20000
diff --git a/rest/src/main/java/org/apache/unomi/rest/deserializers/ContextRequestDeserializer.java b/rest/src/main/java/org/apache/unomi/rest/deserializers/ContextRequestDeserializer.java
index e435341..c14ec89 100644
--- a/rest/src/main/java/org/apache/unomi/rest/deserializers/ContextRequestDeserializer.java
+++ b/rest/src/main/java/org/apache/unomi/rest/deserializers/ContextRequestDeserializer.java
@@ -28,7 +28,6 @@
 import org.apache.unomi.api.Profile;
 import org.apache.unomi.api.services.PersonalizationService;
 import org.apache.unomi.rest.exception.InvalidRequestException;
-import org.apache.unomi.schema.api.JsonSchemaWrapper;
 import org.apache.unomi.schema.api.SchemaService;
 
 import java.io.IOException;