aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKseniya Tomskikh <ktomskikh@driver.xyz>2018-10-17 17:02:58 +0800
committerKseniya Tomskikh <ktomskikh@driver.xyz>2018-10-17 17:02:58 +0800
commit95c3aeecd7e6ad04ce8d216c09e779f5ca38aa6a (patch)
treedfc94f20d00c84f9dde120f065bfc9298bdff0dc
parentf5d0b038457ed9d01687f0949e22e08a6b116066 (diff)
parenta43556851bf986b81351fc9f1ae5ba51bf21dc47 (diff)
downloaddriver-core-kseniya/typized-id.tar.gz
driver-core-kseniya/typized-id.tar.bz2
driver-core-kseniya/typized-id.zip
Merge branch 'master' into kseniya/typized-idkseniya/typized-id
-rw-r--r--.travis.yml2
-rw-r--r--CHANGELOG.md129
-rw-r--r--README.md268
-rw-r--r--build.sbt165
-rw-r--r--core-database/src/main/scala/xyz/driver/core/database/Converters.scala (renamed from src/main/scala/xyz/driver/core/database/Converters.scala)2
-rw-r--r--core-database/src/main/scala/xyz/driver/core/database/PatchedHsqldbProfile.scala (renamed from src/main/scala/xyz/driver/core/database/PatchedHsqldbProfile.scala)0
-rw-r--r--core-database/src/main/scala/xyz/driver/core/database/Repository.scala (renamed from src/main/scala/xyz/driver/core/database/Repository.scala)0
-rw-r--r--core-database/src/main/scala/xyz/driver/core/database/SlickGetResultSupport.scala (renamed from src/main/scala/xyz/driver/core/database/SlickGetResultSupport.scala)0
-rw-r--r--core-database/src/main/scala/xyz/driver/core/database/database.scala (renamed from src/main/scala/xyz/driver/core/database/database.scala)0
-rw-r--r--core-database/src/main/scala/xyz/driver/core/database/package.scala (renamed from src/main/scala/xyz/driver/core/database/package.scala)0
-rw-r--r--core-database/src/test/scala/xyz/driver/core/database/DatabaseTest.scala (renamed from src/test/scala/xyz/driver/core/database/DatabaseTest.scala)2
-rw-r--r--core-init/src/main/resources/deployed-logback.xml (renamed from src/main/resources/deployed-logback.xml)0
-rw-r--r--core-init/src/main/resources/logback-test.xml (renamed from src/main/resources/logback-test.xml)0
-rw-r--r--core-init/src/main/resources/logback.xml (renamed from src/main/resources/logback.xml)0
-rw-r--r--core-init/src/main/resources/reference.conf26
-rw-r--r--core-init/src/main/scala/xyz/driver/core/init/AkkaBootable.scala (renamed from src/main/scala/xyz/driver/core/init/AkkaBootable.scala)2
-rw-r--r--core-init/src/main/scala/xyz/driver/core/init/BuildInfoReflection.scala (renamed from src/main/scala/xyz/driver/core/init/BuildInfoReflection.scala)2
-rw-r--r--core-init/src/main/scala/xyz/driver/core/init/CloudServices.scala (renamed from src/main/scala/xyz/driver/core/init/CloudServices.scala)14
-rw-r--r--core-init/src/main/scala/xyz/driver/core/init/HttpApi.scala (renamed from src/main/scala/xyz/driver/core/init/HttpApi.scala)2
-rw-r--r--core-init/src/main/scala/xyz/driver/core/init/Platform.scala68
-rw-r--r--core-init/src/main/scala/xyz/driver/core/init/ProtobufApi.scala (renamed from src/main/scala/xyz/driver/core/init/ProtobufApi.scala)0
-rw-r--r--core-init/src/main/scala/xyz/driver/core/init/SimpleHttpApp.scala (renamed from src/main/scala/xyz/driver/core/init/SimpleHttpApp.scala)0
-rw-r--r--core-messaging/src/main/scala/xyz/driver/core/messaging/AliyunBus.scala (renamed from src/main/scala/xyz/driver/core/messaging/AliyunBus.scala)32
-rw-r--r--core-messaging/src/main/scala/xyz/driver/core/messaging/Bus.scala (renamed from src/main/scala/xyz/driver/core/messaging/Bus.scala)0
-rw-r--r--core-messaging/src/main/scala/xyz/driver/core/messaging/CreateOnDemand.scala (renamed from src/main/scala/xyz/driver/core/messaging/CreateOnDemand.scala)0
-rw-r--r--core-messaging/src/main/scala/xyz/driver/core/messaging/GoogleBus.scala (renamed from src/main/scala/xyz/driver/core/messaging/GoogleBus.scala)0
-rw-r--r--core-messaging/src/main/scala/xyz/driver/core/messaging/QueueBus.scala (renamed from src/main/scala/xyz/driver/core/messaging/QueueBus.scala)0
-rw-r--r--core-messaging/src/main/scala/xyz/driver/core/messaging/StreamBus.scala (renamed from src/main/scala/xyz/driver/core/messaging/StreamBus.scala)2
-rw-r--r--core-messaging/src/main/scala/xyz/driver/core/messaging/Topic.scala (renamed from src/main/scala/xyz/driver/core/messaging/Topic.scala)0
-rw-r--r--core-messaging/src/test/scala/xyz/driver/core/messaging/QueueBusTest.scala (renamed from src/test/scala/xyz/driver/core/messaging/QueueBusTest.scala)0
-rw-r--r--core-reporting/src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala (renamed from src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala)0
-rw-r--r--core-reporting/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala (renamed from src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala)0
-rw-r--r--core-reporting/src/main/scala/xyz/driver/core/reporting/NoReporter.scala (renamed from src/main/scala/xyz/driver/core/reporting/NoReporter.scala)0
-rw-r--r--core-reporting/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala (renamed from src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala)0
-rw-r--r--core-reporting/src/main/scala/xyz/driver/core/reporting/Reporter.scala (renamed from src/main/scala/xyz/driver/core/reporting/Reporter.scala)0
-rw-r--r--core-reporting/src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala (renamed from src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala)0
-rw-r--r--core-reporting/src/main/scala/xyz/driver/core/reporting/SpanContext.scala (renamed from src/main/scala/xyz/driver/core/reporting/SpanContext.scala)0
-rw-r--r--core-rest/src/main/resources/reference.conf (renamed from src/main/resources/reference.conf)27
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/README.md (renamed from src/main/resources/swagger-ui-dist/README.md)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/absolute-path.js (renamed from src/main/resources/swagger-ui-dist/absolute-path.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/favicon-16x16.png (renamed from src/main/resources/swagger-ui-dist/favicon-16x16.png)bin445 -> 445 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/favicon-32x32.png (renamed from src/main/resources/swagger-ui-dist/favicon-32x32.png)bin1141 -> 1141 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/index.html (renamed from src/main/resources/swagger-ui-dist/index.html)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/index.js (renamed from src/main/resources/swagger-ui-dist/index.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/oauth2-redirect.html (renamed from src/main/resources/swagger-ui-dist/oauth2-redirect.html)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/package.json (renamed from src/main/resources/swagger-ui-dist/package.json)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js (renamed from src/main/resources/swagger-ui-dist/swagger-ui-bundle.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js.map (renamed from src/main/resources/swagger-ui-dist/swagger-ui-bundle.js.map)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js (renamed from src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js.map (renamed from src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js.map)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/swagger-ui.css (renamed from src/main/resources/swagger-ui-dist/swagger-ui.css)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/swagger-ui.css.map (renamed from src/main/resources/swagger-ui-dist/swagger-ui.css.map)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/swagger-ui.js (renamed from src/main/resources/swagger-ui-dist/swagger-ui.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui-dist/swagger-ui.js.map (renamed from src/main/resources/swagger-ui-dist/swagger-ui.js.map)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/.gitattributes (renamed from src/main/resources/swagger-ui/.gitattributes)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/css/print.css (renamed from src/main/resources/swagger-ui/css/print.css)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/css/reset.css (renamed from src/main/resources/swagger-ui/css/reset.css)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/css/screen.css (renamed from src/main/resources/swagger-ui/css/screen.css)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/css/typography.css (renamed from src/main/resources/swagger-ui/css/typography.css)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.eot (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.eot)bin22922 -> 22922 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.svg (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.svg)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.ttf (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.ttf)bin40513 -> 40513 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff)bin25992 -> 25992 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff2 (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff2)bin11480 -> 11480 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.eot (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.eot)bin22008 -> 22008 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.svg (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.svg)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf)bin39069 -> 39069 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff)bin24868 -> 24868 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2 (renamed from src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2)bin11304 -> 11304 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/images/explorer_icons.png (renamed from src/main/resources/swagger-ui/images/explorer_icons.png)bin5763 -> 5763 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/images/favicon-16x16.png (renamed from src/main/resources/swagger-ui/images/favicon-16x16.png)bin645 -> 645 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/images/favicon-32x32.png (renamed from src/main/resources/swagger-ui/images/favicon-32x32.png)bin1654 -> 1654 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/images/favicon.ico (renamed from src/main/resources/swagger-ui/images/favicon.ico)bin5430 -> 5430 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/images/logo_small.png (renamed from src/main/resources/swagger-ui/images/logo_small.png)bin770 -> 770 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/images/pet_store_api.png (renamed from src/main/resources/swagger-ui/images/pet_store_api.png)bin824 -> 824 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/images/throbber.gif (renamed from src/main/resources/swagger-ui/images/throbber.gif)bin9257 -> 9257 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/images/wordnik_api.png (renamed from src/main/resources/swagger-ui/images/wordnik_api.png)bin980 -> 980 bytes
-rw-r--r--core-rest/src/main/resources/swagger-ui/index.html (renamed from src/main/resources/swagger-ui/index.html)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/backbone-min.js (renamed from src/main/resources/swagger-ui/lib/backbone-min.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/handlebars-2.0.0.js (renamed from src/main/resources/swagger-ui/lib/handlebars-2.0.0.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/highlight.7.3.pack.js (renamed from src/main/resources/swagger-ui/lib/highlight.7.3.pack.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/jquery-1.8.0.min.js (renamed from src/main/resources/swagger-ui/lib/jquery-1.8.0.min.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/jquery.ba-bbq.min.js (renamed from src/main/resources/swagger-ui/lib/jquery.ba-bbq.min.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/jquery.slideto.min.js (renamed from src/main/resources/swagger-ui/lib/jquery.slideto.min.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/jquery.wiggle.min.js (renamed from src/main/resources/swagger-ui/lib/jquery.wiggle.min.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/marked.js (renamed from src/main/resources/swagger-ui/lib/marked.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/swagger-oauth.js (renamed from src/main/resources/swagger-ui/lib/swagger-oauth.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/underscore-min.js (renamed from src/main/resources/swagger-ui/lib/underscore-min.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/lib/underscore-min.map (renamed from src/main/resources/swagger-ui/lib/underscore-min.map)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/o2c.html (renamed from src/main/resources/swagger-ui/o2c.html)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/swagger-ui.js (renamed from src/main/resources/swagger-ui/swagger-ui.js)0
-rw-r--r--core-rest/src/main/resources/swagger-ui/swagger-ui.min.js (renamed from src/main/resources/swagger-ui/swagger-ui.min.js)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/auth.scala (renamed from src/main/scala/xyz/driver/core/auth.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/generators.scala (renamed from src/main/scala/xyz/driver/core/generators.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/json.scala (renamed from src/main/scala/xyz/driver/core/json.scala)18
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/DnsDiscovery.scala (renamed from src/main/scala/xyz/driver/core/rest/DnsDiscovery.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/DriverRoute.scala (renamed from src/main/scala/xyz/driver/core/rest/DriverRoute.scala)3
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/HttpRestServiceTransport.scala (renamed from src/main/scala/xyz/driver/core/rest/HttpRestServiceTransport.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/PatchDirectives.scala (renamed from src/main/scala/xyz/driver/core/rest/PatchDirectives.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/PooledHttpClient.scala (renamed from src/main/scala/xyz/driver/core/rest/PooledHttpClient.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/ProxyRoute.scala (renamed from src/main/scala/xyz/driver/core/rest/ProxyRoute.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/RestService.scala (renamed from src/main/scala/xyz/driver/core/rest/RestService.scala)6
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/ServiceDescriptor.scala (renamed from src/main/scala/xyz/driver/core/rest/ServiceDescriptor.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/SingleRequestHttpClient.scala (renamed from src/main/scala/xyz/driver/core/rest/SingleRequestHttpClient.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/Swagger.scala (renamed from src/main/scala/xyz/driver/core/rest/Swagger.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/auth/AlwaysAllowAuthorization.scala (renamed from src/main/scala/xyz/driver/core/rest/auth/AlwaysAllowAuthorization.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/auth/AuthProvider.scala (renamed from src/main/scala/xyz/driver/core/rest/auth/AuthProvider.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/auth/Authorization.scala (renamed from src/main/scala/xyz/driver/core/rest/auth/Authorization.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/auth/AuthorizationResult.scala (renamed from src/main/scala/xyz/driver/core/rest/auth/AuthorizationResult.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/auth/CachedTokenAuthorization.scala (renamed from src/main/scala/xyz/driver/core/rest/auth/CachedTokenAuthorization.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/auth/ChainedAuthorization.scala (renamed from src/main/scala/xyz/driver/core/rest/auth/ChainedAuthorization.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala (renamed from src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala (renamed from src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/directives/Directives.scala (renamed from src/main/scala/xyz/driver/core/rest/directives/Directives.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala (renamed from src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala)12
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala (renamed from src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/domain.scala57
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/errors/serviceException.scala (renamed from src/main/scala/xyz/driver/core/rest/errors/serviceException.scala)3
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/headers/Traceparent.scala (renamed from src/main/scala/xyz/driver/core/rest/headers/Traceparent.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/package.scala (renamed from src/main/scala/xyz/driver/core/rest/package.scala)62
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/serviceDiscovery.scala (renamed from src/main/scala/xyz/driver/core/rest/serviceDiscovery.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/rest/serviceRequestContext.scala (renamed from src/main/scala/xyz/driver/core/rest/serviceRequestContext.scala)0
-rw-r--r--core-rest/src/main/scala/xyz/driver/core/swagger.scala (renamed from src/main/scala/xyz/driver/core/swagger.scala)0
-rw-r--r--core-rest/src/test/scala/xyz/driver/core/AuthTest.scala (renamed from src/test/scala/xyz/driver/core/AuthTest.scala)5
-rw-r--r--core-rest/src/test/scala/xyz/driver/core/GeneratorsTest.scala (renamed from src/test/scala/xyz/driver/core/GeneratorsTest.scala)0
-rw-r--r--core-rest/src/test/scala/xyz/driver/core/JsonTest.scala (renamed from src/test/scala/xyz/driver/core/JsonTest.scala)15
-rw-r--r--core-rest/src/test/scala/xyz/driver/core/TestTypes.scala (renamed from src/test/scala/xyz/driver/core/TestTypes.scala)0
-rw-r--r--core-rest/src/test/scala/xyz/driver/core/rest/DriverRouteTest.scala (renamed from src/test/scala/xyz/driver/core/rest/DriverRouteTest.scala)15
-rw-r--r--core-rest/src/test/scala/xyz/driver/core/rest/PatchDirectivesTest.scala (renamed from src/test/scala/xyz/driver/core/rest/PatchDirectivesTest.scala)0
-rw-r--r--core-rest/src/test/scala/xyz/driver/core/rest/RestTest.scala (renamed from src/test/scala/xyz/driver/core/rest/RestTest.scala)0
-rw-r--r--core-storage/src/main/scala/xyz/driver/core/storage/AliyunBlobStorage.scala (renamed from src/main/scala/xyz/driver/core/storage/AliyunBlobStorage.scala)19
-rw-r--r--core-storage/src/main/scala/xyz/driver/core/storage/BlobStorage.scala (renamed from src/main/scala/xyz/driver/core/storage/BlobStorage.scala)0
-rw-r--r--core-storage/src/main/scala/xyz/driver/core/storage/FileSystemBlobStorage.scala (renamed from src/main/scala/xyz/driver/core/storage/FileSystemBlobStorage.scala)0
-rw-r--r--core-storage/src/main/scala/xyz/driver/core/storage/GcsBlobStorage.scala (renamed from src/main/scala/xyz/driver/core/storage/GcsBlobStorage.scala)0
-rw-r--r--core-storage/src/main/scala/xyz/driver/core/storage/channelStreams.scala (renamed from src/main/scala/xyz/driver/core/storage/channelStreams.scala)0
-rw-r--r--core-storage/src/test/scala/xyz/driver/core/BlobStorageTest.scala (renamed from src/test/scala/xyz/driver/core/BlobStorageTest.scala)0
-rw-r--r--core-testkit/src/main/scala/akka/http/scaladsl/testkit/TestRouteServiceTransport.scala79
-rw-r--r--core-testkit/src/main/scala/xyz/driver/core/testkit/AsyncDatabaseBackedRouteTest.scala28
-rw-r--r--core-testkit/src/main/scala/xyz/driver/core/testkit/DriverFunctionalTest.scala18
-rw-r--r--core-testkit/src/main/scala/xyz/driver/core/testkit/FixtureDatabase.scala60
-rw-r--r--core-testkit/src/main/scala/xyz/driver/core/testkit/RestDatabaseResetService.scala70
-rw-r--r--core-testkit/src/main/scala/xyz/driver/core/testkit/hsql/HsqlTestDatabase.scala26
-rw-r--r--core-testkit/src/main/scala/xyz/driver/core/testkit/postgres/DockerPostgresDatabase.scala46
-rw-r--r--core-testkit/src/main/scala/xyz/driver/core/testkit/postgres/DockerPostgresFixtureDatabase.scala74
-rw-r--r--core-testkit/src/main/scala/xyz/driver/test/renames.scala46
-rw-r--r--core-types/src/main/scala/xyz/driver/core/DatabaseException.scala3
-rw-r--r--core-types/src/main/scala/xyz/driver/core/core.scala (renamed from src/main/scala/xyz/driver/core/core.scala)10
-rw-r--r--core-types/src/main/scala/xyz/driver/core/date.scala (renamed from src/main/scala/xyz/driver/core/date.scala)0
-rw-r--r--core-types/src/main/scala/xyz/driver/core/deprecations.scala5
-rw-r--r--core-types/src/main/scala/xyz/driver/core/domain.scala73
-rw-r--r--core-types/src/main/scala/xyz/driver/core/generators.scala143
-rw-r--r--core-types/src/main/scala/xyz/driver/core/tagging/tagging.scala (renamed from src/main/scala/xyz/driver/core/tagging/tagging.scala)0
-rw-r--r--core-types/src/main/scala/xyz/driver/core/time.scala (renamed from src/main/scala/xyz/driver/core/time.scala)0
-rw-r--r--core-types/src/test/scala/xyz/driver/core/CoreTest.scala (renamed from src/test/scala/xyz/driver/core/CoreTest.scala)0
-rw-r--r--core-types/src/test/scala/xyz/driver/core/DateTest.scala (renamed from src/test/scala/xyz/driver/core/DateTest.scala)0
-rw-r--r--core-types/src/test/scala/xyz/driver/core/PhoneNumberTest.scala (renamed from src/test/scala/xyz/driver/core/PhoneNumberTest.scala)38
-rw-r--r--core-types/src/test/scala/xyz/driver/core/TimeTest.scala (renamed from src/test/scala/xyz/driver/core/TimeTest.scala)0
-rw-r--r--core-types/src/test/scala/xyz/driver/core/tagging/TaggingTest.scala (renamed from src/test/scala/xyz/driver/core/tagging/TaggingTest.scala)0
-rw-r--r--core-util/src/main/scala/xyz/driver/core/Refresh.scala (renamed from src/main/scala/xyz/driver/core/Refresh.scala)0
-rw-r--r--documentation/components.dot21
-rw-r--r--documentation/components.svg133
-rw-r--r--documentation/example/.gitignore1
-rw-r--r--documentation/example/build.sbt6
-rw-r--r--documentation/example/project/build.properties1
-rw-r--r--documentation/example/project/plugins.sbt1
-rw-r--r--documentation/example/src/main/scala/example/Main.scala21
-rw-r--r--project/MiMaSettings.scala28
-rw-r--r--project/build.properties2
-rw-r--r--project/plugins.sbt3
-rw-r--r--src/main/scala/xyz/driver/core/database/MdcAsyncExecutor.scala53
-rw-r--r--src/main/scala/xyz/driver/core/domain.scala40
-rw-r--r--src/main/scala/xyz/driver/core/init/Platform.scala31
-rw-r--r--src/test/scala/xyz/driver/core/DriverAppTest.scala (renamed from src/test/scala/xyz/driver/core/rest/DriverAppTest.scala)2
173 files changed, 1504 insertions, 516 deletions
diff --git a/.travis.yml b/.travis.yml
index 7845eae..5ab8328 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,5 +8,5 @@ scala:
script:
- echo 'credentials += Credentials("Artifactory Realm", "drivergrp.jfrog.io", "sbt-publisher", sys.env("ARTIFACTORY_PASSWORD"))' > project/credentials.sbt
- - sbt clean +test
+ - sbt clean compile checkAbi +test
- if [[ "$TRAVIS_PULL_REQUEST" == "false" && "$TRAVIS_TAG" =~ ^v[0-9].* ]]; then sbt publish; fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..313e712
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,129 @@
+# UNRELEASED
+
+This release represents a major version bump: many new features have
+been added and large parts of the codebase have been refactored.
+Although a lot of effort has gone into keeping API changes minimal,
+this release is unfortunately not source or binary compatible with the
+1.x versions.
+
+The following is a list of major changes. Please see the git commit
+log `git log 1.x..v2.0.0` for details.
+
+## Structural Changes
+
+The core project has been split into many modules, as described in the
+README, each having distinct functionality.
+
+In a nutshell, packages in `xyz.driver.core` have been moved to
+separate sbt projects. For example, all classes contained in
+`xyz.driver.core.messaging` have been moved to the project
+`core-messaging`, and `xyz.driver.core.storage` to
+`core-storage`. Thus, the modules providing utilities from the latest
+1.x version are:
+
+- `core-database`
+- `core-rest`
+- `core-messaging`
+- `core-storage`
+
+Along those functional modules, there are the supporting modules:
+
+- `core-types`, which contains common types, such as `time`, `date`
+ and IDs
+- `core`, which contains the `app` initialization system and also
+ depends on all other modules, acting as an indirection for
+ backwards-compatibility.
+
+Furthermore, some new modules have been added which will be explained
+later on:
+
+- `core-reporting` (tracing and logging utilities)
+- `core-init` (new initialization framework)
+- `core-testkit` (core-specific parts of driver-test-utils)
+
+The split was done in order to reduce coupling between components. Our
+internal domain-model now only depends on `core-rest` and
+`core-messaging`. Services can choose if and what other parts of core
+to use, although it is recommended they use the `core-init` module.
+
+Although the split was mostly seemless, some cyclic dependencies
+between packages were discovered. Since these are no longer possible
+in a multi-project architecture, some source-breaking changes also had
+to made. These will be listed later and marked as such.
+
+## New Features
+
+### Reporting Utilities
+
+A suite of tracing and logging utilities, based on the OpenTrace
+specification, have been added as the `core-reporting` module. This
+module plays a central role because it greatly simplifies debugging
+distributed applications, and as such is depended upon by all other
+modules.
+
+It embraces the "implicit context" pattern, and requires a
+`SpanContext` for all logging actions. This context can be thought of
+as a more type-safe "Message Diagnostic Context" and also replaces the
+slf4j-based MDC utilities in previous versions of core.
+
+### Typeclass-based service discovery
+
+A type-class based service discovery has been added in
+`xyz.driver.rest.DnsDiscovery`. It allows seemless instantiation of
+client implementations that provide a "Service Descriptor" type
+class. It also does not require any configuration (except for
+overrides) and uses the DNS to resolve service hosts.
+
+### Initialization Framework
+
+A trait-based initialization framework has been added in
+`core-init`. It implements common application startup procedures and
+initializes common utilities for cloud-based web services. It
+integrates all other core modules in an idiomatic way.
+
+## API-Breaking Changes
+
+Due to the structural changes mentioned previously, some API-breaking
+changes were made:
+
+- `xyz.driver.rest.errors.DatabaseException` has been moved to
+ `xyz.driver.core.DatabaseException`, into the project
+ `core-types`. This move happened because both the `rest` and
+ `database` module require this exception.
+
+- `xyz.driver.core.FutureExtensions` has been moved to
+ `xyz.driver.core.rest` as it only contained logic that dealt with
+ service exceptions, something that belongs into `core-rest` and must
+ not be depended upon by `core-types`.
+
+- all classes but `AuthStub` in `xyz.driver.test` have been moved to
+ `xyz.driver.core.testkit` and are available in th
+ `core-testkit`. *(`AuthStub` has been moved to
+ `xyz.driver.users.testkit` in the project `users-testkit` in the
+ users service repo)*
+
+## Deprecation Notices
+
+Several major features have been deprecated and will be removed in the future.
+
+- `xyz.driver.core.app` will stop receiving updates as `xyz.driver.core.init` matures
+
+- `xyz.driver.core.time` and `xyz.driver.core.date` will be replaced
+ by their java 8 counterparts
+
+- the initial object-storage utilities in `xyz.driver.core.file` will
+ be removed in favor of the ones provided in `core-storage`
+
+- the first version of the message bus api `xyz.driver.core.pubsub`
+ will be replaced by `core-messaging`
+
+- `xyz.driver.driver.logging` will be removed as it is made obsolete
+ by `core-reporting`
+
+Deprecation of other classes in the [`core` support
+module](src/main/scala/xyz/driver/core) is yet to be
+determined. Please open an issue if you have any comments on them.
+
+# Version 1.x
+
+Please see git commit logs for changes prior to version 2.
diff --git a/README.md b/README.md
index a2d3649..9edbfe1 100644
--- a/README.md
+++ b/README.md
@@ -7,227 +7,119 @@ cherry-picked onto the [1.x
branch](https://github.com/drivergroup/driver-core/tree/1.x).
----
+[![Build Status](https://travis-ci.com/drivergroup/driver-core.svg?token=S4oyfBY3YoEdLmckujJx&branch=master)](https://travis-ci.com/drivergroup/driver-core)
-# Driver Core Library for Scala Services [![Build Status](https://travis-ci.com/drivergroup/driver-core.svg?token=S4oyfBY3YoEdLmckujJx&branch=master)](https://travis-ci.com/drivergroup/driver-core)
+# Driver Core Library for Scala Services
Multi-cloud utilities and application initialization framework.
This library offers many common utilities for building applications
-that run on multiple environments, including Google Cloud, Ali Cloud,
+that run in multiple environments, including Google Cloud, Ali Cloud,
and of course on development machines.
+## Highlights
-# Overview
+- Cloud agnostic.
-*This section applies to the 1.x series of core. The current development master branch may include changes not described here.*
+- Sensible defaults. *Applications that use the initialization
+ framework can run out-of-the box on cloud or locally, with minimal
+ config required.*
-Core library is used to provide ways to implement practices established in [Driver service template](http://github.com/drivergroup/driver-template) (check its [README.md](https://github.com/drivergroup/driver-template/blob/master/README.md)).
+- Distributed tracing built into all major utilities. *Significant
+ actions are logged and reported automatically to an OpenTrace
+ aggregator.*
-## Components
+- Extensible. *Utilities are built to be used standalone. A
+ trait-based initialization framework provides utility instances for
+ common use-cases.*
- * `core package` provides `Id` and `Name` implementations (with equal and ordering), utils for ScalaZ `OptionT`, and also `make` and `using` functions and `@@` (tagged) type,
- * `tagging` Utilities for tagging primitive types for extra type safety, as well as some tags that involve extra transformation upon deserializing with spray,
- * `config` Contains method `loadDefaultConfig` with default way of providing config to the application,
- * `domain` Common generic domain objects, e.g., `Email` and `PhoneNumber`,
- * `messages` Localization messages supporting different locales and methods to read from config,
- * `database` Method for database initialization from config, `Id`, `Name`, `Time`, `Date` etc. mapping, schema lifecycle and base classes to implement and test `Repository` (data access objects),
- * `rest` Wrapper over call to external REST API, authorization, context headers, XSS protection, does logging and allows to add Swagger to a service,
- * `auth` Basic entities for authentication and authorization: `User`, `Role` `Permission` `AuthToken`, `AuthCredentials` etc.,
- * `swagger` Contains custom `AbstractModelConverter` to customize Swagger JSON with any Scala JSON formats created by, for instance, Spray JSON,
- * `json` Json formats for `Id`, `Name`, `Time`, `Revision`, `Email`, `PhoneNumber`, `AuthCredentials` and converters for GADTs, enums and value classes,
- * `file` Interface `FileStorage` and file storage implementations with GCS, S3 and local FS,
- * `app` Base class for Driver service, which initializes swagger, app modules and its routes.
- * `generators` Set of functions to prototype APIs. Combines with `faker` package,
- * `stats` Methods to collect system stats: memory, cpu, gc, file system space usage,
- * `logging` Custom Driver logging layout (not finished yet).
+## Overview
-Dependencies of core modules might be found in [Dependencies of the Modules diagram](https://github.com/drivergroup/driver-template/blob/master/Modules%20dependencies.pdf) file in driver-template repository in "Core component dependencies" section.
+### Components
-## Examples
+This project is split into multiple submodules, each providing
+specific functionality.
-### Functions `make` and `using`
-Those functions are especially useful to make procedural legacy Java APIs more functional and make scope of its usage more explicit. Runnable examples of its usage might be found in [`CoreTest`](https://github.com/drivergroup/driver-core/blob/master/src/test/scala/xyz/driver/core/CoreTest.scala), e.g.,
+Project | Description
+-----------------|------------
+core | *(deprecated)* Previous application initialization framework.
+core&#8209;database | Utilities for working with databases, slick in particular.
+core&#8209;init | Mini-framework that offers default instances of many utilities, configured for the current platform.
+core&#8209;messaging | Library for abstracting over message buses such as Google PubSub.
+core&#8209;reporting | Combined tracing and logging library.
+core&#8209;rest | Abstractions to represent RESTful services, discovery and client implementations.
+core&#8209;storage | Object storage utilities.
+core&#8209;testkit | Mixins for scalatest.
+core&#8209;types | Type definitions that are commonly used by applications.
+core&#8209;util | Other utilities that do not belong anywhere else. **Note that this is a staging place for code that does not have its own submodule yet. Do not depend on it externally!**
- useObject(make(new ObjectWithProceduralInitialization) { o =>
- o.setSetting1(...) // returns Unit
- o.setSetting2(...) // returns Unit
- o.setSetting3(...) // returns Unit
- })
+These components and their internal dependencies can be represented
+with the following graph.
- // instead of
- val o = new ObjectWithProceduralInitialization
- o.setSetting1(...)
- o.setSetting2(...)
- o.setSetting3(...)
+![Inter-Component Dependencies](documentation/components.svg)
- useObject(o)
+### Dependency
-and
-
- using(... open output stream ...) { os =>
- os.write(...)
- }
-
- // it will be close automatically
-
-### `OptionT` utils
-Before
-
-```
-OptionT.optionT[Future](service.getRecords(id).map(Option.apply))
-OptionT.optionT(service.doSomething(id).map(_ => Option(())))
-
-// Do not want to stop and return `None`, if `produceEffect` returns `None`
-for {
- x <- service.doSomething(id)
- _ <- service.produceEffect(id, x).map(_ => ()).orElse(OptionT.some[Future, Unit](())))
-} yield x
+All components are published together under the same version ([latest
+version](https://github.com/drivergroup/driver-core/releases)).
+```sbt
+libraryDependencies += "xyz.driver" %% "core-<component>" % "<version>"
```
-after
+### External dependencies
-```
-service.getRecords(id).toOptionT
-service.doSomething(id).toUnitOptionT
-
-// Do not want to stop and return `None` if `produceEffect` returns `None`
-for {
- x <- service.doSomething(id)
- _ <- service.produceEffect(id, x).continueIgnoringNone
-} yield x
-```
+This project has quite a few external dependencies. See
+[build.sbt](build.sbt) for a list of them. Some notable Scala
+libraries used are:
-### `@@` or Tagged types
+- [akka-http](https://doc.akka.io/docs/akka-http/current/)
+- [akka-stream](https://doc.akka.io/docs/akka/current/stream/)
+- [enumeratum](https://github.com/lloydmeta/enumeratum#enumeratum------)
+- [scala-async](https://github.com/scala/scala-async)
+- [slick](http://slick.lightbend.com/)
+- [spray-json](https://github.com/spray/spray-json)
+- [sttp](https://github.com/softwaremill/sttp)
-For type definitions, the only import required is
+## Example Usage
+The following implements a simple key-value store that uses object
+storage ("buckets") when running in the cloud and a local filesystem
+when running on a development machine.
```scala
-import xyz.driver.core.@@
-```
-
-which provides just the ability to tag types: `val value: String @@ Important`. Two `String`s with different tags will
-be distinguished by the compiler, helping reduce the possibility of mixing values passed into methods with several
-arguments of identical types.
-
-To work with tags in actual values, use the following convenience methods:
-
-```scala
-import xyz.driver.core.tagging._
-
-val str = "abc".tagged[Important]
-```
-
-or go back to plain (say, in case you have an implicit for untagged value)
-
-```scala
-// val trimmedExternalId: String @@ Trimmed = "123"
-
-Trials.filter(_.externalId === trimmedExternalId.untag)
-```
-
-### `Time` and `TimeProvider`
-
-**NOTE: The contents of this section has been deprecated - use java.time.Clock instead**
-
-Usage examples for `Time` (also check [TimeTest](https://github.com/drivergroup/driver-core/blob/master/src/test/scala/xyz/driver/core/TimeTest.scala) for more examples).
-
- Time(234L).isAfter(Time(123L))
-
- Time(123L).isBefore(Time(234L))
-
- Time(123L).advanceBy(4 days)
-
- Seq(Time(321L), Time(123L), Time(231L)).sorted
-
- startOfMonth(Time(1468937089834L)) should be (Time(1467381889834L))
-
- textualDate(Time(1468937089834L)) should be ("July 19, 2016")
+import xyz.driver.core.init
- textualTime(Time(1468937089834L)) should be ("Jul 19, 2016 10:04:49 AM")
+object Main extends init.SimpleHttpApp {
- TimeRange(Time(321L), Time(432L)).duration should be(111.milliseconds)
+ lazy val fs = storage("data")
-
-### Generators
-Example of how to generate a case class instance,
-
- import com.drivergrp.core._
-
- Consumer(
- generators.nextId[Consumer],
- Name[Consumer](faker.Name.name),
- faker.Lorem.sentence(word_count = 10))
-
-
-For more examples check [project tests](https://github.com/drivergroup/driver-core/tree/master/src/test/scala/xyz/driver/core) or [service template](http://github.com/drivergroup/driver-template) repository.
-
-### App
-
-To start a new application using standard Driver application class, follow this pattern:
-
- object MyApp extends App {
-
- new DriverApp(BuildInfo.version,
- BuildInfo.gitHeadCommit.getOrElse("None"),
- modules = Seq(myModule1, myModule2),
- time, log, config,
- interface = "::0", baseUrl, scheme, port)
- (servicesActorSystem, servicesExecutionContext).run()
- }
-
-### REST
-With REST utils, for instance, you can use the following directives in [akka-http](https://github.com/akka/akka-http) routes, as follows
-
- sanitizeRequestEntity { // Prevents XSS
- serviceContext { implicit ctx => // Extracts context headers from request
- authorize(CanSeeUser(userId)) { user => // Authorizes and extracts user
- // Your code using `ctx` and `user`
- }
+ override def applicationRoute = path("data" / Segment) { key =>
+ post {
+ entity(as[Array[Byte]]) { value =>
+ complete(fs.uploadContent(key, value))
+ }
+ } ~ get {
+ rejectEmptyResponse{
+ complete(fs.content(key))
}
}
+ }
-### Swagger
-Swagger JSON formats built using reflection can be overriden by using `CustomSwaggerJsonConverter` at the start of your application initialization in the following way:
-
- ModelConverters.getInstance()
- .addConverter(new CustomSwaggerJsonConverter(Json.mapper(),
- CustomSwaggerFormats.customProperties, CustomSwaggerFormats.customObjectsExamples))
-
-### Locale
-Locale messages can be initialized and used in the following way,
-
- val localeConfig = config.getConfig("locale")
- val log = com.typesafe.scalalogging.Logger(LoggerFactory.getLogger(classOf[MyClass]))
-
- val messages = xyz.driver.core.messages.Messages.messages(localeConfig, log, Locale.US)
-
- messages("my.locale.message")
- messages("my.locale.message.with.param", parameter)
-
-
-### System stats
-Stats it gives access to are,
-
- xyz.driver.core.stats.SystemStats.memoryUsage
-
- xyz.driver.core.stats.SystemStats.availableProcessors
-
- xyz.driver.core.stats.SystemStats.garbageCollectorStats
-
- xyz.driver.core.stats.SystemStats.fileSystemSpace
-
- xyz.driver.core.stats.SystemStats.operatingSystemStats
-
-
-## Running
-
-1. Compile everything and test:
+}
+```
+See [this example project](documentation/example) for a more details.
- $ sbt test
+## Building
-2. Publish to local repository, to use changes in depending projects:
+This project uses [sbt](https://www.scala-sbt.org/) as build tool. It
+has grown organically over time, but all new code should follow the
+best practices outlined
+[here](https://style.driver.engineering/scala.html).
- $ sbt publishLocal
+It is set up to automatically build, test, and publish a release when
+a Git tag is pushed that matches the regular expression
+`v[0-9].*`. Hence, to publish a new version "1.2.3", simply create a
+tag "v1.2.3" and `git push --tags` to origin.
-3. In order to release a new version of core, merge your PR, tag the HEAD master commit with the next version
- (don't forget the "v." prefix) and push tags - Travis will release the artifact automatically!
+## Copying
+Copyright 2016-2018 Driver Inc. Released under the Apache License,
+Version 2.0.
diff --git a/build.sbt b/build.sbt
index 030b12f..d4b3470 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,56 +1,125 @@
import sbt._
import Keys._
-lazy val core = project
- .in(file("."))
+scalacOptions in ThisBuild in (Compile, doc) ++= Seq(
+ "-groups", // group similar methods together based on the @group annotation.
+ "-diagrams", // show class hierarchy diagrams (requires 'dot' to be available on path)
+ "-implicits", // add methods "inherited" through implicit conversions
+ "-sourcepath",
+ baseDirectory.value.getAbsolutePath,
+ "-doc-source-url",
+ s"https://github.com/drivergroup/driver-core/blob/master€{FILE_PATH}.scala"
+)
+abiVersion in ThisBuild := "2.0.0-M3"
+
+val mockito = "org.mockito" % "mockito-core" % "1.9.5"
+val scalatest = "org.scalatest" %% "scalatest" % "3.0.5"
+
+// TODO these shouldn't be declared in the build scope. They should be moved to the individual
+// sub-projects that actually depend on them.
+libraryDependencies in ThisBuild ++= Seq(
+ // please keep these sorted alphabetically
+ "ch.qos.logback" % "logback-classic" % "1.2.3",
+ "ch.qos.logback.contrib" % "logback-jackson" % "0.1.5",
+ "ch.qos.logback.contrib" % "logback-json-classic" % "0.1.5",
+ "com.aliyun.mns" % "aliyun-sdk-mns" % "1.1.8",
+ "com.aliyun.oss" % "aliyun-sdk-oss" % "2.8.2",
+ "com.amazonaws" % "aws-java-sdk-s3" % "1.11.342",
+ "com.beachape" %% "enumeratum" % "1.5.13",
+ "com.github.swagger-akka-http" %% "swagger-akka-http" % "1.0.0",
+ "com.google.cloud" % "google-cloud-pubsub" % "1.31.0",
+ "com.google.cloud" % "google-cloud-storage" % "1.31.0",
+ "com.googlecode.libphonenumber" % "libphonenumber" % "8.9.7",
+ "com.neovisionaries" % "nv-i18n" % "1.23",
+ "com.pauldijou" %% "jwt-core" % "0.16.0",
+ "com.softwaremill.sttp" %% "akka-http-backend" % "1.2.2",
+ "com.softwaremill.sttp" %% "core" % "1.2.2",
+ "com.typesafe" % "config" % "1.3.3",
+ "com.typesafe.akka" %% "akka-actor" % "2.5.14",
+ "com.typesafe.akka" %% "akka-http-core" % "10.1.4",
+ "com.typesafe.akka" %% "akka-http-spray-json" % "10.1.4",
+ "com.typesafe.akka" %% "akka-http-testkit" % "10.1.4",
+ "com.typesafe.akka" %% "akka-stream" % "2.5.14",
+ "com.typesafe.scala-logging" %% "scala-logging" % "3.9.0",
+ "com.typesafe.slick" %% "slick" % "3.2.3",
+ "eu.timepit" %% "refined" % "0.9.0",
+ "io.kamon" %% "kamon-akka-2.5" % "1.0.0",
+ "io.kamon" %% "kamon-core" % "1.1.3",
+ "io.kamon" %% "kamon-statsd" % "1.0.0",
+ "io.kamon" %% "kamon-system-metrics" % "1.0.0",
+ "javax.xml.bind" % "jaxb-api" % "2.2.8",
+ mockito % "test",
+ "org.scala-lang.modules" %% "scala-async" % "0.9.7",
+ "org.scalacheck" %% "scalacheck" % "1.14.0" % "test",
+ "org.scalaz" %% "scalaz-core" % "7.2.24",
+ scalatest % "test",
+ "xyz.driver" %% "spray-json-derivation" % "0.6.0",
+ "xyz.driver" %% "tracing" % "0.1.2"
+)
+
+lazy val `core-util` = project
+ .enablePlugins(LibraryPlugin)
+
+lazy val `core-types` = project
.enablePlugins(LibraryPlugin)
+ .dependsOn(`core-util`)
+
+lazy val `core-rest` = project
+ .enablePlugins(LibraryPlugin)
+ .dependsOn(`core-util`, `core-types`, `core-reporting`)
+
+lazy val `core-reporting` = project
+ .enablePlugins(LibraryPlugin)
+ .dependsOn(`core-util`)
+
+lazy val `core-storage` = project
+ .enablePlugins(LibraryPlugin)
+ .dependsOn(`core-reporting`)
+
+lazy val `core-messaging` = project
+ .enablePlugins(LibraryPlugin)
+ .dependsOn(`core-reporting`)
+
+lazy val `core-database` = project
+ .enablePlugins(LibraryPlugin)
+ .dependsOn(`core-types`)
+
+lazy val `core-init` = project
+ .enablePlugins(LibraryPlugin)
+ .dependsOn(`core-reporting`, `core-storage`, `core-messaging`, `core-rest`, `core-database`)
+
+lazy val `core-testkit` = project
+ .enablePlugins(LibraryPlugin)
+ .dependsOn(`core-rest`, `core-database`)
.settings(
libraryDependencies ++= Seq(
- // please keep these sorted alphabetically
- "ch.qos.logback" % "logback-classic" % "1.2.3",
- "ch.qos.logback.contrib" % "logback-jackson" % "0.1.5",
- "ch.qos.logback.contrib" % "logback-json-classic" % "0.1.5",
- "com.aliyun.mns" % "aliyun-sdk-mns" % "1.1.8",
- "com.aliyun.oss" % "aliyun-sdk-oss" % "2.8.2",
- "com.amazonaws" % "aws-java-sdk-s3" % "1.11.342",
- "com.beachape" %% "enumeratum" % "1.5.13",
- "com.github.swagger-akka-http" %% "swagger-akka-http" % "1.0.0",
- "com.google.cloud" % "google-cloud-pubsub" % "1.31.0",
- "com.google.cloud" % "google-cloud-storage" % "1.31.0",
- "com.googlecode.libphonenumber" % "libphonenumber" % "8.9.7",
- "com.neovisionaries" % "nv-i18n" % "1.23",
- "com.pauldijou" %% "jwt-core" % "0.16.0",
- "com.softwaremill.sttp" %% "akka-http-backend" % "1.2.2",
- "com.softwaremill.sttp" %% "core" % "1.2.2",
- "com.typesafe" % "config" % "1.3.3",
- "com.typesafe.akka" %% "akka-actor" % "2.5.14",
- "com.typesafe.akka" %% "akka-http-core" % "10.1.4",
- "com.typesafe.akka" %% "akka-http-spray-json" % "10.1.4",
- "com.typesafe.akka" %% "akka-http-testkit" % "10.1.4",
- "com.typesafe.akka" %% "akka-stream" % "2.5.14",
- "com.typesafe.scala-logging" %% "scala-logging" % "3.9.0",
- "com.typesafe.slick" %% "slick" % "3.2.3",
- "eu.timepit" %% "refined" % "0.9.0",
- "io.kamon" %% "kamon-akka-2.5" % "1.0.0",
- "io.kamon" %% "kamon-core" % "1.1.3",
- "io.kamon" %% "kamon-statsd" % "1.0.0",
- "io.kamon" %% "kamon-system-metrics" % "1.0.0",
- "javax.xml.bind" % "jaxb-api" % "2.2.8",
- "org.mockito" % "mockito-core" % "1.9.5" % "test",
- "org.scala-lang.modules" %% "scala-async" % "0.9.7",
- "org.scalacheck" %% "scalacheck" % "1.14.0" % "test",
- "org.scalatest" %% "scalatest" % "3.0.5" % "test",
- "org.scalaz" %% "scalaz-core" % "7.2.24",
- "xyz.driver" %% "spray-json-derivation" % "0.6.0",
- "xyz.driver" %% "tracing" % "0.1.2"
- ),
- scalacOptions in (Compile, doc) ++= Seq(
- "-groups", // group similar methods together based on the @group annotation.
- "-diagrams", // show classs hierarchy diagrams (requires 'dot' to be available on path)
- "-implicits", // add methods "inherited" through implicit conversions
- "-sourcepath",
- baseDirectory.value.getAbsolutePath,
- "-doc-source-url",
- s"https://github.com/drivergroup/driver-core/blob/master€{FILE_PATH}.scala"
+ "com.spotify" % "docker-client" % "8.11.7" classifier "shaded",
+ "org.postgresql" % "postgresql" % "9.4.1212",
+ "org.scalamock" %% "scalamock-scalatest-support" % "3.6.0",
+ scalatest
)
)
+
+lazy val core = project
+ .in(file("."))
+ .enablePlugins(LibraryPlugin)
+ .dependsOn(
+ `core-types`,
+ `core-rest`,
+ `core-reporting`,
+ `core-storage`,
+ `core-messaging`,
+ `core-database`,
+ `core-init`
+ )
+ .aggregate(
+ `core-testkit`,
+ `core-types`,
+ `core-rest`,
+ `core-reporting`,
+ `core-storage`,
+ `core-messaging`,
+ `core-database`,
+ `core-init`,
+ `core-util`
+ )
diff --git a/src/main/scala/xyz/driver/core/database/Converters.scala b/core-database/src/main/scala/xyz/driver/core/database/Converters.scala
index ad79abf..26b6eea 100644
--- a/src/main/scala/xyz/driver/core/database/Converters.scala
+++ b/core-database/src/main/scala/xyz/driver/core/database/Converters.scala
@@ -1,6 +1,6 @@
package xyz.driver.core.database
-import xyz.driver.core.rest.errors.DatabaseException
+import xyz.driver.core.DatabaseException
import scala.reflect.ClassTag
diff --git a/src/main/scala/xyz/driver/core/database/PatchedHsqldbProfile.scala b/core-database/src/main/scala/xyz/driver/core/database/PatchedHsqldbProfile.scala
index e2efd32..e2efd32 100644
--- a/src/main/scala/xyz/driver/core/database/PatchedHsqldbProfile.scala
+++ b/core-database/src/main/scala/xyz/driver/core/database/PatchedHsqldbProfile.scala
diff --git a/src/main/scala/xyz/driver/core/database/Repository.scala b/core-database/src/main/scala/xyz/driver/core/database/Repository.scala
index 5d7f787..5d7f787 100644
--- a/src/main/scala/xyz/driver/core/database/Repository.scala
+++ b/core-database/src/main/scala/xyz/driver/core/database/Repository.scala
diff --git a/src/main/scala/xyz/driver/core/database/SlickGetResultSupport.scala b/core-database/src/main/scala/xyz/driver/core/database/SlickGetResultSupport.scala
index 8293371..8293371 100644
--- a/src/main/scala/xyz/driver/core/database/SlickGetResultSupport.scala
+++ b/core-database/src/main/scala/xyz/driver/core/database/SlickGetResultSupport.scala
diff --git a/src/main/scala/xyz/driver/core/database/database.scala b/core-database/src/main/scala/xyz/driver/core/database/database.scala
index f3630ff..f3630ff 100644
--- a/src/main/scala/xyz/driver/core/database/database.scala
+++ b/core-database/src/main/scala/xyz/driver/core/database/database.scala
diff --git a/src/main/scala/xyz/driver/core/database/package.scala b/core-database/src/main/scala/xyz/driver/core/database/package.scala
index aee14c6..aee14c6 100644
--- a/src/main/scala/xyz/driver/core/database/package.scala
+++ b/core-database/src/main/scala/xyz/driver/core/database/package.scala
diff --git a/src/test/scala/xyz/driver/core/database/DatabaseTest.scala b/core-database/src/test/scala/xyz/driver/core/database/DatabaseTest.scala
index 8d2a4ac..3407d64 100644
--- a/src/test/scala/xyz/driver/core/database/DatabaseTest.scala
+++ b/core-database/src/test/scala/xyz/driver/core/database/DatabaseTest.scala
@@ -2,7 +2,7 @@ package xyz.driver.core.database
import org.scalatest.{FlatSpec, Matchers}
import org.scalatest.prop.Checkers
-import xyz.driver.core.rest.errors.DatabaseException
+import xyz.driver.core.DatabaseException
class DatabaseTest extends FlatSpec with Matchers with Checkers {
import xyz.driver.core.generators._
diff --git a/src/main/resources/deployed-logback.xml b/core-init/src/main/resources/deployed-logback.xml
index b626b4b..b626b4b 100644
--- a/src/main/resources/deployed-logback.xml
+++ b/core-init/src/main/resources/deployed-logback.xml
diff --git a/src/main/resources/logback-test.xml b/core-init/src/main/resources/logback-test.xml
index d1a17ef..d1a17ef 100644
--- a/src/main/resources/logback-test.xml
+++ b/core-init/src/main/resources/logback-test.xml
diff --git a/src/main/resources/logback.xml b/core-init/src/main/resources/logback.xml
index 97baf6d..97baf6d 100644
--- a/src/main/resources/logback.xml
+++ b/core-init/src/main/resources/logback.xml
diff --git a/core-init/src/main/resources/reference.conf b/core-init/src/main/resources/reference.conf
new file mode 100644
index 0000000..d5c7c4b
--- /dev/null
+++ b/core-init/src/main/resources/reference.conf
@@ -0,0 +1,26 @@
+######################################################################
+# Default settings for driver core. Any settings defined by users of #
+# this library will take precedence. See the documentation of the #
+# Typesafe Config Library (https://github.com/lightbend/config) for #
+# more information. #
+######################################################################
+
+# Kamon provides monitoring capabilities
+kamon {
+ system-metrics {
+ # sigar reports host-specific metrics. Kubernetes takes care of
+ # that for Driver services.
+ host.enabled = false
+
+ # JVM-related metrics
+ jmx.enabled = true
+ }
+
+ statsd {
+ hostname = localhost
+ port = 8125
+ simple-metric-key-generator {
+ include-hostname = false
+ }
+ }
+}
diff --git a/src/main/scala/xyz/driver/core/init/AkkaBootable.scala b/core-init/src/main/scala/xyz/driver/core/init/AkkaBootable.scala
index df6611e..b012bf9 100644
--- a/src/main/scala/xyz/driver/core/init/AkkaBootable.scala
+++ b/core-init/src/main/scala/xyz/driver/core/init/AkkaBootable.scala
@@ -179,7 +179,7 @@ trait AkkaBootable {
syslog("binding to network interface")
val binding = Await.result(
- Http().bindAndHandle(route, "::", port),
+ Http().bindAndHandle(route, "0.0.0.0", port),
2.seconds
)
syslog(s"listening to ${binding.localAddress}")
diff --git a/src/main/scala/xyz/driver/core/init/BuildInfoReflection.scala b/core-init/src/main/scala/xyz/driver/core/init/BuildInfoReflection.scala
index 0e53085..a8bee9b 100644
--- a/src/main/scala/xyz/driver/core/init/BuildInfoReflection.scala
+++ b/core-init/src/main/scala/xyz/driver/core/init/BuildInfoReflection.scala
@@ -10,7 +10,7 @@ private[init] object BuildInfoReflection {
final val BuildInfoName = "xyz.driver.BuildInfo"
- lazy val name: String = get[String]("name")
+ lazy val name: String = find[String]("name").getOrElse("unknown")
lazy val version: Option[String] = find[String]("version")
/** Lookup a given field in the build configuration. This field is required to exist. */
diff --git a/src/main/scala/xyz/driver/core/init/CloudServices.scala b/core-init/src/main/scala/xyz/driver/core/init/CloudServices.scala
index 857dd4c..8c51e38 100644
--- a/src/main/scala/xyz/driver/core/init/CloudServices.scala
+++ b/core-init/src/main/scala/xyz/driver/core/init/CloudServices.scala
@@ -3,10 +3,10 @@ package init
import java.nio.file.Paths
-import xyz.driver.core.messaging.{CreateOnDemand, GoogleBus, QueueBus, StreamBus}
+import xyz.driver.core.messaging.{AliyunBus, CreateOnDemand, GoogleBus, QueueBus, StreamBus}
import xyz.driver.core.reporting._
import xyz.driver.core.rest.DnsDiscovery
-import xyz.driver.core.storage.{BlobStorage, FileSystemBlobStorage, GcsBlobStorage}
+import xyz.driver.core.storage.{AliyunBlobStorage, BlobStorage, FileSystemBlobStorage, GcsBlobStorage}
import scala.collection.JavaConverters._
@@ -52,6 +52,10 @@ trait CloudServices extends AkkaBootable { self =>
new GoogleReporter(p.credentials, p.namespace) with ScalaLoggingCompat with GoogleMdcLogger {
val logger = ScalaLoggingCompat.defaultScalaLogger(true)
}
+ case _: Platform.AliCloud =>
+ new NoTraceReporter with ScalaLoggingCompat {
+ val logger = ScalaLoggingCompat.defaultScalaLogger(true)
+ }
case Platform.Dev =>
new NoTraceReporter with ScalaLoggingCompat {
val logger = ScalaLoggingCompat.defaultScalaLogger(false)
@@ -64,7 +68,7 @@ trait CloudServices extends AkkaBootable { self =>
/** Object storage.
*
* When running on a cloud platform, prepends `$project-` to bucket names, where `$project`
- * is the project ID (for example 'driverinc-production` or `driverinc-sandbox`).
+ * is the project ID (for example `driverinc-production` or `driverinc-sandbox`).
*
* @group utilities
*/
@@ -72,6 +76,8 @@ trait CloudServices extends AkkaBootable { self =>
platform match {
case p @ Platform.GoogleCloud(keyfile, _) =>
GcsBlobStorage.fromKeyfile(keyfile, s"${p.project}-$bucketName")
+ case Platform.AliCloud(project, _, accessId, accessKey, region, _) =>
+ AliyunBlobStorage(accessId, accessKey, region, s"$project-$bucketName", java.time.Clock.systemDefaultZone())
case Platform.Dev =>
new FileSystemBlobStorage(Paths.get(s".data-$bucketName"))
}
@@ -82,6 +88,8 @@ trait CloudServices extends AkkaBootable { self =>
def messageBus: StreamBus = platform match {
case p @ Platform.GoogleCloud(_, namespace) =>
new GoogleBus(p.credentials, namespace) with StreamBus with CreateOnDemand
+ case Platform.AliCloud(_, accountId, accessId, accessKey, region, namespace) =>
+ new AliyunBus(accountId, accessId, accessKey, region, namespace) with StreamBus with CreateOnDemand
case Platform.Dev =>
new QueueBus()(self.system) with StreamBus
}
diff --git a/src/main/scala/xyz/driver/core/init/HttpApi.scala b/core-init/src/main/scala/xyz/driver/core/init/HttpApi.scala
index 81428bf..2570cb3 100644
--- a/src/main/scala/xyz/driver/core/init/HttpApi.scala
+++ b/core-init/src/main/scala/xyz/driver/core/init/HttpApi.scala
@@ -27,7 +27,7 @@ trait HttpApi extends CloudServices with Directives with SprayJsonSupport { self
/** Classes with Swagger annotations.
* @group hooks
*/
- def swaggerRouteClasses: Set[Class[_]]
+ def swaggerRouteClasses: Set[Class[_]] = Set(self.getClass)
private val healthRoute = path("health") {
complete(Map("status" -> "good").toJson)
diff --git a/core-init/src/main/scala/xyz/driver/core/init/Platform.scala b/core-init/src/main/scala/xyz/driver/core/init/Platform.scala
new file mode 100644
index 0000000..8f8ef83
--- /dev/null
+++ b/core-init/src/main/scala/xyz/driver/core/init/Platform.scala
@@ -0,0 +1,68 @@
+package xyz.driver.core
+package init
+
+import java.nio.file.{Files, Path, Paths}
+
+import com.google.auth.oauth2.ServiceAccountCredentials
+
+sealed trait Platform
+object Platform {
+ case class GoogleCloud(keyfile: Path, namespace: String) extends Platform {
+ def credentials: ServiceAccountCredentials = ServiceAccountCredentials.fromStream(
+ Files.newInputStream(keyfile)
+ )
+ def project: String = credentials.getProjectId
+ }
+
+ object GoogleCloud {
+ lazy val fromEnv: GoogleCloud = {
+ val credentialsFile =
+ sys.env.getOrElse(
+ "GOOGLE_APPLICATION_CREDENTIALS",
+ sys.error("Expected GOOGLE_APPLICATION_CREDENTIALS file to be set in gcp environment"))
+
+ val keyfile = Paths.get(credentialsFile)
+ require(Files.isReadable(keyfile), s"Google credentials file $credentialsFile is not readable.")
+
+ val namespace = sys.env.getOrElse("SERVICE_NAMESPACE", sys.error("Namespace not set"))
+ GoogleCloud(keyfile, namespace)
+ }
+ }
+
+ case class AliCloud(
+ project: String,
+ accountId: String,
+ accessId: String,
+ accessKey: String,
+ region: String,
+ namespace: String)
+ extends Platform
+
+ object AliCloud {
+ lazy val fromEnv: AliCloud = {
+ AliCloud(
+ project = sys.env("CLOUD_PROJECT"),
+ accountId = sys.env("ALICLOUD_ACCOUNT_ID"),
+ accessId = sys.env("ALICLOUD_ACCESS_ID"),
+ accessKey = sys.env("ALICLOUD_ACCESS_KEY"),
+ region = sys.env("CLOUD_REGION"),
+ namespace = sys.env("SERVICE_NAMESPACE")
+ )
+ }
+ }
+
+ case object Dev extends Platform
+
+ lazy val fromEnv: Platform = {
+ sys.env.get("CLOUD_PROVIDER") match {
+ case Some("alicloud") => AliCloud.fromEnv
+ case Some("gcp") => GoogleCloud.fromEnv
+
+ // For backwards compat, try instantiating GCP first, falling back to Dev
+ case _ => util.Try(GoogleCloud.fromEnv).getOrElse(Dev)
+ }
+ }
+
+ def current: Platform = fromEnv
+
+}
diff --git a/src/main/scala/xyz/driver/core/init/ProtobufApi.scala b/core-init/src/main/scala/xyz/driver/core/init/ProtobufApi.scala
index 284ac67..284ac67 100644
--- a/src/main/scala/xyz/driver/core/init/ProtobufApi.scala
+++ b/core-init/src/main/scala/xyz/driver/core/init/ProtobufApi.scala
diff --git a/src/main/scala/xyz/driver/core/init/SimpleHttpApp.scala b/core-init/src/main/scala/xyz/driver/core/init/SimpleHttpApp.scala
index 61ca363..61ca363 100644
--- a/src/main/scala/xyz/driver/core/init/SimpleHttpApp.scala
+++ b/core-init/src/main/scala/xyz/driver/core/init/SimpleHttpApp.scala
diff --git a/src/main/scala/xyz/driver/core/messaging/AliyunBus.scala b/core-messaging/src/main/scala/xyz/driver/core/messaging/AliyunBus.scala
index c23ea0f..063bee3 100644
--- a/src/main/scala/xyz/driver/core/messaging/AliyunBus.scala
+++ b/core-messaging/src/main/scala/xyz/driver/core/messaging/AliyunBus.scala
@@ -16,7 +16,7 @@ class AliyunBus(
accessSecret: String,
region: String,
namespace: String,
- pullTimeout: Int
+ pullTimeout: Int = 30
)(implicit val executionContext: ExecutionContext)
extends Bus {
private val endpoint = s"https://$accountId.mns.$region.aliyuncs.com"
@@ -136,18 +136,22 @@ class AliyunBus(
def createSubscription(topic: Topic[_], config: SubscriptionConfig): Future[Unit] = Future {
val subscriptionName = rawSubscriptionName(config, topic)
- val topicName = rawTopicName(topic)
- val topicRef = client.getTopicRef(topicName)
-
- val queueMeta = new QueueMeta
- queueMeta.setQueueName(subscriptionName)
- queueMeta.setVisibilityTimeout(config.ackTimeout.toSeconds)
- client.createQueue(queueMeta)
-
- val subscriptionMeta = new SubscriptionMeta
- subscriptionMeta.setSubscriptionName(subscriptionName)
- subscriptionMeta.setTopicName(topicName)
- subscriptionMeta.setEndpoint(topicRef.generateQueueEndpoint(subscriptionName))
- topicRef.subscribe(subscriptionMeta)
+ val queueExists = Option(client.listQueue(subscriptionName, "", 1)).exists(!_.getResult.isEmpty)
+
+ if (!queueExists) {
+ val topicName = rawTopicName(topic)
+ val topicRef = client.getTopicRef(topicName)
+
+ val queueMeta = new QueueMeta
+ queueMeta.setQueueName(subscriptionName)
+ queueMeta.setVisibilityTimeout(config.ackTimeout.toSeconds)
+ client.createQueue(queueMeta)
+
+ val subscriptionMeta = new SubscriptionMeta
+ subscriptionMeta.setSubscriptionName(subscriptionName)
+ subscriptionMeta.setTopicName(topicName)
+ subscriptionMeta.setEndpoint(topicRef.generateQueueEndpoint(subscriptionName))
+ topicRef.subscribe(subscriptionMeta)
+ }
}
}
diff --git a/src/main/scala/xyz/driver/core/messaging/Bus.scala b/core-messaging/src/main/scala/xyz/driver/core/messaging/Bus.scala
index 75954f4..75954f4 100644
--- a/src/main/scala/xyz/driver/core/messaging/Bus.scala
+++ b/core-messaging/src/main/scala/xyz/driver/core/messaging/Bus.scala
diff --git a/src/main/scala/xyz/driver/core/messaging/CreateOnDemand.scala b/core-messaging/src/main/scala/xyz/driver/core/messaging/CreateOnDemand.scala
index 1af5308..1af5308 100644
--- a/src/main/scala/xyz/driver/core/messaging/CreateOnDemand.scala
+++ b/core-messaging/src/main/scala/xyz/driver/core/messaging/CreateOnDemand.scala
diff --git a/src/main/scala/xyz/driver/core/messaging/GoogleBus.scala b/core-messaging/src/main/scala/xyz/driver/core/messaging/GoogleBus.scala
index b296c50..b296c50 100644
--- a/src/main/scala/xyz/driver/core/messaging/GoogleBus.scala
+++ b/core-messaging/src/main/scala/xyz/driver/core/messaging/GoogleBus.scala
diff --git a/src/main/scala/xyz/driver/core/messaging/QueueBus.scala b/core-messaging/src/main/scala/xyz/driver/core/messaging/QueueBus.scala
index 45c9ed5..45c9ed5 100644
--- a/src/main/scala/xyz/driver/core/messaging/QueueBus.scala
+++ b/core-messaging/src/main/scala/xyz/driver/core/messaging/QueueBus.scala
diff --git a/src/main/scala/xyz/driver/core/messaging/StreamBus.scala b/core-messaging/src/main/scala/xyz/driver/core/messaging/StreamBus.scala
index a9ba3a7..44d75cd 100644
--- a/src/main/scala/xyz/driver/core/messaging/StreamBus.scala
+++ b/core-messaging/src/main/scala/xyz/driver/core/messaging/StreamBus.scala
@@ -76,7 +76,7 @@ trait StreamBus extends Bus {
maxRestarts
) { () =>
subscribe(topic, config)
- .via(processMessage.recover({ case _ => Nil }))
+ .via(processMessage)
.log(topic.name)
.mapConcat(identity)
}
diff --git a/src/main/scala/xyz/driver/core/messaging/Topic.scala b/core-messaging/src/main/scala/xyz/driver/core/messaging/Topic.scala
index 32fd764..32fd764 100644
--- a/src/main/scala/xyz/driver/core/messaging/Topic.scala
+++ b/core-messaging/src/main/scala/xyz/driver/core/messaging/Topic.scala
diff --git a/src/test/scala/xyz/driver/core/messaging/QueueBusTest.scala b/core-messaging/src/test/scala/xyz/driver/core/messaging/QueueBusTest.scala
index 8dd0776..8dd0776 100644
--- a/src/test/scala/xyz/driver/core/messaging/QueueBusTest.scala
+++ b/core-messaging/src/test/scala/xyz/driver/core/messaging/QueueBusTest.scala
diff --git a/src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala b/core-reporting/src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala
index f5c41cf..f5c41cf 100644
--- a/src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala
+++ b/core-reporting/src/main/scala/xyz/driver/core/reporting/GoogleMdcLogger.scala
diff --git a/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala b/core-reporting/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala
index 14c4954..14c4954 100644
--- a/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala
+++ b/core-reporting/src/main/scala/xyz/driver/core/reporting/GoogleReporter.scala
diff --git a/src/main/scala/xyz/driver/core/reporting/NoReporter.scala b/core-reporting/src/main/scala/xyz/driver/core/reporting/NoReporter.scala
index c1c81f4..c1c81f4 100644
--- a/src/main/scala/xyz/driver/core/reporting/NoReporter.scala
+++ b/core-reporting/src/main/scala/xyz/driver/core/reporting/NoReporter.scala
diff --git a/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala b/core-reporting/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala
index b49cfda..b49cfda 100644
--- a/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala
+++ b/core-reporting/src/main/scala/xyz/driver/core/reporting/NoTraceReporter.scala
diff --git a/src/main/scala/xyz/driver/core/reporting/Reporter.scala b/core-reporting/src/main/scala/xyz/driver/core/reporting/Reporter.scala
index 469084c..469084c 100644
--- a/src/main/scala/xyz/driver/core/reporting/Reporter.scala
+++ b/core-reporting/src/main/scala/xyz/driver/core/reporting/Reporter.scala
diff --git a/src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala b/core-reporting/src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala
index 0ff5574..0ff5574 100644
--- a/src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala
+++ b/core-reporting/src/main/scala/xyz/driver/core/reporting/ScalaLoggingCompat.scala
diff --git a/src/main/scala/xyz/driver/core/reporting/SpanContext.scala b/core-reporting/src/main/scala/xyz/driver/core/reporting/SpanContext.scala
index 04a822d..04a822d 100644
--- a/src/main/scala/xyz/driver/core/reporting/SpanContext.scala
+++ b/core-reporting/src/main/scala/xyz/driver/core/reporting/SpanContext.scala
diff --git a/src/main/resources/reference.conf b/core-rest/src/main/resources/reference.conf
index 0c3b4e2..ea56cb5 100644
--- a/src/main/resources/reference.conf
+++ b/core-rest/src/main/resources/reference.conf
@@ -1,10 +1,3 @@
-######################################################################
-# Default settings for driver core. Any settings defined by users of #
-# this library will take precedence. See the documentation of the #
-# Typesafe Config Library (https://github.com/lightbend/config) for #
-# more information. #
-######################################################################
-
# This scope is for general settings related to the execution of a
# specific service.
application {
@@ -59,23 +52,3 @@ swagger {
licenseUrl = "http://www.apache.org/licenses/LICENSE-2.0"
}
}
-
-# Kamon provides monitoring capabilities
-kamon {
- system-metrics {
- # sigar reports host-specific metrics. Kubernetes takes care of
- # that for Driver services.
- host.enabled = false
-
- # JVM-related metrics
- jmx.enabled = true
- }
-
- statsd {
- hostname = localhost
- port = 8125
- simple-metric-key-generator {
- include-hostname = false
- }
- }
-}
diff --git a/src/main/resources/swagger-ui-dist/README.md b/core-rest/src/main/resources/swagger-ui-dist/README.md
index 6628422..6628422 100644
--- a/src/main/resources/swagger-ui-dist/README.md
+++ b/core-rest/src/main/resources/swagger-ui-dist/README.md
diff --git a/src/main/resources/swagger-ui-dist/absolute-path.js b/core-rest/src/main/resources/swagger-ui-dist/absolute-path.js
index af42bc8..af42bc8 100644
--- a/src/main/resources/swagger-ui-dist/absolute-path.js
+++ b/core-rest/src/main/resources/swagger-ui-dist/absolute-path.js
diff --git a/src/main/resources/swagger-ui-dist/favicon-16x16.png b/core-rest/src/main/resources/swagger-ui-dist/favicon-16x16.png
index 0f7e13b..0f7e13b 100644
--- a/src/main/resources/swagger-ui-dist/favicon-16x16.png
+++ b/core-rest/src/main/resources/swagger-ui-dist/favicon-16x16.png
Binary files differ
diff --git a/src/main/resources/swagger-ui-dist/favicon-32x32.png b/core-rest/src/main/resources/swagger-ui-dist/favicon-32x32.png
index b0a3352..b0a3352 100644
--- a/src/main/resources/swagger-ui-dist/favicon-32x32.png
+++ b/core-rest/src/main/resources/swagger-ui-dist/favicon-32x32.png
Binary files differ
diff --git a/src/main/resources/swagger-ui-dist/index.html b/core-rest/src/main/resources/swagger-ui-dist/index.html
index b7385c0..b7385c0 100644
--- a/src/main/resources/swagger-ui-dist/index.html
+++ b/core-rest/src/main/resources/swagger-ui-dist/index.html
diff --git a/src/main/resources/swagger-ui-dist/index.js b/core-rest/src/main/resources/swagger-ui-dist/index.js
index c229ec4..c229ec4 100644
--- a/src/main/resources/swagger-ui-dist/index.js
+++ b/core-rest/src/main/resources/swagger-ui-dist/index.js
diff --git a/src/main/resources/swagger-ui-dist/oauth2-redirect.html b/core-rest/src/main/resources/swagger-ui-dist/oauth2-redirect.html
index fb68399..fb68399 100644
--- a/src/main/resources/swagger-ui-dist/oauth2-redirect.html
+++ b/core-rest/src/main/resources/swagger-ui-dist/oauth2-redirect.html
diff --git a/src/main/resources/swagger-ui-dist/package.json b/core-rest/src/main/resources/swagger-ui-dist/package.json
index 1e74e17..1e74e17 100644
--- a/src/main/resources/swagger-ui-dist/package.json
+++ b/core-rest/src/main/resources/swagger-ui-dist/package.json
diff --git a/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js
index 8c5384f..8c5384f 100644
--- a/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js
+++ b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js
diff --git a/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js.map b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js.map
index f3d7114..f3d7114 100644
--- a/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js.map
+++ b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui-bundle.js.map
diff --git a/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js
index 7fa58dd..7fa58dd 100644
--- a/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js
+++ b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js
diff --git a/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js.map b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js.map
index ac39601..ac39601 100644
--- a/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js.map
+++ b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui-standalone-preset.js.map
diff --git a/src/main/resources/swagger-ui-dist/swagger-ui.css b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui.css
index f6d75f4..f6d75f4 100644
--- a/src/main/resources/swagger-ui-dist/swagger-ui.css
+++ b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui.css
diff --git a/src/main/resources/swagger-ui-dist/swagger-ui.css.map b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui.css.map
index 3e2402c..3e2402c 100644
--- a/src/main/resources/swagger-ui-dist/swagger-ui.css.map
+++ b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui.css.map
diff --git a/src/main/resources/swagger-ui-dist/swagger-ui.js b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui.js
index ac23356..ac23356 100644
--- a/src/main/resources/swagger-ui-dist/swagger-ui.js
+++ b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui.js
diff --git a/src/main/resources/swagger-ui-dist/swagger-ui.js.map b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui.js.map
index f2393d8..f2393d8 100644
--- a/src/main/resources/swagger-ui-dist/swagger-ui.js.map
+++ b/core-rest/src/main/resources/swagger-ui-dist/swagger-ui.js.map
diff --git a/src/main/resources/swagger-ui/.gitattributes b/core-rest/src/main/resources/swagger-ui/.gitattributes
index 2bf4397..2bf4397 100644
--- a/src/main/resources/swagger-ui/.gitattributes
+++ b/core-rest/src/main/resources/swagger-ui/.gitattributes
diff --git a/src/main/resources/swagger-ui/css/print.css b/core-rest/src/main/resources/swagger-ui/css/print.css
index 8695cf5..8695cf5 100644
--- a/src/main/resources/swagger-ui/css/print.css
+++ b/core-rest/src/main/resources/swagger-ui/css/print.css
diff --git a/src/main/resources/swagger-ui/css/reset.css b/core-rest/src/main/resources/swagger-ui/css/reset.css
index b2b0789..b2b0789 100644
--- a/src/main/resources/swagger-ui/css/reset.css
+++ b/core-rest/src/main/resources/swagger-ui/css/reset.css
diff --git a/src/main/resources/swagger-ui/css/screen.css b/core-rest/src/main/resources/swagger-ui/css/screen.css
index 10a46ac..10a46ac 100644
--- a/src/main/resources/swagger-ui/css/screen.css
+++ b/core-rest/src/main/resources/swagger-ui/css/screen.css
diff --git a/src/main/resources/swagger-ui/css/typography.css b/core-rest/src/main/resources/swagger-ui/css/typography.css
index 27c3751..27c3751 100644
--- a/src/main/resources/swagger-ui/css/typography.css
+++ b/core-rest/src/main/resources/swagger-ui/css/typography.css
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.eot b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.eot
index d852498..d852498 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.eot
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.eot
Binary files differ
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.svg b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.svg
index a54bbbb..a54bbbb 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.svg
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.svg
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.ttf b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.ttf
index 15896c4..15896c4 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.ttf
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.ttf
Binary files differ
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff
index 67e3e25..67e3e25 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff
Binary files differ
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff2 b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff2
index 1e726a7..1e726a7 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff2
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-700.woff2
Binary files differ
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.eot b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.eot
index ac2698e..ac2698e 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.eot
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.eot
Binary files differ
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.svg b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.svg
index d9f2a21..d9f2a21 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.svg
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.svg
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf
index fb8cea6..fb8cea6 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.ttf
Binary files differ
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff
index abf1989..abf1989 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff
Binary files differ
diff --git a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2 b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2
index 9f93f74..9f93f74 100644
--- a/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2
+++ b/core-rest/src/main/resources/swagger-ui/fonts/droid-sans-v6-latin-regular.woff2
Binary files differ
diff --git a/src/main/resources/swagger-ui/images/explorer_icons.png b/core-rest/src/main/resources/swagger-ui/images/explorer_icons.png
index ed9d2ff..ed9d2ff 100644
--- a/src/main/resources/swagger-ui/images/explorer_icons.png
+++ b/core-rest/src/main/resources/swagger-ui/images/explorer_icons.png
Binary files differ
diff --git a/src/main/resources/swagger-ui/images/favicon-16x16.png b/core-rest/src/main/resources/swagger-ui/images/favicon-16x16.png
index 66b1a5b..66b1a5b 100644
--- a/src/main/resources/swagger-ui/images/favicon-16x16.png
+++ b/core-rest/src/main/resources/swagger-ui/images/favicon-16x16.png
Binary files differ
diff --git a/src/main/resources/swagger-ui/images/favicon-32x32.png b/core-rest/src/main/resources/swagger-ui/images/favicon-32x32.png
index 32f319f..32f319f 100644
--- a/src/main/resources/swagger-ui/images/favicon-32x32.png
+++ b/core-rest/src/main/resources/swagger-ui/images/favicon-32x32.png
Binary files differ
diff --git a/src/main/resources/swagger-ui/images/favicon.ico b/core-rest/src/main/resources/swagger-ui/images/favicon.ico
index 8b60bcf..8b60bcf 100644
--- a/src/main/resources/swagger-ui/images/favicon.ico
+++ b/core-rest/src/main/resources/swagger-ui/images/favicon.ico
Binary files differ
diff --git a/src/main/resources/swagger-ui/images/logo_small.png b/core-rest/src/main/resources/swagger-ui/images/logo_small.png
index 5496a65..5496a65 100644
--- a/src/main/resources/swagger-ui/images/logo_small.png
+++ b/core-rest/src/main/resources/swagger-ui/images/logo_small.png
Binary files differ
diff --git a/src/main/resources/swagger-ui/images/pet_store_api.png b/core-rest/src/main/resources/swagger-ui/images/pet_store_api.png
index f9f9cd4..f9f9cd4 100644
--- a/src/main/resources/swagger-ui/images/pet_store_api.png
+++ b/core-rest/src/main/resources/swagger-ui/images/pet_store_api.png
Binary files differ
diff --git a/src/main/resources/swagger-ui/images/throbber.gif b/core-rest/src/main/resources/swagger-ui/images/throbber.gif
index 0639388..0639388 100644
--- a/src/main/resources/swagger-ui/images/throbber.gif
+++ b/core-rest/src/main/resources/swagger-ui/images/throbber.gif
Binary files differ
diff --git a/src/main/resources/swagger-ui/images/wordnik_api.png b/core-rest/src/main/resources/swagger-ui/images/wordnik_api.png
index dca4f14..dca4f14 100644
--- a/src/main/resources/swagger-ui/images/wordnik_api.png
+++ b/core-rest/src/main/resources/swagger-ui/images/wordnik_api.png
Binary files differ
diff --git a/src/main/resources/swagger-ui/index.html b/core-rest/src/main/resources/swagger-ui/index.html
index 9691d7d..9691d7d 100644
--- a/src/main/resources/swagger-ui/index.html
+++ b/core-rest/src/main/resources/swagger-ui/index.html
diff --git a/src/main/resources/swagger-ui/lib/backbone-min.js b/core-rest/src/main/resources/swagger-ui/lib/backbone-min.js
index a3f544b..a3f544b 100644
--- a/src/main/resources/swagger-ui/lib/backbone-min.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/backbone-min.js
diff --git a/src/main/resources/swagger-ui/lib/handlebars-2.0.0.js b/core-rest/src/main/resources/swagger-ui/lib/handlebars-2.0.0.js
index 53cf921..53cf921 100644
--- a/src/main/resources/swagger-ui/lib/handlebars-2.0.0.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/handlebars-2.0.0.js
diff --git a/src/main/resources/swagger-ui/lib/highlight.7.3.pack.js b/core-rest/src/main/resources/swagger-ui/lib/highlight.7.3.pack.js
index 9a95a75..9a95a75 100644
--- a/src/main/resources/swagger-ui/lib/highlight.7.3.pack.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/highlight.7.3.pack.js
diff --git a/src/main/resources/swagger-ui/lib/jquery-1.8.0.min.js b/core-rest/src/main/resources/swagger-ui/lib/jquery-1.8.0.min.js
index 066d72c..066d72c 100644
--- a/src/main/resources/swagger-ui/lib/jquery-1.8.0.min.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/jquery-1.8.0.min.js
diff --git a/src/main/resources/swagger-ui/lib/jquery.ba-bbq.min.js b/core-rest/src/main/resources/swagger-ui/lib/jquery.ba-bbq.min.js
index bcbf248..bcbf248 100644
--- a/src/main/resources/swagger-ui/lib/jquery.ba-bbq.min.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/jquery.ba-bbq.min.js
diff --git a/src/main/resources/swagger-ui/lib/jquery.slideto.min.js b/core-rest/src/main/resources/swagger-ui/lib/jquery.slideto.min.js
index ba32cff..ba32cff 100644
--- a/src/main/resources/swagger-ui/lib/jquery.slideto.min.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/jquery.slideto.min.js
diff --git a/src/main/resources/swagger-ui/lib/jquery.wiggle.min.js b/core-rest/src/main/resources/swagger-ui/lib/jquery.wiggle.min.js
index 2adb0d6..2adb0d6 100644
--- a/src/main/resources/swagger-ui/lib/jquery.wiggle.min.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/jquery.wiggle.min.js
diff --git a/src/main/resources/swagger-ui/lib/marked.js b/core-rest/src/main/resources/swagger-ui/lib/marked.js
index c2a678d..c2a678d 100644
--- a/src/main/resources/swagger-ui/lib/marked.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/marked.js
diff --git a/src/main/resources/swagger-ui/lib/swagger-oauth.js b/core-rest/src/main/resources/swagger-ui/lib/swagger-oauth.js
index 3b69fd3..3b69fd3 100644
--- a/src/main/resources/swagger-ui/lib/swagger-oauth.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/swagger-oauth.js
diff --git a/src/main/resources/swagger-ui/lib/underscore-min.js b/core-rest/src/main/resources/swagger-ui/lib/underscore-min.js
index 11f1d96..11f1d96 100644
--- a/src/main/resources/swagger-ui/lib/underscore-min.js
+++ b/core-rest/src/main/resources/swagger-ui/lib/underscore-min.js
diff --git a/src/main/resources/swagger-ui/lib/underscore-min.map b/core-rest/src/main/resources/swagger-ui/lib/underscore-min.map
index cfb76cc..cfb76cc 100644
--- a/src/main/resources/swagger-ui/lib/underscore-min.map
+++ b/core-rest/src/main/resources/swagger-ui/lib/underscore-min.map
diff --git a/src/main/resources/swagger-ui/o2c.html b/core-rest/src/main/resources/swagger-ui/o2c.html
index 88e8bf1..88e8bf1 100644
--- a/src/main/resources/swagger-ui/o2c.html
+++ b/core-rest/src/main/resources/swagger-ui/o2c.html
diff --git a/src/main/resources/swagger-ui/swagger-ui.js b/core-rest/src/main/resources/swagger-ui/swagger-ui.js
index 1396eb8..1396eb8 100644
--- a/src/main/resources/swagger-ui/swagger-ui.js
+++ b/core-rest/src/main/resources/swagger-ui/swagger-ui.js
diff --git a/src/main/resources/swagger-ui/swagger-ui.min.js b/core-rest/src/main/resources/swagger-ui/swagger-ui.min.js
index 966ce7f..966ce7f 100644
--- a/src/main/resources/swagger-ui/swagger-ui.min.js
+++ b/core-rest/src/main/resources/swagger-ui/swagger-ui.min.js
diff --git a/src/main/scala/xyz/driver/core/auth.scala b/core-rest/src/main/scala/xyz/driver/core/auth.scala
index 896bd89..896bd89 100644
--- a/src/main/scala/xyz/driver/core/auth.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/auth.scala
diff --git a/src/main/scala/xyz/driver/core/generators.scala b/core-rest/src/main/scala/xyz/driver/core/generators.scala
index 0a4a7ab..0a4a7ab 100644
--- a/src/main/scala/xyz/driver/core/generators.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/generators.scala
diff --git a/src/main/scala/xyz/driver/core/json.scala b/core-rest/src/main/scala/xyz/driver/core/json.scala
index 48011e4..23373c6 100644
--- a/src/main/scala/xyz/driver/core/json.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/json.scala
@@ -199,10 +199,18 @@ object json extends PathMatchers with Unmarshallers {
}
implicit object phoneNumberFormat extends RootJsonFormat[PhoneNumber] {
- private val basicFormat = jsonFormat2(PhoneNumber.apply)
- override def write(obj: PhoneNumber): JsValue = basicFormat.write(obj)
- override def read(json: JsValue): PhoneNumber = {
- PhoneNumber.parse(basicFormat.read(json).toString).getOrElse(deserializationError("Invalid phone number"))
+
+ private val basicFormat = jsonFormat3(PhoneNumber.apply)
+
+ def write(obj: PhoneNumber): JsValue = basicFormat.write(obj)
+
+ def read(json: JsValue): PhoneNumber = {
+ val maybePhone = json match {
+ case JsString(number) => PhoneNumber.parse(number)
+ case obj: JsObject => PhoneNumber.parse(basicFormat.read(obj).toString)
+ case _ => None
+ }
+ maybePhone.getOrElse(deserializationError("Invalid phone number"))
}
}
@@ -393,7 +401,6 @@ object json extends PathMatchers with Unmarshallers {
case _: ResourceNotFoundException => "ResourceNotFoundException"
case _: ExternalServiceException => "ExternalServiceException"
case _: ExternalServiceTimeoutException => "ExternalServiceTimeoutException"
- case _: DatabaseException => "DatabaseException"
} {
case "InvalidInputException" => jsonFormat(InvalidInputException, "message")
case "InvalidActionException" => jsonFormat(InvalidActionException, "message")
@@ -402,7 +409,6 @@ object json extends PathMatchers with Unmarshallers {
case "ExternalServiceException" =>
jsonFormat(ExternalServiceException, "serviceName", "serviceMessage", "serviceException")
case "ExternalServiceTimeoutException" => jsonFormat(ExternalServiceTimeoutException, "message")
- case "DatabaseException" => jsonFormat(DatabaseException, "message")
}
}
diff --git a/src/main/scala/xyz/driver/core/rest/DnsDiscovery.scala b/core-rest/src/main/scala/xyz/driver/core/rest/DnsDiscovery.scala
index 87946e4..87946e4 100644
--- a/src/main/scala/xyz/driver/core/rest/DnsDiscovery.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/DnsDiscovery.scala
diff --git a/src/main/scala/xyz/driver/core/rest/DriverRoute.scala b/core-rest/src/main/scala/xyz/driver/core/rest/DriverRoute.scala
index 911e306..b94f611 100644
--- a/src/main/scala/xyz/driver/core/rest/DriverRoute.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/DriverRoute.scala
@@ -88,9 +88,6 @@ trait DriverRoute {
case e: ExternalServiceTimeoutException =>
log.error("Service timeout error", e)
StatusCodes.GatewayTimeout
- case e: DatabaseException =>
- log.error("Database error", e)
- StatusCodes.InternalServerError
}
{ (ctx: RequestContext) =>
diff --git a/src/main/scala/xyz/driver/core/rest/HttpRestServiceTransport.scala b/core-rest/src/main/scala/xyz/driver/core/rest/HttpRestServiceTransport.scala
index e31635b..e31635b 100644
--- a/src/main/scala/xyz/driver/core/rest/HttpRestServiceTransport.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/HttpRestServiceTransport.scala
diff --git a/src/main/scala/xyz/driver/core/rest/PatchDirectives.scala b/core-rest/src/main/scala/xyz/driver/core/rest/PatchDirectives.scala
index f33bf9d..f33bf9d 100644
--- a/src/main/scala/xyz/driver/core/rest/PatchDirectives.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/PatchDirectives.scala
diff --git a/src/main/scala/xyz/driver/core/rest/PooledHttpClient.scala b/core-rest/src/main/scala/xyz/driver/core/rest/PooledHttpClient.scala
index 2854257..2854257 100644
--- a/src/main/scala/xyz/driver/core/rest/PooledHttpClient.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/PooledHttpClient.scala
diff --git a/src/main/scala/xyz/driver/core/rest/ProxyRoute.scala b/core-rest/src/main/scala/xyz/driver/core/rest/ProxyRoute.scala
index c0e9f99..c0e9f99 100644
--- a/src/main/scala/xyz/driver/core/rest/ProxyRoute.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/ProxyRoute.scala
diff --git a/src/main/scala/xyz/driver/core/rest/RestService.scala b/core-rest/src/main/scala/xyz/driver/core/rest/RestService.scala
index 91b3550..09d98b8 100644
--- a/src/main/scala/xyz/driver/core/rest/RestService.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/RestService.scala
@@ -6,8 +6,6 @@ import akka.stream.Materializer
import scala.concurrent.{ExecutionContext, Future}
import scalaz.{ListT, OptionT}
-import scalaz.syntax.equal._
-import scalaz.Scalaz.stringInstance
trait RestService extends Service {
@@ -76,10 +74,10 @@ trait RestService extends Service {
response: HttpResponse): Future[ListResponse[T]] = {
import DefaultJsonProtocol._
val resourceCount = response.headers
- .find(_.name() === ContextHeaders.ResourceCount)
+ .find(_.name() equalsIgnoreCase ContextHeaders.ResourceCount)
.map(_.value().toInt)
.getOrElse(0)
- val meta = ListResponse.Meta(resourceCount, pagination.getOrElse(Pagination(resourceCount, 1)))
+ val meta = ListResponse.Meta(resourceCount, pagination.getOrElse(Pagination(resourceCount max 1, 1)))
Unmarshal(response.entity).to[List[T]].map(ListResponse(_, meta))
}
diff --git a/src/main/scala/xyz/driver/core/rest/ServiceDescriptor.scala b/core-rest/src/main/scala/xyz/driver/core/rest/ServiceDescriptor.scala
index 646fae8..646fae8 100644
--- a/src/main/scala/xyz/driver/core/rest/ServiceDescriptor.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/ServiceDescriptor.scala
diff --git a/src/main/scala/xyz/driver/core/rest/SingleRequestHttpClient.scala b/core-rest/src/main/scala/xyz/driver/core/rest/SingleRequestHttpClient.scala
index 964a5a2..964a5a2 100644
--- a/src/main/scala/xyz/driver/core/rest/SingleRequestHttpClient.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/SingleRequestHttpClient.scala
diff --git a/src/main/scala/xyz/driver/core/rest/Swagger.scala b/core-rest/src/main/scala/xyz/driver/core/rest/Swagger.scala
index 5ceac54..5ceac54 100644
--- a/src/main/scala/xyz/driver/core/rest/Swagger.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/Swagger.scala
diff --git a/src/main/scala/xyz/driver/core/rest/auth/AlwaysAllowAuthorization.scala b/core-rest/src/main/scala/xyz/driver/core/rest/auth/AlwaysAllowAuthorization.scala
index 5007774..5007774 100644
--- a/src/main/scala/xyz/driver/core/rest/auth/AlwaysAllowAuthorization.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/auth/AlwaysAllowAuthorization.scala
diff --git a/src/main/scala/xyz/driver/core/rest/auth/AuthProvider.scala b/core-rest/src/main/scala/xyz/driver/core/rest/auth/AuthProvider.scala
index e1a94e1..e1a94e1 100644
--- a/src/main/scala/xyz/driver/core/rest/auth/AuthProvider.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/auth/AuthProvider.scala
diff --git a/src/main/scala/xyz/driver/core/rest/auth/Authorization.scala b/core-rest/src/main/scala/xyz/driver/core/rest/auth/Authorization.scala
index 1a5e9be..1a5e9be 100644
--- a/src/main/scala/xyz/driver/core/rest/auth/Authorization.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/auth/Authorization.scala
diff --git a/src/main/scala/xyz/driver/core/rest/auth/AuthorizationResult.scala b/core-rest/src/main/scala/xyz/driver/core/rest/auth/AuthorizationResult.scala
index efe28c9..efe28c9 100644
--- a/src/main/scala/xyz/driver/core/rest/auth/AuthorizationResult.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/auth/AuthorizationResult.scala
diff --git a/src/main/scala/xyz/driver/core/rest/auth/CachedTokenAuthorization.scala b/core-rest/src/main/scala/xyz/driver/core/rest/auth/CachedTokenAuthorization.scala
index 66de4ef..66de4ef 100644
--- a/src/main/scala/xyz/driver/core/rest/auth/CachedTokenAuthorization.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/auth/CachedTokenAuthorization.scala
diff --git a/src/main/scala/xyz/driver/core/rest/auth/ChainedAuthorization.scala b/core-rest/src/main/scala/xyz/driver/core/rest/auth/ChainedAuthorization.scala
index 131e7fc..131e7fc 100644
--- a/src/main/scala/xyz/driver/core/rest/auth/ChainedAuthorization.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/auth/ChainedAuthorization.scala
diff --git a/src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala b/core-rest/src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala
index ff3424d..ff3424d 100644
--- a/src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/directives/AuthDirectives.scala
diff --git a/src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala b/core-rest/src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala
index 5a6bbfd..5a6bbfd 100644
--- a/src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/directives/CorsDirectives.scala
diff --git a/src/main/scala/xyz/driver/core/rest/directives/Directives.scala b/core-rest/src/main/scala/xyz/driver/core/rest/directives/Directives.scala
index 0cd4ef1..0cd4ef1 100644
--- a/src/main/scala/xyz/driver/core/rest/directives/Directives.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/directives/Directives.scala
diff --git a/src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala b/core-rest/src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala
index 8ba184f..0d60893 100644
--- a/src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/directives/PathMatchers.scala
@@ -10,6 +10,7 @@ import akka.http.scaladsl.server.PathMatcher.{Matched, Unmatched}
import akka.http.scaladsl.server.{PathMatcher, PathMatcher1, PathMatchers => AkkaPathMatchers}
import eu.timepit.refined.collection.NonEmpty
import eu.timepit.refined.refineV
+import xyz.driver.core.domain.PhoneNumber
import xyz.driver.core.time.Time
import scala.util.control.NonFatal
@@ -76,4 +77,15 @@ trait PathMatchers {
Some(Revision[T](string))
}
+ def PhoneInPath: PathMatcher1[PhoneNumber] = new PathMatcher1[PhoneNumber] {
+ def apply(path: Path) = path match {
+ case Path.Segment(segment, tail) =>
+ PhoneNumber
+ .parse(segment)
+ .map(parsed => Matched(tail, Tuple1(parsed)))
+ .getOrElse(Unmatched)
+ case _ => Unmatched
+ }
+ }
+
}
diff --git a/src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala b/core-rest/src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala
index 93a9a52..93a9a52 100644
--- a/src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/directives/Unmarshallers.scala
diff --git a/core-rest/src/main/scala/xyz/driver/core/rest/domain.scala b/core-rest/src/main/scala/xyz/driver/core/rest/domain.scala
new file mode 100644
index 0000000..7926991
--- /dev/null
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/domain.scala
@@ -0,0 +1,57 @@
+package xyz.driver.core.rest
+
+import akka.http.scaladsl.model.{HttpRequest, HttpResponse, ResponseEntity}
+import akka.http.scaladsl.unmarshalling.Unmarshal
+import akka.stream.Materializer
+
+import scala.concurrent.Future
+
+trait Service
+
+object Service
+
+trait HttpClient {
+ def makeRequest(request: HttpRequest): Future[HttpResponse]
+}
+
+trait ServiceTransport {
+
+ def sendRequestGetResponse(context: ServiceRequestContext)(requestStub: HttpRequest): Future[HttpResponse]
+
+ def sendRequest(context: ServiceRequestContext)(requestStub: HttpRequest)(
+ implicit mat: Materializer): Future[Unmarshal[ResponseEntity]]
+}
+
+sealed trait SortingOrder
+object SortingOrder {
+ case object Asc extends SortingOrder
+ case object Desc extends SortingOrder
+}
+
+final case class SortingField(name: String, sortingOrder: SortingOrder)
+final case class Sorting(sortingFields: Seq[SortingField])
+
+final case class Pagination(pageSize: Int, pageNumber: Int) {
+ require(pageSize > 0, "Page size must be greater than zero")
+ require(pageNumber > 0, "Page number must be greater than zero")
+
+ def offset: Int = pageSize * (pageNumber - 1)
+}
+
+final case class ListResponse[+T](items: Seq[T], meta: ListResponse.Meta)
+
+object ListResponse {
+
+ def apply[T](items: Seq[T], size: Int, pagination: Option[Pagination]): ListResponse[T] =
+ ListResponse(
+ items = items,
+ meta = ListResponse.Meta(size, pagination.fold(1)(_.pageNumber), pagination.fold(size)(_.pageSize)))
+
+ final case class Meta(itemsCount: Int, pageNumber: Int, pageSize: Int)
+
+ object Meta {
+ def apply(itemsCount: Int, pagination: Pagination): Meta =
+ Meta(itemsCount, pagination.pageNumber, pagination.pageSize)
+ }
+
+}
diff --git a/src/main/scala/xyz/driver/core/rest/errors/serviceException.scala b/core-rest/src/main/scala/xyz/driver/core/rest/errors/serviceException.scala
index f2962c9..b43a1d3 100644
--- a/src/main/scala/xyz/driver/core/rest/errors/serviceException.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/errors/serviceException.scala
@@ -22,6 +22,3 @@ final case class ExternalServiceException(
final case class ExternalServiceTimeoutException(serviceName: String)
extends ServiceException(s"$serviceName took too long to respond")
-
-final case class DatabaseException(override val message: String = "Database access error")
- extends ServiceException(message)
diff --git a/src/main/scala/xyz/driver/core/rest/headers/Traceparent.scala b/core-rest/src/main/scala/xyz/driver/core/rest/headers/Traceparent.scala
index 866476d..866476d 100644
--- a/src/main/scala/xyz/driver/core/rest/headers/Traceparent.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/headers/Traceparent.scala
diff --git a/src/main/scala/xyz/driver/core/rest/package.scala b/core-rest/src/main/scala/xyz/driver/core/rest/package.scala
index 3be8f02..4620585 100644
--- a/src/main/scala/xyz/driver/core/rest/package.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/package.scala
@@ -1,4 +1,4 @@
-package xyz.driver.core.rest
+package xyz.driver.core
import java.net.InetAddress
@@ -7,71 +7,29 @@ import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server._
-import akka.http.scaladsl.unmarshalling.Unmarshal
-import akka.stream.Materializer
import akka.stream.scaladsl.Flow
import akka.util.ByteString
import scalaz.Scalaz.{intInstance, stringInstance}
import scalaz.syntax.equal._
import scalaz.{Functor, OptionT}
import xyz.driver.core.rest.auth.AuthProvider
+import xyz.driver.core.rest.errors.ExternalServiceException
import xyz.driver.core.rest.headers.Traceparent
import xyz.driver.tracing.TracingDirectives
-import scala.concurrent.Future
+import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
-trait Service
+package object rest {
-object Service
-
-trait HttpClient {
- def makeRequest(request: HttpRequest): Future[HttpResponse]
-}
-
-trait ServiceTransport {
-
- def sendRequestGetResponse(context: ServiceRequestContext)(requestStub: HttpRequest): Future[HttpResponse]
-
- def sendRequest(context: ServiceRequestContext)(requestStub: HttpRequest)(
- implicit mat: Materializer): Future[Unmarshal[ResponseEntity]]
-}
-
-sealed trait SortingOrder
-object SortingOrder {
- case object Asc extends SortingOrder
- case object Desc extends SortingOrder
-}
-
-final case class SortingField(name: String, sortingOrder: SortingOrder)
-final case class Sorting(sortingFields: Seq[SortingField])
-
-final case class Pagination(pageSize: Int, pageNumber: Int) {
- require(pageSize > 0, "Page size must be greater than zero")
- require(pageNumber > 0, "Page number must be greater than zero")
-
- def offset: Int = pageSize * (pageNumber - 1)
-}
-
-final case class ListResponse[+T](items: Seq[T], meta: ListResponse.Meta)
-
-object ListResponse {
-
- def apply[T](items: Seq[T], size: Int, pagination: Option[Pagination]): ListResponse[T] =
- ListResponse(
- items = items,
- meta = ListResponse.Meta(size, pagination.fold(1)(_.pageNumber), pagination.fold(size)(_.pageSize)))
-
- final case class Meta(itemsCount: Int, pageNumber: Int, pageSize: Int)
-
- object Meta {
- def apply(itemsCount: Int, pagination: Pagination): Meta =
- Meta(itemsCount, pagination.pageNumber, pagination.pageSize)
+ implicit class FutureExtensions[T](future: Future[T]) {
+ def passThroughExternalServiceException(implicit executionContext: ExecutionContext): Future[T] =
+ future.transform(identity, {
+ case ExternalServiceException(_, _, Some(e)) => e
+ case t: Throwable => t
+ })
}
-}
-
-object `package` {
implicit class OptionTRestAdditions[T](optionT: OptionT[Future, T]) {
def responseOrNotFound(successCode: StatusCodes.Success = StatusCodes.OK)(
implicit F: Functor[Future],
diff --git a/src/main/scala/xyz/driver/core/rest/serviceDiscovery.scala b/core-rest/src/main/scala/xyz/driver/core/rest/serviceDiscovery.scala
index 55f1a2e..55f1a2e 100644
--- a/src/main/scala/xyz/driver/core/rest/serviceDiscovery.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/serviceDiscovery.scala
diff --git a/src/main/scala/xyz/driver/core/rest/serviceRequestContext.scala b/core-rest/src/main/scala/xyz/driver/core/rest/serviceRequestContext.scala
index d2e4bc3..d2e4bc3 100644
--- a/src/main/scala/xyz/driver/core/rest/serviceRequestContext.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/rest/serviceRequestContext.scala
diff --git a/src/main/scala/xyz/driver/core/swagger.scala b/core-rest/src/main/scala/xyz/driver/core/swagger.scala
index 0c1e15d..0c1e15d 100644
--- a/src/main/scala/xyz/driver/core/swagger.scala
+++ b/core-rest/src/main/scala/xyz/driver/core/swagger.scala
diff --git a/src/test/scala/xyz/driver/core/AuthTest.scala b/core-rest/src/test/scala/xyz/driver/core/AuthTest.scala
index 2e772fb..8c32aa7 100644
--- a/src/test/scala/xyz/driver/core/AuthTest.scala
+++ b/core-rest/src/test/scala/xyz/driver/core/AuthTest.scala
@@ -9,11 +9,12 @@ import akka.http.scaladsl.model.headers.{
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server._
import akka.http.scaladsl.testkit.ScalatestRouteTest
+import com.typesafe.scalalogging.Logger
import org.scalatest.{FlatSpec, Matchers}
+import org.slf4j.helpers.NOPLogger
import pdi.jwt.{Jwt, JwtAlgorithm}
import xyz.driver.core.auth._
import xyz.driver.core.domain.Email
-import xyz.driver.core.logging._
import xyz.driver.core.rest._
import xyz.driver.core.rest.auth._
import xyz.driver.core.time.Time
@@ -53,7 +54,7 @@ class AuthTest extends FlatSpec with Matchers with ScalatestRouteTest {
val authorization = new ChainedAuthorization[User](tokenAuthorization, basicAuthorization)
- val authStatusService = new AuthProvider[User](authorization, NoLogger) {
+ val authStatusService = new AuthProvider[User](authorization, Logger(NOPLogger.NOP_LOGGER)) {
override def authenticatedUser(implicit ctx: ServiceRequestContext): OptionT[Future, User] =
OptionT.optionT[Future] {
if (ctx.contextHeaders.keySet.contains(AuthProvider.AuthenticationTokenHeader)) {
diff --git a/src/test/scala/xyz/driver/core/GeneratorsTest.scala b/core-rest/src/test/scala/xyz/driver/core/GeneratorsTest.scala
index 7e740a4..7e740a4 100644
--- a/src/test/scala/xyz/driver/core/GeneratorsTest.scala
+++ b/core-rest/src/test/scala/xyz/driver/core/GeneratorsTest.scala
diff --git a/src/test/scala/xyz/driver/core/JsonTest.scala b/core-rest/src/test/scala/xyz/driver/core/JsonTest.scala
index 3e68d90..9f54c04 100644
--- a/src/test/scala/xyz/driver/core/JsonTest.scala
+++ b/core-rest/src/test/scala/xyz/driver/core/JsonTest.scala
@@ -274,6 +274,21 @@ class JsonTest extends WordSpec with Matchers with Inspectors {
json.phoneNumberFormat.read(phoneJson)
}.getMessage shouldBe "Invalid phone number"
}
+
+ "parse phone number from string" in {
+ JsString("+14243039608").convertTo[PhoneNumber] shouldBe PhoneNumber("1", "4243039608")
+ }
+ }
+
+ "Path matcher for PhoneNumber" should {
+ "read valid phone number" in {
+ val string = "+14243039608x23"
+ val phone = PhoneNumber("1", "4243039608", Some("23"))
+
+ val matcher = PathMatcher("foo") / PhoneInPath
+
+ matcher(Uri.Path("foo") / string / "bar") shouldBe Matched(Uri.Path./("bar"), Tuple1(phone))
+ }
}
"Json format for ADT mappings" should {
diff --git a/src/test/scala/xyz/driver/core/TestTypes.scala b/core-rest/src/test/scala/xyz/driver/core/TestTypes.scala
index bb25deb..bb25deb 100644
--- a/src/test/scala/xyz/driver/core/TestTypes.scala
+++ b/core-rest/src/test/scala/xyz/driver/core/TestTypes.scala
diff --git a/src/test/scala/xyz/driver/core/rest/DriverRouteTest.scala b/core-rest/src/test/scala/xyz/driver/core/rest/DriverRouteTest.scala
index 86cf8b5..d1172fa 100644
--- a/src/test/scala/xyz/driver/core/rest/DriverRouteTest.scala
+++ b/core-rest/src/test/scala/xyz/driver/core/rest/DriverRouteTest.scala
@@ -8,9 +8,8 @@ import akka.http.scaladsl.server.{Directives, RejectionHandler, Route}
import akka.http.scaladsl.testkit.ScalatestRouteTest
import com.typesafe.scalalogging.Logger
import org.scalatest.{AsyncFlatSpec, Matchers}
-import xyz.driver.core.FutureExtensions
+import org.slf4j.helpers.NOPLogger
import xyz.driver.core.json.serviceExceptionFormat
-import xyz.driver.core.logging.NoLogger
import xyz.driver.core.rest.errors._
import scala.concurrent.Future
@@ -18,7 +17,7 @@ import scala.concurrent.Future
class DriverRouteTest
extends AsyncFlatSpec with ScalatestRouteTest with SprayJsonSupport with Matchers with Directives {
class TestRoute(override val route: Route) extends DriverRoute {
- override def log: Logger = NoLogger
+ override def log: Logger = Logger(NOPLogger.NOP_LOGGER)
}
"DriverRoute" should "respond with 200 OK for a basic route" in {
@@ -95,16 +94,6 @@ class DriverRouteTest
}
}
- it should "respond with a 500 for DatabaseException" in {
- val route = new TestRoute(akkaComplete(Future.failed[String](DatabaseException())))
-
- Post("/api/v1/foo/bar") ~> route.routeWithDefaults ~> check {
- handled shouldBe true
- status shouldBe StatusCodes.InternalServerError
- responseAs[ServiceException] shouldBe DatabaseException()
- }
- }
-
it should "add a `Connection: close` header to avoid clashing with envoy's timeouts" in {
val rejectionHandler = RejectionHandler.newBuilder().handleNotFound(complete(StatusCodes.NotFound)).result()
val route = new TestRoute(handleRejections(rejectionHandler)((get & path("foo"))(complete("OK"))))
diff --git a/src/test/scala/xyz/driver/core/rest/PatchDirectivesTest.scala b/core-rest/src/test/scala/xyz/driver/core/rest/PatchDirectivesTest.scala
index 987717d..987717d 100644
--- a/src/test/scala/xyz/driver/core/rest/PatchDirectivesTest.scala
+++ b/core-rest/src/test/scala/xyz/driver/core/rest/PatchDirectivesTest.scala
diff --git a/src/test/scala/xyz/driver/core/rest/RestTest.scala b/core-rest/src/test/scala/xyz/driver/core/rest/RestTest.scala
index 19e4ed1..19e4ed1 100644
--- a/src/test/scala/xyz/driver/core/rest/RestTest.scala
+++ b/core-rest/src/test/scala/xyz/driver/core/rest/RestTest.scala
diff --git a/src/main/scala/xyz/driver/core/storage/AliyunBlobStorage.scala b/core-storage/src/main/scala/xyz/driver/core/storage/AliyunBlobStorage.scala
index b5e8678..388b8f5 100644
--- a/src/main/scala/xyz/driver/core/storage/AliyunBlobStorage.scala
+++ b/core-storage/src/main/scala/xyz/driver/core/storage/AliyunBlobStorage.scala
@@ -3,6 +3,7 @@ package xyz.driver.core.storage
import java.io.ByteArrayInputStream
import java.net.URL
import java.nio.file.Path
+import java.time.Clock
import java.util.Date
import akka.Done
@@ -11,7 +12,6 @@ import akka.util.ByteString
import com.aliyun.oss.OSSClient
import com.aliyun.oss.model.ObjectPermission
import com.typesafe.config.Config
-import xyz.driver.core.time.provider.TimeProvider
import scala.collection.JavaConverters._
import scala.concurrent.duration.Duration
@@ -20,7 +20,7 @@ import scala.concurrent.{ExecutionContext, Future}
class AliyunBlobStorage(
client: OSSClient,
bucketId: String,
- timeProvider: TimeProvider,
+ clock: Clock,
chunkSize: Int = AliyunBlobStorage.DefaultChunkSize)(implicit ec: ExecutionContext)
extends SignedBlobStorage {
override def uploadContent(name: String, content: Array[Byte]): Future[String] = Future {
@@ -81,7 +81,7 @@ class AliyunBlobStorage(
override def signedDownloadUrl(name: String, duration: Duration): Future[Option[URL]] = Future {
if (client.doesObjectExist(bucketId, name)) {
- val expiration = new Date(timeProvider.currentTime().advanceBy(duration).millis)
+ val expiration = new Date(clock.millis() + duration.toMillis)
Some(client.generatePresignedUrl(bucketId, name, expiration))
} else {
None
@@ -92,17 +92,18 @@ class AliyunBlobStorage(
object AliyunBlobStorage {
val DefaultChunkSize: Int = 8192
- def apply(config: Config, bucketId: String, timeProvider: TimeProvider)(
- implicit ec: ExecutionContext): AliyunBlobStorage = {
+ def apply(config: Config, bucketId: String, clock: Clock)(implicit ec: ExecutionContext): AliyunBlobStorage = {
val clientId = config.getString("storage.aliyun.clientId")
val clientSecret = config.getString("storage.aliyun.clientSecret")
val endpoint = config.getString("storage.aliyun.endpoint")
- this(clientId, clientSecret, endpoint, bucketId, timeProvider)
+ this(clientId, clientSecret, endpoint, bucketId, clock)
}
- def apply(clientId: String, clientSecret: String, endpoint: String, bucketId: String, timeProvider: TimeProvider)(
+ def apply(clientId: String, clientSecret: String, region: String, bucketId: String, clock: Clock)(
implicit ec: ExecutionContext): AliyunBlobStorage = {
- val client = new OSSClient(endpoint, clientId, clientSecret)
- new AliyunBlobStorage(client, bucketId, timeProvider)
+ // https://www.alibabacloud.com/help/doc-detail/31837.htm
+ val endpoint = s"https://oss-$region.aliyuncs.com"
+ val client = new OSSClient(endpoint, clientId, clientSecret)
+ new AliyunBlobStorage(client, bucketId, clock)
}
}
diff --git a/src/main/scala/xyz/driver/core/storage/BlobStorage.scala b/core-storage/src/main/scala/xyz/driver/core/storage/BlobStorage.scala
index 0cde96a..0cde96a 100644
--- a/src/main/scala/xyz/driver/core/storage/BlobStorage.scala
+++ b/core-storage/src/main/scala/xyz/driver/core/storage/BlobStorage.scala
diff --git a/src/main/scala/xyz/driver/core/storage/FileSystemBlobStorage.scala b/core-storage/src/main/scala/xyz/driver/core/storage/FileSystemBlobStorage.scala
index e12c73d..e12c73d 100644
--- a/src/main/scala/xyz/driver/core/storage/FileSystemBlobStorage.scala
+++ b/core-storage/src/main/scala/xyz/driver/core/storage/FileSystemBlobStorage.scala
diff --git a/src/main/scala/xyz/driver/core/storage/GcsBlobStorage.scala b/core-storage/src/main/scala/xyz/driver/core/storage/GcsBlobStorage.scala
index 95164c7..95164c7 100644
--- a/src/main/scala/xyz/driver/core/storage/GcsBlobStorage.scala
+++ b/core-storage/src/main/scala/xyz/driver/core/storage/GcsBlobStorage.scala
diff --git a/src/main/scala/xyz/driver/core/storage/channelStreams.scala b/core-storage/src/main/scala/xyz/driver/core/storage/channelStreams.scala
index fc652be..fc652be 100644
--- a/src/main/scala/xyz/driver/core/storage/channelStreams.scala
+++ b/core-storage/src/main/scala/xyz/driver/core/storage/channelStreams.scala
diff --git a/src/test/scala/xyz/driver/core/BlobStorageTest.scala b/core-storage/src/test/scala/xyz/driver/core/BlobStorageTest.scala
index 811cc60..811cc60 100644
--- a/src/test/scala/xyz/driver/core/BlobStorageTest.scala
+++ b/core-storage/src/test/scala/xyz/driver/core/BlobStorageTest.scala
diff --git a/core-testkit/src/main/scala/akka/http/scaladsl/testkit/TestRouteServiceTransport.scala b/core-testkit/src/main/scala/akka/http/scaladsl/testkit/TestRouteServiceTransport.scala
new file mode 100644
index 0000000..36e3596
--- /dev/null
+++ b/core-testkit/src/main/scala/akka/http/scaladsl/testkit/TestRouteServiceTransport.scala
@@ -0,0 +1,79 @@
+package akka.http.scaladsl.testkit
+
+import akka.actor.ActorSystem
+import akka.http.javadsl.testkit.DefaultHostInfo
+import akka.http.scaladsl.model._
+import akka.http.scaladsl.model.headers.{Host, RawHeader}
+import akka.http.scaladsl.server._
+import akka.http.scaladsl.settings.RoutingSettings
+import akka.http.scaladsl.unmarshalling.Unmarshal
+import akka.stream.{ActorMaterializer, Materializer}
+import org.slf4j.MDC
+import xyz.driver.core.rest.errors.ExternalServiceException
+import xyz.driver.core.rest.{ContextHeaders, ServiceRequestContext, ServiceTransport}
+
+import scala.concurrent.{ExecutionContextExecutor, Future}
+import scala.concurrent.duration._
+
+class TestRouteServiceTransport(route: Route, routeTimeout: FiniteDuration = 10 seconds)(
+ implicit executor: ExecutionContextExecutor,
+ system: ActorSystem)
+ extends ServiceTransport with RouteTestResultComponent {
+
+ def defaultHost: DefaultHostInfo = DefaultHostInfo(Host("example.com"), securedConnection = false)
+ def routingLog: RoutingLog = RoutingLog(system.log)
+ def routingSettings: RoutingSettings = RoutingSettings.apply(system)
+ implicit val materializer = ActorMaterializer()
+
+ override def sendRequestGetResponse(context: ServiceRequestContext)(
+ requestStub: HttpRequest): Future[HttpResponse] = {
+
+ val request = requestStub
+ .withHeaders(requestStub.headers ++ context.contextHeaders.toSeq.map {
+ case (ContextHeaders.TrackingIdHeader, _) =>
+ RawHeader(ContextHeaders.TrackingIdHeader, context.trackingId)
+ case (ContextHeaders.StacktraceHeader, _) =>
+ RawHeader(
+ ContextHeaders.StacktraceHeader,
+ Option(MDC.get("stack"))
+ .orElse(context.contextHeaders.get(ContextHeaders.StacktraceHeader))
+ .getOrElse(""))
+ case (header, headerValue) => RawHeader(header, headerValue)
+ }: _*)
+
+ // Code below is forked from `akka.http.scaladsl.testkit.RouteTest.TildeArrow.injectIntoRoute`,
+ // because it doesn't allow to call just this code outside of the DSL for testkit tests
+ val routeTestResult = new RouteTestResult(routeTimeout)
+
+ val effectiveRequest = request
+
+ val log = routingLog.requestLog(effectiveRequest)
+ val ctx = new RequestContextImpl(effectiveRequest, log, routingSettings)(executor, materializer)
+ val sealedExceptionHandler = ExceptionHandler.default(routingSettings)
+ val semiSealedRoute = Directives.handleExceptions(sealedExceptionHandler)(route)
+ val deferrableRouteResult = semiSealedRoute(ctx)
+
+ deferrableRouteResult.map(r => { routeTestResult.handleResult(r); routeTestResult.response })
+ }
+
+ override def sendRequest(context: ServiceRequestContext)(requestStub: HttpRequest)(
+ implicit mat: Materializer): Future[Unmarshal[ResponseEntity]] = {
+ sendRequestGetResponse(context)(requestStub) flatMap { response =>
+ if (response.status == StatusCodes.NotFound) {
+ Future.successful(Unmarshal(HttpEntity.Empty: ResponseEntity))
+ } else if (response.status.isFailure()) {
+ val serviceCalled = s"${requestStub.method} ${requestStub.uri}"
+ Unmarshal(response.entity).to[String] flatMap { errorString =>
+ import spray.json._
+ import xyz.driver.core.json._
+ val serviceException = scala.util.Try(serviceExceptionFormat.read(errorString.parseJson)).toOption
+ Future.failed(ExternalServiceException(serviceCalled, errorString, serviceException))
+ }
+ } else {
+ Future.successful(Unmarshal(response.entity))
+ }
+ }
+ }
+
+ override def failTest(msg: String): Nothing = throw new Exception(msg)
+}
diff --git a/core-testkit/src/main/scala/xyz/driver/core/testkit/AsyncDatabaseBackedRouteTest.scala b/core-testkit/src/main/scala/xyz/driver/core/testkit/AsyncDatabaseBackedRouteTest.scala
new file mode 100644
index 0000000..8ce33f6
--- /dev/null
+++ b/core-testkit/src/main/scala/xyz/driver/core/testkit/AsyncDatabaseBackedRouteTest.scala
@@ -0,0 +1,28 @@
+package xyz.driver.core.testkit
+
+import java.util.concurrent.Executors
+
+import akka.actor.ActorSystem
+import akka.http.scaladsl.server.directives.FutureDirectives
+import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
+import org.scalamock.scalatest.AsyncMockFactory
+import org.scalatest.AsyncFlatSpec
+
+import scala.concurrent.duration._
+import scala.concurrent.{ExecutionContext, ExecutionContextExecutor}
+
+trait AsyncDatabaseBackedRouteTest
+ extends AsyncFlatSpec with DriverFunctionalTest with AsyncMockFactory with ScalatestRouteTest {
+
+ def route: FutureDirectives
+
+ val defaultTimeOut: FiniteDuration = 5.seconds
+ implicit def default(implicit system: ActorSystem): RouteTestTimeout = RouteTestTimeout(defaultTimeOut)
+
+ override implicit val executor: ExecutionContextExecutor =
+ ExecutionContext.fromExecutor(Executors.newFixedThreadPool(4))
+ override implicit def executionContext: ExecutionContext = executor
+
+ override def beforeAll: Unit = super.beforeAll()
+ override def afterAll: Unit = super.afterAll()
+}
diff --git a/core-testkit/src/main/scala/xyz/driver/core/testkit/DriverFunctionalTest.scala b/core-testkit/src/main/scala/xyz/driver/core/testkit/DriverFunctionalTest.scala
new file mode 100644
index 0000000..f4c793b
--- /dev/null
+++ b/core-testkit/src/main/scala/xyz/driver/core/testkit/DriverFunctionalTest.scala
@@ -0,0 +1,18 @@
+package xyz.driver.core.testkit
+
+import com.typesafe.scalalogging.Logger
+import org.scalatest.{Matchers, Suite}
+import org.slf4j.helpers.NOPLogger
+import xyz.driver.core.time.Time
+import xyz.driver.core.time.provider.{SpecificTimeProvider, TimeProvider}
+
+trait DriverFunctionalTest extends Matchers with postgres.DockerPostgresFixtureDatabase {
+ self: Suite =>
+
+ // Needed to reset some external libraries timezones (e.g., HSQLDB)
+ // for local development matching CI and deployed environment
+ java.util.TimeZone.setDefault(java.util.TimeZone.getTimeZone("Etc/UTC"))
+
+ val log: Logger = Logger(NOPLogger.NOP_LOGGER)
+ val timeProvider: TimeProvider = new SpecificTimeProvider(Time(100000))
+}
diff --git a/core-testkit/src/main/scala/xyz/driver/core/testkit/FixtureDatabase.scala b/core-testkit/src/main/scala/xyz/driver/core/testkit/FixtureDatabase.scala
new file mode 100644
index 0000000..aa07668
--- /dev/null
+++ b/core-testkit/src/main/scala/xyz/driver/core/testkit/FixtureDatabase.scala
@@ -0,0 +1,60 @@
+package xyz.driver.core.testkit
+
+import java.nio.file.{Files, Path}
+
+import scala.concurrent.Await
+import scala.concurrent.duration._
+
+import org.scalatest.{BeforeAndAfterEach, Suite}
+import slick.basic.BasicProfile
+import slick.jdbc.JdbcProfile
+import slick.relational.RelationalProfile
+
+trait FixtureDatabase[P <: BasicProfile] { self: Suite =>
+ val profile: P
+ val database: P#Backend#DatabaseDef
+}
+
+trait CreateAndDropSchemaForEach[P <: RelationalProfile] extends BeforeAndAfterEach {
+ self: Suite with FixtureDatabase[P] =>
+ import profile.api._
+ def schema: profile.SchemaDescription
+
+ override protected def beforeEach() = {
+ Await.result(database.run(schema.create), 5.seconds)
+ super.beforeEach()
+ }
+
+ override protected def afterEach() = {
+ try super.afterEach()
+ finally Await.result(database.run(schema.drop), 5.seconds)
+ }
+}
+
+trait TruncateSchemaAfterEach[P <: RelationalProfile] extends BeforeAndAfterEach {
+ self: Suite with FixtureDatabase[P] =>
+ import profile.api._
+
+ def schema: profile.SchemaDescription
+
+ override protected def afterEach() = {
+ try super.afterEach()
+ finally Await.result(database.run(schema.truncate), 5.seconds)
+ }
+}
+
+trait InsertBeforeEach[P <: JdbcProfile] extends BeforeAndAfterEach { self: Suite with FixtureDatabase[P] =>
+ import profile.api._
+
+ val insertsFiles: Set[Path]
+
+ def insertTestData(insertsFile: Path): DBIO[Int] = {
+ val rawInserts = new String(Files.readAllBytes(insertsFile), "UTF-8")
+ sql"#$rawInserts".asUpdate
+ }
+
+ override protected def beforeEach(): Unit = {
+ concurrent.Await.result(database.run(DBIO.sequence(insertsFiles.toList.map(insertTestData))), 30.seconds)
+ super.beforeEach()
+ }
+}
diff --git a/core-testkit/src/main/scala/xyz/driver/core/testkit/RestDatabaseResetService.scala b/core-testkit/src/main/scala/xyz/driver/core/testkit/RestDatabaseResetService.scala
new file mode 100644
index 0000000..db02bbb
--- /dev/null
+++ b/core-testkit/src/main/scala/xyz/driver/core/testkit/RestDatabaseResetService.scala
@@ -0,0 +1,70 @@
+package xyz.driver.core.testkit
+
+import akka.actor.ActorSystem
+import akka.http.scaladsl.model._
+import akka.stream.ActorMaterializer
+import spray.json._
+import xyz.driver.core.Name
+import xyz.driver.core.rest.{RestService, ServiceRequestContext, ServiceTransport}
+
+import scala.concurrent.{ExecutionContext, Future}
+import scalaz.{ListT, OptionT}
+
+object RestDatabaseResetService {
+ import DefaultJsonProtocol._
+ import xyz.driver.core.json._
+
+ final case class Application(name: Name[Application], tag: String)
+
+ final case class Environment(name: Name[Environment], applications: Seq[Application], status: String)
+
+ implicit val applicationJsonFormat = jsonFormat2(Application)
+
+ implicit val environmentJsonFormat = jsonFormat3(Environment)
+}
+
+class RestDatabaseResetService(
+ transport: ServiceTransport,
+ baseUri: Uri,
+ actorSystem: ActorSystem,
+ executionContext: ExecutionContext)
+ extends RestService {
+
+ import DefaultJsonProtocol._
+ import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+ import RestDatabaseResetService._
+
+ protected implicit val exec = executionContext
+ protected implicit val materializer = ActorMaterializer()(actorSystem)
+
+ def getAvailableEnvironments(): ListT[Future, String] = {
+ val request = get(baseUri, s"/v1/environment")
+ listResponse[String](transport.sendRequest(new ServiceRequestContext())(request))
+ }
+
+ def getEnvironmentDetails(envName: Name[Environment]): ListT[Future, Environment] = {
+ val requestStub = get(baseUri, s"/v1/environment/$envName")
+ listResponse[Environment](transport.sendRequest(new ServiceRequestContext())(requestStub))
+ }
+
+ def getEnvironmentStatus(envName: Name[Environment]): OptionT[Future, String] = {
+ val requestStub = get(baseUri, s"/v1/environment/$envName/status")
+ optionalResponse[String](transport.sendRequest(new ServiceRequestContext())(requestStub))
+ }
+
+ def resetEnvironmentStatus(envName: Name[Environment], newStatus: String): OptionT[Future, String] = {
+ val requestStub = HttpRequest(HttpMethods.POST, endpointUri(baseUri, s"/v1/environment/$envName/status"))
+ optionalResponse[String](transport.sendRequest(new ServiceRequestContext())(requestStub))
+ }
+
+ def resetEnvironmentApplicationsData(envName: Name[Environment]): OptionT[Future, Unit] = {
+ val requestStub = HttpRequest(HttpMethods.POST, endpointUri(baseUri, s"/v1/environment/$envName/reset"))
+ unitResponse(transport.sendRequest(new ServiceRequestContext())(requestStub))
+ }
+
+ def updateEnvironmentState(updatedEnvironment: Environment)(
+ implicit ctx: ServiceRequestContext): OptionT[Future, Environment] = {
+ val requestStub = postJson(baseUri, s"/v1/environment/${updatedEnvironment.name}", updatedEnvironment.toJson)
+ optionalResponse[Environment](transport.sendRequest(ctx)(requestStub))
+ }
+}
diff --git a/core-testkit/src/main/scala/xyz/driver/core/testkit/hsql/HsqlTestDatabase.scala b/core-testkit/src/main/scala/xyz/driver/core/testkit/hsql/HsqlTestDatabase.scala
new file mode 100644
index 0000000..8a4c3e8
--- /dev/null
+++ b/core-testkit/src/main/scala/xyz/driver/core/testkit/hsql/HsqlTestDatabase.scala
@@ -0,0 +1,26 @@
+package xyz.driver.test.hsql
+
+import java.nio.file.{Files, Paths}
+
+import slick.dbio.DBIO
+
+trait HsqlTestDatabase {
+ def insertTestData(database: xyz.driver.core.database.Database, filePath: String): DBIO[Int] = {
+ import database.profile.api._
+
+ val file = Paths.get(filePath)
+ val sqlLine = new String(Files.readAllBytes(file), "UTF-8")
+
+ val createProcedure =
+ sqlu"""CREATE PROCEDURE INSERT_TEST_DATA()
+ MODIFIES SQL DATA
+ BEGIN ATOMIC
+ #$sqlLine
+ END;
+ """
+ val callProcedure = sqlu"""{call INSERT_TEST_DATA()}"""
+ val dropProcedure = sqlu"""drop PROCEDURE INSERT_TEST_DATA;"""
+
+ createProcedure >> callProcedure >> dropProcedure
+ }
+}
diff --git a/core-testkit/src/main/scala/xyz/driver/core/testkit/postgres/DockerPostgresDatabase.scala b/core-testkit/src/main/scala/xyz/driver/core/testkit/postgres/DockerPostgresDatabase.scala
new file mode 100644
index 0000000..41e6e0d
--- /dev/null
+++ b/core-testkit/src/main/scala/xyz/driver/core/testkit/postgres/DockerPostgresDatabase.scala
@@ -0,0 +1,46 @@
+package xyz.driver.core.testkit
+package postgres
+
+import xyz.driver.core.make
+
+trait DockerPostgresDatabase {
+ import com.spotify.docker.client._
+ import com.spotify.docker.client.messages._
+
+ lazy val dockerClient: DockerClient = DefaultDockerClient.fromEnv().build()
+
+ val postgresVersion: String = "9.6"
+
+ def setupDockerDatabase(
+ username: String = "postgres",
+ password: String = "postgres",
+ database: String = "postgres",
+ hostPort: Int = 15432): String = {
+ import collection.JavaConverters._
+
+ dockerClient.pull(s"postgres:$postgresVersion")
+
+ val portBindings: Map[String, List[PortBinding]] = Map("5432" -> List(PortBinding.of("0.0.0.0", hostPort)))
+ val portBindingsJava = portBindings.mapValues(_.asJava).asJava
+ val hostConfig = HostConfig.builder().portBindings(portBindingsJava).build()
+ val containerConfig =
+ ContainerConfig
+ .builder()
+ .hostConfig(hostConfig)
+ .image(s"postgres:$postgresVersion")
+ .exposedPorts("5432")
+ .env(
+ s"POSTGRES_USER=$username",
+ s"POSTGRES_DB=$database",
+ s"POSTGRES_PASSWORD=$password"
+ )
+ .build()
+
+ make(dockerClient.createContainer(containerConfig).id())(dockerClient.startContainer)
+ }
+
+ def killDockerDatabase(containerId: String): Unit = {
+ dockerClient.killContainer(containerId)
+ dockerClient.removeContainer(containerId)
+ }
+}
diff --git a/core-testkit/src/main/scala/xyz/driver/core/testkit/postgres/DockerPostgresFixtureDatabase.scala b/core-testkit/src/main/scala/xyz/driver/core/testkit/postgres/DockerPostgresFixtureDatabase.scala
new file mode 100644
index 0000000..3e00bf5
--- /dev/null
+++ b/core-testkit/src/main/scala/xyz/driver/core/testkit/postgres/DockerPostgresFixtureDatabase.scala
@@ -0,0 +1,74 @@
+package xyz.driver.core.testkit
+package postgres
+
+import java.net.ServerSocket
+
+import org.scalatest.{BeforeAndAfterAll, Suite}
+import slick.jdbc.PostgresProfile
+import xyz.driver.core.using
+
+import scala.concurrent.Await
+import scala.concurrent.duration._
+
+trait DockerPostgresFixtureDatabase
+ extends FixtureDatabase[PostgresProfile] with DockerPostgresDatabase with BeforeAndAfterAll {
+ self: Suite =>
+ private val dbName: String = sys.env.getOrElse("TEST_DB_NAME", "postgres")
+ private val username: String = sys.env.getOrElse("TEST_DB_USERNAME", "postgres")
+ private val password: String = sys.env.getOrElse("TEST_DB_PASSWORD", "postgres")
+ private val port: Int = sys.env.get("TEST_DB_PORT").fold(getRandomPort())(_.toInt)
+
+ private val enabled: Boolean = !sys.env.get("DISABLE_DOCKER_TEST_DB").contains("true")
+
+ protected val connectionTimeout: Duration = 16.seconds
+
+ final val profile = PostgresProfile
+ final override val database = profile.backend.Database.forURL(
+ driver = "org.postgresql.Driver",
+ url = s"jdbc:postgresql://localhost:$port/$dbName",
+ user = username,
+ password = password)
+
+ object driverDatabase extends xyz.driver.core.database.Database {
+ override val profile = self.profile
+ override val database = self.database
+ }
+
+ private def getRandomPort(): Int = using(new ServerSocket(0))(_.getLocalPort())
+
+ @SuppressWarnings(Array("org.wartremover.warts.Var"))
+ private var dockerContainerId: Option[String] = None
+
+ private def waitForContainer(): Unit = {
+ import concurrent.ExecutionContext.Implicits.global
+ import profile.api._
+
+ val expiration = System.currentTimeMillis() + connectionTimeout.toMillis
+
+ val query = sql"SELECT 1;".as[Int]
+ def dbReady = Await.result(
+ database
+ .run(query)
+ .map(_ == Vector(1))
+ .recover({ case _: org.postgresql.util.PSQLException => false }),
+ connectionTimeout
+ )
+
+ while (!dbReady && System.currentTimeMillis() < expiration) {
+ Thread.sleep(100)
+ }
+ }
+
+ override protected def beforeAll(): Unit = {
+ if (enabled) {
+ dockerContainerId = Some(super.setupDockerDatabase(username, password, dbName, port))
+ waitForContainer()
+ }
+ super.beforeAll()
+ }
+
+ override protected def afterAll(): Unit = {
+ try super.afterAll()
+ finally dockerContainerId.foreach(killDockerDatabase)
+ }
+}
diff --git a/core-testkit/src/main/scala/xyz/driver/test/renames.scala b/core-testkit/src/main/scala/xyz/driver/test/renames.scala
new file mode 100644
index 0000000..a9dfe04
--- /dev/null
+++ b/core-testkit/src/main/scala/xyz/driver/test/renames.scala
@@ -0,0 +1,46 @@
+package xyz.driver
+
+import xyz.driver.core.testkit
+import slick.basic.BasicProfile
+import slick.jdbc.JdbcProfile
+import slick.relational.RelationalProfile
+
+package object test {
+
+ @deprecated("moved to package `xyz.driver.core.testkit`", "2.0")
+ type AsyncDatabaseBackedRouteTest = testkit.AsyncDatabaseBackedRouteTest
+
+ @deprecated("moved to package `xyz.driver.core.testkit`", "2.0")
+ type DriverFunctionalTest = testkit.DriverFunctionalTest
+
+ @deprecated("moved to package `xyz.driver.core.testkit`", "2.0")
+ type FixtureDatabase[P <: BasicProfile] = testkit.FixtureDatabase[P]
+
+ @deprecated("moved to package `xyz.driver.core.testkit`", "2.0")
+ type CreateAndDropSchemaForEach[P <: RelationalProfile] = testkit.CreateAndDropSchemaForEach[P]
+
+ @deprecated("moved to package `xyz.driver.core.testkit`", "2.0")
+ type TruncateSchemaAfterEach[P <: RelationalProfile] = testkit.TruncateSchemaAfterEach[P]
+
+ @deprecated("moved to package `xyz.driver.core.testkit`", "2.0")
+ type InsertBeforeEach[P <: JdbcProfile] = testkit.InsertBeforeEach[P]
+
+ @deprecated("moved to package `xyz.driver.core.testkit`", "2.0")
+ type RestDatabaseResetService = testkit.RestDatabaseResetService
+
+ @deprecated("moved to package `xyz.driver.core.testkit`", "2.0")
+ val RestDatabaseResetService: testkit.RestDatabaseResetService.type = testkit.RestDatabaseResetService
+
+}
+
+package test {
+ package object postgres {
+
+ @deprecated("moved to package `xyz.driver.core.testkit.postgres`", "2.0")
+ type DockerPostgresDatabase = xyz.driver.core.testkit.postgres.DockerPostgresDatabase
+
+ @deprecated("moved to package `xyz.driver.core.testkit.postgres`", "2.0")
+ type DockerPostgresFixtureDatabase = xyz.driver.core.testkit.postgres.DockerPostgresFixtureDatabase
+
+ }
+}
diff --git a/core-types/src/main/scala/xyz/driver/core/DatabaseException.scala b/core-types/src/main/scala/xyz/driver/core/DatabaseException.scala
new file mode 100644
index 0000000..3babf5c
--- /dev/null
+++ b/core-types/src/main/scala/xyz/driver/core/DatabaseException.scala
@@ -0,0 +1,3 @@
+package xyz.driver.core
+
+final case class DatabaseException(message: String = "Database access error") extends RuntimeException(message)
diff --git a/src/main/scala/xyz/driver/core/core.scala b/core-types/src/main/scala/xyz/driver/core/core.scala
index 11c1ffe..d88bf09 100644
--- a/src/main/scala/xyz/driver/core/core.scala
+++ b/core-types/src/main/scala/xyz/driver/core/core.scala
@@ -3,11 +3,8 @@ package xyz.driver
import eu.timepit.refined.api.{Refined, Validate}
import eu.timepit.refined.collection.NonEmpty
import scalaz.{Equal, Monad, OptionT}
-import xyz.driver.core.rest.errors.ExternalServiceException
import xyz.driver.core.tagging.Tagged
-import scala.concurrent.{ExecutionContext, Future}
-
// TODO: this package seems too complex, look at all the features we need!
import scala.language.{higherKinds, implicitConversions, reflectiveCalls}
@@ -53,13 +50,6 @@ package object core {
OptionT.optionT[H](monadT(monadicValue)(_ => Option(())))
}
- implicit class FutureExtensions[T](future: Future[T]) {
- def passThroughExternalServiceException(implicit executionContext: ExecutionContext): Future[T] =
- future.transform(identity, {
- case ExternalServiceException(_, _, Some(e)) => e
- case t: Throwable => t
- })
- }
}
package core {
diff --git a/src/main/scala/xyz/driver/core/date.scala b/core-types/src/main/scala/xyz/driver/core/date.scala
index 5454093..5454093 100644
--- a/src/main/scala/xyz/driver/core/date.scala
+++ b/core-types/src/main/scala/xyz/driver/core/date.scala
diff --git a/core-types/src/main/scala/xyz/driver/core/deprecations.scala b/core-types/src/main/scala/xyz/driver/core/deprecations.scala
new file mode 100644
index 0000000..401e6c6
--- /dev/null
+++ b/core-types/src/main/scala/xyz/driver/core/deprecations.scala
@@ -0,0 +1,5 @@
+package xyz.driver.core.rest
+
+package object errors {
+ //type DatabaseException = xyz.driver.core.errors.DatabaseException
+}
diff --git a/core-types/src/main/scala/xyz/driver/core/domain.scala b/core-types/src/main/scala/xyz/driver/core/domain.scala
new file mode 100644
index 0000000..f3b8337
--- /dev/null
+++ b/core-types/src/main/scala/xyz/driver/core/domain.scala
@@ -0,0 +1,73 @@
+package xyz.driver.core
+
+import com.google.i18n.phonenumbers.PhoneNumberUtil
+import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat
+import scalaz.Equal
+import scalaz.std.string._
+import scalaz.syntax.equal._
+
+import scala.util.Try
+import scala.util.control.NonFatal
+
+object domain {
+
+ final case class Email(username: String, domain: String) {
+
+ def value: String = toString
+
+ override def toString: String = username + "@" + domain
+ }
+
+ object Email {
+ implicit val emailEqual: Equal[Email] = Equal.equal {
+ case (left, right) => left.toString.toLowerCase === right.toString.toLowerCase
+ }
+
+ def parse(emailString: String): Option[Email] = {
+ Some(emailString.split("@")) collect {
+ case Array(username, domain) => Email(username, domain)
+ }
+ }
+ }
+
+ final case class PhoneNumber(countryCode: String, number: String, extension: Option[String] = None) {
+
+ def hasExtension: Boolean = extension.isDefined
+
+ /** This is a more human-friendly alias for #toE164String() */
+ def toCompactString: String = s"+$countryCode$number${extension.fold("")(";ext=" + _)}"
+
+ /** Outputs the phone number in a E.164-compliant way, e.g. +14151234567 */
+ def toE164String: String = toCompactString
+
+ /**
+ * Outputs the phone number in a "readable" way, e.g. "+1 415-123-45-67 ext. 1234"
+ * @throws IllegalStateException if the contents of this object is not a valid phone number
+ */
+ @throws[IllegalStateException]
+ def toHumanReadableString: String =
+ try {
+ val phoneNumber = PhoneNumber.phoneUtil.parse(toE164String, "US")
+ PhoneNumber.phoneUtil.format(phoneNumber, PhoneNumberFormat.INTERNATIONAL)
+ } catch {
+ case NonFatal(e) => throw new IllegalStateException(s"$toString is not a valid number", e)
+ }
+
+ override def toString: String = s"+$countryCode $number${extension.fold("")(" ext. " + _)}"
+ }
+
+ object PhoneNumber {
+
+ private[PhoneNumber] val phoneUtil = PhoneNumberUtil.getInstance()
+
+ def parse(phoneNumber: String): Option[PhoneNumber] = {
+ val validated = Try(phoneUtil.parseAndKeepRawInput(phoneNumber, "US")).toOption.filter(phoneUtil.isValidNumber)
+ validated.map { pn =>
+ PhoneNumber(
+ pn.getCountryCode.toString,
+ pn.getNationalNumber.toString,
+ Option(pn.getExtension).filter(_.nonEmpty))
+ }
+ }
+ }
+}
diff --git a/core-types/src/main/scala/xyz/driver/core/generators.scala b/core-types/src/main/scala/xyz/driver/core/generators.scala
new file mode 100644
index 0000000..d00b6dd
--- /dev/null
+++ b/core-types/src/main/scala/xyz/driver/core/generators.scala
@@ -0,0 +1,143 @@
+package xyz.driver.core
+
+import enumeratum._
+import java.math.MathContext
+import java.time.{Instant, LocalDate, ZoneOffset}
+import java.util.UUID
+
+import xyz.driver.core.time.{Time, TimeOfDay, TimeRange}
+import xyz.driver.core.date.{Date, DayOfWeek}
+
+import scala.reflect.ClassTag
+import scala.util.Random
+import eu.timepit.refined.refineV
+import eu.timepit.refined.api.Refined
+import eu.timepit.refined.collection._
+
+object generators {
+
+ private val random = new Random
+ import random._
+ private val secureRandom = new java.security.SecureRandom()
+
+ private val DefaultMaxLength = 10
+ private val StringLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ".toSet
+ private val NonAmbigiousCharacters = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"
+ private val Numbers = "0123456789"
+
+ private def nextTokenString(length: Int, chars: IndexedSeq[Char]): String = {
+ val builder = new StringBuilder
+ for (_ <- 0 until length) {
+ builder += chars(secureRandom.nextInt(chars.length))
+ }
+ builder.result()
+ }
+
+ /** Creates a random invitation token.
+ *
+ * This token is meant fo human input and avoids using ambiguous characters such as 'O' and '0'. It
+ * therefore contains less entropy and is not meant to be used as a cryptographic secret. */
+ @deprecated(
+ "The term 'token' is too generic and security and readability conventions are not well defined. " +
+ "Services should implement their own version that suits their security requirements.",
+ "1.11.0"
+ )
+ def nextToken(length: Int): String = nextTokenString(length, NonAmbigiousCharacters)
+
+ @deprecated(
+ "The term 'token' is too generic and security and readability conventions are not well defined. " +
+ "Services should implement their own version that suits their security requirements.",
+ "1.11.0"
+ )
+ def nextNumericToken(length: Int): String = nextTokenString(length, Numbers)
+
+ def nextInt(maxValue: Int, minValue: Int = 0): Int = random.nextInt(maxValue - minValue) + minValue
+
+ def nextBoolean(): Boolean = random.nextBoolean()
+
+ def nextDouble(): Double = random.nextDouble()
+
+ def nextId[T](): Id[T] = Id[T](nextUuid().toString)
+
+ def nextId[T](maxLength: Int): Id[T] = Id[T](nextString(maxLength))
+
+ def nextNumericId[T](): Id[T] = Id[T](nextLong.abs.toString)
+
+ def nextNumericId[T](maxValue: Int): Id[T] = Id[T](nextInt(maxValue).toString)
+
+ def nextName[T](maxLength: Int = DefaultMaxLength): Name[T] = Name[T](nextString(maxLength))
+
+ def nextNonEmptyName[T](maxLength: Int = DefaultMaxLength): NonEmptyName[T] =
+ NonEmptyName[T](nextNonEmptyString(maxLength))
+
+ def nextUuid(): UUID = java.util.UUID.randomUUID
+
+ def nextRevision[T](): Revision[T] = Revision[T](nextUuid().toString)
+
+ def nextString(maxLength: Int = DefaultMaxLength): String =
+ (oneOf[Char](StringLetters) +: arrayOf(oneOf[Char](StringLetters), maxLength - 1)).mkString
+
+ def nextNonEmptyString(maxLength: Int = DefaultMaxLength): String Refined NonEmpty = {
+ refineV[NonEmpty](
+ (oneOf[Char](StringLetters) +: arrayOf(oneOf[Char](StringLetters), maxLength - 1)).mkString
+ ).right.get
+ }
+
+ def nextOption[T](value: => T): Option[T] = if (nextBoolean()) Option(value) else None
+
+ def nextPair[L, R](left: => L, right: => R): (L, R) = (left, right)
+
+ def nextTriad[F, S, T](first: => F, second: => S, third: => T): (F, S, T) = (first, second, third)
+
+ def nextInstant(): Instant = Instant.ofEpochMilli(math.abs(nextLong() % System.currentTimeMillis))
+
+ def nextTime(): Time = nextInstant()
+
+ def nextTimeOfDay: TimeOfDay = TimeOfDay(java.time.LocalTime.MIN.plusSeconds(nextLong), java.util.TimeZone.getDefault)
+
+ def nextTimeRange(): TimeRange = {
+ val oneTime = nextTime()
+ val anotherTime = nextTime()
+
+ TimeRange(
+ Time(scala.math.min(oneTime.millis, anotherTime.millis)),
+ Time(scala.math.max(oneTime.millis, anotherTime.millis)))
+ }
+
+ def nextDate(): Date = nextTime().toDate(java.util.TimeZone.getTimeZone("UTC"))
+
+ def nextLocalDate(): LocalDate = nextInstant().atZone(ZoneOffset.UTC).toLocalDate
+
+ def nextDayOfWeek(): DayOfWeek = oneOf(DayOfWeek.All)
+
+ def nextBigDecimal(multiplier: Double = 1000000.00, precision: Int = 2): BigDecimal =
+ BigDecimal(multiplier * nextDouble, new MathContext(precision))
+
+ def oneOf[T](items: T*): T = oneOf(items.toSet)
+
+ def oneOf[T](items: Set[T]): T = items.toSeq(nextInt(items.size))
+
+ def oneOf[T <: EnumEntry](enum: Enum[T]): T = oneOf(enum.values: _*)
+
+ def arrayOf[T: ClassTag](generator: => T, maxLength: Int = DefaultMaxLength, minLength: Int = 0): Array[T] =
+ Array.fill(nextInt(maxLength, minLength))(generator)
+
+ def seqOf[T](generator: => T, maxLength: Int = DefaultMaxLength, minLength: Int = 0): Seq[T] =
+ Seq.fill(nextInt(maxLength, minLength))(generator)
+
+ def vectorOf[T](generator: => T, maxLength: Int = DefaultMaxLength, minLength: Int = 0): Vector[T] =
+ Vector.fill(nextInt(maxLength, minLength))(generator)
+
+ def listOf[T](generator: => T, maxLength: Int = DefaultMaxLength, minLength: Int = 0): List[T] =
+ List.fill(nextInt(maxLength, minLength))(generator)
+
+ def setOf[T](generator: => T, maxLength: Int = DefaultMaxLength, minLength: Int = 0): Set[T] =
+ seqOf(generator, maxLength, minLength).toSet
+
+ def mapOf[K, V](
+ keyGenerator: => K,
+ valueGenerator: => V,
+ maxLength: Int = DefaultMaxLength,
+ minLength: Int = 0): Map[K, V] =
+ seqOf(nextPair(keyGenerator, valueGenerator), maxLength, minLength).toMap
+}
diff --git a/src/main/scala/xyz/driver/core/tagging/tagging.scala b/core-types/src/main/scala/xyz/driver/core/tagging/tagging.scala
index 5b6599e..5b6599e 100644
--- a/src/main/scala/xyz/driver/core/tagging/tagging.scala
+++ b/core-types/src/main/scala/xyz/driver/core/tagging/tagging.scala
diff --git a/src/main/scala/xyz/driver/core/time.scala b/core-types/src/main/scala/xyz/driver/core/time.scala
index 1622068..1622068 100644
--- a/src/main/scala/xyz/driver/core/time.scala
+++ b/core-types/src/main/scala/xyz/driver/core/time.scala
diff --git a/src/test/scala/xyz/driver/core/CoreTest.scala b/core-types/src/test/scala/xyz/driver/core/CoreTest.scala
index 1cd7a7c..1cd7a7c 100644
--- a/src/test/scala/xyz/driver/core/CoreTest.scala
+++ b/core-types/src/test/scala/xyz/driver/core/CoreTest.scala
diff --git a/src/test/scala/xyz/driver/core/DateTest.scala b/core-types/src/test/scala/xyz/driver/core/DateTest.scala
index 0432040..0432040 100644
--- a/src/test/scala/xyz/driver/core/DateTest.scala
+++ b/core-types/src/test/scala/xyz/driver/core/DateTest.scala
diff --git a/src/test/scala/xyz/driver/core/PhoneNumberTest.scala b/core-types/src/test/scala/xyz/driver/core/PhoneNumberTest.scala
index 384c7be..729302b 100644
--- a/src/test/scala/xyz/driver/core/PhoneNumberTest.scala
+++ b/core-types/src/test/scala/xyz/driver/core/PhoneNumberTest.scala
@@ -44,6 +44,21 @@ class PhoneNumberTest extends FlatSpec with Matchers {
PhoneNumber.parse("+86 134 52 52 2256") shouldBe Some(PhoneNumber("86", "13452522256"))
}
+ it should "parse numbers with extensions in different formats" in {
+ // format: off
+ val numbers = List(
+ "+1 800 525 22 25 x23",
+ "+18005252225 ext. 23",
+ "+18005252225,23"
+ )
+ // format: on
+
+ val parsed = numbers.flatMap(PhoneNumber.parse)
+
+ parsed should have size numbers.size
+ parsed should contain only PhoneNumber("1", "8005252225", Some("23"))
+ }
+
it should "return None on numbers that are shorter than the minimum number of digits for the country (i.e. US - 10, AR - 11)" in {
withClue("US and CN numbers are 10 digits - 9 digit (and shorter) numbers should not fit") {
// format: off
@@ -76,4 +91,27 @@ class PhoneNumberTest extends FlatSpec with Matchers {
List(PhoneNumber("45", "27452522"), PhoneNumber("86", "13452522256"))
}
+ "PhoneNumber.toCompactString/toE164String" should "produce phone number in international format without whitespaces" in {
+ PhoneNumber.parse("+1 800 5252225").get.toCompactString shouldBe "+18005252225"
+ PhoneNumber.parse("+1 800 5252225").get.toE164String shouldBe "+18005252225"
+
+ PhoneNumber.parse("+1 800 5252225 x23").get.toCompactString shouldBe "+18005252225;ext=23"
+ PhoneNumber.parse("+1 800 5252225 x23").get.toE164String shouldBe "+18005252225;ext=23"
+ }
+
+ "PhoneNumber.toHumanReadableString" should "produce nice readable result for different countries" in {
+ PhoneNumber.parse("+14154234567").get.toHumanReadableString shouldBe "+1 415-423-4567"
+ PhoneNumber.parse("+14154234567,23").get.toHumanReadableString shouldBe "+1 415-423-4567 ext. 23"
+
+ PhoneNumber.parse("+78005252225").get.toHumanReadableString shouldBe "+7 800 525-22-25"
+
+ PhoneNumber.parse("+41219437898").get.toHumanReadableString shouldBe "+41 21 943 78 98"
+ }
+
+ it should "throw an IllegalArgumentException if the PhoneNumber object is not parsable/valid" in {
+ intercept[IllegalStateException] {
+ PhoneNumber("+123", "1238123120938120938").toHumanReadableString
+ }.getMessage should include("+123 1238123120938120938 is not a valid number")
+ }
+
}
diff --git a/src/test/scala/xyz/driver/core/TimeTest.scala b/core-types/src/test/scala/xyz/driver/core/TimeTest.scala
index 1019f60..1019f60 100644
--- a/src/test/scala/xyz/driver/core/TimeTest.scala
+++ b/core-types/src/test/scala/xyz/driver/core/TimeTest.scala
diff --git a/src/test/scala/xyz/driver/core/tagging/TaggingTest.scala b/core-types/src/test/scala/xyz/driver/core/tagging/TaggingTest.scala
index 14dfaf9..14dfaf9 100644
--- a/src/test/scala/xyz/driver/core/tagging/TaggingTest.scala
+++ b/core-types/src/test/scala/xyz/driver/core/tagging/TaggingTest.scala
diff --git a/src/main/scala/xyz/driver/core/Refresh.scala b/core-util/src/main/scala/xyz/driver/core/Refresh.scala
index 6db9c26..6db9c26 100644
--- a/src/main/scala/xyz/driver/core/Refresh.scala
+++ b/core-util/src/main/scala/xyz/driver/core/Refresh.scala
diff --git a/documentation/components.dot b/documentation/components.dot
new file mode 100644
index 0000000..f00dff3
--- /dev/null
+++ b/documentation/components.dot
@@ -0,0 +1,21 @@
+digraph core {
+
+ rankdir = BT;
+
+ "core-reporting";
+ "core-storage" -> "core-reporting";
+ "core-messaging" -> "core-reporting";
+ "core-database" -> "core-reporting";
+ "core-database" -> "core-types";
+ "core-rest" -> "core-reporting";
+ "core-rest" -> "core-types";
+ "core-init" -> "core-storage";
+ "core-init" -> "core-messaging";
+ "core-init" -> "core-database";
+ "core-init" -> "core-rest";
+ "core-init" -> "core-reporting";
+
+ core [color=red];
+ core -> "core-init" [color=red];
+
+}
diff --git a/documentation/components.svg b/documentation/components.svg
new file mode 100644
index 0000000..0531399
--- /dev/null
+++ b/documentation/components.svg
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (20161225.0304)
+ -->
+<!-- Title: core Pages: 1 -->
+<svg width="542pt" height="260pt"
+ viewBox="0.00 0.00 542.09 260.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 256)">
+<title>core</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-256 538.09,-256 538.09,4 -4,4"/>
+<!-- core&#45;reporting -->
+<g id="node1" class="node">
+<title>core&#45;reporting</title>
+<ellipse fill="none" stroke="#000000" cx="285.9452" cy="-234" rx="61.1893" ry="18"/>
+<text text-anchor="middle" x="285.9452" y="-230.3" font-family="Times,serif" font-size="14.00" fill="#000000">core&#45;reporting</text>
+</g>
+<!-- core&#45;storage -->
+<g id="node2" class="node">
+<title>core&#45;storage</title>
+<ellipse fill="none" stroke="#000000" cx="53.9452" cy="-162" rx="53.8905" ry="18"/>
+<text text-anchor="middle" x="53.9452" y="-158.3" font-family="Times,serif" font-size="14.00" fill="#000000">core&#45;storage</text>
+</g>
+<!-- core&#45;storage&#45;&gt;core&#45;reporting -->
+<g id="edge1" class="edge">
+<title>core&#45;storage&#45;&gt;core&#45;reporting</title>
+<path fill="none" stroke="#000000" d="M93.5638,-174.2954C132.345,-186.331 191.6297,-204.7297 234.1337,-217.9206"/>
+<polygon fill="#000000" stroke="#000000" points="233.155,-221.2815 243.7431,-220.9028 235.2299,-214.596 233.155,-221.2815"/>
+</g>
+<!-- core&#45;messaging -->
+<g id="node3" class="node">
+<title>core&#45;messaging</title>
+<ellipse fill="none" stroke="#000000" cx="191.9452" cy="-162" rx="66.0889" ry="18"/>
+<text text-anchor="middle" x="191.9452" y="-158.3" font-family="Times,serif" font-size="14.00" fill="#000000">core&#45;messaging</text>
+</g>
+<!-- core&#45;messaging&#45;&gt;core&#45;reporting -->
+<g id="edge2" class="edge">
+<title>core&#45;messaging&#45;&gt;core&#45;reporting</title>
+<path fill="none" stroke="#000000" d="M214.2219,-179.063C226.6308,-188.5677 242.254,-200.5344 255.7488,-210.8708"/>
+<polygon fill="#000000" stroke="#000000" points="253.7613,-213.7573 263.8284,-217.0595 258.0179,-208.2001 253.7613,-213.7573"/>
+</g>
+<!-- core&#45;database -->
+<g id="node4" class="node">
+<title>core&#45;database</title>
+<ellipse fill="none" stroke="#000000" cx="474.9452" cy="-162" rx="59.2899" ry="18"/>
+<text text-anchor="middle" x="474.9452" y="-158.3" font-family="Times,serif" font-size="14.00" fill="#000000">core&#45;database</text>
+</g>
+<!-- core&#45;database&#45;&gt;core&#45;reporting -->
+<g id="edge3" class="edge">
+<title>core&#45;database&#45;&gt;core&#45;reporting</title>
+<path fill="none" stroke="#000000" d="M437.6049,-176.2249C407.6744,-187.627 365.4902,-203.6971 333.2074,-215.9954"/>
+<polygon fill="#000000" stroke="#000000" points="331.6298,-212.8509 323.5309,-219.6816 334.1218,-219.3923 331.6298,-212.8509"/>
+</g>
+<!-- core&#45;types -->
+<g id="node5" class="node">
+<title>core&#45;types</title>
+<ellipse fill="none" stroke="#000000" cx="443.9452" cy="-234" rx="47.3916" ry="18"/>
+<text text-anchor="middle" x="443.9452" y="-230.3" font-family="Times,serif" font-size="14.00" fill="#000000">core&#45;types</text>
+</g>
+<!-- core&#45;database&#45;&gt;core&#45;types -->
+<g id="edge4" class="edge">
+<title>core&#45;database&#45;&gt;core&#45;types</title>
+<path fill="none" stroke="#000000" d="M467.1226,-180.1686C463.6452,-188.2453 459.478,-197.9239 455.642,-206.8332"/>
+<polygon fill="#000000" stroke="#000000" points="452.3979,-205.5176 451.6579,-216.0866 458.8273,-208.2859 452.3979,-205.5176"/>
+</g>
+<!-- core&#45;rest -->
+<g id="node6" class="node">
+<title>core&#45;rest</title>
+<ellipse fill="none" stroke="#000000" cx="355.9452" cy="-162" rx="41.6928" ry="18"/>
+<text text-anchor="middle" x="355.9452" y="-158.3" font-family="Times,serif" font-size="14.00" fill="#000000">core&#45;rest</text>
+</g>
+<!-- core&#45;rest&#45;&gt;core&#45;reporting -->
+<g id="edge5" class="edge">
+<title>core&#45;rest&#45;&gt;core&#45;reporting</title>
+<path fill="none" stroke="#000000" d="M339.7099,-178.6992C330.9557,-187.7035 319.978,-198.9948 310.2283,-209.0231"/>
+<polygon fill="#000000" stroke="#000000" points="307.5305,-206.7771 303.0691,-216.3868 312.5494,-211.6566 307.5305,-206.7771"/>
+</g>
+<!-- core&#45;rest&#45;&gt;core&#45;types -->
+<g id="edge6" class="edge">
+<title>core&#45;rest&#45;&gt;core&#45;types</title>
+<path fill="none" stroke="#000000" d="M375.4753,-177.9791C387.3842,-187.7228 402.7997,-200.3355 415.9875,-211.1255"/>
+<polygon fill="#000000" stroke="#000000" points="413.9037,-213.9428 423.8597,-217.5664 418.3364,-208.5251 413.9037,-213.9428"/>
+</g>
+<!-- core&#45;init -->
+<g id="node7" class="node">
+<title>core&#45;init</title>
+<ellipse fill="none" stroke="#000000" cx="285.9452" cy="-90" rx="40.0939" ry="18"/>
+<text text-anchor="middle" x="285.9452" y="-86.3" font-family="Times,serif" font-size="14.00" fill="#000000">core&#45;init</text>
+</g>
+<!-- core&#45;init&#45;&gt;core&#45;reporting -->
+<g id="edge11" class="edge">
+<title>core&#45;init&#45;&gt;core&#45;reporting</title>
+<path fill="none" stroke="#000000" d="M285.9452,-108.2377C285.9452,-132.799 285.9452,-176.7526 285.9452,-205.6459"/>
+<polygon fill="#000000" stroke="#000000" points="282.4453,-205.9103 285.9452,-215.9104 289.4453,-205.9104 282.4453,-205.9103"/>
+</g>
+<!-- core&#45;init&#45;&gt;core&#45;storage -->
+<g id="edge7" class="edge">
+<title>core&#45;init&#45;&gt;core&#45;storage</title>
+<path fill="none" stroke="#000000" d="M252.7053,-100.3158C213.6013,-112.4515 148.3163,-132.7124 103.2985,-146.6834"/>
+<polygon fill="#000000" stroke="#000000" points="102.2479,-143.3448 93.7347,-149.6515 104.3228,-150.0302 102.2479,-143.3448"/>
+</g>
+<!-- core&#45;init&#45;&gt;core&#45;messaging -->
+<g id="edge8" class="edge">
+<title>core&#45;init&#45;&gt;core&#45;messaging</title>
+<path fill="none" stroke="#000000" d="M265.5486,-105.6229C252.9101,-115.3035 236.4447,-127.9153 222.2928,-138.7551"/>
+<polygon fill="#000000" stroke="#000000" points="220.0846,-136.0377 214.274,-144.8971 224.3411,-141.5948 220.0846,-136.0377"/>
+</g>
+<!-- core&#45;init&#45;&gt;core&#45;database -->
+<g id="edge9" class="edge">
+<title>core&#45;init&#45;&gt;core&#45;database</title>
+<path fill="none" stroke="#000000" d="M316.732,-101.7283C347.0407,-113.2745 393.7166,-131.0558 428.5269,-144.3169"/>
+<polygon fill="#000000" stroke="#000000" points="427.4162,-147.639 438.0071,-147.9284 429.9083,-141.0976 427.4162,-147.639"/>
+</g>
+<!-- core&#45;init&#45;&gt;core&#45;rest -->
+<g id="edge10" class="edge">
+<title>core&#45;init&#45;&gt;core&#45;rest</title>
+<path fill="none" stroke="#000000" d="M302.1806,-106.6992C311.2805,-116.0592 322.7832,-127.8905 332.8116,-138.2055"/>
+<polygon fill="#000000" stroke="#000000" points="330.3176,-140.6611 339.7979,-145.3913 335.3366,-135.7816 330.3176,-140.6611"/>
+</g>
+<!-- core -->
+<g id="node8" class="node">
+<title>core</title>
+<ellipse fill="none" stroke="#ff0000" cx="285.9452" cy="-18" rx="27" ry="18"/>
+<text text-anchor="middle" x="285.9452" y="-14.3" font-family="Times,serif" font-size="14.00" fill="#000000">core</text>
+</g>
+<!-- core&#45;&gt;core&#45;init -->
+<g id="edge12" class="edge">
+<title>core&#45;&gt;core&#45;init</title>
+<path fill="none" stroke="#ff0000" d="M285.9452,-36.1686C285.9452,-43.869 285.9452,-53.0257 285.9452,-61.5834"/>
+<polygon fill="#ff0000" stroke="#ff0000" points="282.4453,-61.5867 285.9452,-71.5867 289.4453,-61.5868 282.4453,-61.5867"/>
+</g>
+</g>
+</svg>
diff --git a/documentation/example/.gitignore b/documentation/example/.gitignore
new file mode 100644
index 0000000..2157ef4
--- /dev/null
+++ b/documentation/example/.gitignore
@@ -0,0 +1 @@
+.data-*
diff --git a/documentation/example/build.sbt b/documentation/example/build.sbt
new file mode 100644
index 0000000..d2bd592
--- /dev/null
+++ b/documentation/example/build.sbt
@@ -0,0 +1,6 @@
+lazy val `example-app` = project
+ .in(file("."))
+ .enablePlugins(ServicePlugin)
+ .settings(
+ libraryDependencies += "xyz.driver" %% "core-init" % "2.0.0-M5"
+ )
diff --git a/documentation/example/project/build.properties b/documentation/example/project/build.properties
new file mode 100644
index 0000000..0cd8b07
--- /dev/null
+++ b/documentation/example/project/build.properties
@@ -0,0 +1 @@
+sbt.version=1.2.3
diff --git a/documentation/example/project/plugins.sbt b/documentation/example/project/plugins.sbt
new file mode 100644
index 0000000..53be881
--- /dev/null
+++ b/documentation/example/project/plugins.sbt
@@ -0,0 +1 @@
+addSbtPlugin("xyz.driver" % "sbt-settings" % "2.0.12")
diff --git a/documentation/example/src/main/scala/example/Main.scala b/documentation/example/src/main/scala/example/Main.scala
new file mode 100644
index 0000000..2548f79
--- /dev/null
+++ b/documentation/example/src/main/scala/example/Main.scala
@@ -0,0 +1,21 @@
+package example
+
+import xyz.driver.core.init
+
+object Main extends init.SimpleHttpApp {
+
+ lazy val fs = this.storage("data")
+
+ override def applicationRoute = path("data" / Segment) { key =>
+ post {
+ entity(as[Array[Byte]]) { value =>
+ complete(fs.uploadContent(key, value))
+ }
+ } ~ get {
+ rejectEmptyResponse{
+ complete(fs.content(key))
+ }
+ }
+ }
+
+}
diff --git a/project/MiMaSettings.scala b/project/MiMaSettings.scala
new file mode 100644
index 0000000..d3ec3c1
--- /dev/null
+++ b/project/MiMaSettings.scala
@@ -0,0 +1,28 @@
+import com.typesafe.tools.mima.plugin.MimaPlugin
+import sbt.{Def, _}
+import sbt.Keys._
+
+/** This plugin extends the Migration Manager (MiMa) Plugin with common settings
+ * for driver-core projects.
+ */
+object MiMaSettings extends AutoPlugin {
+
+ override def requires = MimaPlugin
+ override def trigger = allRequirements
+
+ object autoImport {
+ val abiVersion = settingKey[String]("Previous version of binary-compatible projects")
+ val checkAbi = taskKey[Unit]("Check ABI compatibility with declared abiVersion")
+ }
+ import autoImport._
+ import MimaPlugin.autoImport._
+
+ override def buildSettings: Seq[Def.Setting[_]] = Seq(abiVersion := "")
+ override def projectSettings: Seq[Def.Setting[_]] = Seq(
+ mimaPreviousArtifacts := Set(
+ "xyz.driver" %% name.value % abiVersion.value
+ ),
+ checkAbi := mimaReportBinaryIssues.value
+ )
+
+}
diff --git a/project/build.properties b/project/build.properties
index 5620cc5..0cd8b07 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=1.2.1
+sbt.version=1.2.3
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 17a573c..f0e3a6d 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1 +1,2 @@
-addSbtPlugin("xyz.driver" % "sbt-settings" % "2.0.7")
+addSbtPlugin("xyz.driver" % "sbt-settings" % "2.0.9")
+addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.3.0")
diff --git a/src/main/scala/xyz/driver/core/database/MdcAsyncExecutor.scala b/src/main/scala/xyz/driver/core/database/MdcAsyncExecutor.scala
deleted file mode 100644
index 5939efb..0000000
--- a/src/main/scala/xyz/driver/core/database/MdcAsyncExecutor.scala
+++ /dev/null
@@ -1,53 +0,0 @@
-/** Code ported from "de.geekonaut" %% "slickmdc" % "1.0.0"
- * License: @see https://github.com/AVGP/slickmdc/blob/master/LICENSE
- * Blog post: @see http://50linesofco.de/post/2016-07-01-slick-and-slf4j-mdc-logging-in-scala.html
- */
-package xyz.driver.core
-package database
-
-import java.util.concurrent._
-import java.util.concurrent.atomic.AtomicInteger
-
-import scala.concurrent._
-import com.typesafe.scalalogging.StrictLogging
-import slick.util.AsyncExecutor
-
-import logging.MdcExecutionContext
-
-/** Taken from the original Slick AsyncExecutor and simplified
- * @see https://github.com/slick/slick/blob/3.1/slick/src/main/scala/slick/util/AsyncExecutor.scala
- */
-object MdcAsyncExecutor extends StrictLogging {
-
- /** Create an AsyncExecutor with a fixed-size thread pool.
- *
- * @param name The name for the thread pool.
- * @param numThreads The number of threads in the pool.
- */
- def apply(name: String, numThreads: Int): AsyncExecutor = {
- new AsyncExecutor {
- val tf = new DaemonThreadFactory(name + "-")
-
- lazy val executionContext = {
- new MdcExecutionContext(ExecutionContext.fromExecutor(Executors.newFixedThreadPool(numThreads, tf)))
- }
-
- def close(): Unit = {}
- }
- }
-
- def default(name: String = "AsyncExecutor.default"): AsyncExecutor = apply(name, 20)
-
- private class DaemonThreadFactory(namePrefix: String) extends ThreadFactory {
- private[this] val group =
- Option(System.getSecurityManager).fold(Thread.currentThread.getThreadGroup)(_.getThreadGroup)
- private[this] val threadNumber = new AtomicInteger(1)
-
- def newThread(r: Runnable): Thread = {
- val t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement, 0)
- if (!t.isDaemon) t.setDaemon(true)
- if (t.getPriority != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY)
- t
- }
- }
-}
diff --git a/src/main/scala/xyz/driver/core/domain.scala b/src/main/scala/xyz/driver/core/domain.scala
deleted file mode 100644
index 59bed54..0000000
--- a/src/main/scala/xyz/driver/core/domain.scala
+++ /dev/null
@@ -1,40 +0,0 @@
-package xyz.driver.core
-
-import com.google.i18n.phonenumbers.PhoneNumberUtil
-import scalaz.Equal
-import scalaz.std.string._
-import scalaz.syntax.equal._
-
-object domain {
-
- final case class Email(username: String, domain: String) {
- override def toString: String = username + "@" + domain
- }
-
- object Email {
- implicit val emailEqual: Equal[Email] = Equal.equal {
- case (left, right) => left.toString.toLowerCase === right.toString.toLowerCase
- }
-
- def parse(emailString: String): Option[Email] = {
- Some(emailString.split("@")) collect {
- case Array(username, domain) => Email(username, domain)
- }
- }
- }
-
- final case class PhoneNumber(countryCode: String = "1", number: String) {
- override def toString: String = s"+$countryCode $number"
- }
-
- object PhoneNumber {
-
- private val phoneUtil = PhoneNumberUtil.getInstance()
-
- def parse(phoneNumber: String): Option[PhoneNumber] = {
- val validated =
- util.Try(phoneUtil.parseAndKeepRawInput(phoneNumber, "US")).toOption.filter(phoneUtil.isValidNumber)
- validated.map(pn => PhoneNumber(pn.getCountryCode.toString, pn.getNationalNumber.toString))
- }
- }
-}
diff --git a/src/main/scala/xyz/driver/core/init/Platform.scala b/src/main/scala/xyz/driver/core/init/Platform.scala
deleted file mode 100644
index 2daa2c8..0000000
--- a/src/main/scala/xyz/driver/core/init/Platform.scala
+++ /dev/null
@@ -1,31 +0,0 @@
-package xyz.driver.core
-package init
-
-import java.nio.file.{Files, Path, Paths}
-
-import com.google.auth.oauth2.ServiceAccountCredentials
-
-sealed trait Platform
-object Platform {
- case class GoogleCloud(keyfile: Path, namespace: String) extends Platform {
- def credentials: ServiceAccountCredentials = ServiceAccountCredentials.fromStream(
- Files.newInputStream(keyfile)
- )
- def project: String = credentials.getProjectId
- }
- // case object AliCloud extends Platform
- case object Dev extends Platform
-
- lazy val fromEnv: Platform = {
- def isGoogle = sys.env.get("GOOGLE_APPLICATION_CREDENTIALS").map { value =>
- val keyfile = Paths.get(value)
- require(Files.isReadable(keyfile), s"Google credentials file $value is not readable.")
- val namespace = sys.env.getOrElse("SERVICE_NAMESPACE", sys.error("Namespace not set"))
- GoogleCloud(keyfile, namespace)
- }
- isGoogle.getOrElse(Dev)
- }
-
- def current: Platform = fromEnv
-
-}
diff --git a/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala b/src/test/scala/xyz/driver/core/DriverAppTest.scala
index 324c8d8..986e5d3 100644
--- a/src/test/scala/xyz/driver/core/rest/DriverAppTest.scala
+++ b/src/test/scala/xyz/driver/core/DriverAppTest.scala
@@ -1,4 +1,4 @@
-package xyz.driver.core.rest
+package xyz.driver.core
import akka.http.scaladsl.model.headers._
import akka.http.scaladsl.model.{HttpMethod, StatusCodes}