├── project ├── build.properties └── plugins.sbt ├── src ├── main │ ├── resources │ │ ├── swagger │ │ │ ├── images │ │ │ │ ├── expand.gif │ │ │ │ ├── collapse.gif │ │ │ │ ├── favicon.ico │ │ │ │ ├── throbber.gif │ │ │ │ ├── logo_small.png │ │ │ │ ├── wordnik_api.png │ │ │ │ ├── explorer_icons.png │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ └── pet_store_api.png │ │ │ ├── fonts │ │ │ │ ├── DroidSans.ttf │ │ │ │ └── DroidSans-Bold.ttf │ │ │ ├── lib │ │ │ │ ├── jquery.slideto.min.js │ │ │ │ ├── jquery.wiggle.min.js │ │ │ │ ├── highlight.9.1.0.pack_extended.js │ │ │ │ ├── jquery.ba-bbq.min.js │ │ │ │ ├── highlight.9.1.0.pack.js │ │ │ │ ├── swagger-oauth.js │ │ │ │ ├── backbone-min.js │ │ │ │ ├── marked.js │ │ │ │ └── js-yaml.min.js │ │ │ ├── css │ │ │ │ ├── typography.css │ │ │ │ ├── reset.css │ │ │ │ └── style.css │ │ │ ├── o2c.html │ │ │ ├── lang │ │ │ │ ├── translator.js │ │ │ │ ├── zh-cn.js │ │ │ │ ├── ja.js │ │ │ │ ├── tr.js │ │ │ │ ├── pl.js │ │ │ │ ├── pt.js │ │ │ │ ├── en.js │ │ │ │ ├── ru.js │ │ │ │ ├── geo.js │ │ │ │ ├── it.js │ │ │ │ ├── es.js │ │ │ │ └── fr.js │ │ │ └── index.html │ │ ├── sql │ │ │ ├── h2-schema.sql │ │ │ └── postgresql-schema.sql │ │ ├── application.conf │ │ └── logback.xml │ └── scala │ │ ├── persistence │ │ ├── JsonProtocol.scala │ │ ├── entities │ │ │ └── Supplier.scala │ │ └── dal │ │ │ └── SuppliersDal.scala │ │ ├── utils │ │ ├── ActorModule.scala │ │ ├── PersistenceModule.scala │ │ ├── ConfigurationModule.scala │ │ ├── SwaggerDocService.scala │ │ └── CorsSupport.scala │ │ ├── Boot.scala │ │ └── rest │ │ └── SupplierRoutes.scala └── test │ └── scala │ ├── persistence │ └── dal │ │ ├── AbstractPersistenceTest.scala │ │ └── SuppliersDALTest.scala │ └── rest │ ├── AbstractRestTest.scala │ └── RoutesSpec.scala ├── activator.properties ├── .gitignore ├── tutorial └── index.html ├── README.md └── LICENSE /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("io.spray" % "sbt-revolver" % "0.7.2") 2 | -------------------------------------------------------------------------------- /src/main/resources/swagger/images/expand.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/expand.gif -------------------------------------------------------------------------------- /src/main/resources/swagger/fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/fonts/DroidSans.ttf -------------------------------------------------------------------------------- /src/main/resources/swagger/images/collapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/collapse.gif -------------------------------------------------------------------------------- /src/main/resources/swagger/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/swagger/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/throbber.gif -------------------------------------------------------------------------------- /src/main/resources/swagger/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/logo_small.png -------------------------------------------------------------------------------- /src/main/resources/swagger/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/wordnik_api.png -------------------------------------------------------------------------------- /src/main/resources/swagger/fonts/DroidSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/fonts/DroidSans-Bold.ttf -------------------------------------------------------------------------------- /src/main/resources/swagger/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/explorer_icons.png -------------------------------------------------------------------------------- /src/main/resources/swagger/images/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/favicon-16x16.png -------------------------------------------------------------------------------- /src/main/resources/swagger/images/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/favicon-32x32.png -------------------------------------------------------------------------------- /src/main/resources/swagger/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdiniz/quill-async-akka-http/HEAD/src/main/resources/swagger/images/pet_store_api.png -------------------------------------------------------------------------------- /src/main/resources/sql/h2-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS Supplier( 2 | id bigint auto_increment, 3 | name VARCHAR(255), 4 | description VARCHAR(255) 5 | ); 6 | -------------------------------------------------------------------------------- /activator.properties: -------------------------------------------------------------------------------- 1 | name=quill-async-akka-http 2 | title= Quill Async Akka HTTP 3 | description=A starter akka-http and quill async app 4 | tags=quill,akka,scala,async,http,cake 5 | -------------------------------------------------------------------------------- /src/main/resources/sql/postgresql-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS Supplier( 2 | id VARCHAR(255) PRIMARY KEY NOT NULL, 3 | name VARCHAR(255), 4 | description VARCHAR(255) 5 | ); -------------------------------------------------------------------------------- /src/main/scala/persistence/JsonProtocol.scala: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import persistence.entities.SimpleSupplier 4 | import spray.json.DefaultJsonProtocol 5 | 6 | object JsonProtocol extends DefaultJsonProtocol { 7 | implicit val supplierFormat = jsonFormat2(SimpleSupplier) 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache 6 | .history 7 | .lib/ 8 | .idea/* 9 | dist/* 10 | target/ 11 | lib_managed/ 12 | src_managed/ 13 | project/boot/ 14 | project/plugins/project/ 15 | .DS_Store 16 | # Scala-IDE specific 17 | .scala_dependencies 18 | .worksheet 19 | -------------------------------------------------------------------------------- /src/main/scala/utils/ActorModule.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import akka.actor.ActorSystem 4 | 5 | 6 | trait ActorModule { 7 | val system: ActorSystem 8 | } 9 | 10 | 11 | trait ActorModuleImpl extends ActorModule { 12 | this: Configuration => 13 | val system = ActorSystem("akkingslick", config) 14 | } -------------------------------------------------------------------------------- /src/main/resources/swagger/lib/jquery.slideto.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery); 2 | -------------------------------------------------------------------------------- /src/main/scala/persistence/entities/Supplier.scala: -------------------------------------------------------------------------------- 1 | package persistence.entities 2 | 3 | import java.util.UUID 4 | 5 | case class Supplier(id : String, name: String, description: String) { 6 | def toSimpleSupplier = SimpleSupplier(this.name,this.description) 7 | } 8 | 9 | case class SimpleSupplier(name: String, desc: String){ 10 | def toSupplier = Supplier(UUID.randomUUID().toString,this.name,this.desc) 11 | } -------------------------------------------------------------------------------- /src/main/resources/swagger/css/typography.css: -------------------------------------------------------------------------------- 1 | /* Google Font's Droid Sans */ 2 | @font-face { 3 | font-family: 'Droid Sans'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: local('Droid Sans'), local('DroidSans'), url('../fonts/DroidSans.ttf') format('truetype'); 7 | } 8 | /* Google Font's Droid Sans Bold */ 9 | @font-face { 10 | font-family: 'Droid Sans'; 11 | font-style: normal; 12 | font-weight: 700; 13 | src: local('Droid Sans Bold'), local('DroidSans-Bold'), url('../fonts/DroidSans-Bold.ttf') format('truetype'); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/swagger/o2c.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | loglevel = INFO 3 | } 4 | 5 | spray.can.server { 6 | request-timeout = 1s 7 | } 8 | 9 | quilltest { 10 | host=localhost 11 | port=5432 12 | user=postgres 13 | password=postgres 14 | database=quilltest 15 | poolMaxQueueSize=4 16 | poolMaxObjects=4 17 | poolMaxIdle=999999999 18 | poolValidationInterval=100 19 | } 20 | 21 | quill { 22 | host=localhost 23 | port=5432 24 | user=postgres 25 | password=postgres 26 | database=quill 27 | poolMaxQueueSize=4 28 | poolMaxObjects=4 29 | poolMaxIdle=999999999 30 | poolValidationInterval=100 31 | } 32 | -------------------------------------------------------------------------------- /src/main/scala/utils/PersistenceModule.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import io.getquill.{PostgresAsyncContext, SnakeCase} 4 | import persistence.dal.{SuppliersDal, SuppliersDalImpl} 5 | 6 | trait DbContext { 7 | val context : PostgresAsyncContext[SnakeCase] 8 | } 9 | 10 | trait PersistenceModule { 11 | val suppliersDal : SuppliersDal 12 | } 13 | 14 | 15 | trait PersistenceModuleImpl extends PersistenceModule with DbContext{ 16 | this: Configuration => 17 | 18 | override lazy val context = new PostgresAsyncContext[SnakeCase]("quill") 19 | override val suppliersDal = new SuppliersDalImpl(context) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/scala/persistence/dal/AbstractPersistenceTest.scala: -------------------------------------------------------------------------------- 1 | package persistence.dal 2 | 3 | 4 | import io.getquill._ 5 | import utils._ 6 | import org.scalatest.{FunSuite, Suite} 7 | 8 | trait AbstractPersistenceTest extends FunSuite { this: Suite => 9 | 10 | trait Modules extends ConfigurationModuleImpl with PersistenceModuleTest { 11 | } 12 | 13 | 14 | trait PersistenceModuleTest extends PersistenceModule with DbContext{ 15 | this: Configuration => 16 | override lazy val context = new PostgresAsyncContext[SnakeCase]("quilltest") 17 | override val suppliersDal: SuppliersDal = new SuppliersDalImpl(context) 18 | val self = this 19 | 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /src/main/scala/utils/ConfigurationModule.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import java.io.File 4 | 5 | import com.typesafe.config.{Config, ConfigFactory} 6 | 7 | trait Configuration { 8 | def config: Config 9 | } 10 | 11 | trait ConfigurationModuleImpl extends Configuration { 12 | private val internalConfig: Config = { 13 | val configDefaults = ConfigFactory.load(this.getClass().getClassLoader(), "application.conf") 14 | 15 | scala.sys.props.get("application.config") match { 16 | case Some(filename) => ConfigFactory.parseFile(new File(filename)).withFallback(configDefaults) 17 | case None => configDefaults 18 | } 19 | } 20 | 21 | def config = internalConfig 22 | } -------------------------------------------------------------------------------- /src/main/resources/swagger/lib/jquery.wiggle.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery Wiggle 3 | Author: WonderGroup, Jordan Thomas 4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html 5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License) 6 | */ 7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);} 8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});}; -------------------------------------------------------------------------------- /src/main/scala/Boot.scala: -------------------------------------------------------------------------------- 1 | import akka.http.scaladsl.Http 2 | import akka.http.scaladsl.server.RouteConcatenation 3 | import akka.stream.ActorMaterializer 4 | import rest.SupplierRoutes 5 | import utils._ 6 | 7 | object Main extends App with RouteConcatenation with CorsSupport{ 8 | // configuring modules for application, cake pattern for DI 9 | val modules = new ConfigurationModuleImpl with ActorModuleImpl with PersistenceModuleImpl 10 | implicit val system = modules.system 11 | implicit val materializer = ActorMaterializer() 12 | implicit val ec = modules.system.dispatcher 13 | 14 | val swaggerService = new SwaggerDocService(system) 15 | 16 | val bindingFuture = Http().bindAndHandle( 17 | new SupplierRoutes(modules).routes ~ 18 | swaggerService.assets ~ 19 | corsHandler(swaggerService.routes), "localhost", 8080) 20 | 21 | println(s"Server online at http://localhost:8080/") 22 | 23 | } -------------------------------------------------------------------------------- /src/test/scala/rest/AbstractRestTest.scala: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import org.scalatest.{Matchers, WordSpec} 4 | import org.specs2.mock.Mockito 5 | import persistence.dal.{SuppliersDal, SuppliersDalImpl} 6 | import utils.{ActorModule, ConfigurationModuleImpl, DbContext, PersistenceModule} 7 | import com.typesafe.config.Config 8 | import com.typesafe.config.ConfigFactory 9 | import akka.http.scaladsl.testkit.ScalatestRouteTest 10 | import io.getquill._ 11 | trait AbstractRestTest extends WordSpec with Matchers with ScalatestRouteTest with Mockito{ 12 | 13 | trait Modules extends ConfigurationModuleImpl with ActorModule with PersistenceModule with DbContext { 14 | val system = AbstractRestTest.this.system 15 | 16 | override lazy val context = ??? 17 | override val suppliersDal: SuppliersDal = mock[SuppliersDal] 18 | 19 | } 20 | 21 | def getConfig: Config = ConfigFactory.empty(); 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/scala/utils/SwaggerDocService.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import scala.reflect.runtime.{universe => ru} 4 | import akka.actor.ActorSystem 5 | import akka.http.scaladsl.model.StatusCodes 6 | import akka.stream.ActorMaterializer 7 | import com.github.swagger.akka._ 8 | import com.github.swagger.akka.model.Info 9 | import rest.SupplierRoutes 10 | 11 | class SwaggerDocService(system: ActorSystem) extends SwaggerHttpService with HasActorSystem { 12 | override implicit val actorSystem: ActorSystem = system 13 | override implicit val materializer: ActorMaterializer = ActorMaterializer() 14 | override val apiTypes = Seq(ru.typeOf[SupplierRoutes]) 15 | override val host = "localhost:8080" 16 | override val info = Info(version = "2.0") 17 | 18 | def assets = pathPrefix("swagger") { 19 | getFromResourceDirectory("swagger") ~ pathSingleSlash(get(redirect("index.html", StatusCodes.PermanentRedirect))) } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | System.out 6 | 7 | %date{MM/dd HH:mm:ss} %-5level[%.15thread] %logger{1} - %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tutorial/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quill Async Akka Http 5 | 6 | 7 |
8 |

Intro

9 | 10 |

The Quill Async Akka Http is a very simple json rest api showing one way of using akka http with quill using postgres async. 11 |

12 | 13 |

It supports the following features:

14 | 15 | 23 |

Utils: Typesafe config for property management and Typesafe Scala Logging (LazyLogging)

24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lib/highlight.9.1.0.pack_extended.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function () { 4 | var configure, highlightBlock; 5 | 6 | configure = hljs.configure; 7 | // "extending" hljs.configure method 8 | hljs.configure = function _configure (options) { 9 | var size = options.highlightSizeThreshold; 10 | 11 | // added highlightSizeThreshold option to set maximum size 12 | // of processed string. Set to null if not a number 13 | hljs.highlightSizeThreshold = size === +size ? size : null; 14 | 15 | configure.call(this, options); 16 | }; 17 | 18 | highlightBlock = hljs.highlightBlock; 19 | 20 | // "extending" hljs.highlightBlock method 21 | hljs.highlightBlock = function _highlightBlock (el) { 22 | var innerHTML = el.innerHTML; 23 | var size = hljs.highlightSizeThreshold; 24 | 25 | // check if highlightSizeThreshold is not set or element innerHTML 26 | // is less than set option highlightSizeThreshold 27 | if (size == null || size > innerHTML.length) { 28 | // proceed with hljs.highlightBlock 29 | highlightBlock.call(hljs, el); 30 | } 31 | }; 32 | 33 | })(); 34 | 35 | -------------------------------------------------------------------------------- /src/main/scala/persistence/dal/SuppliersDal.scala: -------------------------------------------------------------------------------- 1 | package persistence.dal 2 | 3 | import persistence.entities.Supplier 4 | import io.getquill._ 5 | 6 | import scala.concurrent.{Awaitable, ExecutionContext, Future} 7 | 8 | trait SuppliersDal { 9 | def insert(supplierToInsert: Supplier)(implicit ec: ExecutionContext): Future[Long] 10 | def findById(supId: String)(implicit ec: ExecutionContext) : Future[Option[Supplier]] 11 | def delete(supId: String)(implicit ec: ExecutionContext): Future[Long] 12 | } 13 | 14 | class SuppliersDalImpl(context: PostgresAsyncContext[SnakeCase]) extends SuppliersDal { 15 | 16 | import context._ 17 | 18 | override def insert(supplierToInsert: Supplier)(implicit ec: ExecutionContext): Future[Long] = { 19 | context.run(query[Supplier].insert)(supplierToInsert :: Nil).map(_.head) 20 | } 21 | 22 | override def findById(supId: String)(implicit ec: ExecutionContext) : Future[Option[Supplier]] = { 23 | val q = quote { 24 | query[Supplier].filter(s => s.id == lift(supId)) 25 | } 26 | context.run(q).map(_.headOption) 27 | } 28 | 29 | override def delete(supId: String)(implicit ec: ExecutionContext): Future[Long] = { 30 | val q = quote { 31 | query[Supplier].filter(s => s.id == lift(supId)).delete 32 | } 33 | context.run(q) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/scala/persistence/dal/SuppliersDALTest.scala: -------------------------------------------------------------------------------- 1 | package persistence.dal 2 | 3 | import java.util.UUID 4 | 5 | import persistence.entities.Supplier 6 | import org.junit.runner.RunWith 7 | import org.scalatest.{BeforeAndAfterAll, FunSuite} 8 | import org.scalatest.junit.JUnitRunner 9 | 10 | import scala.concurrent.ExecutionContext.Implicits.global 11 | import scala.concurrent.Await 12 | import scala.concurrent.duration._ 13 | import akka.util.Timeout 14 | 15 | @RunWith(classOf[JUnitRunner]) 16 | class SuppliersDALTest extends FunSuite with AbstractPersistenceTest with BeforeAndAfterAll{ 17 | implicit val timeout = Timeout(5.seconds) 18 | lazy val generatedId = UUID.randomUUID().toString 19 | val modules = new Modules { 20 | } 21 | 22 | test("SuppliersActor: Testing Suppliers DAL") { 23 | val numberOfEntities : Long = Await.result((modules.suppliersDal.insert(Supplier(generatedId,"sup","desc"))),5.seconds) 24 | assert (numberOfEntities == 1) 25 | val supplier : Option[Supplier] = Await.result((modules.suppliersDal.findById(generatedId)),5.seconds) 26 | assert (! supplier.isEmpty && supplier.get.name.compareTo("sup") == 0) 27 | val empty : Option[Supplier] = Await.result((modules.suppliersDal.findById("not an uuid")),5.seconds) 28 | assert (empty.isEmpty) 29 | } 30 | 31 | override def afterAll() { 32 | Await.result((modules.suppliersDal.delete(generatedId)),5.seconds) 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/scala/utils/CorsSupport.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import akka.http.scaladsl.model.HttpMethods._ 4 | import akka.http.scaladsl.model.HttpResponse 5 | import akka.http.scaladsl.model.headers._ 6 | import akka.http.scaladsl.server.Directives._ 7 | import akka.http.scaladsl.server.{Directive0, Route} 8 | import akka.http.scaladsl.marshalling.ToResponseMarshallable.apply 9 | import akka.http.scaladsl.model.StatusCode.int2StatusCode 10 | import akka.http.scaladsl.server.Directive.addByNameNullaryApply 11 | 12 | //see https://groups.google.com/forum/#!topic/akka-user/5RCZIJt7jHo 13 | trait CorsSupport { 14 | 15 | //this directive adds access control headers to normal responses 16 | private def addAccessControlHeaders: Directive0 = { 17 | mapResponseHeaders { headers => 18 | `Access-Control-Allow-Origin`.* +: 19 | `Access-Control-Allow-Credentials`(true) +: 20 | `Access-Control-Allow-Headers`("Authorization", "Content-Type", "X-Requested-With") +: 21 | headers 22 | } 23 | } 24 | 25 | //this handles preflight OPTIONS requests. TODO: see if can be done with rejection handler, 26 | //otherwise has to be under addAccessControlHeaders 27 | private def preflightRequestHandler: Route = options { 28 | complete(HttpResponse(200).withHeaders( 29 | `Access-Control-Allow-Methods`(OPTIONS, POST, PUT, GET, DELETE) 30 | ) 31 | ) 32 | } 33 | 34 | def corsHandler(r: Route) = addAccessControlHeaders { 35 | preflightRequestHandler ~ r 36 | } 37 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quill-async-akka-http 2 | The Quill Async Akka Http is a very simple json rest api showing one way of using akka http with [quill](https://github.com/getquill/quill) using postgres async. 3 | 4 | 5 | It supports the following features: 6 | 7 | * Models as case classes (quill main feature) 8 | * Compile time query generation (quill main feature) 9 | * Cake pattern for DI 10 | * Spray-json to parse json 11 | * Tests for DAL 12 | * tests for routes 13 | 14 | Utils: 15 | 16 | * Typesafe config for property management 17 | * Typesafe Scala Logging (LazyLogging) 18 | * Swagger for api documentation 19 | 20 | The project was thought to be used as an activator template. 21 | 22 | #Running 23 | 24 | You should pre-configure 2 databases on postgres, quill and quilltest, an run the script postgresql-schema.sql to initiate the schema. 25 | Take a look at application.properties and change the db configuration as you need. 26 | After that, just: 27 | 28 | 29 | $ sbt run 30 | 31 | #Testing 32 | 33 | To run all tests (routes and persistence tests): 34 | 35 | 36 | $ sbt test 37 | 38 | #Using 39 | 40 | curl --request POST localhost:8080/supplier -H "Content-type: application/json" --data "{\"name\" : \"sup1\",\"desc\" : \"low prices\"}" 41 | 42 | curl localhost:8080/supplier/valid-uuid 43 | 44 | or just use swagger: 45 | 46 | http://localhost:8080/swagger/index.html 47 | 48 | #Credits 49 | 50 | To make this template, I just mixed the tutorials and templates, so credits for akka and quill guys, and swagger-akka-http. 51 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/translator.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Translator for documentation pages. 5 | * 6 | * To enable translation you should include one of language-files in your index.html 7 | * after . 8 | * For example - 9 | * 10 | * If you wish to translate some new texsts you should do two things: 11 | * 1. Add a new phrase pair ("New Phrase": "New Translation") into your language file (for example lang/ru.js). It will be great if you add it in other language files too. 12 | * 2. Mark that text it templates this way New Phrase or . 13 | * The main thing here is attribute data-sw-translate. Only inner html, title-attribute and value-attribute are going to translate. 14 | * 15 | */ 16 | window.SwaggerTranslator = { 17 | 18 | _words:[], 19 | 20 | translate: function(sel) { 21 | var $this = this; 22 | sel = sel || '[data-sw-translate]'; 23 | 24 | $(sel).each(function() { 25 | $(this).html($this._tryTranslate($(this).html())); 26 | 27 | $(this).val($this._tryTranslate($(this).val())); 28 | $(this).attr('title', $this._tryTranslate($(this).attr('title'))); 29 | }); 30 | }, 31 | 32 | _tryTranslate: function(word) { 33 | return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word; 34 | }, 35 | 36 | learn: function(wordsMap) { 37 | this._words = wordsMap; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/main/resources/swagger/css/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */ 2 | html, 3 | body, 4 | div, 5 | span, 6 | applet, 7 | object, 8 | iframe, 9 | h1, 10 | h2, 11 | h3, 12 | h4, 13 | h5, 14 | h6, 15 | p, 16 | blockquote, 17 | pre, 18 | a, 19 | abbr, 20 | acronym, 21 | address, 22 | big, 23 | cite, 24 | code, 25 | del, 26 | dfn, 27 | em, 28 | img, 29 | ins, 30 | kbd, 31 | q, 32 | s, 33 | samp, 34 | small, 35 | strike, 36 | strong, 37 | sub, 38 | sup, 39 | tt, 40 | var, 41 | b, 42 | u, 43 | i, 44 | center, 45 | dl, 46 | dt, 47 | dd, 48 | ol, 49 | ul, 50 | li, 51 | fieldset, 52 | form, 53 | label, 54 | legend, 55 | table, 56 | caption, 57 | tbody, 58 | tfoot, 59 | thead, 60 | tr, 61 | th, 62 | td, 63 | article, 64 | aside, 65 | canvas, 66 | details, 67 | embed, 68 | figure, 69 | figcaption, 70 | footer, 71 | header, 72 | hgroup, 73 | menu, 74 | nav, 75 | output, 76 | ruby, 77 | section, 78 | summary, 79 | time, 80 | mark, 81 | audio, 82 | video { 83 | margin: 0; 84 | padding: 0; 85 | border: 0; 86 | font-size: 100%; 87 | font: inherit; 88 | vertical-align: baseline; 89 | } 90 | /* HTML5 display-role reset for older browsers */ 91 | article, 92 | aside, 93 | details, 94 | figcaption, 95 | figure, 96 | footer, 97 | header, 98 | hgroup, 99 | menu, 100 | nav, 101 | section { 102 | display: block; 103 | } 104 | body { 105 | line-height: 1; 106 | } 107 | ol, 108 | ul { 109 | list-style: none; 110 | } 111 | blockquote, 112 | q { 113 | quotes: none; 114 | } 115 | blockquote:before, 116 | blockquote:after, 117 | q:before, 118 | q:after { 119 | content: ''; 120 | content: none; 121 | } 122 | table { 123 | border-collapse: collapse; 124 | border-spacing: 0; 125 | } 126 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/zh-cn.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"警告:已过时", 6 | "Implementation Notes":"实现备注", 7 | "Response Class":"响应类", 8 | "Status":"状态", 9 | "Parameters":"参数", 10 | "Parameter":"参数", 11 | "Value":"值", 12 | "Description":"描述", 13 | "Parameter Type":"参数类型", 14 | "Data Type":"数据类型", 15 | "Response Messages":"响应消息", 16 | "HTTP Status Code":"HTTP状态码", 17 | "Reason":"原因", 18 | "Response Model":"响应模型", 19 | "Request URL":"请求URL", 20 | "Response Body":"响应体", 21 | "Response Code":"响应码", 22 | "Response Headers":"响应头", 23 | "Hide Response":"隐藏响应", 24 | "Headers":"头", 25 | "Try it out!":"试一下!", 26 | "Show/Hide":"显示/隐藏", 27 | "List Operations":"显示操作", 28 | "Expand Operations":"展开操作", 29 | "Raw":"原始", 30 | "can't parse JSON. Raw result":"无法解析JSON. 原始结果", 31 | "Model Schema":"模型架构", 32 | "Model":"模型", 33 | "apply":"应用", 34 | "Username":"用户名", 35 | "Password":"密码", 36 | "Terms of service":"服务条款", 37 | "Created by":"创建者", 38 | "See more at":"查看更多:", 39 | "Contact the developer":"联系开发者", 40 | "api version":"api版本", 41 | "Response Content Type":"响应Content Type", 42 | "fetching resource":"正在获取资源", 43 | "fetching resource list":"正在获取资源列表", 44 | "Explore":"浏览", 45 | "Show Swagger Petstore Example Apis":"显示 Swagger Petstore 示例 Apis", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"无法从服务器读取。可能没有正确设置access-control-origin。", 47 | "Please specify the protocol for":"请指定协议:", 48 | "Can't read swagger JSON from":"无法读取swagger JSON于", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"已加载资源信息。正在渲染Swagger UI", 50 | "Unable to read api":"无法读取api", 51 | "from path":"从路径", 52 | "server returned":"服务器返回" 53 | }); 54 | -------------------------------------------------------------------------------- /src/test/scala/rest/RoutesSpec.scala: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import java.util.UUID 4 | 5 | import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport 6 | import persistence.entities.{SimpleSupplier, Supplier} 7 | import persistence.JsonProtocol 8 | import JsonProtocol._ 9 | import SprayJsonSupport._ 10 | 11 | import scala.concurrent.Future 12 | import akka.http.scaladsl.model.StatusCodes._ 13 | 14 | class RoutesSpec extends AbstractRestTest { 15 | 16 | def actorRefFactory = system 17 | val modules = new Modules {} 18 | val suppliers = new SupplierRoutes(modules) 19 | lazy val generatedId = UUID.randomUUID().toString 20 | 21 | "Supplier Routes" should { 22 | 23 | "return an empty array of suppliers" in { 24 | modules.suppliersDal.findById(generatedId) returns Future(None) 25 | 26 | Get(s"/supplier/$generatedId") ~> suppliers.routes ~> check { 27 | handled shouldEqual true 28 | status shouldEqual NotFound 29 | } 30 | } 31 | 32 | "return an array with 1 suppliers" in { 33 | modules.suppliersDal.findById(generatedId) returns Future(Some(Supplier(generatedId,"name 1", "desc 1"))) 34 | Get(s"/supplier/$generatedId") ~> suppliers.routes ~> check { 35 | handled shouldEqual true 36 | status shouldEqual OK 37 | responseAs[Option[SimpleSupplier]].isEmpty shouldEqual false 38 | } 39 | } 40 | 41 | "create a supplier with the json in post" in { 42 | modules.suppliersDal.insert(any)(any) returns Future(1) 43 | Post("/supplier",SimpleSupplier("name 1","desc 1")) ~> suppliers.routes ~> check { 44 | handled shouldEqual true 45 | status shouldEqual Created 46 | } 47 | } 48 | 49 | "not handle the invalid json" in { 50 | Post("/supplier","{\"name\":\"1\"}") ~> suppliers.routes ~> check { 51 | handled shouldEqual false 52 | } 53 | } 54 | 55 | "not handle an empty post" in { 56 | Post("/supplier") ~> suppliers.routes ~> check { 57 | handled shouldEqual false 58 | } 59 | } 60 | 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/ja.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"警告: 廃止予定", 6 | "Implementation Notes":"実装メモ", 7 | "Response Class":"レスポンスクラス", 8 | "Status":"ステータス", 9 | "Parameters":"パラメータ群", 10 | "Parameter":"パラメータ", 11 | "Value":"値", 12 | "Description":"説明", 13 | "Parameter Type":"パラメータタイプ", 14 | "Data Type":"データタイプ", 15 | "Response Messages":"レスポンスメッセージ", 16 | "HTTP Status Code":"HTTPステータスコード", 17 | "Reason":"理由", 18 | "Response Model":"レスポンスモデル", 19 | "Request URL":"リクエストURL", 20 | "Response Body":"レスポンスボディ", 21 | "Response Code":"レスポンスコード", 22 | "Response Headers":"レスポンスヘッダ", 23 | "Hide Response":"レスポンスを隠す", 24 | "Headers":"ヘッダ", 25 | "Try it out!":"実際に実行!", 26 | "Show/Hide":"表示/非表示", 27 | "List Operations":"操作一覧", 28 | "Expand Operations":"操作の展開", 29 | "Raw":"Raw", 30 | "can't parse JSON. Raw result":"JSONへ解釈できません. 未加工の結果", 31 | "Model Schema":"モデルスキーマ", 32 | "Model":"モデル", 33 | "apply":"実行", 34 | "Username":"ユーザ名", 35 | "Password":"パスワード", 36 | "Terms of service":"サービス利用規約", 37 | "Created by":"Created by", 38 | "See more at":"See more at", 39 | "Contact the developer":"開発者に連絡", 40 | "api version":"APIバージョン", 41 | "Response Content Type":"レスポンス コンテンツタイプ", 42 | "fetching resource":"リソースの取得", 43 | "fetching resource list":"リソース一覧の取得", 44 | "Explore":"Explore", 45 | "Show Swagger Petstore Example Apis":"SwaggerペットストアAPIの表示", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"サーバから読み込めません. 適切なaccess-control-origin設定を持っていない可能性があります.", 47 | "Please specify the protocol for":"プロトコルを指定してください", 48 | "Can't read swagger JSON from":"次からswagger JSONを読み込めません", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"リソース情報の読み込みが完了しました. Swagger UIを描画しています", 50 | "Unable to read api":"APIを読み込めません", 51 | "from path":"次のパスから", 52 | "server returned":"サーバからの返答" 53 | }); 54 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/tr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Uyarı: Deprecated", 6 | "Implementation Notes":"Gerçekleştirim Notları", 7 | "Response Class":"Dönen Sınıf", 8 | "Status":"Statü", 9 | "Parameters":"Parametreler", 10 | "Parameter":"Parametre", 11 | "Value":"Değer", 12 | "Description":"Açıklama", 13 | "Parameter Type":"Parametre Tipi", 14 | "Data Type":"Veri Tipi", 15 | "Response Messages":"Dönüş Mesajı", 16 | "HTTP Status Code":"HTTP Statü Kodu", 17 | "Reason":"Gerekçe", 18 | "Response Model":"Dönüş Modeli", 19 | "Request URL":"İstek URL", 20 | "Response Body":"Dönüş İçeriği", 21 | "Response Code":"Dönüş Kodu", 22 | "Response Headers":"Dönüş Üst Bilgileri", 23 | "Hide Response":"Dönüşü Gizle", 24 | "Headers":"Üst Bilgiler", 25 | "Try it out!":"Dene!", 26 | "Show/Hide":"Göster/Gizle", 27 | "List Operations":"Operasyonları Listele", 28 | "Expand Operations":"Operasyonları Aç", 29 | "Raw":"Ham", 30 | "can't parse JSON. Raw result":"JSON çözümlenemiyor. Ham sonuç", 31 | "Model Schema":"Model Şema", 32 | "Model":"Model", 33 | "apply":"uygula", 34 | "Username":"Kullanıcı Adı", 35 | "Password":"Parola", 36 | "Terms of service":"Servis şartları", 37 | "Created by":"Oluşturan", 38 | "See more at":"Daha fazlası için", 39 | "Contact the developer":"Geliştirici ile İletişime Geçin", 40 | "api version":"api versiyon", 41 | "Response Content Type":"Dönüş İçerik Tipi", 42 | "fetching resource":"kaynak getiriliyor", 43 | "fetching resource list":"kaynak listesi getiriliyor", 44 | "Explore":"Keşfet", 45 | "Show Swagger Petstore Example Apis":"Swagger Petstore Örnek Api'yi Gör", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Sunucudan okuma yapılamıyor. Sunucu access-control-origin ayarlarınızı kontrol edin.", 47 | "Please specify the protocol for":"Lütfen istenen adres için protokol belirtiniz", 48 | "Can't read swagger JSON from":"Swagger JSON bu kaynaktan okunamıyor", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Kaynak baglantısı tamamlandı. Swagger UI gösterime hazırlanıyor", 50 | "Unable to read api":"api okunamadı", 51 | "from path":"yoldan", 52 | "server returned":"sunucuya dönüldü" 53 | }); 54 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/pl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Uwaga: Wycofane", 6 | "Implementation Notes":"Uwagi Implementacji", 7 | "Response Class":"Klasa Odpowiedzi", 8 | "Status":"Status", 9 | "Parameters":"Parametry", 10 | "Parameter":"Parametr", 11 | "Value":"Wartość", 12 | "Description":"Opis", 13 | "Parameter Type":"Typ Parametru", 14 | "Data Type":"Typ Danych", 15 | "Response Messages":"Wiadomości Odpowiedzi", 16 | "HTTP Status Code":"Kod Statusu HTTP", 17 | "Reason":"Przyczyna", 18 | "Response Model":"Model Odpowiedzi", 19 | "Request URL":"URL Wywołania", 20 | "Response Body":"Treść Odpowiedzi", 21 | "Response Code":"Kod Odpowiedzi", 22 | "Response Headers":"Nagłówki Odpowiedzi", 23 | "Hide Response":"Ukryj Odpowiedź", 24 | "Headers":"Nagłówki", 25 | "Try it out!":"Wypróbuj!", 26 | "Show/Hide":"Pokaż/Ukryj", 27 | "List Operations":"Lista Operacji", 28 | "Expand Operations":"Rozwiń Operacje", 29 | "Raw":"Nieprzetworzone", 30 | "can't parse JSON. Raw result":"nie można przetworzyć pliku JSON. Nieprzetworzone dane", 31 | "Model Schema":"Schemat Modelu", 32 | "Model":"Model", 33 | "apply":"użyj", 34 | "Username":"Nazwa użytkownika", 35 | "Password":"Hasło", 36 | "Terms of service":"Warunki używania", 37 | "Created by":"Utworzone przez", 38 | "See more at":"Zobacz więcej na", 39 | "Contact the developer":"Kontakt z deweloperem", 40 | "api version":"wersja api", 41 | "Response Content Type":"Typ Zasobu Odpowiedzi", 42 | "fetching resource":"ładowanie zasobu", 43 | "fetching resource list":"ładowanie listy zasobów", 44 | "Explore":"Eksploruj", 45 | "Show Swagger Petstore Example Apis":"Pokaż Przykładowe Api Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Brak połączenia z serwerem. Może on nie mieć odpowiednich ustawień access-control-origin.", 47 | "Please specify the protocol for":"Proszę podać protokół dla", 48 | "Can't read swagger JSON from":"Nie można odczytać swagger JSON z", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Ukończono Ładowanie Informacji o Zasobie. Renderowanie Swagger UI", 50 | "Unable to read api":"Nie można odczytać api", 51 | "from path":"ze ścieżki", 52 | "server returned":"serwer zwrócił" 53 | }); 54 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/pt.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Aviso: Depreciado", 6 | "Implementation Notes":"Notas de Implementação", 7 | "Response Class":"Classe de resposta", 8 | "Status":"Status", 9 | "Parameters":"Parâmetros", 10 | "Parameter":"Parâmetro", 11 | "Value":"Valor", 12 | "Description":"Descrição", 13 | "Parameter Type":"Tipo de parâmetro", 14 | "Data Type":"Tipo de dados", 15 | "Response Messages":"Mensagens de resposta", 16 | "HTTP Status Code":"Código de status HTTP", 17 | "Reason":"Razão", 18 | "Response Model":"Modelo resposta", 19 | "Request URL":"URL requisição", 20 | "Response Body":"Corpo da resposta", 21 | "Response Code":"Código da resposta", 22 | "Response Headers":"Cabeçalho da resposta", 23 | "Headers":"Cabeçalhos", 24 | "Hide Response":"Esconder resposta", 25 | "Try it out!":"Tente agora!", 26 | "Show/Hide":"Mostrar/Esconder", 27 | "List Operations":"Listar operações", 28 | "Expand Operations":"Expandir operações", 29 | "Raw":"Cru", 30 | "can't parse JSON. Raw result":"Falha ao analisar JSON. Resulto cru", 31 | "Model Schema":"Modelo esquema", 32 | "Model":"Modelo", 33 | "apply":"Aplicar", 34 | "Username":"Usuário", 35 | "Password":"Senha", 36 | "Terms of service":"Termos do serviço", 37 | "Created by":"Criado por", 38 | "See more at":"Veja mais em", 39 | "Contact the developer":"Contate o desenvolvedor", 40 | "api version":"Versão api", 41 | "Response Content Type":"Tipo de conteúdo da resposta", 42 | "fetching resource":"busca recurso", 43 | "fetching resource list":"buscando lista de recursos", 44 | "Explore":"Explorar", 45 | "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Não é possível ler do servidor. Pode não ter as apropriadas configurações access-control-origin", 47 | "Please specify the protocol for":"Por favor especifique o protocolo", 48 | "Can't read swagger JSON from":"Não é possível ler o JSON Swagger de", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Carregar informação de recurso finalizada. Renderizando Swagger UI", 50 | "Unable to read api":"Não foi possível ler api", 51 | "from path":"do caminho", 52 | "server returned":"servidor retornou" 53 | }); 54 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/en.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Warning: Deprecated", 6 | "Implementation Notes":"Implementation Notes", 7 | "Response Class":"Response Class", 8 | "Status":"Status", 9 | "Parameters":"Parameters", 10 | "Parameter":"Parameter", 11 | "Value":"Value", 12 | "Description":"Description", 13 | "Parameter Type":"Parameter Type", 14 | "Data Type":"Data Type", 15 | "Response Messages":"Response Messages", 16 | "HTTP Status Code":"HTTP Status Code", 17 | "Reason":"Reason", 18 | "Response Model":"Response Model", 19 | "Request URL":"Request URL", 20 | "Response Body":"Response Body", 21 | "Response Code":"Response Code", 22 | "Response Headers":"Response Headers", 23 | "Hide Response":"Hide Response", 24 | "Headers":"Headers", 25 | "Try it out!":"Try it out!", 26 | "Show/Hide":"Show/Hide", 27 | "List Operations":"List Operations", 28 | "Expand Operations":"Expand Operations", 29 | "Raw":"Raw", 30 | "can't parse JSON. Raw result":"can't parse JSON. Raw result", 31 | "Example Value":"Example Value", 32 | "Model Schema":"Model Schema", 33 | "Model":"Model", 34 | "Click to set as parameter value":"Click to set as parameter value", 35 | "apply":"apply", 36 | "Username":"Username", 37 | "Password":"Password", 38 | "Terms of service":"Terms of service", 39 | "Created by":"Created by", 40 | "See more at":"See more at", 41 | "Contact the developer":"Contact the developer", 42 | "api version":"api version", 43 | "Response Content Type":"Response Content Type", 44 | "Parameter content type:":"Parameter content type:", 45 | "fetching resource":"fetching resource", 46 | "fetching resource list":"fetching resource list", 47 | "Explore":"Explore", 48 | "Show Swagger Petstore Example Apis":"Show Swagger Petstore Example Apis", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Can't read from server. It may not have the appropriate access-control-origin settings.", 50 | "Please specify the protocol for":"Please specify the protocol for", 51 | "Can't read swagger JSON from":"Can't read swagger JSON from", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"Finished Loading Resource Information. Rendering Swagger UI", 53 | "Unable to read api":"Unable to read api", 54 | "from path":"from path", 55 | "server returned":"server returned" 56 | }); 57 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/ru.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Предупреждение: Устарело", 6 | "Implementation Notes":"Заметки", 7 | "Response Class":"Пример ответа", 8 | "Status":"Статус", 9 | "Parameters":"Параметры", 10 | "Parameter":"Параметр", 11 | "Value":"Значение", 12 | "Description":"Описание", 13 | "Parameter Type":"Тип параметра", 14 | "Data Type":"Тип данных", 15 | "HTTP Status Code":"HTTP код", 16 | "Reason":"Причина", 17 | "Response Model":"Структура ответа", 18 | "Request URL":"URL запроса", 19 | "Response Body":"Тело ответа", 20 | "Response Code":"HTTP код ответа", 21 | "Response Headers":"Заголовки ответа", 22 | "Hide Response":"Спрятать ответ", 23 | "Headers":"Заголовки", 24 | "Response Messages":"Что может прийти в ответ", 25 | "Try it out!":"Попробовать!", 26 | "Show/Hide":"Показать/Скрыть", 27 | "List Operations":"Операции кратко", 28 | "Expand Operations":"Операции подробно", 29 | "Raw":"В сыром виде", 30 | "can't parse JSON. Raw result":"Не удается распарсить ответ:", 31 | "Example Value":"Пример", 32 | "Model Schema":"Структура", 33 | "Model":"Описание", 34 | "Click to set as parameter value":"Нажмите, чтобы испльзовать в качестве значения параметра", 35 | "apply":"применить", 36 | "Username":"Имя пользователя", 37 | "Password":"Пароль", 38 | "Terms of service":"Условия использования", 39 | "Created by":"Разработано", 40 | "See more at":"Еще тут", 41 | "Contact the developer":"Связаться с разработчиком", 42 | "api version":"Версия API", 43 | "Response Content Type":"Content Type ответа", 44 | "Parameter content type:":"Content Type параметра:", 45 | "fetching resource":"Получение ресурса", 46 | "fetching resource list":"Получение ресурсов", 47 | "Explore":"Показать", 48 | "Show Swagger Petstore Example Apis":"Показать примеры АПИ", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Не удается получить ответ от сервера. Возможно, проблема с настройками доступа", 50 | "Please specify the protocol for":"Пожалуйста, укажите протокол для", 51 | "Can't read swagger JSON from":"Не получается прочитать swagger json из", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"Загрузка информации о ресурсах завершена. Рендерим", 53 | "Unable to read api":"Не удалось прочитать api", 54 | "from path":"по адресу", 55 | "server returned":"сервер сказал" 56 | }); 57 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/geo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"ყურადღება: აღარ გამოიყენება", 6 | "Implementation Notes":"იმპლემენტაციის აღწერა", 7 | "Response Class":"რესპონს კლასი", 8 | "Status":"სტატუსი", 9 | "Parameters":"პარამეტრები", 10 | "Parameter":"პარამეტრი", 11 | "Value":"მნიშვნელობა", 12 | "Description":"აღწერა", 13 | "Parameter Type":"პარამეტრის ტიპი", 14 | "Data Type":"მონაცემის ტიპი", 15 | "Response Messages":"პასუხი", 16 | "HTTP Status Code":"HTTP სტატუსი", 17 | "Reason":"მიზეზი", 18 | "Response Model":"რესპონს მოდელი", 19 | "Request URL":"მოთხოვნის URL", 20 | "Response Body":"პასუხის სხეული", 21 | "Response Code":"პასუხის კოდი", 22 | "Response Headers":"პასუხის ჰედერები", 23 | "Hide Response":"დამალე პასუხი", 24 | "Headers":"ჰედერები", 25 | "Try it out!":"ცადე !", 26 | "Show/Hide":"გამოჩენა/დამალვა", 27 | "List Operations":"ოპერაციების სია", 28 | "Expand Operations":"ოპერაციები ვრცლად", 29 | "Raw":"ნედლი", 30 | "can't parse JSON. Raw result":"JSON-ის დამუშავება ვერ მოხერხდა. ნედლი პასუხი", 31 | "Example Value":"მაგალითი", 32 | "Model Schema":"მოდელის სტრუქტურა", 33 | "Model":"მოდელი", 34 | "Click to set as parameter value":"პარამეტრისთვის მნიშვნელობის მისანიჭებლად, დააკლიკე", 35 | "apply":"გამოყენება", 36 | "Username":"მოხმარებელი", 37 | "Password":"პაროლი", 38 | "Terms of service":"მომსახურების პირობები", 39 | "Created by":"შექმნა", 40 | "See more at":"ნახე ვრცლად", 41 | "Contact the developer":"დაუკავშირდი დეველოპერს", 42 | "api version":"api ვერსია", 43 | "Response Content Type":"პასუხის კონტენტის ტიპი", 44 | "Parameter content type:":"პარამეტრის კონტენტის ტიპი:", 45 | "fetching resource":"რესურსების მიღება", 46 | "fetching resource list":"რესურსების სიის მიღება", 47 | "Explore":"ნახვა", 48 | "Show Swagger Petstore Example Apis":"ნახე Swagger Petstore სამაგალითო Api", 49 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"სერვერთან დაკავშირება ვერ ხერხდება. შეამოწმეთ access-control-origin.", 50 | "Please specify the protocol for":"მიუთითეთ პროტოკოლი", 51 | "Can't read swagger JSON from":"swagger JSON წაკითხვა ვერ მოხერხდა", 52 | "Finished Loading Resource Information. Rendering Swagger UI":"რესურსების ჩატვირთვა სრულდება. Swagger UI რენდერდება", 53 | "Unable to read api":"api წაკითხვა ვერ მოხერხდა", 54 | "from path":"მისამართიდან", 55 | "server returned":"სერვერმა დააბრუნა" 56 | }); 57 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/it.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Attenzione: Deprecato", 6 | "Implementation Notes":"Note di implementazione", 7 | "Response Class":"Classe della risposta", 8 | "Status":"Stato", 9 | "Parameters":"Parametri", 10 | "Parameter":"Parametro", 11 | "Value":"Valore", 12 | "Description":"Descrizione", 13 | "Parameter Type":"Tipo di parametro", 14 | "Data Type":"Tipo di dato", 15 | "Response Messages":"Messaggi della risposta", 16 | "HTTP Status Code":"Codice stato HTTP", 17 | "Reason":"Motivo", 18 | "Response Model":"Modello di risposta", 19 | "Request URL":"URL della richiesta", 20 | "Response Body":"Corpo della risposta", 21 | "Response Code":"Oggetto della risposta", 22 | "Response Headers":"Intestazioni della risposta", 23 | "Hide Response":"Nascondi risposta", 24 | "Try it out!":"Provalo!", 25 | "Show/Hide":"Mostra/Nascondi", 26 | "List Operations":"Mostra operazioni", 27 | "Expand Operations":"Espandi operazioni", 28 | "Raw":"Grezzo (raw)", 29 | "can't parse JSON. Raw result":"non è possibile parsare il JSON. Risultato grezzo (raw).", 30 | "Model Schema":"Schema del modello", 31 | "Model":"Modello", 32 | "apply":"applica", 33 | "Username":"Nome utente", 34 | "Password":"Password", 35 | "Terms of service":"Condizioni del servizio", 36 | "Created by":"Creato da", 37 | "See more at":"Informazioni aggiuntive:", 38 | "Contact the developer":"Contatta lo sviluppatore", 39 | "api version":"versione api", 40 | "Response Content Type":"Tipo di contenuto (content type) della risposta", 41 | "fetching resource":"recuperando la risorsa", 42 | "fetching resource list":"recuperando lista risorse", 43 | "Explore":"Esplora", 44 | "Show Swagger Petstore Example Apis":"Mostra le api di esempio di Swagger Petstore", 45 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Non è possibile leggere dal server. Potrebbe non avere le impostazioni di controllo accesso origine (access-control-origin) appropriate.", 46 | "Please specify the protocol for":"Si prega di specificare il protocollo per", 47 | "Can't read swagger JSON from":"Impossibile leggere JSON swagger da:", 48 | "Finished Loading Resource Information. Rendering Swagger UI":"Lettura informazioni risorse termianta. Swagger UI viene mostrata", 49 | "Unable to read api":"Impossibile leggere la api", 50 | "from path":"da cartella", 51 | "server returned":"il server ha restituito" 52 | }); 53 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/es.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Advertencia: Obsoleto", 6 | "Implementation Notes":"Notas de implementación", 7 | "Response Class":"Clase de la Respuesta", 8 | "Status":"Status", 9 | "Parameters":"Parámetros", 10 | "Parameter":"Parámetro", 11 | "Value":"Valor", 12 | "Description":"Descripción", 13 | "Parameter Type":"Tipo del Parámetro", 14 | "Data Type":"Tipo del Dato", 15 | "Response Messages":"Mensajes de la Respuesta", 16 | "HTTP Status Code":"Código de Status HTTP", 17 | "Reason":"Razón", 18 | "Response Model":"Modelo de la Respuesta", 19 | "Request URL":"URL de la Solicitud", 20 | "Response Body":"Cuerpo de la Respuesta", 21 | "Response Code":"Código de la Respuesta", 22 | "Response Headers":"Encabezados de la Respuesta", 23 | "Hide Response":"Ocultar Respuesta", 24 | "Try it out!":"Pruébalo!", 25 | "Show/Hide":"Mostrar/Ocultar", 26 | "List Operations":"Listar Operaciones", 27 | "Expand Operations":"Expandir Operaciones", 28 | "Raw":"Crudo", 29 | "can't parse JSON. Raw result":"no puede parsear el JSON. Resultado crudo", 30 | "Example Value":"Valor de Ejemplo", 31 | "Model Schema":"Esquema del Modelo", 32 | "Model":"Modelo", 33 | "apply":"aplicar", 34 | "Username":"Nombre de usuario", 35 | "Password":"Contraseña", 36 | "Terms of service":"Términos de Servicio", 37 | "Created by":"Creado por", 38 | "See more at":"Ver más en", 39 | "Contact the developer":"Contactar al desarrollador", 40 | "api version":"versión de la api", 41 | "Response Content Type":"Tipo de Contenido (Content Type) de la Respuesta", 42 | "fetching resource":"buscando recurso", 43 | "fetching resource list":"buscando lista del recurso", 44 | "Explore":"Explorar", 45 | "Show Swagger Petstore Example Apis":"Mostrar Api Ejemplo de Swagger Petstore", 46 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"No se puede leer del servidor. Tal vez no tiene la configuración de control de acceso de origen (access-control-origin) apropiado.", 47 | "Please specify the protocol for":"Por favor, especificar el protocola para", 48 | "Can't read swagger JSON from":"No se puede leer el JSON de swagger desde", 49 | "Finished Loading Resource Information. Rendering Swagger UI":"Finalizada la carga del recurso de Información. Mostrando Swagger UI", 50 | "Unable to read api":"No se puede leer la api", 51 | "from path":"desde ruta", 52 | "server returned":"el servidor retornó" 53 | }); 54 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lang/fr.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* jshint quotmark: double */ 4 | window.SwaggerTranslator.learn({ 5 | "Warning: Deprecated":"Avertissement : Obsolète", 6 | "Implementation Notes":"Notes d'implémentation", 7 | "Response Class":"Classe de la réponse", 8 | "Status":"Statut", 9 | "Parameters":"Paramètres", 10 | "Parameter":"Paramètre", 11 | "Value":"Valeur", 12 | "Description":"Description", 13 | "Parameter Type":"Type du paramètre", 14 | "Data Type":"Type de données", 15 | "Response Messages":"Messages de la réponse", 16 | "HTTP Status Code":"Code de statut HTTP", 17 | "Reason":"Raison", 18 | "Response Model":"Modèle de réponse", 19 | "Request URL":"URL appelée", 20 | "Response Body":"Corps de la réponse", 21 | "Response Code":"Code de la réponse", 22 | "Response Headers":"En-têtes de la réponse", 23 | "Hide Response":"Cacher la réponse", 24 | "Headers":"En-têtes", 25 | "Try it out!":"Testez !", 26 | "Show/Hide":"Afficher/Masquer", 27 | "List Operations":"Liste des opérations", 28 | "Expand Operations":"Développer les opérations", 29 | "Raw":"Brut", 30 | "can't parse JSON. Raw result":"impossible de décoder le JSON. Résultat brut", 31 | "Example Value":"Exemple la valeur", 32 | "Model Schema":"Définition du modèle", 33 | "Model":"Modèle", 34 | "apply":"appliquer", 35 | "Username":"Nom d'utilisateur", 36 | "Password":"Mot de passe", 37 | "Terms of service":"Conditions de service", 38 | "Created by":"Créé par", 39 | "See more at":"Voir plus sur", 40 | "Contact the developer":"Contacter le développeur", 41 | "api version":"version de l'api", 42 | "Response Content Type":"Content Type de la réponse", 43 | "fetching resource":"récupération de la ressource", 44 | "fetching resource list":"récupération de la liste de ressources", 45 | "Explore":"Explorer", 46 | "Show Swagger Petstore Example Apis":"Montrer les Apis de l'exemple Petstore de Swagger", 47 | "Can't read from server. It may not have the appropriate access-control-origin settings.":"Impossible de lire à partir du serveur. Il se peut que les réglages access-control-origin ne soient pas appropriés.", 48 | "Please specify the protocol for":"Veuillez spécifier un protocole pour", 49 | "Can't read swagger JSON from":"Impossible de lire le JSON swagger à partir de", 50 | "Finished Loading Resource Information. Rendering Swagger UI":"Chargement des informations terminé. Affichage de Swagger UI", 51 | "Unable to read api":"Impossible de lire l'api", 52 | "from path":"à partir du chemin", 53 | "server returned":"réponse du serveur" 54 | }); 55 | -------------------------------------------------------------------------------- /src/main/scala/rest/SupplierRoutes.scala: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import akka.http.scaladsl.model.StatusCodes._ 4 | import akka.http.scaladsl.server.{Directives, Route} 5 | import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport 6 | import persistence.JsonProtocol 7 | import JsonProtocol._ 8 | import SprayJsonSupport._ 9 | import utils.{Configuration, PersistenceModule} 10 | 11 | import scala.util.{Failure, Success} 12 | import io.swagger.annotations._ 13 | import javax.ws.rs.Path 14 | 15 | import persistence.entities.{Supplier,SimpleSupplier} 16 | 17 | import scala.concurrent.ExecutionContext 18 | 19 | @Path("/supplier") 20 | @Api(value = "/supplier", produces = "application/json") 21 | class SupplierRoutes(modules: Configuration with PersistenceModule)(implicit ec: ExecutionContext) extends Directives { 22 | 23 | @Path("/{id}") 24 | @ApiOperation(value = "Return Supplier", notes = "", nickname = "", httpMethod = "GET") 25 | @ApiImplicitParams(Array( 26 | new ApiImplicitParam(name = "id", value = "Supplier Id", required = false, dataType = "int", paramType = "path") 27 | )) 28 | @ApiResponses(Array( 29 | new ApiResponse(code = 200, message = "Return Supplier", response = classOf[Supplier]), 30 | new ApiResponse(code = 400, message = "The supplier id should be greater than zero"), 31 | new ApiResponse(code = 404, message = "Return Supplier Not Found"), 32 | new ApiResponse(code = 500, message = "Internal server error") 33 | )) 34 | def supplierGetRoute = path("supplier" / JavaUUID) { (supId) => 35 | get { 36 | onComplete((modules.suppliersDal.findById(supId.toString)).mapTo[Option[Supplier]]) { 37 | case Success(supplierOpt) => supplierOpt match { 38 | case Some(sup) => complete(sup.toSimpleSupplier) 39 | case None => complete(NotFound, s"The supplier doesn't exist") 40 | } 41 | case Failure(ex) => complete(InternalServerError, s"An error occurred: ${ex.getMessage}") 42 | } 43 | } 44 | } 45 | 46 | @ApiOperation(value = "Add Supplier", notes = "", nickname = "", httpMethod = "POST", produces = "text/plain") 47 | @ApiImplicitParams(Array( 48 | new ApiImplicitParam(name = "body", value = "Supplier Object", required = true, 49 | dataType = "persistence.entities.SimpleSupplier", paramType = "body") 50 | )) 51 | @ApiResponses(Array( 52 | new ApiResponse(code = 500, message = "Internal server error"), 53 | new ApiResponse(code = 400, message = "Bad Request"), 54 | new ApiResponse(code = 201, message = "Entity Created") 55 | )) 56 | def supplierPostRoute = path("supplier") { 57 | post { 58 | entity(as[SimpleSupplier]) { supplierToInsert => onComplete((modules.suppliersDal.insert(supplierToInsert.toSupplier))) { 59 | // ignoring the number of insertedEntities because in this case it should always be one, you might check this in other cases 60 | case Success(insertedEntities) => complete(Created) 61 | case Failure(ex) => complete(InternalServerError, s"An error occurred: ${ex.getMessage}") 62 | } 63 | } 64 | } 65 | } 66 | 67 | val routes: Route = supplierPostRoute ~ supplierGetRoute 68 | 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lib/jquery.ba-bbq.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010 3 | * http://benalman.com/projects/jquery-bbq-plugin/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this); -------------------------------------------------------------------------------- /src/main/resources/swagger/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swagger UI 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 88 | 89 | 90 | 91 | 101 | 102 |
 
103 |
104 | 105 | 106 | -------------------------------------------------------------------------------- /src/main/resources/swagger/css/style.css: -------------------------------------------------------------------------------- 1 | .swagger-section #header a#logo { 2 | font-size: 1.5em; 3 | font-weight: bold; 4 | text-decoration: none; 5 | background: transparent url(../images/logo.png) no-repeat left center; 6 | padding: 20px 0 20px 40px; 7 | } 8 | #text-head { 9 | font-size: 80px; 10 | font-family: 'Roboto', sans-serif; 11 | color: #ffffff; 12 | float: right; 13 | margin-right: 20%; 14 | } 15 | .navbar-fixed-top .navbar-nav { 16 | height: auto; 17 | } 18 | .navbar-fixed-top .navbar-brand { 19 | height: auto; 20 | } 21 | .navbar-header { 22 | height: auto; 23 | } 24 | .navbar-inverse { 25 | background-color: #000; 26 | border-color: #000; 27 | } 28 | #navbar-brand { 29 | margin-left: 20%; 30 | } 31 | .navtext { 32 | font-size: 10px; 33 | } 34 | .h1, 35 | h1 { 36 | font-size: 60px; 37 | } 38 | .navbar-default .navbar-header .navbar-brand { 39 | color: #a2dfee; 40 | } 41 | /* tag titles */ 42 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a { 43 | color: #393939; 44 | font-family: 'Arvo', serif; 45 | font-size: 1.5em; 46 | } 47 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 a:hover { 48 | color: black; 49 | } 50 | .swagger-section .swagger-ui-wrap ul#resources li.resource div.heading h2 { 51 | color: #525252; 52 | padding-left: 0px; 53 | display: block; 54 | clear: none; 55 | float: left; 56 | font-family: 'Arvo', serif; 57 | font-weight: bold; 58 | } 59 | .navbar-default .navbar-collapse, 60 | .navbar-default .navbar-form { 61 | border-color: #0A0A0A; 62 | } 63 | .container1 { 64 | width: 1500px; 65 | margin: auto; 66 | margin-top: 0; 67 | background-image: url('../images/shield.png'); 68 | background-repeat: no-repeat; 69 | background-position: -40px -20px; 70 | margin-bottom: 210px; 71 | } 72 | .container-inner { 73 | width: 1200px; 74 | margin: auto; 75 | background-color: rgba(223, 227, 228, 0.75); 76 | padding-bottom: 40px; 77 | padding-top: 40px; 78 | border-radius: 15px; 79 | } 80 | .header-content { 81 | padding: 0; 82 | width: 1000px; 83 | } 84 | .title1 { 85 | font-size: 80px; 86 | font-family: 'Vollkorn', serif; 87 | color: #404040; 88 | text-align: center; 89 | padding-top: 40px; 90 | padding-bottom: 100px; 91 | } 92 | #icon { 93 | margin-top: -18px; 94 | } 95 | .subtext { 96 | font-size: 25px; 97 | font-style: italic; 98 | color: #08b; 99 | text-align: right; 100 | padding-right: 250px; 101 | } 102 | .bg-primary { 103 | background-color: #00468b; 104 | } 105 | .navbar-default .nav > li > a, 106 | .navbar-default .nav > li > a:focus { 107 | color: #08b; 108 | } 109 | .navbar-default .nav > li > a, 110 | .navbar-default .nav > li > a:hover { 111 | color: #08b; 112 | } 113 | .navbar-default .nav > li > a, 114 | .navbar-default .nav > li > a:focus:hover { 115 | color: #08b; 116 | } 117 | .text-faded { 118 | font-size: 25px; 119 | font-family: 'Vollkorn', serif; 120 | } 121 | .section-heading { 122 | font-family: 'Vollkorn', serif; 123 | font-size: 45px; 124 | padding-bottom: 10px; 125 | } 126 | hr { 127 | border-color: #00468b; 128 | padding-bottom: 10px; 129 | } 130 | .description { 131 | margin-top: 20px; 132 | padding-bottom: 200px; 133 | } 134 | .description li { 135 | font-family: 'Vollkorn', serif; 136 | font-size: 25px; 137 | color: #525252; 138 | margin-left: 28%; 139 | padding-top: 5px; 140 | } 141 | .gap { 142 | margin-top: 200px; 143 | } 144 | .troubleshootingtext { 145 | color: rgba(255, 255, 255, 0.7); 146 | padding-left: 30%; 147 | } 148 | .troubleshootingtext li { 149 | list-style-type: circle; 150 | font-size: 25px; 151 | padding-bottom: 5px; 152 | } 153 | .overlay { 154 | position: absolute; 155 | top: 0; 156 | left: 0; 157 | width: 100%; 158 | height: 100%; 159 | z-index: 1000; 160 | } 161 | .block.response_body.json:hover { 162 | cursor: pointer; 163 | } 164 | .backdrop { 165 | color: blue; 166 | } 167 | #myModal { 168 | height: 100%; 169 | } 170 | .modal-backdrop { 171 | bottom: 0; 172 | position: fixed; 173 | } 174 | .curl { 175 | padding: 10px; 176 | font-family: "Anonymous Pro", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace; 177 | font-size: 0.9em; 178 | max-height: 400px; 179 | margin-top: 5px; 180 | overflow-y: auto; 181 | background-color: #fcf6db; 182 | border: 1px solid #e5e0c6; 183 | border-radius: 4px; 184 | } 185 | .curl_title { 186 | font-size: 1.1em; 187 | margin: 0; 188 | padding: 15px 0 5px; 189 | font-family: 'Open Sans', 'Helvetica Neue', Arial, sans-serif; 190 | font-weight: 500; 191 | line-height: 1.1; 192 | } 193 | .footer { 194 | display: none; 195 | } 196 | .swagger-section .swagger-ui-wrap h2 { 197 | padding: 0; 198 | } 199 | h2 { 200 | margin: 0; 201 | margin-bottom: 5px; 202 | } 203 | .markdown p { 204 | font-size: 15px; 205 | font-family: 'Arvo', serif; 206 | } 207 | .swagger-section .swagger-ui-wrap .code { 208 | font-size: 15px; 209 | font-family: 'Arvo', serif; 210 | } 211 | .swagger-section .swagger-ui-wrap b { 212 | font-family: 'Arvo', serif; 213 | } 214 | #signin:hover { 215 | cursor: pointer; 216 | } 217 | .dropdown-menu { 218 | padding: 15px; 219 | } 220 | .navbar-right .dropdown-menu { 221 | left: 0; 222 | right: auto; 223 | } 224 | #signinbutton { 225 | width: 100%; 226 | height: 32px; 227 | font-size: 13px; 228 | font-weight: bold; 229 | color: #08b; 230 | } 231 | .navbar-default .nav > li .details { 232 | color: #000000; 233 | text-transform: none; 234 | font-size: 15px; 235 | font-weight: normal; 236 | font-family: 'Open Sans', sans-serif; 237 | font-style: italic; 238 | line-height: 20px; 239 | top: -2px; 240 | } 241 | .navbar-default .nav > li .details:hover { 242 | color: black; 243 | } 244 | #signout { 245 | width: 100%; 246 | height: 32px; 247 | font-size: 13px; 248 | font-weight: bold; 249 | color: #08b; 250 | } 251 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lib/highlight.9.1.0.pack.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.1.0 | BSD3 License | git.io/hljslicense */ 2 | !function(e){"undefined"!=typeof exports?e(exports):(self.hljs=e({}),"function"==typeof define&&define.amd&&define("hljs",[],function(){return self.hljs}))}(function(e){function n(e){return e.replace(/&/gm,"&").replace(//gm,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0==t.index}function a(e){return/^(no-?highlight|plain|text)$/i.test(e)}function i(e){var n,t,r,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=/\blang(?:uage)?-([\w-]+)\b/i.exec(i))return E(t[1])?t[1]:"no-highlight";for(i=i.split(/\s+/),n=0,r=i.length;r>n;n++)if(E(i[n])||a(i[n]))return i[n]}function o(e,n){var t,r={};for(t in e)r[t]=e[t];if(n)for(t in n)r[t]=n[t];return r}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3==i.nodeType?a+=i.nodeValue.length:1==i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!=r[0].offset?e[0].offset"}function u(e){l+=""}function c(e){("start"==e.event?o:u)(e.node)}for(var s=0,l="",f=[];e.length||r.length;){var g=i();if(l+=n(a.substr(s,g[0].offset-s)),s=g[0].offset,g==e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g==e&&g.length&&g[0].offset==s);f.reverse().forEach(o)}else"start"==g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return l+n(a.substr(s))}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var u={},c=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");u[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?c("keyword",a.k):Object.keys(a.k).forEach(function(e){c(e,a.k[e])}),a.k=u}a.lR=t(a.l||/\b\w+\b/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),void 0===a.r&&(a.r=1),a.c||(a.c=[]);var s=[];a.c.forEach(function(e){e.v?e.v.forEach(function(n){s.push(o(e,n))}):s.push("self"==e?a:e)}),a.c=s,a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var l=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=l.length?t(l.join("|"),!0):{exec:function(){return null}}}}r(e)}function l(e,t,a,i){function o(e,n){for(var t=0;t";return i+=e+'">',i+n+o}function p(){if(!L.k)return n(M);var e="",t=0;L.lR.lastIndex=0;for(var r=L.lR.exec(M);r;){e+=n(M.substr(t,r.index-t));var a=g(L,r);a?(B+=a[1],e+=h(a[0],n(r[0]))):e+=n(r[0]),t=L.lR.lastIndex,r=L.lR.exec(M)}return e+n(M.substr(t))}function d(){var e="string"==typeof L.sL;if(e&&!R[L.sL])return n(M);var t=e?l(L.sL,M,!0,y[L.sL]):f(M,L.sL.length?L.sL:void 0);return L.r>0&&(B+=t.r),e&&(y[L.sL]=t.top),h(t.language,t.value,!1,!0)}function b(){return void 0!==L.sL?d():p()}function v(e,t){var r=e.cN?h(e.cN,"",!0):"";e.rB?(k+=r,M=""):e.eB?(k+=n(t)+r,M=""):(k+=r,M=t),L=Object.create(e,{parent:{value:L}})}function m(e,t){if(M+=e,void 0===t)return k+=b(),0;var r=o(t,L);if(r)return k+=b(),v(r,t),r.rB?0:t.length;var a=u(L,t);if(a){var i=L;i.rE||i.eE||(M+=t),k+=b();do L.cN&&(k+=""),B+=L.r,L=L.parent;while(L!=a.parent);return i.eE&&(k+=n(t)),M="",a.starts&&v(a.starts,""),i.rE?0:t.length}if(c(t,L))throw new Error('Illegal lexeme "'+t+'" for mode "'+(L.cN||"")+'"');return M+=t,t.length||1}var N=E(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var w,L=i||N,y={},k="";for(w=L;w!=N;w=w.parent)w.cN&&(k=h(w.cN,"",!0)+k);var M="",B=0;try{for(var C,j,I=0;;){if(L.t.lastIndex=I,C=L.t.exec(t),!C)break;j=m(t.substr(I,C.index-I),C[0]),I=C.index+j}for(m(t.substr(I)),w=L;w.parent;w=w.parent)w.cN&&(k+="");return{r:B,value:k,language:e,top:L}}catch(O){if(-1!=O.message.indexOf("Illegal"))return{r:0,value:n(t)};throw O}}function f(e,t){t=t||x.languages||Object.keys(R);var r={r:0,value:n(e)},a=r;return t.forEach(function(n){if(E(n)){var t=l(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}}),a.language&&(r.second_best=a),r}function g(e){return x.tabReplace&&(e=e.replace(/^((<[^>]+>|\t)+)/gm,function(e,n){return n.replace(/\t/g,x.tabReplace)})),x.useBR&&(e=e.replace(/\n/g,"
")),e}function h(e,n,t){var r=n?w[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function p(e){var n=i(e);if(!a(n)){var t;x.useBR?(t=document.createElementNS("http://www.w3.org/1999/xhtml","div"),t.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):t=e;var r=t.textContent,o=n?l(n,r,!0):f(r),s=u(t);if(s.length){var p=document.createElementNS("http://www.w3.org/1999/xhtml","div");p.innerHTML=o.value,o.value=c(s,u(p),r)}o.value=g(o.value),e.innerHTML=o.value,e.className=h(e.className,n,o.language),e.result={language:o.language,re:o.r},o.second_best&&(e.second_best={language:o.second_best.language,re:o.second_best.r})}}function d(e){x=o(x,e)}function b(){if(!b.called){b.called=!0;var e=document.querySelectorAll("pre code");Array.prototype.forEach.call(e,p)}}function v(){addEventListener("DOMContentLoaded",b,!1),addEventListener("load",b,!1)}function m(n,t){var r=R[n]=t(e);r.aliases&&r.aliases.forEach(function(e){w[e]=n})}function N(){return Object.keys(R)}function E(e){return e=(e||"").toLowerCase(),R[e]||R[w[e]]}var x={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0},R={},w={};return e.highlight=l,e.highlightAuto=f,e.fixMarkup=g,e.highlightBlock=p,e.configure=d,e.initHighlighting=b,e.initHighlightingOnLoad=v,e.registerLanguage=m,e.listLanguages=N,e.getLanguage=E,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|like)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e});hljs.registerLanguage("json",function(e){var t={literal:"true false null"},i=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:i,k:t},s={b:"{",e:"}",c:[{cN:"attr",b:'\\s*"',e:'"\\s*:\\s*',eB:!0,eE:!0,c:[e.BE],i:"\\n",starts:r}],i:"\\S"},n={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return i.splice(i.length,0,s,n),{c:i,k:t,i:"\\S"}});hljs.registerLanguage("xml",function(s){var t="[A-Za-z0-9\\._:-]+",e={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php"},r={eW:!0,i:/]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[r],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[r],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},e,{cN:"meta",b:/<\?\w+/,e:/\?>/,r:10},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},r]}]}});hljs.registerLanguage("javascript",function(e){return{aliases:["js"],k:{keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,{cN:"string",b:"`",e:"`",c:[e.BE,{cN:"subst",b:"\\$\\{",e:"\\}"}]},e.CLCM,e.CBCM,{cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{b:/\s*[);\]]/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:[e.CLCM,e.CBCM]}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+e.IR,r:0},{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",t={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\s*\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",c:[{cN:"keyword",b:/\S+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:c,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,t]}]}}); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lib/swagger-oauth.js: -------------------------------------------------------------------------------- 1 | var appName; 2 | var popupMask; 3 | var popupDialog; 4 | var clientId; 5 | var realm; 6 | var redirect_uri; 7 | var clientSecret; 8 | var scopeSeparator; 9 | var additionalQueryStringParams; 10 | 11 | function handleLogin() { 12 | var scopes = []; 13 | 14 | var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions; 15 | if(auths) { 16 | var key; 17 | var defs = auths; 18 | for(key in defs) { 19 | var auth = defs[key]; 20 | if(auth.type === 'oauth2' && auth.scopes) { 21 | var scope; 22 | if(Array.isArray(auth.scopes)) { 23 | // 1.2 support 24 | var i; 25 | for(i = 0; i < auth.scopes.length; i++) { 26 | scopes.push(auth.scopes[i]); 27 | } 28 | } 29 | else { 30 | // 2.0 support 31 | for(scope in auth.scopes) { 32 | scopes.push({scope: scope, description: auth.scopes[scope], OAuthSchemeKey: key}); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | 39 | if(window.swaggerUi.api 40 | && window.swaggerUi.api.info) { 41 | appName = window.swaggerUi.api.info.title; 42 | } 43 | 44 | $('.api-popup-dialog').remove(); 45 | popupDialog = $( 46 | [ 47 | '
', 48 | '
Select OAuth2.0 Scopes
', 49 | '
', 50 | '

Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.', 51 | 'Learn how to use', 52 | '

', 53 | '

' + appName + ' API requires the following scopes. Select which ones you want to grant to Swagger UI.

', 54 | '
    ', 55 | '
', 56 | '

', 57 | '
', 58 | '
', 59 | '
'].join('')); 60 | $(document.body).append(popupDialog); 61 | 62 | //TODO: only display applicable scopes (will need to pass them into handleLogin) 63 | popup = popupDialog.find('ul.api-popup-scopes').empty(); 64 | for (i = 0; i < scopes.length; i ++) { 65 | scope = scopes[i]; 66 | str = '
  • ' + '
  • '; 74 | popup.append(str); 75 | } 76 | 77 | var $win = $(window), 78 | dw = $win.width(), 79 | dh = $win.height(), 80 | st = $win.scrollTop(), 81 | dlgWd = popupDialog.outerWidth(), 82 | dlgHt = popupDialog.outerHeight(), 83 | top = (dh -dlgHt)/2 + st, 84 | left = (dw - dlgWd)/2; 85 | 86 | popupDialog.css({ 87 | top: (top < 0? 0 : top) + 'px', 88 | left: (left < 0? 0 : left) + 'px' 89 | }); 90 | 91 | popupDialog.find('button.api-popup-cancel').click(function() { 92 | popupMask.hide(); 93 | popupDialog.hide(); 94 | popupDialog.empty(); 95 | popupDialog = []; 96 | }); 97 | 98 | $('button.api-popup-authbtn').unbind(); 99 | popupDialog.find('button.api-popup-authbtn').click(function() { 100 | popupMask.hide(); 101 | popupDialog.hide(); 102 | 103 | var authSchemes = window.swaggerUi.api.authSchemes; 104 | var host = window.location; 105 | var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/")); 106 | var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html'; 107 | var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl; 108 | var url = null; 109 | var scopes = [] 110 | var o = popup.find('input:checked'); 111 | var OAuthSchemeKeys = []; 112 | var state; 113 | for(k =0; k < o.length; k++) { 114 | var scope = $(o[k]).attr('scope'); 115 | if (scopes.indexOf(scope) === -1) 116 | scopes.push(scope); 117 | var OAuthSchemeKey = $(o[k]).attr('oauthtype'); 118 | if (OAuthSchemeKeys.indexOf(OAuthSchemeKey) === -1) 119 | OAuthSchemeKeys.push(OAuthSchemeKey); 120 | } 121 | 122 | //TODO: merge not replace if scheme is different from any existing 123 | //(needs to be aware of schemes to do so correctly) 124 | window.enabledScopes=scopes; 125 | 126 | for (var key in authSchemes) { 127 | if (authSchemes.hasOwnProperty(key) && OAuthSchemeKeys.indexOf(key) != -1) { //only look at keys that match this scope. 128 | var flow = authSchemes[key].flow; 129 | 130 | if(authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) { 131 | var dets = authSchemes[key]; 132 | url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code'); 133 | window.swaggerUi.tokenName = dets.tokenName || 'access_token'; 134 | window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null); 135 | state = key; 136 | } 137 | else if(authSchemes[key].type === 'oauth2' && flow && (flow === 'application')) { 138 | var dets = authSchemes[key]; 139 | window.swaggerUi.tokenName = dets.tokenName || 'access_token'; 140 | clientCredentialsFlow(scopes, dets.tokenUrl, key); 141 | return; 142 | } 143 | else if(authSchemes[key].grantTypes) { 144 | // 1.2 support 145 | var o = authSchemes[key].grantTypes; 146 | for(var t in o) { 147 | if(o.hasOwnProperty(t) && t === 'implicit') { 148 | var dets = o[t]; 149 | var ep = dets.loginEndpoint.url; 150 | url = dets.loginEndpoint.url + '?response_type=token'; 151 | window.swaggerUi.tokenName = dets.tokenName; 152 | } 153 | else if (o.hasOwnProperty(t) && t === 'accessCode') { 154 | var dets = o[t]; 155 | var ep = dets.tokenRequestEndpoint.url; 156 | url = dets.tokenRequestEndpoint.url + '?response_type=code'; 157 | window.swaggerUi.tokenName = dets.tokenName; 158 | } 159 | } 160 | } 161 | } 162 | } 163 | 164 | redirect_uri = redirectUrl; 165 | 166 | url += '&redirect_uri=' + encodeURIComponent(redirectUrl); 167 | url += '&realm=' + encodeURIComponent(realm); 168 | url += '&client_id=' + encodeURIComponent(clientId); 169 | url += '&scope=' + encodeURIComponent(scopes.join(scopeSeparator)); 170 | url += '&state=' + encodeURIComponent(state); 171 | for (var key in additionalQueryStringParams) { 172 | url += '&' + key + '=' + encodeURIComponent(additionalQueryStringParams[key]); 173 | } 174 | 175 | window.open(url); 176 | }); 177 | 178 | popupMask.show(); 179 | popupDialog.show(); 180 | return; 181 | } 182 | 183 | 184 | function handleLogout() { 185 | for(key in window.swaggerUi.api.clientAuthorizations.authz){ 186 | window.swaggerUi.api.clientAuthorizations.remove(key) 187 | } 188 | window.enabledScopes = null; 189 | $('.api-ic.ic-on').addClass('ic-off'); 190 | $('.api-ic.ic-on').removeClass('ic-on'); 191 | 192 | // set the info box 193 | $('.api-ic.ic-warning').addClass('ic-error'); 194 | $('.api-ic.ic-warning').removeClass('ic-warning'); 195 | } 196 | 197 | function initOAuth(opts) { 198 | var o = (opts||{}); 199 | var errors = []; 200 | 201 | appName = (o.appName||errors.push('missing appName')); 202 | popupMask = (o.popupMask||$('#api-common-mask')); 203 | popupDialog = (o.popupDialog||$('.api-popup-dialog')); 204 | clientId = (o.clientId||errors.push('missing client id')); 205 | clientSecret = (o.clientSecret||null); 206 | realm = (o.realm||errors.push('missing realm')); 207 | scopeSeparator = (o.scopeSeparator||' '); 208 | additionalQueryStringParams = (o.additionalQueryStringParams||{}); 209 | 210 | if(errors.length > 0){ 211 | log('auth unable initialize oauth: ' + errors); 212 | return; 213 | } 214 | 215 | $('pre code').each(function(i, e) {hljs.highlightBlock(e)}); 216 | $('.api-ic').unbind(); 217 | $('.api-ic').click(function(s) { 218 | if($(s.target).hasClass('ic-off')) 219 | handleLogin(); 220 | else { 221 | handleLogout(); 222 | } 223 | false; 224 | }); 225 | } 226 | 227 | function clientCredentialsFlow(scopes, tokenUrl, OAuthSchemeKey) { 228 | var params = { 229 | 'client_id': clientId, 230 | 'client_secret': clientSecret, 231 | 'scope': scopes.join(' '), 232 | 'grant_type': 'client_credentials' 233 | } 234 | $.ajax( 235 | { 236 | url : tokenUrl, 237 | type: "POST", 238 | data: params, 239 | success:function(data, textStatus, jqXHR) 240 | { 241 | onOAuthComplete(data,OAuthSchemeKey); 242 | }, 243 | error: function(jqXHR, textStatus, errorThrown) 244 | { 245 | onOAuthComplete(""); 246 | } 247 | }); 248 | 249 | } 250 | 251 | window.processOAuthCode = function processOAuthCode(data) { 252 | var OAuthSchemeKey = data.state; 253 | var params = { 254 | 'client_id': clientId, 255 | 'code': data.code, 256 | 'grant_type': 'authorization_code', 257 | 'redirect_uri': redirect_uri 258 | }; 259 | 260 | if (clientSecret) { 261 | params.client_secret = clientSecret; 262 | } 263 | 264 | $.ajax( 265 | { 266 | url : window.swaggerUi.tokenUrl, 267 | type: "POST", 268 | data: params, 269 | success:function(data, textStatus, jqXHR) 270 | { 271 | onOAuthComplete(data, OAuthSchemeKey); 272 | }, 273 | error: function(jqXHR, textStatus, errorThrown) 274 | { 275 | onOAuthComplete(""); 276 | } 277 | }); 278 | }; 279 | 280 | window.onOAuthComplete = function onOAuthComplete(token,OAuthSchemeKey) { 281 | if(token) { 282 | if(token.error) { 283 | var checkbox = $('input[type=checkbox],.secured') 284 | checkbox.each(function(pos){ 285 | checkbox[pos].checked = false; 286 | }); 287 | alert(token.error); 288 | } 289 | else { 290 | var b = token[window.swaggerUi.tokenName]; 291 | if (!OAuthSchemeKey){ 292 | OAuthSchemeKey = token.state; 293 | } 294 | if(b){ 295 | // if all roles are satisfied 296 | var o = null; 297 | $.each($('.auth .api-ic .api_information_panel'), function(k, v) { 298 | var children = v; 299 | if(children && children.childNodes) { 300 | var requiredScopes = []; 301 | $.each((children.childNodes), function (k1, v1){ 302 | var inner = v1.innerHTML; 303 | if(inner) 304 | requiredScopes.push(inner); 305 | }); 306 | var diff = []; 307 | for(var i=0; i < requiredScopes.length; i++) { 308 | var s = requiredScopes[i]; 309 | if(window.enabledScopes && window.enabledScopes.indexOf(s) == -1) { 310 | diff.push(s); 311 | } 312 | } 313 | if(diff.length > 0){ 314 | o = v.parentNode.parentNode; 315 | $(o.parentNode).find('.api-ic.ic-on').addClass('ic-off'); 316 | $(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on'); 317 | 318 | // sorry, not all scopes are satisfied 319 | $(o).find('.api-ic').addClass('ic-warning'); 320 | $(o).find('.api-ic').removeClass('ic-error'); 321 | } 322 | else { 323 | o = v.parentNode.parentNode; 324 | $(o.parentNode).find('.api-ic.ic-off').addClass('ic-on'); 325 | $(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off'); 326 | 327 | // all scopes are satisfied 328 | $(o).find('.api-ic').addClass('ic-info'); 329 | $(o).find('.api-ic').removeClass('ic-warning'); 330 | $(o).find('.api-ic').removeClass('ic-error'); 331 | } 332 | } 333 | }); 334 | window.swaggerUi.api.clientAuthorizations.add(window.OAuthSchemeKey, new SwaggerClient.ApiKeyAuthorization('Authorization', 'Bearer ' + b, 'header')); 335 | window.swaggerUi.load(); 336 | } 337 | } 338 | } 339 | }; 340 | -------------------------------------------------------------------------------- /src/main/resources/swagger/lib/backbone-min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 1.1.2 2 | 3 | (function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('