Skip to content

Commit

Permalink
Move code from http4s repo and change name to rho
Browse files Browse the repository at this point in the history
https://github.com/http4s/http4s/tree/topic/awsomeDSL/5
commit: a13d9453f82517d59c238549e049b04439b56c10
  • Loading branch information
bryce-anderson committed Jun 29, 2014
1 parent e677578 commit 96748fe
Show file tree
Hide file tree
Showing 29 changed files with 3,110 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
target/
/.lib/

*~
/.ensime
/.idea/
/.idea_modules/

.cache
.classpath
.project
.settings/
atlassian-ide-plugin.xml
*.sublime-*
.tags
.tags_sorted_by_file
.ensime_lucene
15 changes: 15 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
This software is licensed under the Apache 2 license, quoted below.

Copyright 2013-2014 http4s [http://www.http4s.org]

Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

[http://www.apache.org/licenses/LICENSE-2.0]

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
47 changes: 47 additions & 0 deletions project/build.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import sbt._
import Keys._

object MyBuild extends Build {
import Dependencies._

lazy val buildSettings = Defaults.defaultSettings ++
Seq(
scalaVersion := "2.11.1",
resolvers += Resolver.sonatypeRepo("snapshots"),
resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
fork in run := true,
libraryDependencies ++= Seq(
http4sCore,
http4sDSL,
http4sBlaze,
specs2 % "test"
) ++ swaggerDeps
)

lazy val myProject = Project(
id = "http4s-rho",
base = file("."),
settings = buildSettings ++ Seq(version := "0.1.0-SNAPSHOT")
)

}


object Dependencies {
lazy val http4sVersion = "0.2.0-SNAPSHOT"

lazy val http4sCore = "org.http4s" %% "http4s-core" % http4sVersion
lazy val http4sDSL = "org.http4s" %% "http4s-dsl" % http4sVersion
lazy val http4sBlaze = "org.http4s" %% "http4s-blaze" % http4sVersion
lazy val http4sJetty = "org.http4s" %% "http4s-servlet" % http4sVersion
lazy val config = "com.typesafe" % "config" % "1.0.0"
lazy val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.0.9"
lazy val scalaloggingSlf4j = "com.typesafe.scala-logging" %% "scala-logging-slf4j" % "2.1.2"
lazy val specs2 = "org.specs2" %% "specs2" % "2.3.11"

lazy val swaggerDeps = Seq(
"org.json4s" %% "json4s-jackson" % "3.2.10",
"org.json4s" %% "json4s-ext" % "3.2.10"
)

}
45 changes: 45 additions & 0 deletions project/build.scala~
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import sbt._
import Keys._

object MyBuild extends Build {
import Dependencies._

lazy val buildSettings = Defaults.defaultSettings ++
Seq(
scalaVersion := "2.11.1",
resolvers += Resolver.sonatypeRepo("snapshots"),
resolvers += "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
libraryDependencies ++= Seq(
http4sCore,
http4sDSL,
http4sBlaze
) ++ swaggerDeps
)

lazy val myProject = Project(
id = "http4s-rho",
base = file("."),
settings = buildSettings ++ Seq(version := "0.1.0-SNAPSHOT")
)

}


object Dependencies {
lazy val http4sVersion = "0.2.0-SNAPSHOT"

lazy val http4sCore = "org.http4s" %% "http4s-core" % http4sVersion
lazy val http4sDSL = "org.http4s" %% "http4s-dsl" % http4sVersion
lazy val http4sBlaze = "org.http4s" %% "http4s-blaze" % http4sVersion
lazy val http4sJetty = "org.http4s" %% "http4s-servlet" % http4sVersion
lazy val config = "com.typesafe" % "config" % "1.0.0"
lazy val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.0.9"
lazy val scalaloggingSlf4j = "com.typesafe.scala-logging" %% "scala-logging-slf4j" % "2.1.2"
lazy val specs2 = "org.specs2" %% "specs2" % "2.3.11"

lazy val swaggerDeps = Seq(
"org.json4s" %% "json4s-jackson" % "3.2.10",
"org.json4s" %% "json4s-ext" % "3.2.10"
)

}
12 changes: 12 additions & 0 deletions src/main/java/org/http4s/rho/swagger/annotations/ApiEnum.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.http4s.rho.swagger.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiEnum {
public String[] values() default {};
}
30 changes: 30 additions & 0 deletions src/main/java/org/http4s/rho/swagger/annotations/ApiModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.http4s.rho.swagger.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* A bean class used in the REST-api.
* Suppose you have an interface
* <code>@PUT @ApiOperation(...) void foo(FooBean fooBean)</code>, there is
* no direct way to see what fields <code>FooBean</code> would have. This
* annotation is meant to give a description of <code>FooBean</code> and
* then have the fields of it be annotated with
* <code>@ApiModelProperty</code>.
*
* @author Heiko W. Rupp
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiModel {
/** Provide a synopsis of this class */
String value() default "";
/** Provide a longer description of the class */
String description() default "";
/** Provide a superclass for the model to allow describing inheritence */
Class<?> parent() default Void.class;
/** for models with a base class, a discriminator can be provided for polymorphic use cases */
String discriminator() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.http4s.rho.swagger.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/** ApiProperty can be put on a Method to allow swagger to understand the json fields datatype and more. */
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiModelProperty {
/** Provide a human readable synopsis of this property */
String description() default "";

/**
* If the values that can be set are restricted, they can be set here. In the form of a comma separated list
* <code>registered, active, closed</code>.
*
* @return the allowable values
*/
String allowableValues() default "";

/**
* specify an optional access value for filtering in a Filter
* implementation. This
* allows you to hide certain parameters if a user doesn't have access to them
*/
String access() default "";

/** long description of the property */
String notes() default "";

/**
* Whether or not the property is required, defaults to true.
*
* @return true if required, false otherwise
*/
boolean required() default true;

/**
* allows explicitly ordering the property in the model. Since reflection has no guarantee on
* ordering, you should specify property order to keep models consistent across different VM implementations and versions.
*/
int position() default 0;
}
10 changes: 10 additions & 0 deletions src/main/scala/org/http4s/rho/CompileService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.http4s.rho

import shapeless.HList

/**
* Created by Bryce Anderson on 5/4/14.
*/
trait CompileService[A] {
def compile[T <: HList, F, O](action: CoolAction[T, F, O]): A
}
69 changes: 69 additions & 0 deletions src/main/scala/org/http4s/rho/CoolService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.http4s
package rho

import scalaz.concurrent.Task
import shapeless.{HNil, HList}
import scalaz.{-\/, \/-, \/}
import org.http4s.rho.bits.HListToFunc
import scala.collection.mutable

/**
* Created by Bryce Anderson on 4/30/14.
*/
trait CoolService extends HttpService with ExecutableCompiler with bits.PathTree {

private val methods: mutable.Map[Method, Node] = mutable.HashMap.empty

implicit protected def compilerSrvc = new CompileService[Unit] {
override def compile[T <: HList, F, O](action: CoolAction[T, F, O]): Unit = append(action)
}

private def missingMethod = sys.error("Somehow an unknown Method type was found!")

private def getMethod(method: Method) = methods.get(method).getOrElse(missingMethod)

protected def append[T <: HList, F, O](action: CoolAction[T, F, O]): Unit = {
val m = action.router.method
val newLeaf = makeLeaf(action)
val newNode = methods.get(m).getOrElse(HeadNode()).append(action.router.path, newLeaf)
methods(m) = newNode
}

private def getResult(req: Request): Option[()=>Task[Response]] = {
val path = req.requestUri.path.split("/").toList
getMethod(req.requestMethod).walk(req, path, HNil) match {
case null => None
case \/-(t) => Some(t)
case -\/(s) => Some(()=>onBadRequest(s))
}
}

override def isDefinedAt(x: Request): Boolean = getResult(x).isDefined

override def apply(v1: Request): Task[Response] = getResult(v1)
.map(_.apply())
.getOrElse{throw new MatchError("Route not defined")}

override def applyOrElse[A1 <: Request, B1 >: Task[Response]](x: A1, default: (A1) => B1): B1 = {
getResult(x) match {
case Some(f) => f()
case None => default(x)
}
}

override def toString(): String = s"CoolService($methods)"
}

case class CoolAction[T <: HList, F, O](private[rho] val router: RouteExecutable[T],
private[rho] val f: F,
private[rho] val hf: HListToFunc[T, O, F]) {
final def method: Method = router.method
final def path: PathRule[_ <: HList] = router.path
final def validators: HeaderRule[_ <: HList] = router.validators
final def responseEncodings: Seq[MediaType] = hf.encodings
final def responseType: Option[Manifest[O]] = hf.manifest
final def decoders: Seq[MediaType] = router match {
case r: CodecRouter[_,_] => r.decoder.consumes
case _ => Nil
}
}
66 changes: 66 additions & 0 deletions src/main/scala/org/http4s/rho/Decoder.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.http4s
package rho

import scalaz.concurrent.Task
import shapeless.HNil
import scalaz.{-\/, \/-, \/}

/**
* Created by Bryce Anderson on 4/27/14.
*/

sealed trait Decoder[T] {

def decode(req: Request): Task[String\/T]
def consumes: Seq[MediaType]
def force: Boolean
def or(other: Decoder[T]): Decoder[T] = Decoder.OrDec(this, other)

private[Decoder] def checkMediaType(req: Request): Boolean = {
if (!consumes.isEmpty) {
req.headers.get(Header.`Content-Type`).map {
h =>
consumes.find {
m => m == h.mediaType
}.isDefined
}.getOrElse(false)
}
else false
}
}

object Decoder {

private type Result[T] = Task[String\/T]


private case class BasicDecoder[T](decoder: Request => Result[T], consumes: Seq[MediaType], force: Boolean) extends Decoder[T] {
override def decode(req: Request): Result[T] = decoder(req)
}

private case class OrDec[T](c1: Decoder[T], c2: Decoder[T]) extends Decoder[T] {

override val consumes: Seq[MediaType] = c1.consumes ++ c2.consumes

override def force: Boolean = c1.force || c2.force

override def decode(req: Request): Result[T] = {
if (c1.checkMediaType(req)) c1.decode(req)
else if (c2.checkMediaType(req)) c2.decode(req)
else if (c1.force) c1.decode(req)
else if (c2.force) c2.decode(req)
else Task.now(-\/(s"No suitable codec found. Supported media types: ${consumes.mkString(", ")}"))
}
}

implicit val strDec: Decoder[String] = {
val dec: Request => Result[String] = _.body.runLog.map(vs => \/-(new String(vs.reduce(_ ++ _).toArray)))
BasicDecoder(dec, MediaType.`text/plain`::Nil, true)
}

implicit def reqDecoder[T](f: Request => Task[T], mediaType: Seq[MediaType] = Nil, force: Boolean = true): Decoder[T] =
BasicDecoder(f.andThen(_.attempt.map(_.leftMap(t => t.getMessage))), mediaType, force)

implicit def bodyDecoder[T](f: HttpBody => Task[T]): Decoder[T] = reqDecoder(r => f(r.body))

}
Loading

0 comments on commit 96748fe

Please sign in to comment.