diff options
author | Omar Alejandro Mainegra Sarduy <omainegra@gmail.com> | 2017-08-04 19:36:07 -0400 |
---|---|---|
committer | Omar Alejandro Mainegra Sarduy <omainegra@gmail.com> | 2017-08-04 19:36:07 -0400 |
commit | affc8cef21ba2ff8f46b2144b2064b8150840f2b (patch) | |
tree | 6e0edb41759f423e77d6efa5053111fc1b48ed94 | |
parent | 436ba7dc5afbecbe529b68835a8a4473bd92e56b (diff) | |
parent | ff76a1737bb88c2664927db309d196e677ba3e98 (diff) | |
download | sttp-affc8cef21ba2ff8f46b2144b2064b8150840f2b.tar.gz sttp-affc8cef21ba2ff8f46b2144b2064b8150840f2b.tar.bz2 sttp-affc8cef21ba2ff8f46b2144b2064b8150840f2b.zip |
Merge branch 'master' into okhttp3-monix
# Conflicts:
# okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala
12 files changed, 340 insertions, 34 deletions
@@ -59,7 +59,7 @@ If you are an [Ammonite](http://ammonite.io) user, you can quickly start experimenting with sttp by copy-pasting the following: ```scala -import $ivy.`com.softwaremill.sttp::core:0.0.4` +import $ivy.`com.softwaremill.sttp::core:0.0.5` import com.softwaremill.sttp._ implicit val handler = HttpURLConnectionSttpHandler sttp.get(uri"http://httpbin.org/ip").send() @@ -70,7 +70,7 @@ sttp.get(uri"http://httpbin.org/ip").send() SBT dependency: ```scala -"com.softwaremill.sttp" %% "core" % "0.0.4" +"com.softwaremill.sttp" %% "core" % "0.0.5" ``` `sttp` is available for Scala 2.11 and 2.12, and requires Java 8. The core @@ -208,7 +208,7 @@ implicit val sttpHandler = HttpURLConnectionSttpHandler To use, add the following dependency to your project: ```scala -"com.softwaremill.sttp" %% "akka-http-handler" % "0.0.4" +"com.softwaremill.sttp" %% "akka-http-handler" % "0.0.5" ``` This handler depends on [akka-http](http://doc.akka.io/docs/akka-http/current/scala/http/). @@ -267,13 +267,13 @@ val response: Future[Response[Source[ByteString, Any]]] = To use, add the following dependency to your project: ```scala -"com.softwaremill.sttp" %% "async-http-client-handler-future" % "0.0.4" +"com.softwaremill.sttp" %% "async-http-client-handler-future" % "0.0.5" // or -"com.softwaremill.sttp" %% "async-http-client-handler-scalaz" % "0.0.4" +"com.softwaremill.sttp" %% "async-http-client-handler-scalaz" % "0.0.5" // or -"com.softwaremill.sttp" %% "async-http-client-handler-monix" % "0.0.4" +"com.softwaremill.sttp" %% "async-http-client-handler-monix" % "0.0.5" // or -"com.softwaremill.sttp" %% "async-http-client-handler-cats" % "0.0.4" +"com.softwaremill.sttp" %% "async-http-client-handler-cats" % "0.0.5" ``` This handler depends on [async-http-client](https://github.com/AsyncHttpClient/async-http-client). @@ -355,7 +355,7 @@ val response: Task[Response[Observable[ByteBuffer]]] = To use, add the following dependency to your project: ```scala -"com.softwaremill.sttp" %% "okhttp-client-handler" % "0.0.4" +"com.softwaremill.sttp" %% "okhttp-client-handler" % "0.0.5" ``` This handler depends on [OkHttp](http://square.github.io/okhttp/), and offers @@ -443,3 +443,4 @@ and pick a task you'd like to work on! * [Adam Warski](https://github.com/adamw) * [Omar Alejandro Mainegra Sarduy](https://github.com/omainegra) * [Bjørn Madsen](https://github.com/aeons) +* [Piotr Buda](https://github.com/pbuda) diff --git a/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala b/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala index d4ca3d8..0a2a467 100644 --- a/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala +++ b/akka-http-handler/src/main/scala/com/softwaremill/sttp/akkahttp/AkkaHttpSttpHandler.scala @@ -1,6 +1,6 @@ package com.softwaremill.sttp.akkahttp -import java.io.UnsupportedEncodingException +import java.io.{File, IOException, UnsupportedEncodingException} import akka.actor.ActorSystem import akka.http.scaladsl.Http @@ -10,7 +10,7 @@ import akka.http.scaladsl.model._ import akka.http.scaladsl.model.headers.{HttpEncodings, `Content-Type`} import akka.http.scaladsl.model.ContentTypes.`application/octet-stream` import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{Source, StreamConverters} +import akka.stream.scaladsl.{FileIO, Source, StreamConverters} import akka.util.ByteString import com.softwaremill.sttp._ import com.softwaremill.sttp.model._ @@ -67,6 +67,18 @@ class AkkaHttpSttpHandler private (actorSystem: ActorSystem, .runFold(ByteString(""))(_ ++ _) .map(_.toArray[Byte]) + def saved(file: File, overwrite: Boolean) = { + if (!file.exists()) { + file.getParentFile.mkdirs() + file.createNewFile() + } else if (!overwrite) { + throw new IOException( + s"File ${file.getAbsolutePath} exists - overwriting prohibited") + } + + hr.entity.dataBytes.runWith(FileIO.toPath(file.toPath)) + } + rr match { case MappedResponseAs(raw, g) => bodyFromAkka(raw, hr).map(g) @@ -82,6 +94,9 @@ class AkkaHttpSttpHandler private (actorSystem: ActorSystem, case r @ ResponseAsStream() => Future.successful(r.responseIsStream(hr.entity.dataBytes)) + + case ResponseAsFile(file, overwrite) => + saved(file, overwrite).map(_ => file) } } diff --git a/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala b/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala index 9043882..8f75030 100644 --- a/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala +++ b/async-http-client-handler/src/main/scala/com/softwaremill/sttp/asynchttpclient/AsyncHttpClientHandler.scala @@ -222,6 +222,11 @@ abstract class AsyncHttpClientHandler[R[_], S](asyncHttpClient: AsyncHttpClient, Failure( new IllegalStateException( "Requested a streaming response, trying to read eagerly.")) + + case ResponseAsFile(file, overwrite) => + Try( + ResponseAs + .saveFile(file, response.getResponseBodyAsStream, overwrite)) } } diff --git a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala index d5c2ccd..29da886 100644 --- a/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala +++ b/core/src/main/scala/com/softwaremill/sttp/HttpURLConnectionSttpHandler.scala @@ -115,19 +115,8 @@ object HttpURLConnectionSttpHandler extends SttpHandler[Id, Nothing] { case ResponseAsByteArray => val os = new ByteArrayOutputStream - var read = 0 - val buf = new Array[Byte](1024) - - @tailrec - def transfer(): Unit = { - read = is.read(buf, 0, buf.length) - if (read != -1) { - os.write(buf, 0, read) - transfer() - } - } - transfer() + transfer(is, os) os.toByteArray @@ -135,6 +124,10 @@ object HttpURLConnectionSttpHandler extends SttpHandler[Id, Nothing] { // only possible when the user requests the response as a stream of // Nothing. Oh well ... throw new IllegalStateException() + + case ResponseAsFile(input, overwrite) => + ResponseAs.saveFile(input, is, overwrite) + } } diff --git a/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala b/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala index 3862483..24b9b2b 100644 --- a/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala +++ b/core/src/main/scala/com/softwaremill/sttp/model/ResponseAs.scala @@ -1,8 +1,10 @@ package com.softwaremill.sttp.model +import java.io.{File, FileOutputStream, IOException, InputStream} import java.net.URLDecoder +import java.nio.file.Path -import com.softwaremill.sttp.MonadError +import com.softwaremill.sttp.{MonadError, transfer} import scala.collection.immutable.Seq import scala.language.higherKinds @@ -38,6 +40,9 @@ case class MappedResponseAs[T, T2, S](raw: BasicResponseAs[T, S], g: T => T2) MappedResponseAs[T, T3, S](raw, g andThen f) } +case class ResponseAsFile(input: File, overwrite: Boolean) + extends BasicResponseAs[File, Nothing] + object ResponseAs { private[sttp] def parseParams(s: String, encoding: String): Seq[(String, String)] = { @@ -52,6 +57,23 @@ object ResponseAs { }) } + private[sttp] def saveFile(file: File, + is: InputStream, + overwrite: Boolean): File = { + if (!file.exists()) { + file.getParentFile.mkdirs() + file.createNewFile() + } else if (!overwrite) { + throw new IOException( + s"File ${file.getAbsolutePath} exists - overwriting prohibited") + } + + val os = new FileOutputStream(file) + + transfer(is, os) + file + } + /** * Handles responses according to the given specification when basic * response specifications can be handled eagerly, that is without diff --git a/core/src/main/scala/com/softwaremill/sttp/package.scala b/core/src/main/scala/com/softwaremill/sttp/package.scala index daba574..884d2f9 100644 --- a/core/src/main/scala/com/softwaremill/sttp/package.scala +++ b/core/src/main/scala/com/softwaremill/sttp/package.scala @@ -1,12 +1,12 @@ package com.softwaremill -import java.io.{File, InputStream} +import java.io._ import java.nio.ByteBuffer import java.nio.file.Path import com.softwaremill.sttp.model._ -import scala.annotation.implicitNotFound +import scala.annotation.{implicitNotFound, tailrec} import scala.language.higherKinds import scala.collection.immutable.Seq @@ -88,6 +88,14 @@ package object sttp { def asStream[S]: ResponseAs[S, S] = ResponseAsStream[S, S]() + def asFile(file: File, + overwrite: Boolean = false): ResponseAs[File, Nothing] = + ResponseAsFile(file, overwrite) + + def asPath(path: Path, + overwrite: Boolean = false): ResponseAs[Path, Nothing] = + ResponseAsFile(path.toFile, overwrite).map(_.toPath) + // multi part factory methods /** @@ -159,6 +167,22 @@ package object sttp { private[sttp] def contentTypeWithEncoding(ct: String, enc: String) = s"$ct; charset=$enc" + private[sttp] def transfer(is: InputStream, os: OutputStream) { + var read = 0 + val buf = new Array[Byte](1024) + + @tailrec + def transfer(): Unit = { + read = is.read(buf, 0, buf.length) + if (read != -1) { + os.write(buf, 0, read) + transfer() + } + } + + transfer() + } + // uri interpolator implicit class UriContext(val sc: StringContext) extends AnyVal { diff --git a/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala b/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala index af896d4..2090e02 100644 --- a/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala +++ b/okhttp-client-handler/src/main/scala/com/softwaremill/sttp/okhttp/OkHttpClientHandler.scala @@ -90,6 +90,8 @@ abstract class OkHttpClientHandler[R[_], S](client: OkHttpClient) case ResponseAsByteArray => Try(res.body().bytes()) case ras @ ResponseAsStream() => responseBodyToStream(res).map(ras.responseIsStream) + case ResponseAsFile(file, overwrite) => + Try(ResponseAs.saveFile(file, res.body().byteStream(), overwrite)) } } @@ -131,9 +133,10 @@ abstract class OkHttpAsyncClientHandler[R[_], S](client: OkHttpClient, .enqueue(new Callback { override def onFailure(call: Call, e: IOException): Unit = error(e) - + override def onResponse(call: Call, response: OkHttpResponse): Unit = - success(readResponse(response, r.responseAs)) + try success(readResponse(response, r.responseAs)) + catch { case e: Exception => error(e) } }) }) } diff --git a/tests/src/test/resources/binaryfile.jpg b/tests/src/test/resources/binaryfile.jpg Binary files differnew file mode 100644 index 0000000..b9f5c5a --- /dev/null +++ b/tests/src/test/resources/binaryfile.jpg diff --git a/tests/src/test/resources/textfile.txt b/tests/src/test/resources/textfile.txt new file mode 100644 index 0000000..9904f90 --- /dev/null +++ b/tests/src/test/resources/textfile.txt @@ -0,0 +1,100 @@ +- Lorem ipsum dolor sit amet +- Vivamus sem ipsum. +- Ut molestie. +- Donec. +- Fusce non porta. +- Nulla ac metus. Morbi mattis. +- Etiam varius. +- Nulla. +- Phasellus id mollis. +- Suspendisse at. +- Quisque nec leo velit. +- Fusce. +- Maecenas nec tristique senectus et. +- Integer vestibulum lorem fermentum. +- Vestibulum consectetuer dolor. +- Lorem ipsum in. +- Fusce condimentum auctor scelerisque, wisi. +- Quisque. +- Curae. +- Curae, Nullam. +- Curae, Integer. +- Vestibulum dignissim massa. Donec. +- Pellentesque sed sem. +- Vivamus est. +- Maecenas elit sed est. +- Quisque sed tellus. +- Cum sociis natoque penatibus et. +- Fusce aliquam. +- Donec. +- Sed elementum, sapien accumsan odio. +- Nam mattis, magna lectus, tincidunt. +- Pellentesque scelerisque a, sodales. +- Sed sed condimentum. +- Curae, In nonummy. Phasellus adipiscing. +- Vestibulum quis diam mollis. +- Sed eros. Duis ipsum. +- Aenean pellentesque at, mollis tempus. +- Cras ornare facilisis sodales. Aenean. +- Cum sociis natoque penatibus. +- Donec id nulla. +- Quisque ut sapien. +- Phasellus purus. Proin ultricies. +- Aliquam auctor neque. Nunc. +- Nam nunc fringilla non, vehicula. +- Morbi molestie, felis ut lobortis. +- Nulla quis. +- In. +- Phasellus laoreet urna. +- Lorem ipsum. +- Phasellus. +- Class aptent taciti sociosqu ad. +- Sed lacinia. +- Pellentesque dapibus diam. Duis. +- Suspendisse est. Curabitur. +- Fusce condimentum justo. +- Aenean congue quis, faucibus. +- Ut pharetra leo. Donec. +- Fusce. +- Donec porta. +- Pellentesque orci. +- Sed. +- Quisque rutrum, wisi vulputate wisi. +- Cum sociis. +- Cras. +- Sed eros. Curabitur. +- Proin in velit wisi, tempor. +- Quisque eu. +- Proin. +- Nam pellentesque sed, imperdiet aliquam. +- Mauris euismod. Sed euismod orci. +- Etiam. +- Donec. +- Fusce wisi a metus. Proin. +- Phasellus quis. +- Donec non imperdiet. +- Aenean vel bibendum a, laoreet. +- Fusce non enim. Phasellus vulputate. +- Donec urna elit, sit. +- Pellentesque habitant morbi. +- Nulla ante. Curabitur elit. Donec. +- Cum sociis natoque penatibus. +- Maecenas eget leo at. +- Cum sociis natoque penatibus et. +- Vivamus lacus. +- Integer. +- Curae. +- Maecenas rhoncus. Morbi. +- Aenean posuere. +- Duis. +- Suspendisse a odio fermentum libero. +- Nam enim. Fusce enim. In. +- Maecenas. +- Lorem ipsum primis. +- Curabitur ac turpis semper sed. +- Quisque condimentum. Donec sit. +- Integer convallis non, posuere. +- Etiam vulputate, odio. +- Proin id lorem. Donec quis. +- Curae, Sed nec augue. +- Aliquam ut turpis.
\ No newline at end of file diff --git a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala index 6d383f5..d8e3d03 100644 --- a/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala +++ b/tests/src/test/scala/com/softwaremill/sttp/BasicTests.scala @@ -1,7 +1,8 @@ package com.softwaremill.sttp -import java.io.ByteArrayInputStream +import java.io.{ByteArrayInputStream, IOException} import java.nio.ByteBuffer +import java.nio.file.Paths import java.time.{ZoneId, ZonedDateTime} import akka.http.scaladsl.coding.{Deflate, Gzip, NoCoding} @@ -14,7 +15,7 @@ import akka.http.scaladsl.server.directives.Credentials import com.softwaremill.sttp.akkahttp.AkkaHttpSttpHandler import com.typesafe.scalalogging.StrictLogging import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures} -import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers} +import org.scalatest.{path => _, _} import better.files._ import com.softwaremill.sttp.asynchttpclient.cats.CatsAsyncHttpClientHandler import com.softwaremill.sttp.asynchttpclient.future.FutureAsyncHttpClientHandler @@ -33,14 +34,27 @@ class BasicTests with Matchers with BeforeAndAfterAll with ScalaFutures + with OptionValues with StrictLogging with IntegrationPatience with TestHttpServer - with ForceWrapped { + with ForceWrapped + with BeforeAndAfterEach { + + override def afterEach() { + val file = File(outPath) + if (file.exists) file.delete() + } private def paramsToString(m: Map[String, String]): String = m.toList.sortBy(_._1).map(p => s"${p._1}=${p._2}").mkString(" ") + private val textFile = + new java.io.File("tests/src/test/resources/textfile.txt") + private val binaryFile = + new java.io.File("tests/src/test/resources/binaryfile.jpg") + private val outPath = Paths.get("out") + override val serverRoutes: Route = pathPrefix("echo") { pathPrefix("form_params") { @@ -116,6 +130,12 @@ class BasicTests encodeResponseWith(Gzip, Deflate, NoCoding) { complete("I'm compressed!") } + } ~ pathPrefix("download") { + path("binary") { + getFromFile(binaryFile) + } ~ path("text") { + getFromFile(textFile) + } } override def port = 51823 @@ -405,6 +425,95 @@ class BasicTests resp.body should be(decompressedBody) } } + + def downloadFileTests(): Unit = { + import CustomMatchers._ + + name should "download a binary file using asFile" in { + val file = outPath.resolve("binaryfile.jpg").toFile + val req = + sttp.get(uri"$endpoint/download/binary").response(asFile(file)) + val resp = req.send().force() + + resp.body shouldBe file + file should exist + file should haveSameContentAs(binaryFile) + } + + name should "download a text file using asFile" in { + val file = outPath.resolve("textfile.txt").toFile + val req = + sttp.get(uri"$endpoint/download/text").response(asFile(file)) + val resp = req.send().force() + + resp.body shouldBe file + file should exist + file should haveSameContentAs(textFile) + } + + name should "download a binary file using asPath" in { + val path = outPath.resolve("binaryfile.jpg") + val req = + sttp.get(uri"$endpoint/download/binary").response(asPath(path)) + val resp = req.send().force() + + resp.body shouldBe path + path.toFile should exist + path.toFile should haveSameContentAs(binaryFile) + } + + name should "download a text file using asPath" in { + val path = outPath.resolve("textfile.txt") + val req = + sttp.get(uri"$endpoint/download/text").response(asPath(path)) + val resp = req.send().force() + + resp.body shouldBe path + path.toFile should exist + path.toFile should haveSameContentAs(textFile) + } + + name should "fail at trying to save file to a restricted location" in { + val path = Paths.get("/").resolve("textfile.txt") + val req = + sttp.get(uri"$endpoint/download/text").response(asPath(path)) + val caught = intercept[IOException] { + req.send().force() + } + + caught.getMessage shouldBe "Permission denied" + } + + name should "fail when file exists and overwrite flag is false" in { + val path = outPath.resolve("textfile.txt") + path.toFile.getParentFile.mkdirs() + path.toFile.createNewFile() + val req = + sttp.get(uri"$endpoint/download/text").response(asPath(path)) + + val caught = intercept[IOException] { + req.send().force() + } + + caught.getMessage shouldBe s"File ${path.toFile.getAbsolutePath} exists - overwriting prohibited" + + } + + name should "not fail when file exists and overwrite flag is true" in { + val path = outPath.resolve("textfile.txt") + path.toFile.getParentFile.mkdirs() + path.toFile.createNewFile() + val req = + sttp + .get(uri"$endpoint/download/text") + .response(asPath(path, overwrite = true)) + val resp = req.send().force() + + resp.body shouldBe path + path.toFile should exist + path.toFile should haveSameContentAs(textFile) + } + } } override protected def afterAll(): Unit = { diff --git a/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala b/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala index 558e9dd..59e1612 100644 --- a/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala +++ b/tests/src/test/scala/com/softwaremill/sttp/testHelpers.scala @@ -1,14 +1,20 @@ package com.softwaremill.sttp +import java.nio.file.{Files, Paths} +import java.{io, util} + import akka.actor.ActorSystem import akka.http.scaladsl.Http import akka.http.scaladsl.server.Route import akka.stream.ActorMaterializer import org.scalatest.{BeforeAndAfterAll, Suite} import org.scalatest.concurrent.ScalaFutures +import org.scalatest.exceptions.TestFailedException +import org.scalatest.matchers.{MatchResult, Matcher} import scala.concurrent.Future import scala.language.higherKinds +import scalaz._ trait TestHttpServer extends BeforeAndAfterAll with ScalaFutures { this: Suite => @@ -41,23 +47,35 @@ trait ForceWrapped extends ScalaFutures { this: Suite => } val future = new ForceWrappedValue[Future] { override def force[T](wrapped: Future[T]): T = - wrapped.futureValue + try { + wrapped.futureValue + } catch { + case e: TestFailedException if e.getCause != null => throw e.getCause + } } val scalazTask = new ForceWrappedValue[scalaz.concurrent.Task] { override def force[T](wrapped: scalaz.concurrent.Task[T]): T = - wrapped.unsafePerformSync + wrapped.unsafePerformSyncAttempt match { + case -\/(error) => throw error + case \/-(value) => value + } } val monixTask = new ForceWrappedValue[monix.eval.Task] { import monix.execution.Scheduler.Implicits.global override def force[T](wrapped: monix.eval.Task[T]): T = - wrapped.runAsync.futureValue + try { + wrapped.runAsync.futureValue + } catch { + case e: TestFailedException => throw e.getCause + } } val catsIo = new ForceWrappedValue[cats.effect.IO] { override def force[T](wrapped: cats.effect.IO[T]): T = wrapped.unsafeRunSync } } + implicit class ForceDecorator[R[_], T](wrapped: R[T]) { def force()(implicit fwv: ForceWrappedValue[R]): T = fwv.force(wrapped) } @@ -72,3 +90,19 @@ object EvalScala { tb.eval(tb.parse(code)) } } + +object CustomMatchers { + class FileContentsMatch(file: java.io.File) extends Matcher[java.io.File] { + override def apply(left: io.File): MatchResult = { + val inBA = Files.readAllBytes(Paths.get(left.getAbsolutePath)) + val expectedBA = Files.readAllBytes(Paths.get(file.getAbsolutePath)) + MatchResult( + util.Arrays.equals(inBA, expectedBA), + "The files' contents are not the same", + "The files' contents are the same" + ) + } + } + + def haveSameContentAs(file: io.File) = new FileContentsMatch(file) +} diff --git a/version.sbt b/version.sbt index ccd484c..3b1722c 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.0.5-SNAPSHOT" +version in ThisBuild := "0.0.6-SNAPSHOT" |