diff --git a/src/main/scala/com/github/qubo/seed/Boot.scala b/src/main/scala/com/github/qubo/seed/Boot.scala index 060a21f..cc147c8 100644 --- a/src/main/scala/com/github/qubo/seed/Boot.scala +++ b/src/main/scala/com/github/qubo/seed/Boot.scala @@ -4,7 +4,7 @@ import akka.actor.{ActorRef, ActorSystem, Props} import akka.io.IO import akka.pattern.ask import akka.util.Timeout -import com.github.qubo.seed.router.ApiRouterActor +import com.github.qubo.seed.routes.ApiRouterActor import com.github.qubo.seed.utils.Config import Config.app import spray.can.Http diff --git a/src/main/scala/com/github/qubo/seed/SwaggerDefinition.scala b/src/main/scala/com/github/qubo/seed/SwaggerDefinition.scala deleted file mode 100644 index e1fa943..0000000 --- a/src/main/scala/com/github/qubo/seed/SwaggerDefinition.scala +++ /dev/null @@ -1,57 +0,0 @@ -package com.github.qubo.seed - -import java.util - -import io.swagger.jaxrs.Reader -import io.swagger.jaxrs.config.ReaderConfig -import io.swagger.models.{Info, Scheme, Swagger} -import io.swagger.util.Json - -import scala.collection.JavaConversions._ -import scala.reflect.runtime.universe.Type - -abstract class SwaggerDefinition { - val types: Seq[Type] - val basePath: String = "/" - val host: String = "localhost:8080" - val info: Info = new Info() - val description = "" - val scheme: Scheme = Scheme.HTTP - - protected val swaggerConfig: Swagger = new Swagger() - .scheme(scheme) - .basePath(basePath) - .host(host) - .info(info) - - protected val reader = new Reader(swaggerConfig, new ReaderConfig() { - override def getIgnoredRoutes: util.Collection[String] = List() - override def isScanAllResources: Boolean = true - }) - - protected lazy val swagger = reader.read(types.map(t ⇒ { - Class.forName(getClassNameForType(t)) - }).toSet) - - def prettyJson: String = - Json.pretty(swagger) - - def compactJson: String = - Json.mapper().writeValueAsString(swagger) - - private def getClassNameForType(t: Type): String = { - val typeSymbol = t.typeSymbol - val fullName = typeSymbol.fullName - if (typeSymbol.isModuleClass) { - val idx = fullName.lastIndexOf('.') - if (idx >= 0) { - val mangledName = s"${fullName.slice(0, idx)}$$${fullName.slice(idx + 1, fullName.length)}$$" - mangledName - } else { - fullName - } - } else { - fullName - } - } -} diff --git a/src/main/scala/com/github/qubo/seed/persistence/User.scala b/src/main/scala/com/github/qubo/seed/persistences/User.scala similarity index 96% rename from src/main/scala/com/github/qubo/seed/persistence/User.scala rename to src/main/scala/com/github/qubo/seed/persistences/User.scala index a2611a6..a3abe53 100644 --- a/src/main/scala/com/github/qubo/seed/persistence/User.scala +++ b/src/main/scala/com/github/qubo/seed/persistences/User.scala @@ -1,4 +1,4 @@ -package com.github.qubo.seed.persistence +package com.github.qubo.seed.persistences import io.swagger.annotations.ApiModel import slick.dbio.Effect.Read diff --git a/src/main/scala/com/github/qubo/seed/router/ApiBase.scala b/src/main/scala/com/github/qubo/seed/routes/ApiBase.scala similarity index 91% rename from src/main/scala/com/github/qubo/seed/router/ApiBase.scala rename to src/main/scala/com/github/qubo/seed/routes/ApiBase.scala index 17b10b7..38a0333 100644 --- a/src/main/scala/com/github/qubo/seed/router/ApiBase.scala +++ b/src/main/scala/com/github/qubo/seed/routes/ApiBase.scala @@ -1,4 +1,4 @@ -package com.github.qubo.seed.router +package com.github.qubo.seed.routes import org.slf4j.LoggerFactory import spray.httpx.SprayJsonSupport diff --git a/src/main/scala/com/github/qubo/seed/router/ApiRouterActor.scala b/src/main/scala/com/github/qubo/seed/routes/ApiRouterActor.scala similarity index 90% rename from src/main/scala/com/github/qubo/seed/router/ApiRouterActor.scala rename to src/main/scala/com/github/qubo/seed/routes/ApiRouterActor.scala index e889918..2176531 100644 --- a/src/main/scala/com/github/qubo/seed/router/ApiRouterActor.scala +++ b/src/main/scala/com/github/qubo/seed/routes/ApiRouterActor.scala @@ -1,11 +1,11 @@ -package com.github.qubo.seed.router +package com.github.qubo.seed.routes import java.util.NoSuchElementException import akka.actor.{Actor, ActorContext, ActorLogging} import com.github.qubo.seed.swagger.{SwaggerDefinition, SwaggerDefinitionConfig} import spray.http.MediaTypes._ -import spray.http.StatusCodes +import spray.http.{AllOrigins, StatusCodes} import spray.httpx.SprayJsonSupport import spray.json.DefaultJsonProtocol import spray.routing.{ExceptionHandler, HttpService, Route} @@ -19,6 +19,7 @@ class ApiRouterActor with DefaultJsonProtocol with SprayJsonSupport with UserApi + with CorsHelper with HttpService { def actorRefFactory: ActorContext = context implicit val ec: ExecutionContext = actorRefFactory.dispatcher @@ -28,7 +29,7 @@ class ApiRouterActor ) def receive: Receive = runRoute( - respondWithMediaType(`application/json`) { + (cors(AllOrigins) & respondWithMediaType(`application/json`)) { apiRoute ~ swaggerRoute } ~ swaggerUiRoute ) diff --git a/src/main/scala/com/github/qubo/seed/routes/CorsHelper.scala b/src/main/scala/com/github/qubo/seed/routes/CorsHelper.scala new file mode 100644 index 0000000..a8f654b --- /dev/null +++ b/src/main/scala/com/github/qubo/seed/routes/CorsHelper.scala @@ -0,0 +1,55 @@ +package com.github.qubo.seed.routes + + +import spray.http.HttpHeaders._ +import spray.http.HttpMethods._ +import spray.http._ +import spray.routing._ +import spray.util._ + +trait CorsHelper { + this: HttpService => + val MaxAge = 1728000 + + def p3p(value: String = """CP="CAO PSA OUR""""): Directive0 = + mapHttpResponseHeaders(_ ++ List(RawHeader("P3P", value))) + + def cors(origins: Seq[String]): Directive0 = + cors(SomeOrigins(origins.map(HttpOrigin(_)))) + + def cors(origin: String): Directive0 = + if (origin == "*") { + cors(AllOrigins) + } else { + cors(SomeOrigins(Seq(HttpOrigin(origin)))) + } + + def cors(origins: AllowedOrigins): Directive0 = mapRequestContext { ctx => + val originHeader = if (origins == AllOrigins) { + ctx.request.headers.findByType[`Origin`] + } else { + None + } + val resOrigins = originHeader.map { case Origin(origin) => SomeOrigins(origin) }.getOrElse(origins) + ctx.withRouteResponseHandling({ + //It is an option request for a resource that responds to some other method + case Rejected(x) if ctx.request.method.equals(HttpMethods.OPTIONS) => + ctx.complete(HttpResponse().withHeaders( + allowOriginHeader(resOrigins) ++ fixedCorsHeaders + )) + }).withHttpResponseHeadersMapped { headers => + allowOriginHeader(resOrigins) ++ fixedCorsHeaders ++ headers + } + } & p3p() + + private def fixedCorsHeaders: List[HttpHeader] = + List( + `Access-Control-Allow-Methods`(GET, POST, PATCH, PUT, DELETE, OPTIONS), + `Access-Control-Allow-Headers`("Content-Type,Accept-Language,Authorization"), + `Access-Control-Allow-Credentials`(true), + `Access-Control-Max-Age`(MaxAge) + ) + + private def allowOriginHeader(origins: AllowedOrigins): List[HttpHeader] = + List(`Access-Control-Allow-Origin`(origins)) +} diff --git a/src/main/scala/com/github/qubo/seed/router/ProductApi.scala b/src/main/scala/com/github/qubo/seed/routes/ProductApi.scala similarity index 94% rename from src/main/scala/com/github/qubo/seed/router/ProductApi.scala rename to src/main/scala/com/github/qubo/seed/routes/ProductApi.scala index b0f6894..3b09943 100644 --- a/src/main/scala/com/github/qubo/seed/router/ProductApi.scala +++ b/src/main/scala/com/github/qubo/seed/routes/ProductApi.scala @@ -1,4 +1,4 @@ -package com.github.qubo.seed.router +package com.github.qubo.seed.routes import javax.ws.rs.Path diff --git a/src/main/scala/com/github/qubo/seed/router/UserApi.scala b/src/main/scala/com/github/qubo/seed/routes/UserApi.scala similarity index 92% rename from src/main/scala/com/github/qubo/seed/router/UserApi.scala rename to src/main/scala/com/github/qubo/seed/routes/UserApi.scala index 46c045a..23f3ac9 100644 --- a/src/main/scala/com/github/qubo/seed/router/UserApi.scala +++ b/src/main/scala/com/github/qubo/seed/routes/UserApi.scala @@ -1,8 +1,8 @@ -package com.github.qubo.seed.router +package com.github.qubo.seed.routes import javax.ws.rs.{GET, Path, PathParam} -import com.github.qubo.seed.persistence.User +import com.github.qubo.seed.persistences.User import com.github.qubo.seed.utils.Config import io.swagger.annotations._ import spray.routing.{Route, StandardRoute} diff --git a/src/test/scala/com/github/qubo/seed/persistence/SpecBase.scala b/src/test/scala/com/github/qubo/seed/persistences/SpecBase.scala similarity index 87% rename from src/test/scala/com/github/qubo/seed/persistence/SpecBase.scala rename to src/test/scala/com/github/qubo/seed/persistences/SpecBase.scala index 4a1d07b..553b89e 100644 --- a/src/test/scala/com/github/qubo/seed/persistence/SpecBase.scala +++ b/src/test/scala/com/github/qubo/seed/persistences/SpecBase.scala @@ -1,4 +1,4 @@ -package com.github.qubo.seed.persistence +package com.github.qubo.seed.persistences import org.specs2.mutable.Specification import org.specs2.specification.process.RandomSequentialExecution diff --git a/src/test/scala/com/github/qubo/seed/persistence/UserSpec.scala b/src/test/scala/com/github/qubo/seed/persistences/UserSpec.scala similarity index 75% rename from src/test/scala/com/github/qubo/seed/persistence/UserSpec.scala rename to src/test/scala/com/github/qubo/seed/persistences/UserSpec.scala index 709e5d9..9059cc5 100644 --- a/src/test/scala/com/github/qubo/seed/persistence/UserSpec.scala +++ b/src/test/scala/com/github/qubo/seed/persistences/UserSpec.scala @@ -1,4 +1,4 @@ -package com.github.qubo.seed.persistence +package com.github.qubo.seed.persistences import scalaz._, Scalaz._