Some progress capturing api path, input case class and output case class for the purposes of doc generation

This commit is contained in:
Everett Sochowski 2013-07-03 15:41:42 +02:00
parent 38842ac95b
commit c7c74daa31
3 changed files with 119 additions and 0 deletions

View File

@ -144,6 +144,11 @@
<artifactId>selenium-java</artifactId>
<version>2.29.0</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
</dependency>
</dependencies>
<build>

View File

@ -8,6 +8,21 @@ import net.liftweb.http.JsonResponse
import code.util.APIUtil._
import code.model.User
import code.api.OAuthHandshake._
import net.liftweb.json.JsonAST.JValue
import net.liftweb.http.RequestType
import net.liftweb.http.ParsePath
import net.liftweb.http.LiftRules
import net.liftweb.http.ParamCalcInfo
import net.liftweb.http.provider.HTTPRequest
import net.liftweb.http.provider.servlet.HTTPRequestServlet
import net.liftweb.http.LiftServlet
import javax.servlet.http.HttpServletRequest
import org.apache.commons.lang.NotImplementedException
import net.liftweb.json.Extraction
class ConvertableToJson[T](conv : T) {
def toJValue = Extraction.decompose(conv)
}
class OBPRestHelper extends RestHelper with Loggable {
@ -49,11 +64,91 @@ class OBPRestHelper extends RestHelper with Loggable {
def apply(req: Req): Box[User] => Box[JsonResponse] =
pf.apply(req.withNewPath(req.path.drop(listLen)))
}
def oPrefix2[IN, OUT](pf: PartialFunction[Req, (Box[User], Box[IN]) => Box[OUT]]): PartialFunction[Req, (Box[User], Box[IN]) => Box[OUT]] =
new PartialFunction[Req, (Box[User], Box[IN]) => Box[OUT]] {
def isDefinedAt(req: Req): Boolean =
req.path.partPath.startsWith(list) && {
pf.isDefinedAt(req.withNewPath(req.path.drop(listLen)))
}
def apply(req: Req): (Box[User], Box[IN]) => Box[OUT] =
pf.apply(req.withNewPath(req.path.drop(listLen)))
}
}
trait PathElement {
def name : String
}
case class StaticElement(name : String) extends PathElement
case class VariableElement(name : String) extends PathElement
type ApiPath = List[PathElement]
def caseClassBoxToJsonResponse[T](output : Box[T]) : Box[JsonResponse] = {
output.map(x => successJsonResponse(Extraction.decompose(x)))
}
//TODO: input and output should be optional
def registerApiCall[INPUT, OUTPUT](path : ApiPath, reqType : RequestType, handler : PartialFunction[Req, (Box[User], Box[INPUT]) => Box[OUTPUT]])
(implicit m: ClassManifest[OUTPUT], m2 : Manifest[INPUT]) = {
val testPath : List[String] = path.map{
case StaticElement(name) => name
case VariableElement(_) => "test"
}
val reqPath = ParsePath(testPath, "", true, false)
import net.liftweb.http.provider.HTTPProvider
//Some bits of httpRequest have to be mocked to avoid exceptions in handler.isDefinedAt(testRequest)
import org.mockito.Mockito._
val httpRequest : HTTPRequest = mock(classOf[HTTPRequest])
when(httpRequest.contentType).thenReturn(Full("application/json"))
when(httpRequest.headers).thenReturn(Nil)
val testRequest : Req = new Req(reqPath, LiftRules.context.path, reqType,
Full("application/json"), httpRequest, 5, 6, () => ParamCalcInfo(Nil, Map(), Nil, Empty), Map())
import net.liftweb.util.Helpers.tryo
val aaa = m.erasure.getCanonicalName()
println("AAA: " + aaa)
val bbb = m2.erasure.getCanonicalName()
println("BBB: " + bbb)
//TODO assuming it's a case class, use 2.10 reflection to generate some sample json
//Convert the handler into the more general Box[User] => Box[JsonResponse] that oauthServe expects
val oauthHandler = new PartialFunction[Req, Box[User] => Box[JsonResponse]] {
def isDefinedAt(req: Req) : Boolean = handler.isDefinedAt(req)
def apply(req: Req) : Box[User] => Box[JsonResponse] = {
val foo = handler.apply(req)
val json = req.json
import net.liftweb.json._
val in = tryo{json.map(_.extract[INPUT](DefaultFormats, m2))}.getOrElse(Empty)
(user: Box[User]) => caseClassBoxToJsonResponse(handler.apply(req).apply(user, in))
}
}
if(handler.isDefinedAt(testRequest)) {
//TODO how can we verify the expected input? test it and see if the result is a "bad format" error? -> that would require someone explicitely write this
//particular error type in every (ne
oauthServe(oauthHandler)
//TODO add to docs
logger.info("added api call!!!")
}
else {
logger.error("Api call did not fulfill documented behaviour!!!") //TODO: describe which api call
}
}
//Give all lists of strings in OBPRestHelpers the oPrefix method
implicit def stringListToRichStringList(list : List[String]) : RichStringList = new RichStringList(list)
//TODO: Fold this into registerApiCall once that method is properly implemented
def oauthServe(handler : PartialFunction[Req, Box[User] => Box[JsonResponse]]) : Unit = {
val obpHandler : PartialFunction[Req, () => Box[LiftResponse]] = {
new PartialFunction[Req, () => Box[LiftResponse]] {

View File

@ -57,6 +57,8 @@ import code.model._
import java.net.URL
import code.util.APIUtil._
import code.api.OBPRestHelper
import code.api.ConvertableToJson
import net.liftweb.http.GetRequest
object OBPAPI1_2 extends OBPRestHelper with Loggable {
@ -117,7 +119,24 @@ object OBPAPI1_2 extends OBPRestHelper with Loggable {
Full(successJsonResponse(banksToJson(Bank.all)))
}
})
val testApiPath : ApiPath = List(StaticElement("obp"), StaticElement("v1.2"), StaticElement("banks"), VariableElement("BANK_ID"))
val reqType = GetRequest
//get bank by id
val handler = ("obp" / "v1.2").oPrefix2[BankJSON, BankJSON] {
case "banks" :: bankId :: Nil JsonGet json => {
(user, input) =>
def bankToJson(bank : Bank) = {
JSONFactory.createBankJSON(bank)
}
for(bank <- Bank(bankId))
yield bankToJson(bank)
}
}
registerApiCall[BankJSON, BankJSON](testApiPath, reqType, handler)
oauthServe(apiPrefix{
//get bank by id
case "banks" :: bankId :: Nil JsonGet json => {