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

io.prediction.data.webhooks.segmentio.SegmentIOConnector.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.webhooks.segmentio

import io.prediction.data.webhooks.{ConnectorException, JsonConnector}
import org.json4s._

private[prediction] object SegmentIOConnector extends JsonConnector {

  // private lazy val supportedAPI = Vector("2", "2.0", "2.0.0")

  implicit val json4sFormats: Formats = DefaultFormats

  override
  def toEventJson(data: JObject): JObject = {
    try {
      val version: String = data.values("version").toString
/*
      if (!supportedAPI.contains(version)) {
        throw new ConnectorException(
          s"Supported segment.io API versions: [2]. got [$version]"
        )
      }
*/
    } catch { case _: Throwable ⇒
      throw new ConnectorException(s"Failed to get segment.io API version.")
    }

    val common = try {
      data.extract[Common]
    } catch {
      case e: Throwable ⇒ throw new ConnectorException(
        s"Cannot extract Common field from $data. ${e.getMessage}", e
      )
    }

    try {
      common.`type` match {
        case "identify" ⇒
          toEventJson(
            common = common,
            identify = data.extract[Events.Identify]
          )

        case "track" ⇒
          toEventJson(
            common = common,
            track = data.extract[Events.Track]
          )

        case "alias" ⇒
          toEventJson(
            common = common,
            alias = data.extract[Events.Alias]
          )

        case "page" ⇒
          toEventJson(
            common = common,
            page = data.extract[Events.Page]
          )

        case "screen" ⇒
          toEventJson(
            common = common,
            screen = data.extract[Events.Screen]
          )

        case "group" ⇒
          toEventJson(
            common = common,
            group = data.extract[Events.Group]
          )

        case _ ⇒
          throw new ConnectorException(
            s"Cannot convert unknown type ${common.`type`} to event JSON."
          )
      }
    } catch {
      case e: ConnectorException => throw e
      case e: Exception =>
        throw new ConnectorException(
          s"Cannot convert $data to event JSON. ${e.getMessage}", e
        )
    }
  }

  def toEventJson(common: Common, identify: Events.Identify ): JObject = {
    import org.json4s.JsonDSL._
    val eventProperties = "traits" → identify.traits
    toJson(common, eventProperties)
  }

  def toEventJson(common: Common, track: Events.Track): JObject = {
    import org.json4s.JsonDSL._
    val eventProperties =
      ("properties" → track.properties) ~
      ("event" → track.event)
    toJson(common, eventProperties)
  }

  def toEventJson(common: Common, alias: Events.Alias): JObject = {
    import org.json4s.JsonDSL._
    toJson(common, "previous_id" → alias.previous_id)
  }

  def toEventJson(common: Common, screen: Events.Screen): JObject = {
    import org.json4s.JsonDSL._
    val eventProperties =
      ("name" → screen.name) ~
      ("properties" → screen.properties)
    toJson(common, eventProperties)
  }

  def toEventJson(common: Common, page: Events.Page): JObject = {
    import org.json4s.JsonDSL._
    val eventProperties =
      ("name" → page.name) ~
      ("properties" → page.properties)
    toJson(common, eventProperties)
  }

  def toEventJson(common: Common, group: Events.Group): JObject = {
    import org.json4s.JsonDSL._
    val eventProperties =
      ("group_id" → group.group_id) ~
      ("traits" → group.traits)
    toJson(common, eventProperties)
  }

  private def toJson(common: Common, props: JObject): JsonAST.JObject = {
    val commonFields = commonToJson(common)
    JObject(("properties" → properties(common, props)) :: commonFields.obj)
  }

  private def properties(common: Common, eventProps: JObject): JObject = {
    import org.json4s.JsonDSL._
    common.context map { context ⇒
      try {
        ("context" → Extraction.decompose(context)) ~ eventProps
      } catch {
        case e: Throwable ⇒
          throw new ConnectorException(
            s"Cannot convert $context to event JSON. ${e.getMessage }", e
          )
      }
    } getOrElse eventProps
  }

  private def commonToJson(common: Common): JObject =
    commonToJson(common, common.`type`)

  private def commonToJson(common: Common, typ: String): JObject = {
    import org.json4s.JsonDSL._
      common.user_id.orElse(common.anonymous_id) match {
        case Some(userId) ⇒
          ("event" → typ) ~
            ("entityType" → "user") ~
            ("entityId" → userId) ~
            ("eventTime" → common.timestamp)

        case None ⇒
          throw new ConnectorException(
            "there was no `userId` or `anonymousId` in the common fields."
          )
      }
  }
}

object Events {

  private[prediction] case class Track(
    event: String,
    properties: Option[JObject] = None
  )

  private[prediction] case class Alias(previous_id: String, user_id: String)

  private[prediction] case class Group(
    group_id: String,
    traits: Option[JObject] = None
  )

  private[prediction] case class Screen(
    name: Option[String] = None,
    properties: Option[JObject] = None
  )

  private[prediction] case class Page(
    name: Option[String] = None,
    properties: Option[JObject] = None
  )

  private[prediction] case class Identify(
    user_id: String,
    traits: Option[JObject]
  )

}

object Common {

  private[prediction] case class Integrations(
    All: Boolean = false,
    Mixpanel: Boolean = false,
    Marketo: Boolean = false,
    Salesforse: Boolean = false
  )

  private[prediction] case class Context(
    ip: String,
    library: Library,
    user_agent: String,
    app: Option[App] = None,
    campaign: Option[Campaign] = None,
    device: Option[Device] = None,
    network: Option[Network] = None,
    location: Option[Location] = None,
    os: Option[OS] = None,
    referrer: Option[Referrer] = None,
    screen: Option[Screen] = None,
    timezone: Option[String] = None
  )

  private[prediction] case class Screen(width: Int, height: Int, density: Int)

  private[prediction] case class Referrer(id: String, `type`: String)

  private[prediction] case class OS(name: String, version: String)

  private[prediction] case class Location(
    city: Option[String] = None,
    country: Option[String] = None,
    latitude: Option[Double] = None,
    longitude: Option[Double] = None,
    speed: Option[Int] = None
  )

  case class Page(
    path: String,
    referrer: String,
    search: String,
    title: String,
    url: String
  )

  private[prediction] case class Network(
    bluetooth: Option[Boolean] = None,
    carrier: Option[String] = None,
    cellular: Option[Boolean] = None,
    wifi: Option[Boolean] = None
  )

  private[prediction] case class Library(name: String, version: String)

  private[prediction] case class Device(
    id: Option[String] = None,
    advertising_id: Option[String] = None,
    ad_tracking_enabled: Option[Boolean] = None,
    manufacturer: Option[String] = None,
    model: Option[String] = None,
    name: Option[String] = None,
    `type`: Option[String] = None,
    token: Option[String] = None
  )

  private[prediction] case class Campaign(
    name: Option[String] = None,
    source: Option[String] = None,
    medium: Option[String] = None,
    term: Option[String] = None,
    content: Option[String] = None
  )

  private[prediction] case class App(
    name: Option[String] = None,
    version: Option[String] = None,
    build: Option[String] = None
  )

}

private[prediction] case class Common(
  `type`: String,
  sent_at: String,
  timestamp: String,
  version: String,
  anonymous_id: Option[String] = None,
  user_id: Option[String] = None,
  context: Option[Common.Context] = None,
  integrations: Option[Common.Integrations] = None
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy