This is an automated email from the ASF dual-hosted git repository. shuber pushed a commit to branch draft-json-schema-integration in repository https://gitbox.apache.org/repos/asf/unomi.git
commit 1d44d76b73b87352a63a768a23978496bbcb7114 Author: Serge Huber <[email protected]> AuthorDate: Tue Oct 12 14:43:04 2021 +0200 JSON Schema integration --- .../main/java/org/apache/unomi/api/SchemaType.java | 60 ++++++ .../apache/unomi/api/services/SchemaRegistry.java | 32 +++ services/pom.xml | 10 + .../services/impl/schemas/PropertyTypeKeyword.java | 110 ++++++++++ .../services/impl/schemas/SchemaRegistryImpl.java | 224 +++++++++++++++++++++ .../resources/META-INF/cxs/schemas/condition.json | 15 ++ .../META-INF/cxs/schemas/conditiontype.json | 22 ++ .../resources/META-INF/cxs/schemas/consent.json | 25 +++ .../META-INF/cxs/schemas/consentType.json | 20 ++ .../resources/META-INF/cxs/schemas/customitem.json | 20 ++ .../META-INF/cxs/schemas/customitems/page.json | 66 ++++++ .../META-INF/cxs/schemas/customitems/site.json | 13 ++ .../main/resources/META-INF/cxs/schemas/event.json | 29 +++ .../META-INF/cxs/schemas/events/modifyConsent.json | 20 ++ .../META-INF/cxs/schemas/events/view.json | 20 ++ .../main/resources/META-INF/cxs/schemas/goal.json | 18 ++ .../main/resources/META-INF/cxs/schemas/item.json | 31 +++ .../resources/META-INF/cxs/schemas/metadata.json | 44 ++++ .../META-INF/cxs/schemas/metadataitem.json | 14 ++ .../resources/META-INF/cxs/schemas/parameter.json | 20 ++ .../resources/META-INF/cxs/schemas/profile.json | 41 ++++ .../resources/META-INF/cxs/schemas/session.json | 41 ++++ .../META-INF/cxs/schemas/timestampeditem.json | 13 ++ .../META-INF/cxs/schemas/values/boolean.json | 6 + .../META-INF/cxs/schemas/values/date.json | 7 + .../META-INF/cxs/schemas/values/email.json | 7 + .../META-INF/cxs/schemas/values/integer.json | 6 + .../META-INF/cxs/schemas/values/long.json | 6 + .../resources/META-INF/cxs/schemas/values/set.json | 7 + .../META-INF/cxs/schemas/values/string.json | 6 + .../resources/OSGI-INF/blueprint/blueprint.xml | 8 + 31 files changed, 961 insertions(+) diff --git a/api/src/main/java/org/apache/unomi/api/SchemaType.java b/api/src/main/java/org/apache/unomi/api/SchemaType.java new file mode 100644 index 0000000..a3bd37a --- /dev/null +++ b/api/src/main/java/org/apache/unomi/api/SchemaType.java @@ -0,0 +1,60 @@ +/* + * 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.api; + +import java.util.Map; + +public class SchemaType implements PluginType { + + private transient long pluginId; + private String schemaId; + private String target; + private Map<String,Object> schemaTree; + + public long getPluginId() { + return pluginId; + } + + public void setPluginId(long pluginId) { + this.pluginId = pluginId; + } + + public String getSchemaId() { + return schemaId; + } + + public void setSchemaId(String schemaId) { + this.schemaId = schemaId; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public Map<String, Object> getSchemaTree() { + return schemaTree; + } + + public void setSchemaTree(Map<String, Object> schemaTree) { + this.schemaTree = schemaTree; + } +} diff --git a/api/src/main/java/org/apache/unomi/api/services/SchemaRegistry.java b/api/src/main/java/org/apache/unomi/api/services/SchemaRegistry.java new file mode 100644 index 0000000..03a234e --- /dev/null +++ b/api/src/main/java/org/apache/unomi/api/services/SchemaRegistry.java @@ -0,0 +1,32 @@ +/* + * 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.api.services; + +import org.apache.unomi.api.SchemaType; + +import java.util.List; + +public interface SchemaRegistry { + + boolean isValid(Object object, String schemaId); + + SchemaType getSchema(String schemaId); + + List<SchemaType> getTargetSchemas(String target); + +} diff --git a/services/pom.xml b/services/pom.xml index 9438cc9..c4acbb2 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -172,6 +172,12 @@ <scope>provided</scope> </dependency> + <dependency> + <groupId>com.networknt</groupId> + <artifactId>json-schema-validator</artifactId> + <version>1.0.49</version> + </dependency> + </dependencies> <build> @@ -185,6 +191,10 @@ <Embed-Dependency>*;scope=compile|runtime</Embed-Dependency> <Import-Package> sun.misc;resolution:=optional, + org.jcodings;resolution:=optional, + org.jcodings.specific;resolution:=optional, + org.joni;resolution:=optional, + org.joni.exception;resolution:=optional, * </Import-Package> </instructions> diff --git a/services/src/main/java/org/apache/unomi/services/impl/schemas/PropertyTypeKeyword.java b/services/src/main/java/org/apache/unomi/services/impl/schemas/PropertyTypeKeyword.java new file mode 100644 index 0000000..68afb41 --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/impl/schemas/PropertyTypeKeyword.java @@ -0,0 +1,110 @@ +/* + * 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.services.impl.schemas; + +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.schema.*; +import org.apache.unomi.api.PropertyType; +import org.apache.unomi.api.services.ProfileService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.MessageFormat; +import java.util.*; + +class PropertyTypeKeyword extends AbstractKeyword { + + private static final Logger logger = LoggerFactory.getLogger(PropertyTypeKeyword.class); + + private final ProfileService profileService; + private final SchemaRegistryImpl schemaRegistry; + + private static final class PropertyTypeJsonValidator extends AbstractJsonValidator { + + String schemaPath; + JsonNode schemaNode; + JsonSchema parentSchema; + ValidationContext validationContext; + ProfileService profileService; + SchemaRegistryImpl schemaRegistry; + + public PropertyTypeJsonValidator(String keyword, String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext, ProfileService profileService, SchemaRegistryImpl schemaRegistry) { + super(keyword); + this.schemaPath = schemaPath; + this.schemaNode = schemaNode; + this.parentSchema = parentSchema; + this.validationContext = validationContext; + this.profileService = profileService; + this.schemaRegistry = schemaRegistry; + } + + @Override + public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String at) { + Set<ValidationMessage> validationMessages = new HashSet<>(); + Iterator<String> fieldNames = node.fieldNames(); + while (fieldNames.hasNext()) { + String fieldName = fieldNames.next(); + PropertyType propertyType = getPropertyType(fieldName); + if (propertyType == null) { + validationMessages.add(buildValidationMessage(CustomErrorMessageType.of("property-not-found", new MessageFormat("{0} : Couldn''t find property type with id={1}")), at, fieldName)); + } else { + // @todo further validation, if it can be used in this context (event, profile, session) + String valueTypeId = propertyType.getValueTypeId(); + JsonSchema jsonSchema = schemaRegistry.getJsonSchema("https://unomi.apache.org/schemas/json/values/" + valueTypeId + ".json"); + if (jsonSchema == null) { + validationMessages.add(buildValidationMessage(CustomErrorMessageType.of("value-schema-not-found", new MessageFormat("{0} : Couldn''t find schema type with id={1}")), at, "https://unomi.apache.org/schemas/json/values/" + valueTypeId + ".json")); + } else { + Set<ValidationMessage> propertyValidationMessages = jsonSchema.validate(node.get(fieldName)); + if (propertyValidationMessages != null) { + validationMessages.addAll(propertyValidationMessages); + } + } + } + } + return validationMessages; + } + + private PropertyType getPropertyType(String fieldName) { + Map<String, PropertyType> propertyTypes = new HashMap<>(); + if (schemaNode.size() > 0) { + for (Iterator<JsonNode> it = schemaNode.iterator(); it.hasNext(); ) { + JsonNode target = it.next(); + if ("_all".equals(target.asText())) { + return profileService.getPropertyType(fieldName); + } else { + Collection<PropertyType> targetPropertyTypes = profileService.getTargetPropertyTypes(target.asText()); + targetPropertyTypes.stream().map(propertyType -> propertyTypes.put(propertyType.getItemId(), propertyType)); + } + } + return propertyTypes.get(fieldName); + } else { + return profileService.getPropertyType(fieldName); + } + } + } + + public PropertyTypeKeyword(ProfileService profileService, SchemaRegistryImpl schemaRegistry) { + super("propertyTypes"); + this.profileService = profileService; + this.schemaRegistry = schemaRegistry; + } + + @Override + public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception { + return new PropertyTypeJsonValidator(this.getValue(), schemaPath, schemaNode, parentSchema, validationContext, profileService, schemaRegistry); + } +} diff --git a/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaRegistryImpl.java b/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaRegistryImpl.java new file mode 100644 index 0000000..ee13aab --- /dev/null +++ b/services/src/main/java/org/apache/unomi/services/impl/schemas/SchemaRegistryImpl.java @@ -0,0 +1,224 @@ +/* + * 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.services.impl.schemas; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.*; +import com.networknt.schema.uri.URIFetcher; +import org.apache.commons.lang3.StringUtils; +import org.apache.unomi.api.SchemaType; +import org.apache.unomi.api.services.ProfileService; +import org.apache.unomi.api.services.SchemaRegistry; +import org.apache.unomi.persistence.spi.CustomObjectMapper; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.SynchronousBundleListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.net.URL; +import java.util.*; +import java.util.stream.Collectors; + +public class SchemaRegistryImpl implements SchemaRegistry, SynchronousBundleListener { + + private static final String URI = "https://json-schema.org/draft/2019-09/schema"; + + private static final Logger logger = LoggerFactory.getLogger(SchemaRegistryImpl.class.getName()); + + private final Map<Long, List<SchemaType>> schemaTypesByBundle = new HashMap<>(); + private final Map<String, SchemaType> schemaTypesById = new HashMap<>(); + + private final Map<String, JsonSchema> jsonSchemasById = new LinkedHashMap<>(); + + private final Map<String, Long> bundleIdBySchemaId = new HashMap<>(); + + private BundleContext bundleContext; + + private ProfileService profileService; + + public void bundleChanged(BundleEvent event) { + switch (event.getType()) { + case BundleEvent.STARTED: + processBundleStartup(event.getBundle().getBundleContext()); + break; + case BundleEvent.STOPPING: + processBundleStop(event.getBundle().getBundleContext()); + break; + } + } + + public void init() { + processBundleStartup(bundleContext); + + // process already started bundles + for (Bundle bundle : bundleContext.getBundles()) { + if (bundle.getBundleContext() != null && bundle.getBundleId() != bundleContext.getBundle().getBundleId()) { + processBundleStartup(bundle.getBundleContext()); + } + } + + bundleContext.addBundleListener(this); + logger.info("Schema registry initialized."); + } + + public void destroy() { + bundleContext.removeBundleListener(this); + logger.info("Schema registry shutdown."); + } + + public void setBundleContext(BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + public void setProfileService(ProfileService profileService) { + this.profileService = profileService; + } + + public boolean isValid(Object object, String schemaId) { + JsonSchema jsonSchema = jsonSchemasById.get(schemaId); + if (jsonSchema != null) { + try { + // this is a workaround to get the JsonNode from the object, maybe there is a better way (more efficient) to do this ? + StringWriter stringWriter = new StringWriter(); + CustomObjectMapper.getObjectMapper().writeValue(stringWriter, object); + JsonNode jsonNode = CustomObjectMapper.getObjectMapper().readTree(stringWriter.toString()); + Set<ValidationMessage> validationMessages = jsonSchema.validate(jsonNode); + if (validationMessages == null || validationMessages.isEmpty()) { + return true; + } + for (ValidationMessage validationMessage : validationMessages) { + logger.error("Error validating object against schema {}: {}", schemaId, validationMessage); + } + return false; + } catch (IOException e) { + logger.error("Error validating object with schema {}", schemaId, e); + } + } + return false; + } + + @Override + public List<SchemaType> getTargetSchemas(String target) { + return schemaTypesById.values().stream().filter(schemaType -> schemaType.getTarget().equals(target)).collect(Collectors.toList()); + } + + @Override + public SchemaType getSchema(String schemaId) { + return schemaTypesById.get(schemaId); + } + + private void loadPredefinedSchemas(BundleContext bundleContext) { + Enumeration<URL> predefinedSchemas = bundleContext.getBundle().findEntries("META-INF/cxs/schemas", "*.json", true); + if (predefinedSchemas == null) { + return; + } + + ObjectMapper objectMapper = new ObjectMapper(); + + List<SchemaType> schemaTypes = this.schemaTypesByBundle.get(bundleContext.getBundle().getBundleId()); + + while (predefinedSchemas.hasMoreElements()) { + URL predefinedSchemaURL = predefinedSchemas.nextElement(); + logger.debug("Found predefined JSON schema at " + predefinedSchemaURL + ", loading... "); + + try { + JsonMetaSchema jsonMetaSchema = JsonMetaSchema.builder(URI, JsonMetaSchema.getV201909()) + .addKeyword(new PropertyTypeKeyword(profileService, this)).build(); + JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)) + .addMetaSchema(jsonMetaSchema) + .defaultMetaSchemaURI(URI) + .uriFetcher(getBundleUriFetcher(bundleContext), "https", "http").build(); + InputStream schemaInputStream = predefinedSchemaURL.openStream(); + JsonSchema jsonSchema = jsonSchemaFactory.getSchema(schemaInputStream); + String schemaId = jsonSchema.getSchemaNode().get("$id").asText(); + jsonSchemasById.put(schemaId, jsonSchema); + bundleIdBySchemaId.put(schemaId, bundleContext.getBundle().getBundleId()); + SchemaType schemaType = new SchemaType(); + schemaType.setPluginId(bundleContext.getBundle().getBundleId()); + schemaType.setSchemaId(schemaId); + Map<String, Object> schemaTree = (Map<String, Object>) objectMapper.treeToValue(jsonSchema.getSchemaNode(), Map.class); + schemaType.setSchemaTree(schemaTree); + String[] splitPath = predefinedSchemaURL.getPath().split("/"); + if (splitPath.length > 5) { + String target = splitPath[4]; + if (StringUtils.isNotBlank(target)) { + schemaType.setTarget(target); + } + } + schemaTypes.add(schemaType); + schemaTypesById.put(schemaId, schemaType); + schemaInputStream.close(); + } catch (Exception e) { + logger.error("Error while loading schema definition " + predefinedSchemaURL, e); + } + } + + } + + private URIFetcher getBundleUriFetcher(BundleContext bundleContext) { + return uri -> { + logger.debug("Fetching schema {}", uri); + Long bundleId = bundleIdBySchemaId.get(uri.toString()); + if (bundleId == null) { + logger.error("Couldn't find bundle for schema {}", uri); + return null; + } + String uriPath = uri.getPath().substring("/schemas/json".length()); + URL schemaURL = bundleContext.getBundle(bundleId).getResource("META-INF/cxs/schemas" + uriPath); + if (schemaURL != null) { + return schemaURL.openStream(); + } else { + logger.error("Couldn't find resource {} in bundle {}", "META-INF/cxs/schemas" + uriPath, bundleId); + return null; + } + }; + } + + private void processBundleStartup(BundleContext bundleContext) { + if (bundleContext == null) { + return; + } + schemaTypesByBundle.put(bundleContext.getBundle().getBundleId(), new ArrayList<>()); + loadPredefinedSchemas(bundleContext); + } + + private void processBundleStop(BundleContext bundleContext) { + if (bundleContext == null) { + return; + } + List<SchemaType> schemaTypes = schemaTypesByBundle.remove(bundleContext.getBundle().getBundleId()); + if (schemaTypes != null) { + for (SchemaType schemaType : schemaTypes) { + jsonSchemasById.remove(schemaType.getSchemaId()); + bundleIdBySchemaId.remove(schemaType.getSchemaId()); + schemaTypesById.remove(schemaType.getSchemaId()); + } + } + } + + protected JsonSchema getJsonSchema(String schemaId) { + return jsonSchemasById.get(schemaId); + } + +} diff --git a/services/src/main/resources/META-INF/cxs/schemas/condition.json b/services/src/main/resources/META-INF/cxs/schemas/condition.json new file mode 100644 index 0000000..000ee1d --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/condition.json @@ -0,0 +1,15 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/condition.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Condition", + "type": "object", + "properties" : { + "conditionTypeId" : { + "type" : "string" + }, + "parameterValues" : { + "type" : "object", + "maxProperties": 50 + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/conditiontype.json b/services/src/main/resources/META-INF/cxs/schemas/conditiontype.json new file mode 100644 index 0000000..85e78c8 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/conditiontype.json @@ -0,0 +1,22 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/conditiontype.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "ConditionType", + "type": "object", + "properties" : { + "conditionEvaluator" : { + "type" : "string" + }, + "queryBuilder" : { + "type" : "string" + }, + "parentCondition" : { + "$ref" : "https://unomi.apache.org/schemas/json/condition.json" + }, + "parameters" : { + "type" : "object", + "additionalProperties": { "$ref" : "https://unomi.apache.org/schemas/json/parameter.json" }, + "maxProperties": 50 + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/consent.json b/services/src/main/resources/META-INF/cxs/schemas/consent.json new file mode 100644 index 0000000..c1eb229 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/consent.json @@ -0,0 +1,25 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/consent.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Consent", + "type": "object", + "properties" : { + "scope" : { + "type" : "string" + }, + "typeIdentifier" : { + "type" : "string" + }, + "status" : { + "enum" : [ "GRANTED", "DENIED", "REVOKED" ] + }, + "statusDate" : { + "type" : "string", + "format" : "date-time" + }, + "revokeDate" : { + "type" : "string", + "format" : "date-time" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/consentType.json b/services/src/main/resources/META-INF/cxs/schemas/consentType.json new file mode 100644 index 0000000..19f4bef --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/consentType.json @@ -0,0 +1,20 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/consentType.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "ConsentType", + "type": "object", + "properties" : { + "typeIdentifier" : { + "type" : "string" + }, + "activated" : { + "type" : [ "null", "boolean" ] + }, + "title" : { + "type" : ["null", "string"] + }, + "description" : { + "type" : ["null", "string"] + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/customitem.json b/services/src/main/resources/META-INF/cxs/schemas/customitem.json new file mode 100644 index 0000000..0c59480 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/customitem.json @@ -0,0 +1,20 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/customitem.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "CustomItem", + "type": "object", + "allOf": [ + { + "$ref": "https://unomi.apache.org/schemas/json/item.json" + } + ], + "properties": { + "customItemType" : { + "type" : "string" + }, + "properties" : { + "type" : "object", + "maxProperties": 50 + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/customitems/page.json b/services/src/main/resources/META-INF/cxs/schemas/customitems/page.json new file mode 100644 index 0000000..252a51d --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/customitems/page.json @@ -0,0 +1,66 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/customitems/page.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "PageCustomItem", + "type": "object", + "allOf": [ + { + "$ref": "https://unomi.apache.org/schemas/json/customitem.json" + } + ], + "properties": { + "pageInfo": { + "type" : "object", + "properties" : { + "templateName": { + "type" : "string" + }, + "language": { + "type" : "string", + "maxLength": 5 + }, + "destinationURL": { + "type" : "string", + "format" : "uri" + }, + "categories": { + "type" : "array", + "items" : { "type" : "string"} + }, + "pageID": { + "type" : "string", + "pattern" : "^(\\w|[-_@\\.]){0,50}$" + }, + "nodeType": { + "type" : "string" + }, + "pagePath": { + "type" : "string" + }, + "pageName": { + "type" : "string" + }, + "referringURL": { + "type" : "string", + "format" : "uri" + }, + "tags": { + "type" : "array", + "items" : { "type" : "string" } + }, + "isContentTemplate": { + "type" : "boolean" + } + } + }, + "attributes": { + "type" : ["null", "object"] + }, + "consentTypes": { + "type" : ["null", "array"], + "items" : { + "$href" : "https://unomi.apache.org/schemas/json/consentType.json" + } + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/customitems/site.json b/services/src/main/resources/META-INF/cxs/schemas/customitems/site.json new file mode 100644 index 0000000..b6de454 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/customitems/site.json @@ -0,0 +1,13 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/customitems/site.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "SiteCustomItem", + "type": "object", + "allOf": [ + { + "$ref": "https://unomi.apache.org/schemas/json/customitem.json" + } + ], + "properties": { + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/event.json b/services/src/main/resources/META-INF/cxs/schemas/event.json new file mode 100644 index 0000000..a0e71b8 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/event.json @@ -0,0 +1,29 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/event.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Event", + "type": "object", + "allOf": [{ "$ref": "https://unomi.apache.org/schemas/json/timestampeditem.json" }], + "properties" : { + "eventType" : { + "type" : "string", + "pattern" : "^(\\w|[-_@\\.]){0,50}$" + }, + "profileId" : { + "type" : [ "null", "string"], + "pattern" : "^(\\w|[-_@\\.]){0,50}$" + }, + "sessionId" : { + "type" : [ "null", "string"], + "pattern" : "^(\\w|[-_@\\.]){0,50}$" + }, + "scope" : { + "type" : [ "null", "string"], + "pattern" : "^(\\w|[-_@\\.]){0,50}$" + }, + "sourceId" : { + "type" : [ "null", "string"], + "pattern" : "^(\\w|[-_@\\.]){0,50}$" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/events/modifyConsent.json b/services/src/main/resources/META-INF/cxs/schemas/events/modifyConsent.json new file mode 100644 index 0000000..e203d47 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/events/modifyConsent.json @@ -0,0 +1,20 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/events/modifyConsent.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "ModifyConsentEvent", + "type": "object", + "allOf": [{ "$ref": "https://unomi.apache.org/schemas/json/event.json" }], + "properties" : { + "properties" : { + "consent" : { + "$ref" : "https://unomi.apache.org/schemas/json/consent.json" + } + }, + "source" : { + "$ref" : "https://unomi.apache.org/schemas/json/customitems/page.json" + }, + "target" : { + "$ref" : "https://unomi.apache.org/schemas/json/customitem.json" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/events/view.json b/services/src/main/resources/META-INF/cxs/schemas/events/view.json new file mode 100644 index 0000000..5ea14a9 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/events/view.json @@ -0,0 +1,20 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/events/view.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "ViewEvent", + "type": "object", + "allOf": [{ "$ref": "https://unomi.apache.org/schemas/json/event.json" }], + "properties" : { + "properties" : { + "type" : "object", + "propertyTypes" : [ "events" ], + "maxProperties": 50 + }, + "source" : { + "$ref" : "https://unomi.apache.org/schemas/json/customitems/site.json" + }, + "target" : { + "$ref" : "https://unomi.apache.org/schemas/json/customitems/page.json" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/goal.json b/services/src/main/resources/META-INF/cxs/schemas/goal.json new file mode 100644 index 0000000..ee7c715 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/goal.json @@ -0,0 +1,18 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/goal.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Goal", + "type": "object", + "allOf": [{ "$ref": "https://unomi.apache.org/schemas/json/metadataitem.json" }], + "properties" : { + "startEvent" : { + "$ref" : "https://unomi.apache.org/schemas/json/condition.json" + }, + "targetEvent" : { + "$ref" : "https://unomi.apache.org/schemas/json/condition.json" + }, + "campaignId" : { + "type" : "string" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/item.json b/services/src/main/resources/META-INF/cxs/schemas/item.json new file mode 100644 index 0000000..3a4f148 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/item.json @@ -0,0 +1,31 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/item.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Item", + "type": "object", + "properties" : { + "itemId" : { + "type" : "string", + "pattern" : "^(\\w|[-_@\\.]){0,50}$", + "description" : "The identifier for the item" + }, + "itemType" : { + "type" : "string", + "description" : "The type for the item" + }, + "scope" : { + "type" : ["null","string"], + "description" : "The item's scope" + }, + "version" : { + "type" : ["null","integer"], + "minimum" : 0, + "description" : "The item's version number" + }, + "systemMetadata" : { + "type" : ["null","object"], + "description" : "Any system metadata for the item", + "maxProperties" : 50 + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/metadata.json b/services/src/main/resources/META-INF/cxs/schemas/metadata.json new file mode 100644 index 0000000..2116bba --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/metadata.json @@ -0,0 +1,44 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/metadata.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Metadata", + "type": "object", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "scope" : { + "type" : "string" + }, + "tags" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "systemTags" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "enabled" : { + "type" : "boolean" + }, + "missingPlugins" : { + "type" : "boolean" + }, + "hidden" : { + "type" : "boolean" + }, + "readOnly" : { + "type" : "boolean" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/metadataitem.json b/services/src/main/resources/META-INF/cxs/schemas/metadataitem.json new file mode 100644 index 0000000..325dafe --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/metadataitem.json @@ -0,0 +1,14 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/metadataitem.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "MetadataItem", + "type": "object", + "allOf": [ + { "$ref": "https://unomi.apache.org/schemas/json/item.json" } + ], + "properties" : { + "metadata" : { + "$ref" : "https://unomi.apache.org/schemas/json/metadata.json" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/parameter.json b/services/src/main/resources/META-INF/cxs/schemas/parameter.json new file mode 100644 index 0000000..abea6ae --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/parameter.json @@ -0,0 +1,20 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/parameter.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Parameter", + "type": "object", + "properties" : { + "id" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "multivalued" : { + "type" : "boolean" + }, + "defaultValue" : { + "type" : "string" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/profile.json b/services/src/main/resources/META-INF/cxs/schemas/profile.json new file mode 100644 index 0000000..2381b81 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/profile.json @@ -0,0 +1,41 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/profile.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Profile", + "type": "object", + "allOf": [ + { + "$ref": "https://unomi.apache.org/schemas/json/item.json" + } + ], + "properties": { + "properties" : { + "type" : "object", + "propertyTypes" : [ "profile" ], + "maxProperties": 50 + }, + "systemProperties" : { + "type" : "object", + "maxProperties": 50 + }, + "segments" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "scores" : { + "type" : "object", + "additionalProperties": { "type": "integer" }, + "maxProperties": 100 + }, + "mergedWith" : { + "type" : "string" + }, + "consents" : { + "type" : "object", + "additionalProperties": { "$ref" : "https://unomi.apache.org/schemas/json/consent.json" }, + "maxProperties": 50 + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/session.json b/services/src/main/resources/META-INF/cxs/schemas/session.json new file mode 100644 index 0000000..b2534b4 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/session.json @@ -0,0 +1,41 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/session.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Session", + "type": "object", + "allOf": [ + { + "$ref": "https://unomi.apache.org/schemas/json/timestampeditem.json" + } + ], + "properties": { + "profileId" : { + "type" : "string" + }, + "profile" : { + "$ref" : "https://unomi.apache.org/schemas/json/profile.json" + }, + "properties" : { + "type" : "object", + "maxProperties": 50 + }, + "systemProperties" : { + "type" : "object", + "maxProperties": 50 + }, + "timeStamp" : { + "type" : "string", + "format" : "date-time" + }, + "lastEventDate" : { + "type" : "string", + "format" : "date-time" + }, + "size" : { + "type" : "integer" + }, + "duration" : { + "type" : "integer" + } + } +} diff --git a/services/src/main/resources/META-INF/cxs/schemas/timestampeditem.json b/services/src/main/resources/META-INF/cxs/schemas/timestampeditem.json new file mode 100644 index 0000000..bee6c8f --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/timestampeditem.json @@ -0,0 +1,13 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/timestampeditem.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "TimestampedItem", + "type": "object", + "allOf": [{ "$ref": "https://unomi.apache.org/schemas/json/item.json" }], + "properties" : { + "timeStamp" : { + "type" : ["null","string"], + "format" : "date-time" + } + } +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/values/boolean.json b/services/src/main/resources/META-INF/cxs/schemas/values/boolean.json new file mode 100644 index 0000000..8a3ed95 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/values/boolean.json @@ -0,0 +1,6 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/values/boolean.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Boolean", + "type": "boolean" +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/values/date.json b/services/src/main/resources/META-INF/cxs/schemas/values/date.json new file mode 100644 index 0000000..7a39489 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/values/date.json @@ -0,0 +1,7 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/values/date.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Date", + "type": "string", + "format" : "date-time" +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/values/email.json b/services/src/main/resources/META-INF/cxs/schemas/values/email.json new file mode 100644 index 0000000..705f2f6 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/values/email.json @@ -0,0 +1,7 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/values/email.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Email", + "type": "string", + "format" : "email" +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/values/integer.json b/services/src/main/resources/META-INF/cxs/schemas/values/integer.json new file mode 100644 index 0000000..f5cb7b9 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/values/integer.json @@ -0,0 +1,6 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/values/integer.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Integer", + "type": "integer" +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/values/long.json b/services/src/main/resources/META-INF/cxs/schemas/values/long.json new file mode 100644 index 0000000..769c78a --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/values/long.json @@ -0,0 +1,6 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/values/long.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Long", + "type": "integer" +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/values/set.json b/services/src/main/resources/META-INF/cxs/schemas/values/set.json new file mode 100644 index 0000000..34b8611 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/values/set.json @@ -0,0 +1,7 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/values/set.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "Set", + "type": "object", + "maxProperties": 50 +} \ No newline at end of file diff --git a/services/src/main/resources/META-INF/cxs/schemas/values/string.json b/services/src/main/resources/META-INF/cxs/schemas/values/string.json new file mode 100644 index 0000000..6d5e0e8 --- /dev/null +++ b/services/src/main/resources/META-INF/cxs/schemas/values/string.json @@ -0,0 +1,6 @@ +{ + "$id": "https://unomi.apache.org/schemas/json/values/string.json", + "$schema": "https://json-schema.org/draft/2019-09/schema", + "title": "String", + "type": "string" +} \ No newline at end of file diff --git a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml index 771c740..ed8b398 100644 --- a/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml +++ b/services/src/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -104,12 +104,20 @@ </bean> <service id="eventTypeRegistry" ref="eventTypeRegistryImpl" interface="org.apache.unomi.api.services.EventTypeRegistry"/> + <bean id="schemaRegistryImpl" class="org.apache.unomi.services.impl.schemas.SchemaRegistryImpl" init-method="init" + destroy-method="destroy"> + <property name="bundleContext" ref="blueprintBundleContext"/> + <property name="profileService" ref="profileServiceImpl" /> + </bean> + <service id="schemaRegistry" ref="schemaRegistryImpl" interface="org.apache.unomi.api.services.SchemaRegistry"/> + <bean id="eventServiceImpl" class="org.apache.unomi.services.impl.events.EventServiceImpl"> <property name="persistenceService" ref="persistenceService"/> <property name="definitionsService" ref="definitionsServiceImpl"/> <property name="sourceService" ref="sourceServiceImpl"/> <property name="bundleContext" ref="blueprintBundleContext"/> <property name="eventTypeRegistry" ref="eventTypeRegistryImpl"/> + <property name="schemaRegistry" ref="schemaRegistryImpl" /> <property name="predefinedEventTypeIds"> <set> <value>view</value>
