Skip to content

Commit

Permalink
Merge branch 'release/0.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
chuwy committed Jan 25, 2018
2 parents 9279006 + 4481d03 commit 0fa5377
Show file tree
Hide file tree
Showing 19 changed files with 920 additions and 469 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
Version 0.5.0 (2018-01-25)
--------------------------
Add trackError method (#47)
Add support for missing ecommerce events (#62)
Add support for Google Compute Engine metadata (#45)
Add callbacks for sent events (#58)
Remove default parameters (#63)
Include iglu-core (#35)

Version 0.4.0 (2017-10-18)
--------------------------
Add Scala 2.12 support (#51)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ guest$ sbt test

## Copyright and license

The Snowplow Scala Tracker is copyright 2015-2017 Snowplow Analytics Ltd.
The Snowplow Scala Tracker is copyright 2015-2018 Snowplow Analytics Ltd.

Licensed under the **[Apache License, Version 2.0][license]** (the "License");
you may not use this software except in compliance with the License.
Expand All @@ -46,7 +46,7 @@ limitations under the License.
[license-image]: http://img.shields.io/badge/license-Apache--2-blue.svg?style=flat
[license]: http://www.apache.org/licenses/LICENSE-2.0

[release-image]: http://img.shields.io/badge/release-0.4.0-blue.svg?style=flat
[release-image]: http://img.shields.io/badge/release-0.5.0-blue.svg?style=flat
[releases]: https://github.com/snowplow/snowplow-scala-tracker/releases

[snowplow]: http://snowplowanalytics.com
Expand Down
9 changes: 5 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@
lazy val root = project.in(file("."))
.settings(Seq[Setting[_]](
organization := "com.snowplowanalytics",
version := "0.4.0",
version := "0.5.0",
description := "Scala tracker for Snowplow",
name := "snowplow-scala-tracker",
description := "Scala analytics SDK for Snowplow",
scalaVersion := "2.11.11",
crossScalaVersions := Seq("2.10.6", "2.11.11", "2.12.3"),
scalaVersion := "2.11.12",
crossScalaVersions := Seq("2.10.7", "2.11.12", "2.12.3"),
scalacOptions := Seq("-deprecation", "-encoding", "utf8"),
javacOptions ++= Seq("-source", "1.8", "-target", "1.8")
))
Expand All @@ -30,6 +29,8 @@ lazy val root = project.in(file("."))
libraryDependencies := Seq(
Dependencies.Libraries.scalajHttp,
Dependencies.Libraries.json4sJackson,
Dependencies.Libraries.igluCore,
Dependencies.Libraries.igluCoreJson4s,
Dependencies.Libraries.mockito,
Dependencies.Libraries.specs2,
Dependencies.Libraries.scalaCheck)
Expand Down
7 changes: 5 additions & 2 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ object Dependencies {
// Scala
val scalajHttp = "2.3.0"
val json4s = "3.2.11"
val igluCore = "0.2.0"

// Java (test only)
val mockito = "1.9.5"
Expand All @@ -29,8 +30,10 @@ object Dependencies {

object Libraries {
// Scala
val scalajHttp = "org.scalaj" %% "scalaj-http" % V.scalajHttp
val json4sJackson = "org.json4s" %% "json4s-jackson" % V.json4s
val scalajHttp = "org.scalaj" %% "scalaj-http" % V.scalajHttp
val json4sJackson = "org.json4s" %% "json4s-jackson" % V.json4s
val igluCore = "com.snowplowanalytics" %% "iglu-core" % V.igluCore
val igluCoreJson4s = "com.snowplowanalytics" %% "iglu-core-json4s" % V.igluCore

// Java (test only)
val mockito = "org.mockito" % "mockito-all" % V.mockito % "test"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015-2017 Snowplow Analytics Ltd. All rights reserved.
* Copyright (c) 2015-2018 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
Expand All @@ -24,30 +24,28 @@ import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._

import com.snowplowanalytics.iglu.core.{SchemaKey, SchemaVer, SelfDescribingData }

/**
* Trait with parsing EC2 meta data logic
* http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
* Module with parsing EC2-metadata logic
* @see http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
*/
object Ec2Metadata {

val instanceIdentitySchema = "iglu:com.amazon.aws.ec2/instance_identity_document/jsonschema/1-0-0"
val instanceIdentityUri = "http://169.254.169.254/latest/dynamic/instance-identity/document/"
val InstanceIdentitySchema = SchemaKey("com.amazon.aws.ec2", "instance_identity_document", "jsonschema", SchemaVer.Full(1,0,0))
val InstanceIdentityUri = "http://169.254.169.254/latest/dynamic/instance-identity/document/"

private var contextSlot: Option[SelfDescribingJson] = None

/**
* Get context stored in mutable variable
*
* @return some context or None in case of any error or not completed request
*/
/** Retrieve some context if available or nothing in case of any error */
def context: Option[SelfDescribingJson] = contextSlot

/**
* Set callback on successful instance identity GET request
*/
def initializeContextRequest(): Unit = {
getInstanceContextFuture.onComplete {
case Success(json: SelfDescribingJson) => contextSlot = Some(json)
case Success(json) => contextSlot = Some(json)
case Failure(error) => System.err.println(s"Unable to retrieve EC2 context. ${error.getMessage}")
}
}
Expand All @@ -73,15 +71,15 @@ object Ec2Metadata {
* @return future JSON with identity data
*/
def getInstanceContextFuture: Future[SelfDescribingJson] =
getInstanceIdentity.map(SelfDescribingJson(instanceIdentitySchema, _))
getInstanceIdentity.map(SelfDescribingData(InstanceIdentitySchema, _))

/**
* Tries to GET instance identity document for EC2 instance
*
* @return future JSON object with identity data
*/
def getInstanceIdentity: Future[JObject] = {
val instanceIdentityDocument = getContent(instanceIdentityUri)
val instanceIdentityDocument = getContent(InstanceIdentityUri)
instanceIdentityDocument.map { (resp: String) =>
parseOpt(resp) match {
case Some(jsonObject: JObject) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2015-2018 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package com.snowplowanalytics.snowplow.scalatracker

import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.control.NonFatal

import scalaj.http._

import org.json4s._
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods._

import com.snowplowanalytics.iglu.core.{SchemaKey, SchemaVer, SelfDescribingData}

import scala.util.{Failure, Success}

/**
* Module with parsing GCE-metadata logic
* @see https://cloud.google.com/compute/docs/storing-retrieving-metadata
*
* Unlike EC2 instance document, GCE does not provide an excerpt, but instad
* this module collect only meaningful properties
*/
object GceMetadata {

val InstanceMetadataSchema = SchemaKey("com.google.cloud.gce", "instance_metadata", "jsonschema", SchemaVer.Full(1,0,0))
val InstanceMetadataUri = "http://metadata.google.internal/computeMetadata/v1/instance/"

private var contextSlot: Option[SelfDescribingJson] = None

/** Retrieve some context if available or nothing in case of any error */
def context: Option[SelfDescribingJson] = contextSlot

/**
* Tries to make blocking request to EC2 instance identity document
* On EC2 request takes ~6ms, while on non-EC2 box it blocks thread for 3 second
*
* @return some context or None in case of any error including 3 sec timeout
*/
def getInstanceContextBlocking: Option[SelfDescribingJson] =
try {
Some(Await.result(getInstanceContextFuture, 3.seconds))
} catch {
case NonFatal(_) => None
}

/** Set callback on successful instance metadata GET request */
def initializeContextRequest(): Unit = {
getInstanceContextFuture.onComplete {
case Success(json) => contextSlot = Some(json)
case Failure(error) => System.err.println(s"Unable to retrieve GCP context. ${error.getMessage}")
}
}

/**
* Tries to GET self-describing JSON with instance identity
* or timeout after 10 seconds
*
* @return future JSON with identity data
*/
def getInstanceContextFuture: Future[SelfDescribingJson] =
getMetadata.map(SelfDescribingData(InstanceMetadataSchema, _))

/** Construct metadata context */
def getMetadata: Future[JObject] =
getString("cpu-platform").zip(getString("hostname")).zip(getString("id"))
.zip(getString("image")).zip(getString("machine-type")).zip(getString("name"))
.zip(getJson("tags")).zip(getString("zone")).zip(getDir("attributes/")).map {
case ((((((((cpuPlatform, hostname), id), image), machineType), name), tags), zone), attributes) =>
("cpuPlatform", cpuPlatform) ~
("hostname", hostname) ~
("id", id) ~
("image", image) ~
("machineType", machineType) ~
("name", name) ~
("tags", tags) ~
("zone", zone) ~
("attributes", attributes)
}

def request(path: String) =
Http(InstanceMetadataUri + path).header("Metadata-Flavor", "Google")

private def getString(path: String): Future[String] =
Future(request(path).asString.body)

private def getJson(path: String): Future[JValue] =
Future(parse(request(path).asString.body)).map {
case JObject(Nil) => JNull
case JArray(Nil) => JNull
case other => other
}

private def getDir(path: String): Future[JValue] =
Future(parse(request(path + "?recursive=true").asString.body)).map {
case JObject(Nil) => JNull
case other => other
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015-2017 Snowplow Analytics Ltd. All rights reserved.
* Copyright (c) 2015-2018 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
Expand All @@ -19,10 +19,13 @@ import org.json4s.jackson.JsonMethods._

import scala.collection.mutable.{Map => MMap}

import emitters.TEmitter.EmitterPayload

/**
* Contains the map of key-value pairs making up an event
* Must be used within single function as **not thread-safe**
*/
class Payload {
private[scalatracker] class Payload {

val Encoding = "UTF-8"

Expand Down Expand Up @@ -69,7 +72,7 @@ class Payload {
* @param typeWhenNotEncoded Key to use if encodeBase64 is false
*/
def addJson(
json: JObject,
json: JValue,
encodeBase64: Boolean,
typeWhenEncoded: String,
typeWhenNotEncoded: String): Unit = {
Expand All @@ -88,6 +91,5 @@ class Payload {
*
* @return Event map
*/
def get(): Map[String, String] = Map(nvPairs.toList: _*)

def get: EmitterPayload = Map(nvPairs.toList: _*)
}

This file was deleted.

Loading

0 comments on commit 0fa5377

Please sign in to comment.