From 167ddd85d5fb4f7c9c35af6080b640b402baa115 Mon Sep 17 00:00:00 2001 From: Christian Kaps Date: Wed, 8 Jun 2016 13:33:15 +0200 Subject: [PATCH] Play 2.5 support --- .coveralls.yml | 1 + .github/ISSUE_TEMPLATE.md | 42 ++ .github/PULL_REQUEST_TEMPLATE.md | 21 + .travis.yml | 22 + CONTRIBUTING.md | 47 ++ README.md | 14 +- clientstub/build.sbt | 6 +- .../com/mohiva/swagger/codegen/TestApi.scala | 177 ++++-- .../swagger/codegen/core/ApiImplicits.scala | 86 +-- .../swagger/codegen/core/ApiInvoker.scala | 7 +- .../mohiva/swagger/codegen/models/Error.scala | 2 +- .../swagger/codegen/models/Status.scala | 2 +- .../mohiva/swagger/codegen/models/User.scala | 2 +- .../mohiva/swagger/codegen/TestApiSpec.scala | 3 +- conf/config.json | 4 +- project/BuildSettings.scala | 6 +- project/Dependencies.scala | 23 +- project/build-jvm-opts | 6 + project/plugins.sbt | 6 +- scripts/ci-build | 31 + scripts/coveralls | 27 + scripts/sbt | 560 +----------------- scripts/sbt-runner | 543 +++++++++++++++++ scripts/validate-format | 39 ++ 24 files changed, 975 insertions(+), 702 deletions(-) create mode 100644 .coveralls.yml create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .travis.yml create mode 100644 CONTRIBUTING.md create mode 100644 project/build-jvm-opts create mode 100755 scripts/ci-build create mode 100755 scripts/coveralls create mode 100755 scripts/sbt-runner create mode 100755 scripts/validate-format diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 0000000..9160059 --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1 @@ +service_name: travis-ci diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..2c02476 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,42 @@ +### Are you looking for help? + +This is an issue tracker, used to manage and track the development of Swagger Codegen - Play Scala. It is not a support system and so it is not a place to ask questions or get help. If you're not sure if you have found a bug, the best place to start is with [Gitter channel](https://gitter.im/mohiva/swagger-codegen-play-scala). If you have a feature request, the [Gitter channel](https://gitter.im/mohiva/swagger-codegen-play-scala) is a better forum than an issue tracker to discuss it. + +### Swagger Codegen - Play Scala Version (0.1.x / etc) + + +### Operating System (Ubuntu 15.10 / MacOS 10.10 / Windows 10) + +Use `uname -a` if on Linux. + +### JDK (Oracle 1.8.0_72, OpenJDK 1.8.x, Azul Zing) + +Paste the output from `java -version` at the command line. + +### Library Dependencies + +If this is an issue that involves integration with another system, include the exact version and OS of the other system. + +### Expected Behavior + +Please describe the expected behavior of the issue, starting from the first action. + +1. +2. +3. + +### Actual Behavior + +Please provide a description of what actually happens, working from the same starting point. + +Be descriptive: "it doesn't work" does not describe what the behavior actually is -- instead, provide a meaningful description of the issue. + +1. +2. +3. + +### Reproducible Test Case + +Please provide a PR with a failing test. + +If the issue is more complex or requires configuration, please provide a link to a project on GitHub that reproduces the issue. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..adff93a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ +# Pull Request Checklist + +* [ ] Have you read [How to write the perfect pull request](https://github.com/blog/1943-how-to-write-the-perfect-pull-request)? +* [ ] Have you read through the [contributor guidelines](https://github.com/mohiva/swagger-codegen-play-scala/blob/master/CONTRIBUTING.md)? +* [ ] Have you [squashed your commits](https://docs.openshift.org/origin-m4/oo_contributors_guide.html#submitting-code)? + +## Fixes + +Fixes #xxxx + +## Purpose + +What does this PR do? + +## Background Context + +Why did you take this approach? + +## References + +Are there any relevant issues / PRs / mailing lists discussions? diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3f11099 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +sudo: false +language: scala +scala: + - 2.11.8 +jdk: + - oraclejdk8 +cache: + directories: + # caching scala stuff based on + # http://www.scala-sbt.org/0.13/docs/Travis-CI-with-sbt.html#%28Experimental%29+Reusing+Ivy+cache + - $HOME/.ivy2/cache + - $HOME/.sbt/boot/ +script: + - scripts/ci-build + # cleanup travis trick from + # http://www.scala-sbt.org/0.13/docs/Travis-CI-with-sbt.html#%28Experimental%29+Reusing+Ivy+cache + - find $HOME/.sbt -name "*.lock" -delete + - find $HOME/.ivy2 -name "ivydata-*.properties" -delete +after_success: + - scripts/coveralls +notifications: + email: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..dd0f48d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +Contributing to Swagger Codegen - Play Scala +============================================ + +How to contribute +----------------- + +Swagger Codegen - Play Scala is an open source project. Contributions are appreciated. + +Some ways in which you can contribute are: reporting errors, improving documentation, adding examples, adding support +for more services, fixing bugs, suggesting new features, adding test cases, translating messages, and whatever else +you can think of that may be helpful. If in doubt, just ask. + + +Development workflow +-------------------- + +Development is coordinated via [GitHub]. Ideas for improvements are discussed using the [chat]. + +To submit issues, please use the [GitHub issue tracker]. **Please do not use the issue tracker for questions!** + +For a more streamlined experience for all people involved, we encourage contributors to follow the practices described +at [GitHub workflow for submitting pull requests]. + +Scala source code should follow the conventions documented in the [Scala Style Guide]. Additionally, acronyms should +be capitalized. To have your code automatically reformatted, run this command before committing your changes: + + scripts/reformat + +After submitting your pull request, please [watch the result] of the automated Travis CI build and correct any reported +errors or inconsistencies. + + +License and Copyright +--------------------- + +By submitting work via pull requests, issues, documentation, or any other means, contributors indicate their agreement to +publish their work under this project's license and also attest that they are the authors of the work and grant a +copyright license to the Mohiva Organisation, unless the contribution clearly states a different copyright notice +(e.g., it contains original work by a third party). + + +[GitHub]: https://github.com/mohiva/swagger-codegen-play-scala +[GitHub issue tracker]: https://github.com/mohiva/swagger-codegen-play-scala/issues +[GitHub workflow for submitting pull requests]: http://docs.openshift.org/origin-m4/oo_contributors_guide.html#submitting-code +[chat]: https://gitter.im/mohiva/swagger-codegen-play-scala +[Scala Style Guide]: http://docs.scala-lang.org/style/ +[watch the result]: https://travis-ci.org/mohiva/swagger-codegen-play-scala/pull_requests diff --git a/README.md b/README.md index 9c636c3..7cd75f8 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,21 @@ -# Swagger Codegen - Play Scala +# Swagger Codegen - Play Scala [![Build Status](https://travis-ci.org/mohiva/swagger-codegen-play-scala.png)](https://travis-ci.org/mohiva/swagger-codegen-play-scala) [![Coverage Status](https://coveralls.io/repos/mohiva/swagger-codegen-play-scala/badge.svg?branch=master&service=github)](https://coveralls.io/github/mohiva/swagger-codegen-play-scala?branch=master) [![Join the chat at https://gitter.im/mohiva/swagger-codegen-play-scala](https://badges.gitter.im/mohiva/swagger-codegen-play-scala.svg)](https://gitter.im/mohiva/swagger-codegen-play-scala?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +========= A code generator for [Swagger] which generates API clients with the help of the PlayWS library. This code generator is based on the `akka-scala` generator included in the [Swagger Codegen] distribution. +## Versions + +Project | Play +--------------------|------------------ +0.1.x | 2.4 +master | 2.5 + ## Create client Download swagger codegen executable into the root directory of this project: ``` -wget http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.1.5/swagger-codegen-cli-2.1.5.jar -O swagger-codegen-cli.jar +wget http://repo1.maven.org/maven2/io/swagger/swagger-codegen-cli/2.1.6/swagger-codegen-cli-2.1.6.jar -O swagger-codegen-cli.jar ``` Execute the script @@ -45,4 +53,4 @@ playVersion | The Play version to use in generated build.sbt [Swagger]: http://swagger.io/ -[Swagger Codegen]: https://github.com/swagger-api/swagger-codegen \ No newline at end of file +[Swagger Codegen]: https://github.com/swagger-api/swagger-codegen diff --git a/clientstub/build.sbt b/clientstub/build.sbt index a1d1a09..73958d9 100644 --- a/clientstub/build.sbt +++ b/clientstub/build.sbt @@ -5,8 +5,8 @@ libraryDependencies ++= Seq( Dependencies.Library.playJsonExtension, Dependencies.Library.jodaTime, Dependencies.Library.Play.test % "test", - Dependencies.Library.Specs2.core % "test", - Dependencies.Library.Specs2.matcherExtra % "test", - Dependencies.Library.Specs2.mock % "test", + Dependencies.Library.Play.specs2 % "test", + Dependencies.Library.Play.Specs2.matcherExtra % "test", + Dependencies.Library.Play.Specs2.mock % "test", Dependencies.Library.playWSMock % "test" ) diff --git a/clientstub/src/main/scala/com/mohiva/swagger/codegen/TestApi.scala b/clientstub/src/main/scala/com/mohiva/swagger/codegen/TestApi.scala index 14e7bb7..966f812 100644 --- a/clientstub/src/main/scala/com/mohiva/swagger/codegen/TestApi.scala +++ b/clientstub/src/main/scala/com/mohiva/swagger/codegen/TestApi.scala @@ -162,7 +162,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with an empty body will be sent successfully. */ def testRequestWithEmptyBody(rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withPrimitiveSuccessResponse[Unit](204) @@ -173,7 +174,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with a Json object body will be sent successfully. */ def testRequestWithJsonObjectBody(body: User, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[User]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[User]] = { apiInvoker.execute[User](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withJsonBody[User](body) @@ -185,7 +187,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with a Json array body will be sent successfully. */ def testRequestWithJsonArrayBody(body: Seq[User], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Seq[User]]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Seq[User]]] = { apiInvoker.execute[Seq[User]](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withJsonBody[Seq[User]](body) @@ -197,7 +200,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with Some Json body will be sent successfully. */ def testRequestWithSomeJsonBody(body: Option[User], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[User]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[User]] = { apiInvoker.execute[User](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withJsonBody[User](body) @@ -209,7 +213,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with None Json body will be sent successfully. */ def testRequestWithNoneJsonBody(body: Option[User], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withJsonBody[User](body) @@ -221,7 +226,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with a File body will be sent successfully. */ def testRequestWithFileBody(body: File, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[File]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[File]] = { apiInvoker.execute[File](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withPrimitiveBody(body) @@ -233,7 +239,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with an Int body will be sent successfully. */ def testRequestWithIntBody(body: Int, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withPrimitiveBody(body) @@ -245,7 +252,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with a String body will be sent successfully. */ def testRequestWithStringBody(body: String, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withPrimitiveBody(body) @@ -257,7 +265,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with a Boolean body will be sent successfully. */ def testRequestWithBooleanBody(body: Boolean, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withPrimitiveBody(body) @@ -269,7 +278,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with Some primitive body will be sent successfully. */ def testRequestWithSomePrimitiveBody(body: Option[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withPrimitiveBody(body) @@ -281,7 +291,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with None primitive body will be sent successfully. */ def testRequestWithNonePrimitiveBody(body: Option[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.POST, "", "/test", None, rc) .withPrimitiveBody(body) @@ -293,7 +304,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with multipart form data will be sent successfully. */ def testRequestWithMultipartFormData(file: File, returnFile: Boolean, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[File]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[File]] = { apiInvoker.execute[File](ApiRequest(RequestMethod.POST, "", "/test", Some("multipart/form-data; charset=utf-8"), rc) .withFormParam("file", file) @@ -306,7 +318,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with form-url-encoded data will be sent successfully. */ def testRequestWithFormURLEncodedData(param1: String, param2: Int, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.POST, "", "/test", Some("application/x-www-form-urlencoded; charset=utf-8"), rc) .withFormParam("param1", param1) @@ -319,7 +332,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with an Int header will be sent successfully. */ def testRequestWithIntHeader(header: Int, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withHeaderParam("X-HEADER", header) @@ -331,7 +345,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with a String header will be sent successfully. */ def testRequestWithStringHeader(header: String, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withHeaderParam("X-HEADER", header) @@ -343,7 +358,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with a Boolean header will be sent successfully. */ def testRequestWithBooleanHeader(header: Boolean, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withHeaderParam("X-HEADER", header) @@ -355,7 +371,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with Some string header will be sent successfully. */ def testRequestWithSomeStringHeader(header: Option[String] = None, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withHeaderParam("X-HEADER", header) @@ -367,7 +384,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with None string header will be sent successfully. */ def testRequestWithNoneStringHeader(header: Option[String] = None, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withHeaderParam("X-HEADER", header) @@ -379,7 +397,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with query parameters will be sent successfully. */ def testRequestWithQueryParameters(param1: String, param2: Int, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param1", param1) @@ -392,7 +411,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with array values[CSV] in query parameter will be sent successfully. */ def testRequestWithArrayCsvQueryParameters(param: Seq[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", ArrayValues(param, CollectionFormats.CSV)) @@ -404,7 +424,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with array values[TSV] in query parameter will be sent successfully. */ def testRequestWithArrayTsvQueryParameters(param: Seq[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", ArrayValues(param, CollectionFormats.TSV)) @@ -416,7 +437,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with array values[SSV] in query parameter will be sent successfully. */ def testRequestWithArraySsvQueryParameters(param: Seq[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", ArrayValues(param, CollectionFormats.SSV)) @@ -428,7 +450,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with array values[PIPES] in query parameter will be sent successfully. */ def testRequestWithArrayPipesQueryParameters(param: Seq[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", ArrayValues(param, CollectionFormats.PIPES)) @@ -440,7 +463,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with array values[MULTI] in query parameter will be sent successfully. */ def testRequestWithArrayMultiQueryParameters(param: Seq[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", ArrayValues(param, CollectionFormats.MULTI)) @@ -452,7 +476,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with Some array in query parameter will be sent successfully. */ def testRequestWithSomeArrayQueryParameters(param: Option[Seq[String]], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", ArrayValues(param, CollectionFormats.CSV)) @@ -464,7 +489,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with None array in query parameter will be sent successfully. */ def testRequestWithNoneArrayQueryParameters(param: Option[Seq[String]], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", ArrayValues(param, CollectionFormats.CSV)) @@ -476,7 +502,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with Some string parameter will be sent successfully. */ def testRequestWithSomeStringQueryParameter(param: Option[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", param) @@ -488,7 +515,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with None string parameter will be sent successfully. */ def testRequestWithNoneStringQueryParameter(param: Option[String], rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withQueryParam("param", param) @@ -500,7 +528,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with path parameters will be sent successfully. */ def testRequestWithPathParameters(param1: String, param2: Int, rc: Config = Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.GET, "", "/test/{param1}/{param2}", None, rc) .withPathParam("param1", param1) @@ -513,7 +542,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with basic auth credentials will be sent successfully. */ def testRequestWithBasicCredentials(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext, basicAuth: ApiRequest.BasicCredentials): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext, basicAuth: ApiRequest.BasicCredentials): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withCredentials(basicAuth) @@ -525,7 +555,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with API credentials will be sent successfully in the header. */ def testRequestWithAPICredentialsInHeader(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext, apiKey: ApiRequest.ApiKey): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext, apiKey: ApiRequest.ApiKey): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withCredentials(ApiRequest.ApiKeyCredentials(apiKey, "X-AUTH", ApiRequest.ApiKeyLocations.HEADER)) @@ -537,7 +568,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a request with API credentials will be sent successfully in the query string. */ def testRequestWithAPICredentialsInQueryString(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext, apiKey: ApiRequest.ApiKey): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext, apiKey: ApiRequest.ApiKey): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withCredentials(ApiRequest.ApiKeyCredentials(apiKey, "auth", ApiRequest.ApiKeyLocations.QUERY)) @@ -549,7 +581,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return Unit as value. */ def testApiResponseWithUnitAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Unit](204) @@ -560,7 +593,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a Json object as value. */ def testApiResponseWithJsonObjectAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[User]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[User]] = { apiInvoker.execute[User](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withJsonSuccessResponse[User](200) @@ -571,7 +605,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a Json array as value. */ def testApiResponseWithJsonArrayAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Seq[User]]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Seq[User]]] = { apiInvoker.execute[Seq[User]](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withJsonSuccessResponse[Seq[User]](200) @@ -582,7 +617,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if the client returns an error if the API returns unexpected Json. */ def testApiResponseForUnexpectedJson(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[User]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[User]] = { apiInvoker.execute[User](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withJsonSuccessResponse[User](200) @@ -593,7 +629,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a File as value. */ def testApiResponseWithFileAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[File]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[File]] = { apiInvoker.execute[File](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[File](200) @@ -604,7 +641,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a String as value. */ def testApiResponseWithStringAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[String](200) @@ -615,7 +653,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a Long as value. */ def testApiResponseWithLongAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Long]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Long]] = { apiInvoker.execute[Long](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Long](200) @@ -626,7 +665,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a Int as value. */ def testApiResponseWithIntAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Int]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Int]] = { apiInvoker.execute[Int](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Int](200) @@ -637,7 +677,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a Double as value. */ def testApiResponseWithDoubleAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Double]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Double]] = { apiInvoker.execute[Double](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Double](200) @@ -648,7 +689,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a Float as value. */ def testApiResponseWithFloatAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Float]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Float]] = { apiInvoker.execute[Float](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Float](200) @@ -659,7 +701,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a Boolean as value. */ def testApiResponseWithBooleanAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Boolean]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Boolean]] = { apiInvoker.execute[Boolean](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Boolean](200) @@ -670,7 +713,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a response can return a Byte as value. */ def testApiResponseWithByteAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Byte]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Byte]] = { apiInvoker.execute[Byte](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Byte](200) @@ -681,7 +725,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if the client returns an error if the API returns an unexpected primitive type. */ def testApiResponseForUnexpectedPrimitiveType(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Boolean]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Boolean]] = { apiInvoker.execute[Boolean](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Boolean](200) @@ -692,7 +737,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return Unit as value. */ def testApiErrorWithUnitAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[Unit](500) @@ -703,7 +749,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a Json object as value. */ def testApiErrorWithJsonObjectAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[User]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[User]] = { apiInvoker.execute[User](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withJsonErrorResponse[Status](500) @@ -714,7 +761,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a Json array as value. */ def testApiErrorWithJsonArrayAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Seq[User]]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Seq[User]]] = { apiInvoker.execute[Seq[User]](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withJsonErrorResponse[Seq[Status]](500) @@ -725,7 +773,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if the client returns an error if the API returns unexpected Json. */ def testApiErrorForUnexpectedJson(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[User]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[User]] = { apiInvoker.execute[User](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withJsonErrorResponse[Status](500) @@ -736,7 +785,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a File as value. */ def testApiErrorWithFileAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[File]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[File]] = { apiInvoker.execute[File](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[File](500) @@ -747,7 +797,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a String as value. */ def testApiErrorWithStringAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[String]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[String]] = { apiInvoker.execute[String](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[String](500) @@ -758,7 +809,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a Long as value. */ def testApiErrorWithLongAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Long]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Long]] = { apiInvoker.execute[Long](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[Long](500) @@ -769,7 +821,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a Int as value. */ def testApiErrorWithIntAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Int]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Int]] = { apiInvoker.execute[Int](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[Int](500) @@ -780,7 +833,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a Double as value. */ def testApiErrorWithDoubleAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Double]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Double]] = { apiInvoker.execute[Double](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[Double](500) @@ -791,7 +845,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a Float as value. */ def testApiErrorWithFloatAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Float]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Float]] = { apiInvoker.execute[Float](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[Float](500) @@ -802,7 +857,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a Boolean as value. */ def testApiErrorWithBooleanAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Boolean]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Boolean]] = { apiInvoker.execute[Boolean](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[Boolean](500) @@ -813,7 +869,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if a error can return a Byte as value. */ def testApiErrorWithByteAsValue(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Byte]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Byte]] = { apiInvoker.execute[Byte](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[Byte](500) @@ -824,7 +881,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if the client returns an error if the API returns an unexpected primitive type. */ def testApiErrorForUnexpectedPrimitiveType(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Boolean]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Boolean]] = { apiInvoker.execute[Boolean](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveErrorResponse[Boolean](500) @@ -835,7 +893,8 @@ class TestApi @Inject() (apiInvoker: ApiInvoker) { * Test if the client returns extractable headers. */ def testResponseHeaders(rc: ApiRequest.Config = ApiRequest.Config())( - implicit ec: ExecutionContext): Future[ApiResponse[Unit]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[Unit]] = { apiInvoker.execute[Unit](ApiRequest(RequestMethod.GET, "", "/test", None, rc) .withPrimitiveSuccessResponse[Unit](200) diff --git a/clientstub/src/main/scala/com/mohiva/swagger/codegen/core/ApiImplicits.scala b/clientstub/src/main/scala/com/mohiva/swagger/codegen/core/ApiImplicits.scala index 5af49d4..0703ecf 100644 --- a/clientstub/src/main/scala/com/mohiva/swagger/codegen/core/ApiImplicits.scala +++ b/clientstub/src/main/scala/com/mohiva/swagger/codegen/core/ApiImplicits.scala @@ -20,15 +20,16 @@ package com.mohiva.swagger.codegen.core import java.io.File -import java.nio.file.{ Files, Paths } +import akka.stream.scaladsl.{ FileIO, Source } +import akka.util.ByteString import com.mohiva.swagger.codegen.core.ApiRequest._ import org.joda.time.{ DateTime, DateTimeZone } -import play.api.http.{ HeaderNames, MediaType, Writeable } +import play.api.http.MediaType import play.api.libs.json._ import play.api.libs.ws._ -import play.api.mvc.MultipartFormData.FilePart -import play.api.mvc.{ Codec, MultipartFormData } +import play.api.mvc.MultipartFormData.{ DataPart, FilePart, Part } +import play.core.formatters.Multipart import scala.concurrent.{ ExecutionContext, Future } import scala.reflect.ClassTag @@ -253,7 +254,7 @@ object PlayRequest { */ private def requestTimeoutPipeline: Pipeline = { case wsRequest => - wsRequest.withRequestTimeout(config.requestTimeout.toMillis) + wsRequest.withRequestTimeout(config.requestTimeout) } /** @@ -273,14 +274,17 @@ object PlayRequest { apiRequest.formParams.normalize match { case p if p.isEmpty => wsRequest case p if contentType.contains("multipart/form-data") => - import PlayRequest.MultipartFormDataWritable._ - wsRequest.withBody(p.foldLeft(MultipartFormData(Map[String, Seq[String]](), Seq[FilePart[File]](), Seq(), Seq())) { - case (formData, (key, value)) => + val boundary = Multipart.randomBoundary() + val contentType = s"multipart/form-data; boundary=$boundary" + val body = p.foldLeft(List[Part[Source[ByteString, Any]]]()) { + case (parts, (key, value)) => value match { - case f: File => formData.copy(files = formData.files :+ FilePart(key, f.getName, None, f)) - case _ => formData.copy(dataParts = formData.dataParts + (key -> Seq(String.valueOf(value)))) + case f: File => parts :+ FilePart(key, f.getName, None, FileIO.fromFile(f)) + case _ => parts :+ DataPart(key, String.valueOf(value)) } - }) + } + + wsRequest.withBody(StreamedBody(Multipart.transform(Source(body), boundary))).withHeaders("Content-Type" -> contentType) case p => // default: application/x-www-form-urlencoded wsRequest.withBody(p.toMap.mapValues(v => Seq(String.valueOf(v)))) } @@ -324,64 +328,4 @@ object PlayRequest { } } } - - /** - * A Play writable for multipart/form-data. - * - * @see http://tech.fongmun.com/post/125479939452/test-multipartformdata-in-play - */ - object MultipartFormDataWritable { - val boundary = "--------ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" - - /** - * Create the form data parts. - * - * @param data The form data. - * @return The form data part as string. - */ - def formatDataParts(data: Map[String, Seq[String]]) = { - val dataParts = data.flatMap { - case (key, values) => - values.map { value => - val name = s""""$key"""" - s"--$boundary\r\n${HeaderNames.CONTENT_DISPOSITION}: form-data; name=$name\r\n\r\n$value\r\n" - } - }.mkString("") - Codec.utf_8.encode(dataParts) - } - - /** - * Creates the file header parts. - * - * @param file The file data. - * @return The file data parts as string. - */ - def filePartHeader(file: FilePart[File]) = { - val name = s""""${file.key}"""" - val filename = s""""${file.filename}"""" - val contentType = file.contentType.map { ct => - s"${HeaderNames.CONTENT_TYPE}: $ct\r\n" - }.getOrElse("") - Codec.utf_8.encode(s"--$boundary\r\n${HeaderNames.CONTENT_DISPOSITION}: form-data; name=$name; filename=$filename\r\n$contentType\r\n") - } - - /** - * The writeable. - * - * @param ec The execution context. - */ - implicit def writeable(implicit ec: ExecutionContext): Writeable[MultipartFormData[File]] = { - Writeable[MultipartFormData[File]]( - transform = { form: MultipartFormData[File] => - formatDataParts(form.dataParts) ++ - form.files.flatMap { file => - val fileBytes = Files.readAllBytes(Paths.get(file.ref.getAbsolutePath)) - filePartHeader(file) ++ fileBytes ++ Codec.utf_8.encode("\r\n") - } ++ - Codec.utf_8.encode(s"--$boundary--") - }, - contentType = Some(s"multipart/form-data; boundary=$boundary") - ) - } - } } diff --git a/clientstub/src/main/scala/com/mohiva/swagger/codegen/core/ApiInvoker.scala b/clientstub/src/main/scala/com/mohiva/swagger/codegen/core/ApiInvoker.scala index e6290d9..9f90c85 100644 --- a/clientstub/src/main/scala/com/mohiva/swagger/codegen/core/ApiInvoker.scala +++ b/clientstub/src/main/scala/com/mohiva/swagger/codegen/core/ApiInvoker.scala @@ -49,7 +49,8 @@ class ApiInvoker @Inject() (config: ApiConfig, wsClient: WSClient) { * @return The response. */ def execute[C](apiRequest: ApiRequest)( - implicit ec: ExecutionContext): Future[ApiResponse[C]] = { + implicit + ec: ExecutionContext): Future[ApiResponse[C]] = { val playRequest = apiRequest.toPlay(config, wsClient) playRequest.execute().flatMap { response => @@ -79,7 +80,7 @@ class ApiInvoker @Inject() (config: ApiConfig, wsClient: WSClient) { // Parse success response as File case Some((ResponseState.Success, tag, None)) if tag.tpe <:< typeOf[File] => - serialize(response, tag)(createTempFile(response.bodyAsBytes)) { result => + serialize(response, tag)(createTempFile(response.bodyAsBytes.toArray)) { result => Success(ApiResponse(response.status, result.asInstanceOf[C], response.allHeaders)) } @@ -101,7 +102,7 @@ class ApiInvoker @Inject() (config: ApiConfig, wsClient: WSClient) { // Parse error response as File case Some((ResponseState.Error, tag, None)) if tag.tpe <:< typeOf[File] => - serialize(response, tag)(createTempFile(response.bodyAsBytes)) { result => + serialize(response, tag)(createTempFile(response.bodyAsBytes.toArray)) { result => Failure(ApiError(response.status, ApiResponseError, Some(result), headers = response.allHeaders)) } diff --git a/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/Error.scala b/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/Error.scala index 6d31597..432a47c 100644 --- a/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/Error.scala +++ b/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/Error.scala @@ -15,7 +15,7 @@ */ package com.mohiva.swagger.codegen.models -import org.cvogt.play.json.Jsonx +import ai.x.play.json.Jsonx /** * A Json model to test. diff --git a/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/Status.scala b/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/Status.scala index 2df0667..5401804 100644 --- a/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/Status.scala +++ b/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/Status.scala @@ -15,7 +15,7 @@ */ package com.mohiva.swagger.codegen.models -import org.cvogt.play.json.Jsonx +import ai.x.play.json.Jsonx /** * A Json model to test. diff --git a/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/User.scala b/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/User.scala index 4cdc757..8335e15 100644 --- a/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/User.scala +++ b/clientstub/src/main/scala/com/mohiva/swagger/codegen/models/User.scala @@ -15,8 +15,8 @@ */ package com.mohiva.swagger.codegen.models +import ai.x.play.json.Jsonx import com.mohiva.swagger.codegen.core.ApiJsonFormats._ -import org.cvogt.play.json.Jsonx import org.joda.time.DateTime /** diff --git a/clientstub/src/test/scala/com/mohiva/swagger/codegen/TestApiSpec.scala b/clientstub/src/test/scala/com/mohiva/swagger/codegen/TestApiSpec.scala index 5522659..e4d4984 100644 --- a/clientstub/src/test/scala/com/mohiva/swagger/codegen/TestApiSpec.scala +++ b/clientstub/src/test/scala/com/mohiva/swagger/codegen/TestApiSpec.scala @@ -31,6 +31,7 @@ import play.api.libs.json.Json import play.api.mvc.Action import play.api.mvc.BodyParsers._ import play.api.mvc.Results._ +import play.api.test.WithApplication import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.duration._ @@ -269,7 +270,7 @@ class TestApiSpec extends Specification with NoLanguageFeatures with ContentMatc await(testApi.testRequestWithNonePrimitiveBody(None)).content must beEqualTo(()) } - "send a request with multipart-form data" in new Context { + "send a request with multipart-form data" in new WithApplication with Context { val route = Route { case ("POST", "/test") => Action(parse.multipartFormData) { request => val file = request.body.file("file") diff --git a/conf/config.json b/conf/config.json index b02ad97..36eea06 100644 --- a/conf/config.json +++ b/conf/config.json @@ -6,6 +6,6 @@ "projectOrganization": "com.mohiva", "projectName": "api-client", "projectVersion": "1.1.1", - "scalaVersion": "2.11.7", - "playVersion": "2.4.6" + "scalaVersion": "2.11.8", + "playVersion": "2.5.3" } diff --git a/project/BuildSettings.scala b/project/BuildSettings.scala index f88ec80..320118c 100644 --- a/project/BuildSettings.scala +++ b/project/BuildSettings.scala @@ -27,7 +27,7 @@ object BasicSettings extends AutoPlugin { override def projectSettings = Seq( organization := "com.mohiva", - version := "0.1.0-SNAPSHOT", + version := "0.2.0-SNAPSHOT", resolvers ++= Dependencies.resolvers, scalaVersion := Dependencies.Versions.scalaVersion, crossScalaVersions := Dependencies.Versions.crossScala, @@ -46,7 +46,7 @@ object BasicSettings extends AutoPlugin { scalacOptions in Test ~= { (options: Seq[String]) => options filterNot (_ == "-Ywarn-dead-code") // Allow dead code in tests (to support using mockito). }, - parallelExecution in Test := false, + parallelExecution in Test := true, javaOptions in Test ++= Seq("-Xms512M", "-Xmx2048M", "-XX:MaxPermSize=2048M", "-XX:+CMSClassUnloadingEnabled"), fork in Test := true ) @@ -64,7 +64,7 @@ object CodeFormatter extends AutoPlugin { ScalariformKeys.preferences := ScalariformKeys.preferences.value .setPreference(FormatXml, false) .setPreference(DoubleIndentClassDeclaration, false) - .setPreference(PreserveDanglingCloseParenthesis, true) + .setPreference(DanglingCloseParenthesis, Preserve) ) override def trigger = allRequirements diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 8d6f5c4..e061913 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -18,7 +18,7 @@ import sbt._ object Dependencies { object Versions { - val crossScala = Seq("2.11.7") + val crossScala = Seq("2.11.8") val scalaVersion = crossScala.head } @@ -30,23 +30,22 @@ object Dependencies { object Library { object Play { - val version = "2.4.6" + val version = "2.5.3" val ws = "com.typesafe.play" %% "play-ws" % version val json = "com.typesafe.play" %% "play-json" % version val test = "com.typesafe.play" %% "play-test" % version - } - - object Specs2 { - private val version = "3.6.5" - val core = "org.specs2" %% "specs2-core" % version - val matcherExtra = "org.specs2" %% "specs2-matcher-extra" % version - val mock = "org.specs2" %% "specs2-mock" % version + val specs2 = "com.typesafe.play" %% "play-specs2" % version + object Specs2 { + private val version = "3.6.6" + val matcherExtra = "org.specs2" %% "specs2-matcher-extra" % version + val mock = "org.specs2" %% "specs2-mock" % version + } } val jodaTime = "joda-time" % "joda-time" % "2.8.1" - val playJsonExtension = "org.cvogt" %% "play-json-extensions" % "0.6.0" - val playWSMock = "de.leanovate.play-mockws" %% "play-mockws" % "2.4.2" - val swaggerCodegen = "io.swagger" % "swagger-codegen" % "2.1.5" + val playJsonExtension = "ai.x" %% "play-json-extensions" % "0.8.0" + val playWSMock = "de.leanovate.play-mockws" %% "play-mockws" % "2.5.0" + val swaggerCodegen = "io.swagger" % "swagger-codegen" % "2.1.6" val javaxInject = "javax.inject" % "javax.inject" % "1" val testNG = "org.testng" % "testng" % "6.8" } diff --git a/project/build-jvm-opts b/project/build-jvm-opts new file mode 100644 index 0000000..430b310 --- /dev/null +++ b/project/build-jvm-opts @@ -0,0 +1,6 @@ +# JVM options for building. + +-Xms2048M +-Xmx2048M +-Xss6M +-XX:MaxPermSize=512M diff --git a/project/plugins.sbt b/project/plugins.sbt index 28b4186..2032ff3 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1,7 @@ logLevel := Level.Warn -addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.3.0") +addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.6.0") + +addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.3.5") + +addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.1.0") diff --git a/scripts/ci-build b/scripts/ci-build new file mode 100755 index 0000000..eff13a1 --- /dev/null +++ b/scripts/ci-build @@ -0,0 +1,31 @@ +#!/bin/bash -e +# +# Builds the project in the continuous integration environment. +# +# Copyright 2015 Mohiva Organisation (license at mohiva dot com) +# +# 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. +# +set -o nounset -o errexit + +echo "" +echo "Validating code formatting" +scripts/validate-format + +echo "" +echo "Testing and generating documentation" +scripts/sbt clean coverage test coverageReport + +echo "" +echo "Build finished" +echo "" diff --git a/scripts/coveralls b/scripts/coveralls new file mode 100755 index 0000000..39b8b55 --- /dev/null +++ b/scripts/coveralls @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Publishes the coverage report to http://coveralls.io. +# +# Copyright 2015 Mohiva Organisation (license at mohiva dot com) +# +# 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. +# +set -o nounset -o errexit + +echo "" +echo "Publish coverage report" +scripts/sbt coveralls + +echo "" +echo "Report published" +echo "" diff --git a/scripts/sbt b/scripts/sbt index b853122..1c01fad 100755 --- a/scripts/sbt +++ b/scripts/sbt @@ -1,543 +1,21 @@ -#!/usr/bin/env bash +#!/bin/bash # -# A more capable sbt runner, coincidentally also called sbt. -# Author: Paul Phillips - -# todo - make this dynamic -declare -r sbt_release_version="0.13.8" -declare -r sbt_unreleased_version="0.13.9-M1" -declare -r buildProps="project/build.properties" - -declare sbt_jar sbt_dir sbt_create sbt_version -declare scala_version sbt_explicit_version -declare verbose noshare batch trace_level log_level -declare sbt_saved_stty debugUs - -echoerr () { echo >&2 "$@"; } -vlog () { [[ -n "$verbose" ]] && echoerr "$@"; } - -# spaces are possible, e.g. sbt.version = 0.13.0 -build_props_sbt () { - [[ -r "$buildProps" ]] && \ - grep '^sbt\.version' "$buildProps" | tr '=\r' ' ' | awk '{ print $2; }' -} - -update_build_props_sbt () { - local ver="$1" - local old="$(build_props_sbt)" - - [[ -r "$buildProps" ]] && [[ "$ver" != "$old" ]] && { - perl -pi -e "s/^sbt\.version\b.*\$/sbt.version=${ver}/" "$buildProps" - grep -q '^sbt.version[ =]' "$buildProps" || printf "\nsbt.version=%s\n" "$ver" >> "$buildProps" - - vlog "!!!" - vlog "!!! Updated file $buildProps setting sbt.version to: $ver" - vlog "!!! Previous value was: $old" - vlog "!!!" - } -} - -set_sbt_version () { - sbt_version="${sbt_explicit_version:-$(build_props_sbt)}" - [[ -n "$sbt_version" ]] || sbt_version=$sbt_release_version - export sbt_version -} - -# restore stty settings (echo in particular) -onSbtRunnerExit() { - [[ -n "$sbt_saved_stty" ]] || return - vlog "" - vlog "restoring stty: $sbt_saved_stty" - stty "$sbt_saved_stty" - unset sbt_saved_stty -} - -# save stty and trap exit, to ensure echo is reenabled if we are interrupted. -trap onSbtRunnerExit EXIT -sbt_saved_stty="$(stty -g 2>/dev/null)" -vlog "Saved stty: $sbt_saved_stty" - -# this seems to cover the bases on OSX, and someone will -# have to tell me about the others. -get_script_path () { - local path="$1" - [[ -L "$path" ]] || { echo "$path" ; return; } - - local target="$(readlink "$path")" - if [[ "${target:0:1}" == "/" ]]; then - echo "$target" - else - echo "${path%/*}/$target" - fi -} - -die() { - echo "Aborting: $@" - exit 1 -} - -make_url () { - version="$1" - - case "$version" in - 0.7.*) echo "http://simple-build-tool.googlecode.com/files/sbt-launch-0.7.7.jar" ;; - 0.10.* ) echo "$sbt_launch_repo/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;; - 0.11.[12]) echo "$sbt_launch_repo/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;; - *) echo "$sbt_launch_repo/org.scala-sbt/sbt-launch/$version/sbt-launch.jar" ;; - esac -} - -init_default_option_file () { - local overriding_var="${!1}" - local default_file="$2" - if [[ ! -r "$default_file" && "$overriding_var" =~ ^@(.*)$ ]]; then - local envvar_file="${BASH_REMATCH[1]}" - if [[ -r "$envvar_file" ]]; then - default_file="$envvar_file" - fi - fi - echo "$default_file" -} - -declare -r cms_opts="-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC" -declare -r jit_opts="-XX:ReservedCodeCacheSize=256m -XX:+TieredCompilation" -declare -r default_jvm_opts_common="-Xms512m -Xmx1536m -Xss2m $jit_opts $cms_opts" -declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy" -declare -r latest_28="2.8.2" -declare -r latest_29="2.9.3" -declare -r latest_210="2.10.5" -declare -r latest_211="2.11.7" - -declare -r script_path="$(get_script_path "$BASH_SOURCE")" -declare -r script_name="${script_path##*/}" - -# some non-read-onlies set with defaults -declare java_cmd="java" -declare sbt_opts_file="$(init_default_option_file SBT_OPTS .sbtopts)" -declare jvm_opts_file="$(init_default_option_file JVM_OPTS .jvmopts)" -declare sbt_launch_repo="http://repo.typesafe.com/typesafe/ivy-releases" - -# pull -J and -D options to give to java. -declare -a residual_args -declare -a java_args -declare -a scalac_args -declare -a sbt_commands - -# args to jvm/sbt via files or environment variables -declare -a extra_jvm_opts extra_sbt_opts - -addJava () { - vlog "[addJava] arg = '$1'" - java_args+=("$1") -} -addSbt () { - vlog "[addSbt] arg = '$1'" - sbt_commands+=("$1") -} -setThisBuild () { - vlog "[addBuild] args = '$@'" - local key="$1" && shift - addSbt "set $key in ThisBuild := $@" -} -addScalac () { - vlog "[addScalac] arg = '$1'" - scalac_args+=("$1") -} -addResidual () { - vlog "[residual] arg = '$1'" - residual_args+=("$1") -} -addResolver () { - addSbt "set resolvers += $1" -} -addDebugger () { - addJava "-Xdebug" - addJava "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" -} -setScalaVersion () { - [[ "$1" == *"-SNAPSHOT" ]] && addResolver 'Resolver.sonatypeRepo("snapshots")' - addSbt "++ $1" -} -setJavaHome () { - java_cmd="$1/bin/java" - setThisBuild javaHome "Some(file(\"$1\"))" - export JAVA_HOME="$1" - export JDK_HOME="$1" - export PATH="$JAVA_HOME/bin:$PATH" -} -setJavaHomeQuietly () { - addSbt warn - setJavaHome "$1" - addSbt info -} - -# if set, use JDK_HOME/JAVA_HOME over java found in path -if [[ -e "$JDK_HOME/lib/tools.jar" ]]; then - setJavaHomeQuietly "$JDK_HOME" -elif [[ -e "$JAVA_HOME/bin/java" ]]; then - setJavaHomeQuietly "$JAVA_HOME" -fi - -# directory to store sbt launchers -declare sbt_launch_dir="$HOME/.sbt/launchers" -[[ -d "$sbt_launch_dir" ]] || mkdir -p "$sbt_launch_dir" -[[ -w "$sbt_launch_dir" ]] || sbt_launch_dir="$(mktemp -d -t sbt_extras_launchers.XXXXXX)" - -java_version () { - local version=$("$java_cmd" -version 2>&1 | grep -E -e '(java|openjdk) version' | awk '{ print $3 }' | tr -d \") - vlog "Detected Java version: $version" - echo "${version:2:1}" -} - -# MaxPermSize critical on pre-8 jvms but incurs noisy warning on 8+ -default_jvm_opts () { - local v="$(java_version)" - if [[ $v -ge 8 ]]; then - echo "$default_jvm_opts_common" - else - echo "-XX:MaxPermSize=384m $default_jvm_opts_common" - fi -} - -build_props_scala () { - if [[ -r "$buildProps" ]]; then - versionLine="$(grep '^build.scala.versions' "$buildProps")" - versionString="${versionLine##build.scala.versions=}" - echo "${versionString%% .*}" - fi -} - -execRunner () { - # print the arguments one to a line, quoting any containing spaces - vlog "# Executing command line:" && { - for arg; do - if [[ -n "$arg" ]]; then - if printf "%s\n" "$arg" | grep -q ' '; then - printf >&2 "\"%s\"\n" "$arg" - else - printf >&2 "%s\n" "$arg" - fi - fi - done - vlog "" - } - - [[ -n "$batch" ]] && exec /dev/null; then - curl --fail --silent --location "$url" --output "$jar" - elif which wget >/dev/null; then - wget --quiet -O "$jar" "$url" - fi - } && [[ -r "$jar" ]] -} - -acquire_sbt_jar () { - sbt_url="$(jar_url "$sbt_version")" - sbt_jar="$(jar_file "$sbt_version")" - - [[ -r "$sbt_jar" ]] || download_url "$sbt_url" "$sbt_jar" -} - -usage () { - cat < display stack traces with a max of frames (default: -1, traces suppressed) - -debug-inc enable debugging log for the incremental compiler - -no-colors disable ANSI color codes - -sbt-create start sbt even if current directory contains no sbt project - -sbt-dir path to global settings/plugins directory (default: ~/.sbt/) - -sbt-boot path to shared boot directory (default: ~/.sbt/boot in 0.11+) - -ivy path to local Ivy repository (default: ~/.ivy2) - -no-share use all local caches; no sharing - -offline put sbt in offline mode - -jvm-debug Turn on JVM debugging, open at the given port. - -batch Disable interactive mode - -prompt Set the sbt prompt; in expr, 's' is the State and 'e' is Extracted - - # sbt version (default: sbt.version from $buildProps if present, otherwise $sbt_release_version) - -sbt-force-latest force the use of the latest release of sbt: $sbt_release_version - -sbt-version use the specified version of sbt (default: $sbt_release_version) - -sbt-dev use the latest pre-release version of sbt: $sbt_unreleased_version - -sbt-jar use the specified jar as the sbt launcher - -sbt-launch-dir directory to hold sbt launchers (default: ~/.sbt/launchers) - -sbt-launch-repo repo url for downloading sbt launcher jar (default: $sbt_launch_repo) - - # scala version (default: as chosen by sbt) - -28 use $latest_28 - -29 use $latest_29 - -210 use $latest_210 - -211 use $latest_211 - -scala-home use the scala build at the specified directory - -scala-version use the specified version of scala - -binary-version use the specified scala version when searching for dependencies - - # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) - -java-home alternate JAVA_HOME - - # passing options to the jvm - note it does NOT use JAVA_OPTS due to pollution - # The default set is used if JVM_OPTS is unset and no -jvm-opts file is found - $(default_jvm_opts) - JVM_OPTS environment variable holding either the jvm args directly, or - the reference to a file containing jvm args if given path is prepended by '@' (e.g. '@/etc/jvmopts') - Note: "@"-file is overridden by local '.jvmopts' or '-jvm-opts' argument. - -jvm-opts file containing jvm args (if not given, .jvmopts in project root is used if present) - -Dkey=val pass -Dkey=val directly to the jvm - -J-X pass option -X directly to the jvm (-J is stripped) - - # passing options to sbt, OR to this runner - SBT_OPTS environment variable holding either the sbt args directly, or - the reference to a file containing sbt args if given path is prepended by '@' (e.g. '@/etc/sbtopts') - Note: "@"-file is overridden by local '.sbtopts' or '-sbt-opts' argument. - -sbt-opts file containing sbt args (if not given, .sbtopts in project root is used if present) - -S-X add -X to sbt's scalacOptions (-S is stripped) -EOM -} - -process_args () -{ - require_arg () { - local type="$1" - local opt="$2" - local arg="$3" - - if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then - die "$opt requires <$type> argument" - fi - } - while [[ $# -gt 0 ]]; do - case "$1" in - -h|-help) usage; exit 1 ;; - -v) verbose=true && shift ;; - -d) addSbt "--debug" && addSbt debug && shift ;; - -w) addSbt "--warn" && addSbt warn && shift ;; - -q) addSbt "--error" && addSbt error && shift ;; - -x) debugUs=true && shift ;; - -trace) require_arg integer "$1" "$2" && trace_level="$2" && shift 2 ;; - -ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;; - -no-colors) addJava "-Dsbt.log.noformat=true" && shift ;; - -no-share) noshare=true && shift ;; - -sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;; - -sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;; - -debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;; - -offline) addSbt "set offline := true" && shift ;; - -jvm-debug) require_arg port "$1" "$2" && addDebugger "$2" && shift 2 ;; - -batch) batch=true && shift ;; - -prompt) require_arg "expr" "$1" "$2" && setThisBuild shellPrompt "(s => { val e = Project.extract(s) ; $2 })" && shift 2 ;; - - -sbt-create) sbt_create=true && shift ;; - -sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;; - -sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;; - -sbt-force-latest) sbt_explicit_version="$sbt_release_version" && shift ;; - -sbt-dev) sbt_explicit_version="$sbt_unreleased_version" && shift ;; - -sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;; - -sbt-launch-repo) require_arg path "$1" "$2" && sbt_launch_repo="$2" && shift 2 ;; - -scala-version) require_arg version "$1" "$2" && setScalaVersion "$2" && shift 2 ;; - -binary-version) require_arg version "$1" "$2" && setThisBuild scalaBinaryVersion "\"$2\"" && shift 2 ;; - -scala-home) require_arg path "$1" "$2" && setThisBuild scalaHome "Some(file(\"$2\"))" && shift 2 ;; - -java-home) require_arg path "$1" "$2" && setJavaHome "$2" && shift 2 ;; - -sbt-opts) require_arg path "$1" "$2" && sbt_opts_file="$2" && shift 2 ;; - -jvm-opts) require_arg path "$1" "$2" && jvm_opts_file="$2" && shift 2 ;; - - -D*) addJava "$1" && shift ;; - -J*) addJava "${1:2}" && shift ;; - -S*) addScalac "${1:2}" && shift ;; - -28) setScalaVersion "$latest_28" && shift ;; - -29) setScalaVersion "$latest_29" && shift ;; - -210) setScalaVersion "$latest_210" && shift ;; - -211) setScalaVersion "$latest_211" && shift ;; - - --debug) addSbt debug && addResidual "$1" && shift ;; - --warn) addSbt warn && addResidual "$1" && shift ;; - --error) addSbt error && addResidual "$1" && shift ;; - *) addResidual "$1" && shift ;; - esac - done -} - -# process the direct command line arguments -process_args "$@" - -# skip #-styled comments and blank lines -readConfigFile() { - while read line; do - [[ $line =~ ^# ]] || [[ -z $line ]] || echo "$line" - done < "$1" -} - -# if there are file/environment sbt_opts, process again so we -# can supply args to this runner -if [[ -r "$sbt_opts_file" ]]; then - vlog "Using sbt options defined in file $sbt_opts_file" - while read opt; do extra_sbt_opts+=("$opt"); done < <(readConfigFile "$sbt_opts_file") -elif [[ -n "$SBT_OPTS" && ! ("$SBT_OPTS" =~ ^@.*) ]]; then - vlog "Using sbt options defined in variable \$SBT_OPTS" - extra_sbt_opts=( $SBT_OPTS ) -else - vlog "No extra sbt options have been defined" -fi - -[[ -n "${extra_sbt_opts[*]}" ]] && process_args "${extra_sbt_opts[@]}" - -# reset "$@" to the residual args -set -- "${residual_args[@]}" -argumentCount=$# - -# set sbt version -set_sbt_version - -# only exists in 0.12+ -setTraceLevel() { - case "$sbt_version" in - "0.7."* | "0.10."* | "0.11."* ) echoerr "Cannot set trace level in sbt version $sbt_version" ;; - *) setThisBuild traceLevel $trace_level ;; - esac -} - -# set scalacOptions if we were given any -S opts -[[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[@]}\"" - -# Update build.properties on disk to set explicit version - sbt gives us no choice -[[ -n "$sbt_explicit_version" ]] && update_build_props_sbt "$sbt_explicit_version" -vlog "Detected sbt version $sbt_version" - -[[ -n "$scala_version" ]] && vlog "Overriding scala version to $scala_version" - -# no args - alert them there's stuff in here -(( argumentCount > 0 )) || { - vlog "Starting $script_name: invoke with -help for other options" - residual_args=( shell ) -} - -# verify this is an sbt dir or -create was given -[[ -r ./build.sbt || -d ./project || -n "$sbt_create" ]] || { - cat < + +# todo - make this dynamic +declare -r sbt_release_version="0.13.9" +declare -r sbt_unreleased_version="0.13.9" +declare -r buildProps="project/build.properties" + +declare sbt_jar sbt_dir sbt_create sbt_version +declare scala_version sbt_explicit_version +declare verbose noshare batch trace_level log_level +declare sbt_saved_stty debugUs + +echoerr () { echo >&2 "$@"; } +vlog () { [[ -n "$verbose" ]] && echoerr "$@"; } + +# spaces are possible, e.g. sbt.version = 0.13.0 +build_props_sbt () { + [[ -r "$buildProps" ]] && \ + grep '^sbt\.version' "$buildProps" | tr '=\r' ' ' | awk '{ print $2; }' +} + +update_build_props_sbt () { + local ver="$1" + local old="$(build_props_sbt)" + + [[ -r "$buildProps" ]] && [[ "$ver" != "$old" ]] && { + perl -pi -e "s/^sbt\.version\b.*\$/sbt.version=${ver}/" "$buildProps" + grep -q '^sbt.version[ =]' "$buildProps" || printf "\nsbt.version=%s\n" "$ver" >> "$buildProps" + + vlog "!!!" + vlog "!!! Updated file $buildProps setting sbt.version to: $ver" + vlog "!!! Previous value was: $old" + vlog "!!!" + } +} + +set_sbt_version () { + sbt_version="${sbt_explicit_version:-$(build_props_sbt)}" + [[ -n "$sbt_version" ]] || sbt_version=$sbt_release_version + export sbt_version +} + +# restore stty settings (echo in particular) +onSbtRunnerExit() { + [[ -n "$sbt_saved_stty" ]] || return + vlog "" + vlog "restoring stty: $sbt_saved_stty" + stty "$sbt_saved_stty" + unset sbt_saved_stty +} + +# save stty and trap exit, to ensure echo is reenabled if we are interrupted. +trap onSbtRunnerExit EXIT +sbt_saved_stty="$(stty -g 2>/dev/null)" +vlog "Saved stty: $sbt_saved_stty" + +# this seems to cover the bases on OSX, and someone will +# have to tell me about the others. +get_script_path () { + local path="$1" + [[ -L "$path" ]] || { echo "$path" ; return; } + + local target="$(readlink "$path")" + if [[ "${target:0:1}" == "/" ]]; then + echo "$target" + else + echo "${path%/*}/$target" + fi +} + +die() { + echo "Aborting: $@" + exit 1 +} + +make_url () { + version="$1" + + case "$version" in + 0.7.*) echo "http://simple-build-tool.googlecode.com/files/sbt-launch-0.7.7.jar" ;; + 0.10.* ) echo "$sbt_launch_repo/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;; + 0.11.[12]) echo "$sbt_launch_repo/org.scala-tools.sbt/sbt-launch/$version/sbt-launch.jar" ;; + *) echo "$sbt_launch_repo/org.scala-sbt/sbt-launch/$version/sbt-launch.jar" ;; + esac +} + +init_default_option_file () { + local overriding_var="${!1}" + local default_file="$2" + if [[ ! -r "$default_file" && "$overriding_var" =~ ^@(.*)$ ]]; then + local envvar_file="${BASH_REMATCH[1]}" + if [[ -r "$envvar_file" ]]; then + default_file="$envvar_file" + fi + fi + echo "$default_file" +} + +declare -r cms_opts="-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC" +declare -r jit_opts="-XX:ReservedCodeCacheSize=256m -XX:+TieredCompilation" +declare -r default_jvm_opts_common="-Xms512m -Xmx1536m -Xss2m $jit_opts $cms_opts" +declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy" +declare -r latest_28="2.8.2" +declare -r latest_29="2.9.3" +declare -r latest_210="2.10.5" +declare -r latest_211="2.11.7" + +declare -r script_path="$(get_script_path "$BASH_SOURCE")" +declare -r script_name="${script_path##*/}" + +# some non-read-onlies set with defaults +declare java_cmd="java" +declare sbt_opts_file="$(init_default_option_file SBT_OPTS .sbtopts)" +declare jvm_opts_file="$(init_default_option_file JVM_OPTS .jvmopts)" +declare sbt_launch_repo="http://repo.typesafe.com/typesafe/ivy-releases" + +# pull -J and -D options to give to java. +declare -a residual_args +declare -a java_args +declare -a scalac_args +declare -a sbt_commands + +# args to jvm/sbt via files or environment variables +declare -a extra_jvm_opts extra_sbt_opts + +addJava () { + vlog "[addJava] arg = '$1'" + java_args+=("$1") +} +addSbt () { + vlog "[addSbt] arg = '$1'" + sbt_commands+=("$1") +} +setThisBuild () { + vlog "[addBuild] args = '$@'" + local key="$1" && shift + addSbt "set $key in ThisBuild := $@" +} +addScalac () { + vlog "[addScalac] arg = '$1'" + scalac_args+=("$1") +} +addResidual () { + vlog "[residual] arg = '$1'" + residual_args+=("$1") +} +addResolver () { + addSbt "set resolvers += $1" +} +addDebugger () { + addJava "-Xdebug" + addJava "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1" +} +setScalaVersion () { + [[ "$1" == *"-SNAPSHOT" ]] && addResolver 'Resolver.sonatypeRepo("snapshots")' + addSbt "++ $1" +} +setJavaHome () { + java_cmd="$1/bin/java" + setThisBuild javaHome "Some(file(\"$1\"))" + export JAVA_HOME="$1" + export JDK_HOME="$1" + export PATH="$JAVA_HOME/bin:$PATH" +} +setJavaHomeQuietly () { + addSbt warn + setJavaHome "$1" + addSbt info +} + +# if set, use JDK_HOME/JAVA_HOME over java found in path +if [[ -e "$JDK_HOME/lib/tools.jar" ]]; then + setJavaHomeQuietly "$JDK_HOME" +elif [[ -e "$JAVA_HOME/bin/java" ]]; then + setJavaHomeQuietly "$JAVA_HOME" +fi + +# directory to store sbt launchers +declare sbt_launch_dir="$HOME/.sbt/launchers" +[[ -d "$sbt_launch_dir" ]] || mkdir -p "$sbt_launch_dir" +[[ -w "$sbt_launch_dir" ]] || sbt_launch_dir="$(mktemp -d -t sbt_extras_launchers.XXXXXX)" + +java_version () { + local version=$("$java_cmd" -version 2>&1 | grep -E -e '(java|openjdk) version' | awk '{ print $3 }' | tr -d \") + vlog "Detected Java version: $version" + echo "${version:2:1}" +} + +# MaxPermSize critical on pre-8 jvms but incurs noisy warning on 8+ +default_jvm_opts () { + local v="$(java_version)" + if [[ $v -ge 8 ]]; then + echo "$default_jvm_opts_common" + else + echo "-XX:MaxPermSize=384m $default_jvm_opts_common" + fi +} + +build_props_scala () { + if [[ -r "$buildProps" ]]; then + versionLine="$(grep '^build.scala.versions' "$buildProps")" + versionString="${versionLine##build.scala.versions=}" + echo "${versionString%% .*}" + fi +} + +execRunner () { + # print the arguments one to a line, quoting any containing spaces + vlog "# Executing command line:" && { + for arg; do + if [[ -n "$arg" ]]; then + if printf "%s\n" "$arg" | grep -q ' '; then + printf >&2 "\"%s\"\n" "$arg" + else + printf >&2 "%s\n" "$arg" + fi + fi + done + vlog "" + } + + [[ -n "$batch" ]] && exec /dev/null; then + curl --fail --silent --location "$url" --output "$jar" + elif which wget >/dev/null; then + wget --quiet -O "$jar" "$url" + fi + } && [[ -r "$jar" ]] +} + +acquire_sbt_jar () { + sbt_url="$(jar_url "$sbt_version")" + sbt_jar="$(jar_file "$sbt_version")" + + [[ -r "$sbt_jar" ]] || download_url "$sbt_url" "$sbt_jar" +} + +usage () { + cat < display stack traces with a max of frames (default: -1, traces suppressed) + -debug-inc enable debugging log for the incremental compiler + -no-colors disable ANSI color codes + -sbt-create start sbt even if current directory contains no sbt project + -sbt-dir path to global settings/plugins directory (default: ~/.sbt/) + -sbt-boot path to shared boot directory (default: ~/.sbt/boot in 0.11+) + -ivy path to local Ivy repository (default: ~/.ivy2) + -no-share use all local caches; no sharing + -offline put sbt in offline mode + -jvm-debug Turn on JVM debugging, open at the given port. + -batch Disable interactive mode + -prompt Set the sbt prompt; in expr, 's' is the State and 'e' is Extracted + + # sbt version (default: sbt.version from $buildProps if present, otherwise $sbt_release_version) + -sbt-force-latest force the use of the latest release of sbt: $sbt_release_version + -sbt-version use the specified version of sbt (default: $sbt_release_version) + -sbt-dev use the latest pre-release version of sbt: $sbt_unreleased_version + -sbt-jar use the specified jar as the sbt launcher + -sbt-launch-dir directory to hold sbt launchers (default: ~/.sbt/launchers) + -sbt-launch-repo repo url for downloading sbt launcher jar (default: $sbt_launch_repo) + + # scala version (default: as chosen by sbt) + -28 use $latest_28 + -29 use $latest_29 + -210 use $latest_210 + -211 use $latest_211 + -scala-home use the scala build at the specified directory + -scala-version use the specified version of scala + -binary-version use the specified scala version when searching for dependencies + + # java version (default: java from PATH, currently $(java -version 2>&1 | grep version)) + -java-home alternate JAVA_HOME + + # passing options to the jvm - note it does NOT use JAVA_OPTS due to pollution + # The default set is used if JVM_OPTS is unset and no -jvm-opts file is found + $(default_jvm_opts) + JVM_OPTS environment variable holding either the jvm args directly, or + the reference to a file containing jvm args if given path is prepended by '@' (e.g. '@/etc/jvmopts') + Note: "@"-file is overridden by local '.jvmopts' or '-jvm-opts' argument. + -jvm-opts file containing jvm args (if not given, .jvmopts in project root is used if present) + -Dkey=val pass -Dkey=val directly to the jvm + -J-X pass option -X directly to the jvm (-J is stripped) + + # passing options to sbt, OR to this runner + SBT_OPTS environment variable holding either the sbt args directly, or + the reference to a file containing sbt args if given path is prepended by '@' (e.g. '@/etc/sbtopts') + Note: "@"-file is overridden by local '.sbtopts' or '-sbt-opts' argument. + -sbt-opts file containing sbt args (if not given, .sbtopts in project root is used if present) + -S-X add -X to sbt's scalacOptions (-S is stripped) +EOM +} + +process_args () +{ + require_arg () { + local type="$1" + local opt="$2" + local arg="$3" + + if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then + die "$opt requires <$type> argument" + fi + } + while [[ $# -gt 0 ]]; do + case "$1" in + -h|-help) usage; exit 1 ;; + -v) verbose=true && shift ;; + -d) addSbt "--debug" && addSbt debug && shift ;; + -w) addSbt "--warn" && addSbt warn && shift ;; + -q) addSbt "--error" && addSbt error && shift ;; + -x) debugUs=true && shift ;; + -trace) require_arg integer "$1" "$2" && trace_level="$2" && shift 2 ;; + -ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;; + -no-colors) addJava "-Dsbt.log.noformat=true" && shift ;; + -no-share) noshare=true && shift ;; + -sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;; + -sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;; + -debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;; + -offline) addSbt "set offline := true" && shift ;; + -jvm-debug) require_arg port "$1" "$2" && addDebugger "$2" && shift 2 ;; + -batch) batch=true && shift ;; + -prompt) require_arg "expr" "$1" "$2" && setThisBuild shellPrompt "(s => { val e = Project.extract(s) ; $2 })" && shift 2 ;; + + -sbt-create) sbt_create=true && shift ;; + -sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;; + -sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;; + -sbt-force-latest) sbt_explicit_version="$sbt_release_version" && shift ;; + -sbt-dev) sbt_explicit_version="$sbt_unreleased_version" && shift ;; + -sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;; + -sbt-launch-repo) require_arg path "$1" "$2" && sbt_launch_repo="$2" && shift 2 ;; + -scala-version) require_arg version "$1" "$2" && setScalaVersion "$2" && shift 2 ;; + -binary-version) require_arg version "$1" "$2" && setThisBuild scalaBinaryVersion "\"$2\"" && shift 2 ;; + -scala-home) require_arg path "$1" "$2" && setThisBuild scalaHome "Some(file(\"$2\"))" && shift 2 ;; + -java-home) require_arg path "$1" "$2" && setJavaHome "$2" && shift 2 ;; + -sbt-opts) require_arg path "$1" "$2" && sbt_opts_file="$2" && shift 2 ;; + -jvm-opts) require_arg path "$1" "$2" && jvm_opts_file="$2" && shift 2 ;; + + -D*) addJava "$1" && shift ;; + -J*) addJava "${1:2}" && shift ;; + -S*) addScalac "${1:2}" && shift ;; + -28) setScalaVersion "$latest_28" && shift ;; + -29) setScalaVersion "$latest_29" && shift ;; + -210) setScalaVersion "$latest_210" && shift ;; + -211) setScalaVersion "$latest_211" && shift ;; + + --debug) addSbt debug && addResidual "$1" && shift ;; + --warn) addSbt warn && addResidual "$1" && shift ;; + --error) addSbt error && addResidual "$1" && shift ;; + *) addResidual "$1" && shift ;; + esac + done +} + +# process the direct command line arguments +process_args "$@" + +# skip #-styled comments and blank lines +readConfigFile() { + while read line; do + [[ $line =~ ^# ]] || [[ -z $line ]] || echo "$line" + done < "$1" +} + +# if there are file/environment sbt_opts, process again so we +# can supply args to this runner +if [[ -r "$sbt_opts_file" ]]; then + vlog "Using sbt options defined in file $sbt_opts_file" + while read opt; do extra_sbt_opts+=("$opt"); done < <(readConfigFile "$sbt_opts_file") +elif [[ -n "$SBT_OPTS" && ! ("$SBT_OPTS" =~ ^@.*) ]]; then + vlog "Using sbt options defined in variable \$SBT_OPTS" + extra_sbt_opts=( $SBT_OPTS ) +else + vlog "No extra sbt options have been defined" +fi + +[[ -n "${extra_sbt_opts[*]}" ]] && process_args "${extra_sbt_opts[@]}" + +# reset "$@" to the residual args +set -- "${residual_args[@]}" +argumentCount=$# + +# set sbt version +set_sbt_version + +# only exists in 0.12+ +setTraceLevel() { + case "$sbt_version" in + "0.7."* | "0.10."* | "0.11."* ) echoerr "Cannot set trace level in sbt version $sbt_version" ;; + *) setThisBuild traceLevel $trace_level ;; + esac +} + +# set scalacOptions if we were given any -S opts +[[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[@]}\"" + +# Update build.properties on disk to set explicit version - sbt gives us no choice +[[ -n "$sbt_explicit_version" ]] && update_build_props_sbt "$sbt_explicit_version" +vlog "Detected sbt version $sbt_version" + +[[ -n "$scala_version" ]] && vlog "Overriding scala version to $scala_version" + +# no args - alert them there's stuff in here +(( argumentCount > 0 )) || { + vlog "Starting $script_name: invoke with -help for other options" + residual_args=( shell ) +} + +# verify this is an sbt dir or -create was given +[[ -r ./build.sbt || -d ./project || -n "$sbt_create" ]] || { + cat <