All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.prediction.data.storage.Event.scala Maven / Gradle / Ivy

The newest version!
/** 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 org.joda.time.DateTime
import org.joda.time.DateTimeZone

/** Each event in the Event Store can be represented by fields in this case
  * class.
  *
  * @param eventId Unique ID of this event.
  * @param event Name of this event.
  * @param entityType Type of the entity associated with this event.
  * @param entityId ID of the entity associated with this event.
  * @param targetEntityType Type of the target entity associated with this
  *                         event.
  * @param targetEntityId ID of the target entity associated with this event.
  * @param properties Properties associated with this event.
  * @param eventTime Time of the happening of this event.
  * @param tags Tags of this event.
  * @param prId PredictedResultId of this event.
  * @param creationTime Time of creation in the system of this event.
  * @group Event Data
  */
case class Event(
  val eventId: Option[String] = None,
  val event: String,
  val entityType: String,
  val entityId: String,
  val targetEntityType: Option[String] = None,
  val targetEntityId: Option[String] = None,
  val properties: DataMap = DataMap(), // default empty
  val eventTime: DateTime = DateTime.now,
  val tags: Seq[String] = Nil,
  val prId: Option[String] = None,
  val creationTime: DateTime = DateTime.now
) {
  override def toString(): String = {
    s"Event(id=$eventId,event=$event,eType=$entityType,eId=$entityId," +
    s"tType=$targetEntityType,tId=$targetEntityId,p=$properties,t=$eventTime," +
    s"tags=$tags,pKey=$prId,ct=$creationTime)"
  }
}

/** :: DeveloperApi ::
  * Utilities for validating [[Event]]s
  *
  * @group Event Data
  */
@DeveloperApi
object EventValidation {
  /** Default time zone is set to UTC */
  val defaultTimeZone = DateTimeZone.UTC

  /** Checks whether an event name contains a reserved prefix
    *
    * @param name Event name
    * @return true if event name starts with \$ or pio_, false otherwise
    */
  def isReservedPrefix(name: String): Boolean = name.startsWith("$") ||
    name.startsWith("pio_")

  /** PredictionIO reserves some single entity event names. They are currently
    * \$set, \$unset, and \$delete.
    */
  val specialEvents = Set("$set", "$unset", "$delete")

  /** Checks whether an event name is a special PredictionIO event name
    *
    * @param name Event name
    * @return true if the name is a special event, false otherwise
    */
  def isSpecialEvents(name: String): Boolean = specialEvents.contains(name)

  /** Validate an [[Event]], throwing exceptions when the candidate violates any
    * of the following:
    *
    *  - event name must not be empty
    *  - entityType must not be empty
    *  - entityId must not be empty
    *  - targetEntityType must not be Some of empty
    *  - targetEntityId must not be Some of empty
    *  - targetEntityType and targetEntityId must be both Some or None
    *  - properties must not be empty when event is \$unset
    *  - event name must be a special event if it has a reserved prefix
    *  - targetEntityType and targetEntityId must be None if the event name has
    *    a reserved prefix
    *  - entityType must be a built-in entity type if entityType has a
    *    reserved prefix
    *  - targetEntityType must be a built-in entity type if targetEntityType is
    *    Some and has a reserved prefix
    *
    * @param e Event to be validated
    */
  def validate(e: Event): Unit = {

    require(!e.event.isEmpty, "event must not be empty.")
    require(!e.entityType.isEmpty, "entityType must not be empty string.")
    require(!e.entityId.isEmpty, "entityId must not be empty string.")
    require(e.targetEntityType.map(!_.isEmpty).getOrElse(true),
      "targetEntityType must not be empty string")
    require(e.targetEntityId.map(!_.isEmpty).getOrElse(true),
      "targetEntityId must not be empty string.")
    require(!((e.targetEntityType != None) && (e.targetEntityId == None)),
      "targetEntityType and targetEntityId must be specified together.")
    require(!((e.targetEntityType == None) && (e.targetEntityId != None)),
      "targetEntityType and targetEntityId must be specified together.")
    require(!((e.event == "$unset") && e.properties.isEmpty),
      "properties cannot be empty for $unset event")
    require(!isReservedPrefix(e.event) || isSpecialEvents(e.event),
      s"${e.event} is not a supported reserved event name.")
    require(!isSpecialEvents(e.event) ||
      ((e.targetEntityType == None) && (e.targetEntityId == None)),
      s"Reserved event ${e.event} cannot have targetEntity")
    require(!isReservedPrefix(e.entityType) ||
      isBuiltinEntityTypes(e.entityType),
      s"The entityType ${e.entityType} is not allowed. " +
        s"'pio_' is a reserved name prefix.")
    require(e.targetEntityType.map{ t =>
      (!isReservedPrefix(t) || isBuiltinEntityTypes(t))}.getOrElse(true),
      s"The targetEntityType ${e.targetEntityType.get} is not allowed. " +
        s"'pio_' is a reserved name prefix.")
    validateProperties(e)
  }

  /** Defines built-in entity types. The current built-in type is pio_pr. */
  val builtinEntityTypes: Set[String] = Set("pio_pr")

  /** Defines built-in properties. This is currently empty. */
  val builtinProperties: Set[String] = Set()

  /** Checks whether an entity type is a built-in entity type */
  def isBuiltinEntityTypes(name: String): Boolean = builtinEntityTypes.contains(name)

  /** Validate event properties, throwing exceptions when the candidate violates
    * any of the following:
    *
    *  - property name must not contain a reserved prefix
    *
    * @param e Event to be validated
    */
  def validateProperties(e: Event): Unit = {
    e.properties.keySet.foreach { k =>
      require(!isReservedPrefix(k) || builtinProperties.contains(k),
        s"The property ${k} is not allowed. " +
          s"'pio_' is a reserved name prefix.")
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy