blob: a1935d71eb6dfb399094b03674c649369a268af8 [file] [log] [blame]
/** Copyright 2015 TappingStone, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.prediction.data.storage
import io.prediction.annotation.DeveloperApi
import io.prediction.data.{Utils => DataUtils}
import org.joda.time.DateTime
import org.json4s._
/** :: DeveloperApi ::
* Support library for dealing with [[Event]] and JSON4S
*
* @group Event Data
*/
@DeveloperApi
object EventJson4sSupport {
/** This is set to org.json4s.DefaultFormats. Do not use JSON4S to serialize
* or deserialize Joda-Time DateTime because it has some issues with timezone
* (as of version 3.2.10)
*/
implicit val formats = DefaultFormats
/** :: DeveloperApi ::
* Convert JSON from Event Server to [[Event]]
*
* @return deserialization routine used by [[APISerializer]]
*/
@DeveloperApi
def readJson: PartialFunction[JValue, Event] = {
case JObject(x) => {
val fields = new DataMap(x.toMap)
// use get() if required in json
// use getOpt() if not required in json
try {
val event = fields.get[String]("event")
val entityType = fields.get[String]("entityType")
val entityId = fields.get[String]("entityId")
val targetEntityType = fields.getOpt[String]("targetEntityType")
val targetEntityId = fields.getOpt[String]("targetEntityId")
val properties = fields.getOrElse[Map[String, JValue]](
"properties", Map())
// default currentTime expressed as UTC timezone
lazy val currentTime = DateTime.now(EventValidation.defaultTimeZone)
val eventTime = fields.getOpt[String]("eventTime")
.map{ s =>
try {
DataUtils.stringToDateTime(s)
} catch {
case _: Exception =>
throw new MappingException(s"Fail to extract eventTime ${s}")
}
}.getOrElse(currentTime)
// disable tags from API for now.
val tags = List()
// val tags = fields.getOpt[Seq[String]]("tags").getOrElse(List())
val prId = fields.getOpt[String]("prId")
// don't allow user set creationTime from API for now.
val creationTime = currentTime
// val creationTime = fields.getOpt[String]("creationTime")
// .map{ s =>
// try {
// DataUtils.stringToDateTime(s)
// } catch {
// case _: Exception =>
// throw new MappingException(s"Fail to extract creationTime ${s}")
// }
// }.getOrElse(currentTime)
val newEvent = Event(
event = event,
entityType = entityType,
entityId = entityId,
targetEntityType = targetEntityType,
targetEntityId = targetEntityId,
properties = DataMap(properties),
eventTime = eventTime,
prId = prId,
creationTime = creationTime
)
EventValidation.validate(newEvent)
newEvent
} catch {
case e: Exception => throw new MappingException(e.toString, e)
}
}
}
/** :: DeveloperApi ::
* Convert [[Event]] to JSON for use by the Event Server
*
* @return serialization routine used by [[APISerializer]]
*/
@DeveloperApi
def writeJson: PartialFunction[Any, JValue] = {
case d: Event => {
JObject(
JField("eventId",
d.eventId.map( eid => JString(eid)).getOrElse(JNothing)) ::
JField("event", JString(d.event)) ::
JField("entityType", JString(d.entityType)) ::
JField("entityId", JString(d.entityId)) ::
JField("targetEntityType",
d.targetEntityType.map(JString(_)).getOrElse(JNothing)) ::
JField("targetEntityId",
d.targetEntityId.map(JString(_)).getOrElse(JNothing)) ::
JField("properties", d.properties.toJObject) ::
JField("eventTime", JString(DataUtils.dateTimeToString(d.eventTime))) ::
// disable tags from API for now
// JField("tags", JArray(d.tags.toList.map(JString(_)))) ::
// disable tags from API for now
JField("prId",
d.prId.map(JString(_)).getOrElse(JNothing)) ::
// don't show creationTime for now
JField("creationTime",
JString(DataUtils.dateTimeToString(d.creationTime))) ::
Nil)
}
}
/** :: DeveloperApi ::
* Convert JSON4S JValue to [[Event]]
*
* @return deserialization routine used by [[DBSerializer]]
*/
@DeveloperApi
def deserializeFromJValue: PartialFunction[JValue, Event] = {
case jv: JValue => {
val event = (jv \ "event").extract[String]
val entityType = (jv \ "entityType").extract[String]
val entityId = (jv \ "entityId").extract[String]
val targetEntityType = (jv \ "targetEntityType").extract[Option[String]]
val targetEntityId = (jv \ "targetEntityId").extract[Option[String]]
val properties = (jv \ "properties").extract[JObject]
val eventTime = DataUtils.stringToDateTime(
(jv \ "eventTime").extract[String])
val tags = (jv \ "tags").extract[Seq[String]]
val prId = (jv \ "prId").extract[Option[String]]
val creationTime = DataUtils.stringToDateTime(
(jv \ "creationTime").extract[String])
Event(
event = event,
entityType = entityType,
entityId = entityId,
targetEntityType = targetEntityType,
targetEntityId = targetEntityId,
properties = DataMap(properties),
eventTime = eventTime,
tags = tags,
prId = prId,
creationTime = creationTime)
}
}
/** :: DeveloperApi ::
* Convert [[Event]] to JSON4S JValue
*
* @return serialization routine used by [[DBSerializer]]
*/
@DeveloperApi
def serializeToJValue: PartialFunction[Any, JValue] = {
case d: Event => {
JObject(
JField("event", JString(d.event)) ::
JField("entityType", JString(d.entityType)) ::
JField("entityId", JString(d.entityId)) ::
JField("targetEntityType",
d.targetEntityType.map(JString(_)).getOrElse(JNothing)) ::
JField("targetEntityId",
d.targetEntityId.map(JString(_)).getOrElse(JNothing)) ::
JField("properties", d.properties.toJObject) ::
JField("eventTime", JString(DataUtils.dateTimeToString(d.eventTime))) ::
JField("tags", JArray(d.tags.toList.map(JString(_)))) ::
JField("prId",
d.prId.map(JString(_)).getOrElse(JNothing)) ::
JField("creationTime",
JString(DataUtils.dateTimeToString(d.creationTime))) ::
Nil)
}
}
/** :: DeveloperApi ::
* Custom JSON4S serializer for [[Event]] intended to be used by database
* access, or anywhere that demands serdes of [[Event]] to/from JSON4S JValue
*/
@DeveloperApi
class DBSerializer extends CustomSerializer[Event](format => (
deserializeFromJValue, serializeToJValue))
/** :: DeveloperApi ::
* Custom JSON4S serializer for [[Event]] intended to be used by the Event
* Server, or anywhere that demands serdes of [[Event]] to/from JSON
*/
@DeveloperApi
class APISerializer extends CustomSerializer[Event](format => (
readJson, writeJson))
}