diff --git a/.gitignore b/.gitignore index 7cd6015..01056ba 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,8 @@ project/target/ target/ .history -/sbt.sh \ No newline at end of file +/sbt.sh +/.classpath +/.project +/.settings +/.cache diff --git a/README.md b/README.md index 45d8f9f..d24c407 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,65 @@ -What is async-dynamo? -===================== -async-dynamo is an asynchronous scala client for Amazon Dynamo database. It is based on Akka library and provides asynchronous API. +# async-dynamo -Quick Start -=========== -For detailed information please read [User Guide][user_guide]. +This fork was created to work with AWS 1.5, AKKA 2.1, and Play 2.1. + +With this fork you can publish to your local maven repo so it can easily be included in your own projects without waiting on others to setup new releases. + +## KNOWN ISSUES + +Despite the examples you CANNOT convert case classes directly to the AWS msg unless the case class has all Strings as arguments. This means code like -SBT ---- -Add the following to your build.sbt file for Scala 2.10: ```scala -resolvers += "piotrga" at "https://raw.github.com/piotrga/piotrga.github.com/master/maven-repo/" + case class Person(id :String, name: String, email: String) + implicit val personDO = DynamoObject.of3(Person) // make Person dynamo-enabled + + if (! TableExists[Person]()) //implicit kicks in to execute operation as blocking + CreateTable[Person](5,5).blockingExecute(dynamo, 1 minute) // overriding implicit timeout -libraryDependencies += "asyncdynamo" % "async-dynamo" % "1.6.0" + val julian = Person("123", "Julian", "julian@gmail.com") + val saved : Option[Person] = Save(julian) andThen Read[Person](julian.id) // implicit automatically executes and blocks for convenience + assert(saved == Some(julian)) ``` -or for scala 2.9.2 +may work, but code like this does not due to the Long id value: + +```scala + case class Person(id :Long, name: String, email: String) + implicit val personDO = DynamoObject.of3(Person) // make Person dynamo-enabled + + if (! TableExists[Person]()) //implicit kicks in to execute operation as blocking + CreateTable[Person](5,5).blockingExecute(dynamo, 1 minute) // overriding implicit timeout + + val julian = Person("123", "Julian", "julian@gmail.com") + val saved : Option[Person] = Save(julian) andThen Read[Person](julian.id) // implicit automatically executes and blocks for convenience + assert(saved == Some(julian)) ``` -libraryDependencies += "asyncdynamo" % "async-dynamo" % "1.5.4" + +This means you have to write custom converters for each of your objects unless all your objects have only strings and less than 8 arguments. That being said, unless you plan on taking advantage of their async layer on top of the normal calls, it is not worth using this library IMO. + +If these things are not show stoppers for you, feel free to use the code. I am no longer going to fix/update/maintain any code within this library. + +## Overview + +async-dynamo is an asynchronous scala client for Amazon Dynamo database. It is based on Akka library and provides asynchronous API. + +## Quick Start + +For detailed information please read [User Guide][user_guide]. + +### SBT + +First checkout this fork, make any changes you want/need, then run sbt publish to generate the jar in your local repo + +In any project you want to utilize the jar in, just add this to your `built.sbt` file: + +```scala +resolvers += "Local Maven Repository" at "file://" + Path.userHome.absolutePath + "/.m2/repository" + +libraryDependencies += "asyncdynamo" %% "async-dynamo" % "1.7.3" ``` -Example -------- +### Example + ```scala import asyncdynamo._ @@ -48,8 +86,8 @@ object QuckStart extends App{ } ``` -Asynchronous version --------------------- +### Asynchronous version + ```scala val operation = for { _ <- Save(julian) @@ -62,8 +100,8 @@ val operation = for { .onComplete{ case _ => dynamo ! 'stop } ``` -Explicit type class definition ------------------------------- +### Explicit type class definition + If you need more flexibility when mapping your object to Dynamo table you can define the type class yourself, i.e. ```scala case class Account(id: String, balance: Double, lastModified: Date) @@ -79,18 +117,14 @@ If you need more flexibility when mapping your object to Dynamo table you can de } ``` -Documentation -============= -For detailed information please read [User Guide][user_guide]. +## Information for developers -Information for developers -========================== +### Documentation -Building --------- -This library is build with SBT. +For detailed information please read [User Guide][user_guide]. ### AWS Credentials + In order for tests to be able to connect to Dynamo you have to open Amazon AWS account and pass the AWS credentials to scala via properties. The easiest way to do this is to add them to SBT_OPTS variable, i.e. @@ -100,39 +134,18 @@ To build async-dynamo run: sbt clean test -IntelliJ and SBT ----------------- -Generating IntelliJ project files: - - sbt gen-idea - -_IMPORTANT: You need to run `sbt gen-idea` every time you change the dependencies._ -If you want to refresh the snapshot dependencies (WHICH I TRY TO AVOID) run: - - sbt clean update -Click on Synchronize icon in IntelliJ - it should pick it up. - -VERSIONING ----------- -major.minor.patch-SNAPSHOT -ie. -0.12.1 -or -0.12.2-SNAPSHOT - -Please increment patch (release plugin does that) if the change is backward compatible. -Otherwise please bump the minor version. +## Copyright and license -Please do not depend on SNAPSHOTs as they promote chaos and lack of determinism. +Copyright 2012 2ndlanguage Limited. This product includes software developed at 2ndlanguage Limited. -RELEASING ---------- -Since we are not expecting many changes in this library we SHOULD not depend on snapshot versions. -It is much easier to apply this policy to the library. +Licensed under the [Apache License, Version 2.0] [license] (the "License"); +you may not use this software except in compliance with the License. -In order to release a new version: - - run `sbt release` - - confirm or amend the release version - - confirm next development version +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. [user_guide]: doc/user_guide.md "User Guide" +[license]: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/build.sbt b/build.sbt index c6827ac..75d1ebe 100644 --- a/build.sbt +++ b/build.sbt @@ -2,19 +2,29 @@ organization := "asyncdynamo" name := "async-dynamo" -scalaVersion := "2.10.1" +version := "1.7.3" -//resolvers += Resolver.file("piotrga", file(sys.env("PIOTRGA_GITHUB_REPO"))) -resolvers += "piotrga-remote" at "https://raw.github.com/piotrga/piotrga.github.com/master/maven-repo" +scalaVersion := "2.10.2" -// Libraries -libraryDependencies ++= Seq( - "com.amazonaws" % "aws-java-sdk" % "1.4.6", - "com.typesafe.akka" %% "akka-actor" % "2.2.0-RC1" -) +scalacOptions ++= Seq("-encoding", "UTF-8", "-deprecation", "-Xcheckinit") + +publishTo := Some(Resolver.file("local repo",Path.userHome / ".m2/repository" asFile)) + +resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" + +resolvers += "OSS Sonatype" at "https://oss.sonatype.org/content/repositories/releases" + +resolvers += "Sonatype Nexus releases" at "https://oss.sonatype.org/content/repositories/releases" + +resolvers += "Sonatype Nexus snapshots" at "https://oss.sonatype.org/content/repositories/snapshots" + +resolvers += "maven central" at "http://repo1.maven.org/maven2/" + +resolvers += "piotrga-remote" at "https://raw.github.com/piotrga/piotrga.github.com/master/maven-repo" -// Test libraries libraryDependencies ++= Seq( + "com.amazonaws" % "aws-java-sdk" % "1.4.7", + "com.typesafe.akka" %% "akka-actor" % "2.2.1", "org.scalatest" %% "scalatest" % "1.9.1" % "test", "log4j" % "log4j" % "1.2.17" % "test", "monitoring" %% "monitoring" % "1.4.0" % "test" diff --git a/project/Git.scala b/project/Git.scala deleted file mode 100644 index f5ff57b..0000000 --- a/project/Git.scala +++ /dev/null @@ -1,17 +0,0 @@ -import sbt._ - -object Git { - object devnull extends ProcessLogger { - def info (s: => String) {} - def error (s: => String) {} - def buffer[T] (f: => T): T = f - } - - def silentExec(cmd: String) = cmd lines_! devnull - - def branch = - silentExec("git status -sb").headOption getOrElse "-" stripPrefix "## " - - def hash = - silentExec("git log --pretty=format:%H -n1").headOption getOrElse "-" -} \ No newline at end of file diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..f069b10 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=0.12.4 \ No newline at end of file diff --git a/project/plugins.sbt b/project/plugins.sbt index 35e75e6..601ce34 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,7 +1,2 @@ -addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.2.0") +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0") -addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.0.0") - -resolvers += Resolver.url( "sbt-plugin-releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases/") )(Resolver.ivyStylePatterns) - -addSbtPlugin("com.github.gseitz" % "sbt-release" % "0.6") \ No newline at end of file diff --git a/release.sbt b/release.sbt deleted file mode 100644 index 741d79a..0000000 --- a/release.sbt +++ /dev/null @@ -1,34 +0,0 @@ -import sbtrelease._ - -import sbtrelease.ReleasePlugin.ReleaseKeys._ - -import sbt.Package.ManifestAttributes - -// RELEASE PLUGIN -releaseSettings - -nextVersion := { ver => Version(ver).map(_.bumpBugfix.asSnapshot.string).getOrElse(versionFormatError) } - -// PUBLISHING -publishMavenStyle := true - -publishTo := Some(Resolver.file("piotrga", file(sys.env("PIOTRGA_GITHUB_REPO")))) - - -packageOptions <<= (Keys.version, Keys.name, Keys.artifact) map { - (version: String, name: String, artifact: Artifact) => - Seq(ManifestAttributes( - "Implementation-Vendor" -> "piotrga", - //"Implementation-Title" -> name, - "Version" -> version, - "Build-Number" -> Option(System.getenv("GO_PIPELINE_COUNTER")).getOrElse("NOT_GO_BUILD"), - //"Group-Id" -> organization, - "Artifact-Id" -> artifact.name, - "Git-SHA1" -> Git.hash, - "Git-Branch" -> Git.branch, - "Build-Jdk" -> System.getProperty("java.version"), - "Built-When" -> (new java.util.Date).toString, - "Build-Machine" -> java.net.InetAddress.getLocalHost.getHostName - ) - ) -} diff --git a/src/main/scala/asyncdynamo/Dynamo.scala b/src/main/scala/asyncdynamo/Dynamo.scala index e967d8d..352f67a 100644 --- a/src/main/scala/asyncdynamo/Dynamo.scala +++ b/src/main/scala/asyncdynamo/Dynamo.scala @@ -20,16 +20,18 @@ import akka.actor._ import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient import com.amazonaws.ClientConfiguration import com.amazonaws.auth.BasicAWSCredentials +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB import akka.routing.SmallestMailboxRouter import com.typesafe.config.ConfigFactory import com.amazonaws.services.dynamodbv2.model._ import akka.actor.Status.Failure import asyncdynamo.Operation.Type import concurrent.duration._ +import scala.language.existentials class Dynamo(config: DynamoConfig) extends Actor { - private val clientConfig = { + val clientConfig = { val c = new ClientConfiguration() c.setMaxConnections(1) c.setMaxErrorRetry(config.throttlingRecoveryStrategy.amazonMaxErrorRetry) @@ -38,7 +40,7 @@ class Dynamo(config: DynamoConfig) extends Actor { c } - private val delegate = if (!config.accessKey.isEmpty) + lazy val delegate: AmazonDynamoDB = if (!config.accessKey.isEmpty) new AmazonDynamoDBClient(new BasicAWSCredentials(config.accessKey, config.secret), clientConfig) else new AmazonDynamoDBClient(clientConfig) @@ -80,11 +82,13 @@ class Dynamo(config: DynamoConfig) extends Actor { } object Dynamo { - def apply(config: DynamoConfig, connectionCount: Int) : ActorRef = { + def apply(config: DynamoConfig, connectionCount: Int): ActorRef = apply(new Dynamo(config), connectionCount) + + def apply(dynamo: => Dynamo, connectionCount: Int) : ActorRef = { val system = ActorSystem("Dynamo", ConfigFactory.load().getConfig("Dynamo") ) system.actorOf(Props(new Actor { - val router = context.actorOf(Props(new Dynamo(config)) + val router = context.actorOf(Props(dynamo) .withRouter(SmallestMailboxRouter(connectionCount)) .withDispatcher("dynamo-connection-dispatcher"), "DynamoConnection") @@ -118,7 +122,7 @@ class ThirdPartyException(msg: String, cause:Throwable=null) extends RuntimeExce trait DynamoEvent -case class DynamoRequestExecuted(operation:Operation, readUnits: Double = 0 , writeUnits: Double =0, time : Long = System.currentTimeMillis(), duration : Long) extends DynamoEvent +case class DynamoRequestExecuted(operation:Operation, readUnits: Option[Double] = None , writeUnits: Option[Double] = None, time : Long = System.currentTimeMillis(), duration : Long) extends DynamoEvent case class OperationExecuted(duration:FiniteDuration, operation: DbOperation[_]) extends DynamoEvent case class OperationFailed(operation: DbOperation[_], reason: Throwable) extends DynamoEvent case class ProvisionedThroughputExceeded(operation: DbOperation[_], msg:String) extends DynamoEvent diff --git a/src/main/scala/asyncdynamo/DynamoObject.scala b/src/main/scala/asyncdynamo/DynamoObject.scala index 990dedd..961d381 100644 --- a/src/main/scala/asyncdynamo/DynamoObject.scala +++ b/src/main/scala/asyncdynamo/DynamoObject.scala @@ -16,6 +16,7 @@ package asyncdynamo +import scala.language.implicitConversions import com.amazonaws.services.dynamodbv2.model.{ AttributeDefinition, AttributeValue, diff --git a/src/main/scala/asyncdynamo/TracingAmazonDynamoDB.scala b/src/main/scala/asyncdynamo/TracingAmazonDynamoDB.scala index 18dc51c..658d683 100644 --- a/src/main/scala/asyncdynamo/TracingAmazonDynamoDB.scala +++ b/src/main/scala/asyncdynamo/TracingAmazonDynamoDB.scala @@ -6,7 +6,7 @@ import com.amazonaws.AmazonWebServiceRequest import com.amazonaws.regions.Region import akka.event.EventStream -private class TracingAmazonDynamoDB(delegate : AmazonDynamoDB, eventStream : EventStream) extends AmazonDynamoDB { +protected class TracingAmazonDynamoDB(delegate : AmazonDynamoDB, eventStream : EventStream) extends AmazonDynamoDB { def setEndpoint(endpoint: String) {delegate.setEndpoint(endpoint)} def setRegion(region: Region) { delegate.setRegion(region) } @@ -25,76 +25,85 @@ private class TracingAmazonDynamoDB(delegate : AmazonDynamoDB, eventStream : Ev def deleteItem(deleteItemRequest: DeleteItemRequest) = { deleteItemRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL) - val (res, duration) = time (delegate.deleteItem(deleteItemRequest)) - pub(DynamoRequestExecuted(Operation(deleteItemRequest.getTableName, Write, "DeleteItem"), writeUnits = res.getConsumedCapacity.getCapacityUnits, duration = duration)) + val (res, duration) = time (delegate.deleteItem(deleteItemRequest), deleteItemRequest.getTableName) + pub(DynamoRequestExecuted(Operation(deleteItemRequest.getTableName, Write, "DeleteItem"), writeUnits = Option(scala.Double.unbox(res.getConsumedCapacity.getCapacityUnits)), duration = duration)) res } def getItem(getItemRequest: GetItemRequest) = { getItemRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL) - val (res, duration) = time(delegate.getItem(getItemRequest)) - pub(DynamoRequestExecuted(Operation(getItemRequest.getTableName, Read,"GetItem"), readUnits = res.getConsumedCapacity.getCapacityUnits, duration = duration)) + val (res, duration) = time(delegate.getItem(getItemRequest), getItemRequest.getTableName) + pub(DynamoRequestExecuted(Operation(getItemRequest.getTableName, Read, "GetItem"), readUnits = Option(scala.Double.unbox(res.getConsumedCapacity.getCapacityUnits)), duration = duration)) res } def scan(scanRequest: ScanRequest) = { scanRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL) - val (res, duration) = time(delegate.scan(scanRequest)) - pub(DynamoRequestExecuted(Operation(scanRequest.getTableName, Read,"Scan"), readUnits = res.getConsumedCapacity.getCapacityUnits, duration = duration)) + val (res, duration) = time(delegate.scan(scanRequest), scanRequest.getTableName) + pub(DynamoRequestExecuted(Operation(scanRequest.getTableName, Read, "Scan"), readUnits = Option(scala.Double.unbox(res.getConsumedCapacity.getCapacityUnits)), duration = duration)) res } def updateItem(updateItemRequest: UpdateItemRequest) = { updateItemRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL) - val (res, duration) = time(delegate.updateItem(updateItemRequest)) - pub(DynamoRequestExecuted(Operation(updateItemRequest.getTableName, Write,"UpdateItem"), writeUnits = res.getConsumedCapacity.getCapacityUnits, duration = duration)) + val (res, duration) = time(delegate.updateItem(updateItemRequest), updateItemRequest.getTableName) + pub(DynamoRequestExecuted(Operation(updateItemRequest.getTableName, Write, "UpdateItem"), writeUnits = Option(scala.Double.unbox(res.getConsumedCapacity.getCapacityUnits)), duration = duration)) res } def query(queryRequest: QueryRequest) = { queryRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL) - val (res, duration) = time(delegate.query(queryRequest)) - pub(DynamoRequestExecuted(Operation(queryRequest.getTableName, Read,"Query"), readUnits = res.getConsumedCapacity.getCapacityUnits, duration = duration)) + val (res, duration) = time(delegate.query(queryRequest), queryRequest.getTableName) + pub(DynamoRequestExecuted(Operation(queryRequest.getTableName, Read, "Query"), readUnits = Option(scala.Double.unbox(res.getConsumedCapacity.getCapacityUnits)), duration = duration)) res } def putItem(putItemRequest: PutItemRequest) = { putItemRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL) - val (res, duration) = time(delegate.putItem(putItemRequest)) - pub(DynamoRequestExecuted(Operation(putItemRequest.getTableName, Write,"PutItem"), writeUnits = res.getConsumedCapacity.getCapacityUnits, duration = duration)) + val (res, duration) = time(delegate.putItem(putItemRequest), putItemRequest.getTableName) + pub(DynamoRequestExecuted(Operation(putItemRequest.getTableName, Write, "PutItem"), writeUnits = Option(scala.Double.unbox(res.getConsumedCapacity.getCapacityUnits)), duration = duration)) res } - import collection.JavaConversions._ def batchGetItem(batchGetItemRequest: BatchGetItemRequest) = { batchGetItemRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL) - val (res, duration) = time(delegate.batchGetItem(batchGetItemRequest)) + val (res, duration) = time(delegate.batchGetItem(batchGetItemRequest), batchGetItemRequest.getRequestItems.keySet().mkString(",")) res.getConsumedCapacity foreach { - case consumedCapacity => - pub(DynamoRequestExecuted(Operation(consumedCapacity.getTableName(), Read, "BatchGetItem"), readUnits = consumedCapacity.getCapacityUnits, duration = duration)) + case consumedCapacity => + pub(DynamoRequestExecuted(Operation(consumedCapacity.getTableName(), Read, "BatchGetItem"), readUnits = Option(scala.Double.unbox(consumedCapacity.getCapacityUnits)), duration = duration)) } res } def batchWriteItem(batchWriteItemRequest: BatchWriteItemRequest) = { batchWriteItemRequest.setReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL) - val (res, duration) = time(delegate.batchWriteItem(batchWriteItemRequest)) + val (res, duration) = time(delegate.batchWriteItem(batchWriteItemRequest),batchWriteItemRequest.getRequestItems.keySet().mkString(",")) res.getConsumedCapacity foreach { - case consumedCapacity => - pub(DynamoRequestExecuted(Operation(consumedCapacity.getTableName(), Write, "BatchWriteItem"), writeUnits = consumedCapacity.getCapacityUnits, duration = duration)) + case consumedCapacity => + pub(DynamoRequestExecuted(Operation(consumedCapacity.getTableName(), Write, "BatchWriteItem"), writeUnits = Option(scala.Double.unbox(consumedCapacity.getCapacityUnits)), duration = duration)) } res } private def pub(op:DynamoRequestExecuted) = eventStream.publish(op) - def time[T]( f: => T) : (T, Long) ={ + def time[T](f: => T, tables: => String): (T, Long) = try { val start = System.currentTimeMillis() val res = f (res, System.currentTimeMillis() - start) + } catch { + case ptee: ProvisionedThroughputExceededException => + val newPtee = new ProvisionedThroughputExceededException(s"provisioned throughput for the table(s) was exceeded: $tables . ${ptee.getMessage}") + newPtee.setRequestId(ptee.getRequestId) + newPtee.setErrorCode(ptee.getErrorCode) + newPtee.setErrorType(ptee.getErrorType) + newPtee.setStatusCode(ptee.getStatusCode) + newPtee.setServiceName(ptee.getServiceName) + throw newPtee } + } diff --git a/src/main/scala/asyncdynamo/functional/Iteratee.scala b/src/main/scala/asyncdynamo/functional/Iteratee.scala index 3a5281f..5c56a58 100644 --- a/src/main/scala/asyncdynamo/functional/Iteratee.scala +++ b/src/main/scala/asyncdynamo/functional/Iteratee.scala @@ -3,7 +3,7 @@ package asyncdynamo.functional import annotation.tailrec import concurrent.{ExecutionContext, Promise, Future} import util.{Try, Failure, Success} - +import scala.language.reflectiveCalls sealed trait Iteratee[E, A]{ def map[B](g: B => E) : Iteratee[B, A] = this match { diff --git a/src/main/scala/asyncdynamo/functional/ReaderMonad.scala b/src/main/scala/asyncdynamo/functional/ReaderMonad.scala index 050a180..4f2b241 100644 --- a/src/main/scala/asyncdynamo/functional/ReaderMonad.scala +++ b/src/main/scala/asyncdynamo/functional/ReaderMonad.scala @@ -16,6 +16,7 @@ package asyncdynamo.functional +import scala.language.implicitConversions /** * This reader monad in combination with CassandraOperation makes Cassandra operations composable, so we can do: * {{{op1 flatMap(r => op2(r))}}} diff --git a/src/main/scala/asyncdynamo/nonblocking/admin-operations.scala b/src/main/scala/asyncdynamo/nonblocking/admin-operations.scala index 47170b7..d7b3699 100644 --- a/src/main/scala/asyncdynamo/nonblocking/admin-operations.scala +++ b/src/main/scala/asyncdynamo/nonblocking/admin-operations.scala @@ -25,6 +25,7 @@ import asyncdynamo._ import concurrent.{Promise, Future, Await} import util.{Failure, Success} +import scala.language.postfixOps import scala.collection.JavaConversions._ case class CreateTable[T](readThroughput: Long = 5, writeThrougput: Long = 5)(implicit dyn:DynamoObject[T]) extends DbOperation[Unit]{ diff --git a/src/main/scala/asyncdynamo/package.scala b/src/main/scala/asyncdynamo/package.scala index ad6ee77..647db29 100644 --- a/src/main/scala/asyncdynamo/package.scala +++ b/src/main/scala/asyncdynamo/package.scala @@ -18,6 +18,7 @@ import com.amazonaws.services.dynamodbv2.AmazonDynamoDB import akka.actor.ActorRef import akka.util.Timeout import concurrent.Future +import scala.language.implicitConversions package object asyncdynamo { diff --git a/src/test/scala/asyncdynamo/DynamoObjectTest.scala b/src/test/scala/asyncdynamo/DynamoObjectTest.scala index b8296ba..0b8d3d1 100644 --- a/src/test/scala/asyncdynamo/DynamoObjectTest.scala +++ b/src/test/scala/asyncdynamo/DynamoObjectTest.scala @@ -41,7 +41,7 @@ class DynamoObjectTest extends FreeSpec with MustMatchers with DynamoSupport{ val tst = Person("12312321", "Piotr", "piotrga@gmail.com") if (! TableExists[Person]()) CreateTable[Person](5,5).blockingExecute(dynamo, 1 minute) - val saved : Option[Person] = Save(tst) andThen Read[Person](tst.id) + val saved : Option[Person] = Save(tst) andThen Read[Person](tst.id,false) saved.get must be(tst) } diff --git a/src/test/scala/asyncdynamo/DynamoSupport.scala b/src/test/scala/asyncdynamo/DynamoSupport.scala index 9e7d73e..d8ed4d7 100644 --- a/src/test/scala/asyncdynamo/DynamoSupport.scala +++ b/src/test/scala/asyncdynamo/DynamoSupport.scala @@ -16,47 +16,53 @@ package asyncdynamo -import nonblocking.{CreateTable, TableExists} -import org.scalatest.{BeforeAndAfterAll, Suite} +import nonblocking.{ CreateTable, TableExists } +import org.scalatest.{ BeforeAndAfterAll, Suite } import akka.util.Timeout import scala.concurrent.duration._ import com.amazonaws.services.dynamodbv2.model.AttributeValue +import java.io.File -object DynamoTestDataObjects{ - case class DynamoTestObject(id:String, someValue:String) +object DynamoTestDataObjects { + case class DynamoTestObject(id: String, someValue: String) - implicit object DynamoTestDO extends DynamoObject[DynamoTestObject]{ - def toDynamo(t: DynamoTestObject) = Map("id"->t.id, "someValue"->t.someValue) + implicit object DynamoTestDO extends DynamoObject[DynamoTestObject] { + def toDynamo(t: DynamoTestObject) = Map("id" -> t.id, "someValue" -> t.someValue) def fromDynamo(a: Map[String, AttributeValue]) = DynamoTestObject(a("id").getS, a("someValue").getS) protected val table = "%s_dynamotest" format Option(System.getenv("USER")).getOrElse("unknown") } - case class DynamoTestWithRangeObject(id:String, rangeValue:String, otherValue: String) - implicit object DynamoTestWithRangeDO extends DynamoObject[DynamoTestWithRangeObject]{ - def toDynamo(t: DynamoTestWithRangeObject) = Map("id"->t.id, "rangeValue"->t.rangeValue, "otherValue" -> t.otherValue) + case class DynamoTestWithRangeObject(id: String, rangeValue: String, otherValue: String) + implicit object DynamoTestWithRangeDO extends DynamoObject[DynamoTestWithRangeObject] { + def toDynamo(t: DynamoTestWithRangeObject) = Map("id" -> t.id, "rangeValue" -> t.rangeValue, "otherValue" -> t.otherValue) def fromDynamo(a: Map[String, AttributeValue]) = DynamoTestWithRangeObject(a("id").getS, a("rangeValue").getS, a("otherValue").getS) protected val table = "%s_dynamotest_withrange" format Option(System.getenv("USER")).getOrElse("unknown") override val range = Some(defineAttribute("rangeValue", "S")) } - case class DynamoTestWithNumericRangeObject(id:String, rangeValue:Int, otherValue: String) - implicit object DynamoTestWithNumericRangeObjectDO extends DynamoObject[DynamoTestWithNumericRangeObject]{ - def toDynamo(t: DynamoTestWithNumericRangeObject) = Map("id"->t.id, "rangeValue"->toN(t.rangeValue), "otherValue" -> t.otherValue) + case class DynamoTestWithNumericRangeObject(id: String, rangeValue: Int, otherValue: String) + implicit object DynamoTestWithNumericRangeObjectDO extends DynamoObject[DynamoTestWithNumericRangeObject] { + def toDynamo(t: DynamoTestWithNumericRangeObject) = Map("id" -> t.id, "rangeValue" -> toN(t.rangeValue), "otherValue" -> t.otherValue) def fromDynamo(a: Map[String, AttributeValue]) = DynamoTestWithNumericRangeObject(a("id").getS, a("rangeValue").getN.toInt, a("otherValue").getS) protected val table = "%s_dynamotest_with_numeric_range" format Option(System.getenv("USER")).getOrElse("unknown") override val range = Some(defineAttribute("rangeValue", "N")) } - case class Broken(id:String) - implicit object BrokenDO extends DynamoObject[Broken]{ + case class Broken(id: String) + implicit object BrokenDO extends DynamoObject[Broken] { def toDynamo(t: Broken) = Map() def fromDynamo(a: Map[String, AttributeValue]) = Broken("wiejfi") protected def table = "nonexistenttable" } } -trait DynamoSupport extends BeforeAndAfterAll{ self : Suite => - implicit val dynamo = Dynamo(DynamoConfig(System.getProperty("amazon.accessKey"), System.getProperty("amazon.secret"), tablePrefix = "devng_", endpointUrl = System.getProperty("dynamo.url", "https://dynamodb.eu-west-1.amazonaws.com" )), connectionCount = 4) +trait DynamoSupport extends BeforeAndAfterAll { self: Suite => + + implicit val dynamo = Dynamo(DynamoConfig( + accessKey = "", + secret = "", + tablePrefix = "devng_", + endpointUrl = "dynamodb.us-east-1.amazonaws.com"), connectionCount = 4) implicit val timeout = Timeout(10 seconds) override protected def afterAll() { @@ -65,21 +71,21 @@ trait DynamoSupport extends BeforeAndAfterAll{ self : Suite => } } -trait DynamoTestObjectSupport extends DynamoSupport{ self : Suite => +trait DynamoTestObjectSupport extends DynamoSupport { self: Suite => import DynamoTestDataObjects._ protected def createTables() { println("Creating test tables... It might take a while...") - if (!TableExists[DynamoTestObject]().blockingExecute){ - CreateTable[DynamoTestObject]().blockingExecute(dynamo,1 minute) + if (!TableExists[DynamoTestObject]().blockingExecute) { + CreateTable[DynamoTestObject]().blockingExecute(dynamo, 1 minute) } - if (!TableExists[DynamoTestWithRangeObject]().blockingExecute){ - CreateTable[DynamoTestWithRangeObject](100, 100).blockingExecute(dynamo,1 minute) + if (!TableExists[DynamoTestWithRangeObject]().blockingExecute) { + CreateTable[DynamoTestWithRangeObject](100, 100).blockingExecute(dynamo, 1 minute) } - if (!TableExists[DynamoTestWithNumericRangeObject]().blockingExecute){ - CreateTable[DynamoTestWithNumericRangeObject](100, 100).blockingExecute(dynamo,1 minute) + if (!TableExists[DynamoTestWithNumericRangeObject]().blockingExecute) { + CreateTable[DynamoTestWithNumericRangeObject](100, 100).blockingExecute(dynamo, 1 minute) } } } diff --git a/src/test/scala/asyncdynamo/functional/IterateeTest.scala b/src/test/scala/asyncdynamo/functional/IterateeTest.scala index ef6745e..4bafdc2 100644 --- a/src/test/scala/asyncdynamo/functional/IterateeTest.scala +++ b/src/test/scala/asyncdynamo/functional/IterateeTest.scala @@ -4,12 +4,12 @@ import org.scalatest.FreeSpec import org.scalatest.matchers.MustMatchers import Iteratee._ -class IterateeTest extends FreeSpec with MustMatchers{ +class IterateeTest extends FreeSpec with MustMatchers { val list = List(1, 5, 3, 9, 12) "takeAll" in { - enumerate(list, takeAll[Int]()) must be (Done(list)) + enumerate(list, takeAll[Int]()) must be(Done(list)) } "takeOnly" in { @@ -25,14 +25,14 @@ class IterateeTest extends FreeSpec with MustMatchers{ } "map" in { - enumerate(list, takeOnly[Int](2).map[Int](2 * _)) must be (Done(List(2, 10))) + enumerate(list, takeOnly[Int](2).map[Int](2 * _)) must be(Done(List(2, 10))) } "withFilter" in { - enumerate(1 to 10 toList, takeOnly[Int](2).withFilter(_ % 2 == 0)) must be (Done(List(2, 4))) + enumerate(1 to 10 toList, takeOnly[Int](2).withFilter(_ % 2 == 0)) must be(Done(List(2, 4))) } "map withFilter" in { - enumerate(1 to 10 toList, takeOnly[Int](2).map[Int](_*2).withFilter(_ % 2 == 0)) must be (Done(List(4, 8))) + enumerate(1 to 10 toList, takeOnly[Int](2).map[Int](_ * 2).withFilter(_ % 2 == 0)) must be(Done(List(4, 8))) } } diff --git a/version.sbt b/version.sbt deleted file mode 100644 index ba18661..0000000 --- a/version.sbt +++ /dev/null @@ -1,2 +0,0 @@ - -version in ThisBuild := "1.7.0-SNAPSHOT"