diff --git a/src/main/scala/io/keen/client/scala/Client.scala b/src/main/scala/io/keen/client/scala/Client.scala index ea08778..c64af25 100644 --- a/src/main/scala/io/keen/client/scala/Client.scala +++ b/src/main/scala/io/keen/client/scala/Client.scala @@ -1,11 +1,10 @@ package io.keen.client.scala -import com.ning.http.client.Response -import dispatch._ -import Defaults._ +import dispatch.{Http,Req,url} + import grizzled.slf4j.Logging import java.net.URL -import scala.concurrent.Promise +import scala.concurrent.Future import java.nio.charset.StandardCharsets // XXX Remaining: Extraction, Funnel, Saved Queries List, Saved Queries Row, Saved Queries Row Result @@ -17,7 +16,8 @@ class Client( projectId: String, masterKey: String, writeKey: String, - readKey: String) extends Logging { + readKey: String, + httpAdapter: HttpAdapter = new HttpAdapter()) extends Logging { /** * Publish a single event. See [[https://keen.io/docs/api/reference/#event-collection-resource Event Collection Resource]]. @@ -28,7 +28,7 @@ class Client( def addEvent(collection: String, event: String): Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId / "events" / collection).secure .setBody(event.getBytes(StandardCharsets.UTF_8)) - doRequest(freq.POST, writeKey) + httpAdapter.doRequest(freq.POST, writeKey) } /** @@ -39,7 +39,7 @@ class Client( def addEvents(events: String): Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId / "events").secure .setBody(events.getBytes(StandardCharsets.UTF_8)) - doRequest(freq.POST, writeKey) + httpAdapter.doRequest(freq.POST, writeKey) } /** @@ -81,7 +81,7 @@ class Client( filters: Option[String] = None, timeframe: Option[String] = None, timezone: Option[String] = None, - groupBy: Option[String]= None): Future[Response] = { + groupBy: Option[String]= None): Future[Response] = doQuery( analysisType = "count", @@ -91,7 +91,6 @@ class Client( timeframe = timeframe, timezone = timezone, groupBy = groupBy) - } /** * Returns the number of '''unique''' resources in the event collection matching the given criteria. See [[https://keen.io/docs/api/reference/#event-resource Event Resource]]. @@ -109,7 +108,7 @@ class Client( filters: Option[String] = None, timeframe: Option[String] = None, timezone: Option[String] = None, - groupBy: Option[String]= None): Future[Response] = { + groupBy: Option[String]= None): Future[Response] = doQuery( analysisType = "count", @@ -119,7 +118,6 @@ class Client( timeframe = timeframe, timezone = timezone, groupBy = groupBy) - } /** * Returns the maximum numeric value for the target property in the event collection matching the given criteria. See [[https://keen.io/docs/api/reference/#maximum-resource Maximum Resource]]. @@ -191,7 +189,7 @@ class Client( filters: Option[String] = None, timeframe: Option[String] = None, timezone: Option[String] = None, - groupBy: Option[String]= None): Future[Response] = { + groupBy: Option[String]= None): Future[Response] = doQuery( analysisType = "select_unique", @@ -201,7 +199,6 @@ class Client( timeframe = timeframe, timezone = timezone, groupBy = groupBy) - } /** * Returns the sum across all numeric values for the target property in the event collection matching the given criteria. See [[https://keen.io/docs/api/reference/#sum-resource Sum Resource]]. @@ -237,7 +234,7 @@ class Client( */ def deleteCollection(collection: String): Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId / "events" / collection).secure - doRequest(freq.DELETE, masterKey) + httpAdapter.doRequest(freq.DELETE, masterKey) } /** @@ -245,7 +242,7 @@ class Client( */ def deleteProperty(collection: String, name: String): Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId / "events" / collection / "properties" / name).secure - doRequest(freq.DELETE, masterKey) + httpAdapter.doRequest(freq.DELETE, masterKey) } /** @@ -255,7 +252,7 @@ class Client( */ def getEvents: Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId / "events").secure - doRequest(freq.GET, masterKey) + httpAdapter.doRequest(freq.GET, masterKey) } /** @@ -266,7 +263,7 @@ class Client( */ def getCollection(collection: String): Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId / "events" / collection).secure - doRequest(freq.GET, masterKey) + httpAdapter.doRequest(freq.GET, masterKey) } /** @@ -275,7 +272,7 @@ class Client( */ def getProjects: Future[Response] = { val freq = (url(apiURL) / version / "projects").secure - doRequest(freq.GET, masterKey) + httpAdapter.doRequest(freq.GET, masterKey) } /** @@ -284,7 +281,7 @@ class Client( */ def getProject: Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId).secure - doRequest(freq.GET, masterKey) + httpAdapter.doRequest(freq.GET, masterKey) } /** @@ -292,7 +289,7 @@ class Client( */ def getProperty(collection: String, name: String): Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId / "events" / collection / "properties" / name).secure - doRequest(freq.GET, masterKey) + httpAdapter.doRequest(freq.GET, masterKey) } /** @@ -301,26 +298,7 @@ class Client( */ def getQueries: Future[Response] = { val freq = (url(apiURL) / version / "projects" / projectId / "queries").secure - doRequest(freq.GET, masterKey) - } - - private def doQuery( - analysisType: String, - collection: String, - targetProperty: Option[String], - filters: Option[String] = None, - timeframe: Option[String] = None, - timezone: Option[String] = None, - groupBy: Option[String]= None): Future[Response] = { - - val req = (url(apiURL) / version / "projects" / projectId / "queries" / analysisType).secure - .addQueryParameter("event_collection", collection) - - val paramNames = List("target_property", "filters", "timeframe", "timezone", "group_by") - val params = List(targetProperty, filters, timeframe, timezone, groupBy) - - val reqWithParams = parameterizeUrl(req, paramNames, params) - doRequest(reqWithParams.GET, readKey) + httpAdapter.doRequest(freq.GET, masterKey) } /** @@ -343,23 +321,28 @@ class Client( .foldLeft(req)((r, nameAndParam) => r.addQueryParameter(nameAndParam._1, nameAndParam._2.get)) } - /** - * Perform the request with some debugging for good measure. - * - * @param req The request - */ - private def doRequest(req: Req, key: String) = { - val breq = req.toRequest - debug("%s: %s".format(breq.getMethod, breq.getUrl)) - Http( - req.setHeader("Content-type", "application/json; charset=utf-8") - // Set the provided key, for authentication. - .setHeader("Authorization", key) - ) + private def doQuery( + analysisType: String, + collection: String, + targetProperty: Option[String], + filters: Option[String] = None, + timeframe: Option[String] = None, + timezone: Option[String] = None, + groupBy: Option[String]= None): Future[Response] = { + + val req = (url(apiURL) / version / "projects" / projectId / "queries" / analysisType).secure + .addQueryParameter("event_collection", collection) + + val paramNames = List("target_property", "filters", "timeframe", "timezone", "group_by") + val params = List(targetProperty, filters, timeframe, timezone, groupBy) + + val reqWithParams = parameterizeUrl(req, paramNames, params) + httpAdapter.doRequest(reqWithParams.GET, readKey) } } object Client { + /** * Disconnects any remaining connections. Both idle and active. If you are accessing * Keen through a proxy that keeps connections alive this is useful. diff --git a/src/main/scala/io/keen/client/scala/HttpAdapter.scala b/src/main/scala/io/keen/client/scala/HttpAdapter.scala new file mode 100644 index 0000000..b91a3dc --- /dev/null +++ b/src/main/scala/io/keen/client/scala/HttpAdapter.scala @@ -0,0 +1,25 @@ +package io.keen.client.scala + +import dispatch._ +import grizzled.slf4j.Logging +import scala.concurrent.ExecutionContext.Implicits.global + +class HttpAdapter() extends Logging { + + /** + * Perform the request with some debugging for good measure. + * + * @param req The request + */ + def doRequest(req: Req, key: String): Future[Response] = { + val breq = req.toRequest + debug("%s: %s".format(breq.getMethod, breq.getUrl)) + Http( + req.setHeader("Content-type", "application/json; charset=utf-8") + // Set the provided key, for authentication. + .setHeader("Authorization", key) + ) map { res => + Response(statusCode = res.getStatusCode, body =res.getResponseBody) + } + } +} diff --git a/src/main/scala/io/keen/client/scala/package.scala b/src/main/scala/io/keen/client/scala/package.scala new file mode 100644 index 0000000..dc1ba1b --- /dev/null +++ b/src/main/scala/io/keen/client/scala/package.scala @@ -0,0 +1,6 @@ +package io.keen.client + +package object scala { + + case class Response(statusCode: Int, body: String) +} \ No newline at end of file diff --git a/src/test/scala/ClientSpec.scala b/src/test/scala/ClientSpec.scala index 19432e3..ea635dc 100644 --- a/src/test/scala/ClientSpec.scala +++ b/src/test/scala/ClientSpec.scala @@ -3,7 +3,11 @@ package test import org.specs2.mutable._ import scala.concurrent.Await import scala.concurrent.duration._ -import io.keen.client.scala.Client +import io.keen.client.scala.{Client,HttpAdapter} + +class TestHttpAdapter() extends HttpAdapter { + +} class ClientSpec extends Specification { @@ -15,7 +19,8 @@ class ClientSpec extends Specification { projectId = sys.env("KEEN_PROJECT_ID"), masterKey = sys.env("KEEN_MASTER_KEY"), writeKey = sys.env("KEEN_WRITE_KEY"), - readKey = sys.env("KEEN_READ_KEY") + readKey = sys.env("KEEN_READ_KEY"), + httpAdapter = new TestHttpAdapter() ) "fetch collection" in { @@ -23,19 +28,19 @@ class ClientSpec extends Specification { collection = "foo" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "fetch projects" in { val res = Await.result(client.getProjects, Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "fetch project" in { val res = Await.result(client.getProject, Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "fetch property" in { @@ -44,14 +49,14 @@ class ClientSpec extends Specification { name = "foo" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "fetch event collection" in { val res = Await.result(client.getEvents, Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "write an event" in { @@ -61,13 +66,13 @@ class ClientSpec extends Specification { event = """{"foo": "bar"}""" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(201) + res.statusCode must beEqualTo(201) } "fetch queries" in { val res = Await.result(client.getQueries, Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "count" in { @@ -75,7 +80,7 @@ class ClientSpec extends Specification { collection = "foo" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "count with filters and timeframe" in { @@ -85,7 +90,7 @@ class ClientSpec extends Specification { timeframe = Some("this_week") ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "count unique" in { @@ -94,7 +99,7 @@ class ClientSpec extends Specification { targetProperty = "gorch" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "minimum" in { @@ -103,7 +108,7 @@ class ClientSpec extends Specification { targetProperty = "gorch" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "maximum" in { @@ -112,7 +117,7 @@ class ClientSpec extends Specification { targetProperty = "gorch" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "average" in { @@ -121,7 +126,7 @@ class ClientSpec extends Specification { targetProperty = "gorch" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "sum" in { @@ -130,7 +135,7 @@ class ClientSpec extends Specification { targetProperty = "gorch" ), Duration(5, "second")) // println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + res.statusCode must beEqualTo(200) } "select unique" in { @@ -138,18 +143,18 @@ class ClientSpec extends Specification { collection = "foo", targetProperty = "gorch" ), Duration(5, "second")) - println(res.getResponseBody) - res.getStatusCode must beEqualTo(200) + // println(res.getResponseBody) + res.statusCode must beEqualTo(200) } - // Is this working? + // // Is this working? // "delete property" in { // val res = Await.result(client.deleteProperty( // collection = "foo", // name = "foo" // ), Duration(5, "second")) // println(res.getResponseBody) - // res.getStatusCode must beEqualTo(204) + // res.statusCode must beEqualTo(204) // } // "write many events" in { @@ -159,7 +164,7 @@ class ClientSpec extends Specification { // ), Duration(5, "second")) // // println(res.getResponseBody) // // Not working!! XXX - // res.getStatusCode must beEqualTo(500) + // res.statusCode must beEqualTo(500) // } "shutdown" in {