Skip to content

Instantly share code, notes, and snippets.

@afranzi
Created January 9, 2019 15:39
Show Gist options
  • Save afranzi/76497177ea8bc305c355b49bc3b2f6b0 to your computer and use it in GitHub Desktop.
Save afranzi/76497177ea8bc305c355b49bc3b2f6b0 to your computer and use it in GitHub Desktop.
Schema Validation Path Traceability
{
"user": {
"id": "5a34008f8cece4000764cd5a"
},
"device": {
"id": "5a3400a48cece4000764d342",
"platform": "Android"
},
"product": {
"id": "remixprototype",
"version": "0.0.1"
},
"deploymentEnv": "prod",
"createdAt":"2018-07-19T13:37:28+02:00",
"schema": "/schemas/events/device-sensor-event/1.json",
"source": "sensor",
"data": {
"connected": true,
"enabled": true,
"scan": [
{
"bssid": "37d091cf4531c14a206dd94178a82570",
"connectionState": "completed",
"rssi": -67,
"ssid": "b937d93cce3b3bc8c34bd16ea724c6f2"
},
{
"bssid": "b937d93cce3b3bc8c34bd16ea724c6f2",
"connectionState": "disconnected",
"rssi": -127,
"ssid": "37d091cf4531c14a206dd94178a82570"
}
],
"type": "wifi"
}
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/events/base-device-event/1.json",
"additionalProperties": true,
"allOf": [
{
"$ref": "/schemas/events/base-event/1.json"
}
],
"description": "Base schema for all user-generated events (on device).",
"properties": {
"device": {
"description": "Device information",
"$ref": "/schemas/objects/Device/1.json"
}
},
"required": [
"device"
]
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/events/base-event/1.json",
"additionalProperties": true,
"description": "Base schema for all user-generated events (on device)",
"properties": {
"user": {
"description": "User information",
"$ref": "/schemas/objects/User/1.json"
},
"product": {
"description": "Product information",
"$ref": "/schemas/objects/Product/1.json"
},
"deploymentEnv": {
"description": "Deployment environment in use",
"enum": [
"dev",
"test",
"stage",
"prod"
]
},
"createdAt": {
"description": "Timestamp when the event was generate (following rfc 3339 format)",
"type": "string",
"format": "date-time"
},
"schema": {
"description": "Name of the schema to validate against",
"type": "string"
},
"source": {
"description": "Source of the data point",
"type": "string",
"enum": ["sensor", "questionnaire"]
}
},
"required": [
"source",
"schema",
"user",
"product",
"deploymentEnv",
"createdAt"
],
"type": "object"
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/events/device-sensor-event/1.json",
"additionalProperties": true,
"allOf": [
{
"$ref": "/schemas/events/base-device-event/1.json"
}
],
"description": "User event including sensor data",
"properties": {
"data": {
"oneOf": [
{"$ref": "/schemas/objects/sensors/SensorAccelerometer/1.json"},
{"$ref": "/schemas/objects/sensors/SensorX/1.json"},
{"$ref": "/schemas/objects/sensors/SensorY/1.json"},
{"$ref": "/schemas/objects/sensors/SensorWifi/1.json"}
]
}
},
"required": [
"data",
"device",
"product",
"user"
]
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/objects/Device/1.json",
"additionalProperties": true,
"description": "Device data",
"properties": {
"id": {
"description": "ID uniquely identifying the device the event was sent from",
"type": "string",
"pattern": "[0-9a-z]{24}|undefined"
},
"platform": {
"description": "Platform the device is running on",
"enum": [
"Android",
"iPhone"
]
}
},
"required": [
"id",
"platform"
]
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/objects/Product/1.json",
"additionalProperties": true,
"description": "User data",
"properties": {
"id": {
"description": "Unique product ID",
"type": "string"
},
"name": {
"description": "Name of the product (e.g., Minä Mix, Reflections",
"type": "string"
},
"version": {
"description": "Version of the product",
"type": "string"
}
},
"required": [
"id",
"version"
]
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/objects/sensors/WifiConnection/1.json",
"additionalProperties": true,
"description": "Wifi connection data.",
"properties": {
"bssid": {
"description": "Hashed Basic Service Set ID (relates to the access point used to connect to the network (pattern to be defined).",
"type": "string",
"pattern": "[0-9a-z]{32}"
},
"connectionState": {
"description": "State of the connection (like associated, disconnected, completed, group handshake...).",
"type": "string",
"enum": [
"associated",
"associating",
"completed",
"disconnected",
"fourWayHandshake",
"groupHandshake",
"inactive",
"scanning",
"uninitialized",
"unknown"
]
},
"rssi": {
"description": "Strength of the signal measured in dBm (also known as RSSI).",
"type": "integer"
},
"ssid": {
"description": "Hashed Service Set ID of the WLAN network (relates to the name of the network) (pattern to be defined).",
"type": "string",
"pattern": "[0-9a-z]{32}"
}
},
"required": [
"bssid",
"rssi",
"ssid"
]
}
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "/schemas/objects/User/1.json",
"additionalProperties": true,
"description": "User data",
"properties": {
"id": {
"description": "User ID",
"type": "string"
}
},
"required": [
"id"
]
}
import grizzled.slf4j.Logging
import org.everit.json.schema.Schema
import org.everit.json.schema.event.{CombinedSchemaMatchEvent, SchemaReferencedEvent, ValidationListener}
import scala.collection.mutable.ListBuffer
import scala.util.matching.Regex
import scala.collection.JavaConverters._
object SchemaValidationListener {
case class SchemaReferenced(location: String, schemaId: String)
val SchemaId = "schemaId"
val schemaRefRe: Regex = "^\\{\"\\$ref\":\"([\\w\\/._-]+)\"\\}$".r(SchemaId)
def extractSchemaReferenced(schema: String): Option[String] = {
schemaRefRe
.findFirstMatchIn(schema)
.map(_.group(SchemaId))
}
}
case class SchemaValidationListener() extends ValidationListener with Logging {
val schemasReferencedMatching: ListBuffer[SchemaReferenced] = ListBuffer.empty
override def schemaReferenced(event: SchemaReferencedEvent): Unit = {
val subSchema: Schema = event.getReferredSchema
val schemaReferenced = Option(subSchema.getId).getOrElse(subSchema.getSchemaLocation)
val path = event.getPath.asScala.mkString("/")
val reference = SchemaReferenced(path, schemaReferenced)
schemasReferencedMatching.append(reference)
}
override def combinedSchemaMatch(event: CombinedSchemaMatchEvent): Unit = {
val subSchema: Schema = event.getSubSchema
val path = event.getPath.asScala.mkString("/")
extractSchemaReferenced(subSchema.toString)
.foreach { schemaId =>
val reference = SchemaReferenced(path, schemaId)
schemasReferencedMatching.append(reference)
}
}
}
// Output after validating the 1.device-sensor-wifi-event.json
SchemaReferenced("#/device", "/schemas/objects/Device/1.json")
SchemaReferenced("#/product","/schemas/objects/Product/1.json")
SchemaReferenced("#/user","/schemas/objects/User/1.json")
SchemaReferenced("#","/schemas/events/base-event/1.json")
SchemaReferenced("#","/schemas/events/base-device-event/1.json")
SchemaReferenced("#/data/scan/[0]","/schemas/objects/sensors/WifiConnection/1.json")
SchemaReferenced("#/data/scan/[1]","/schemas/objects/sensors/WifiConnection/1.json")
SchemaReferenced("#/data","/schemas/objects/sensors/SensorWifi/1.json")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment