From c7c74daa3189cf9dca95334d8871a454307ea13c Mon Sep 17 00:00:00 2001 From: Everett Sochowski Date: Wed, 3 Jul 2013 15:41:42 +0200 Subject: [PATCH] Some progress capturing api path, input case class and output case class for the purposes of doc generation --- pom.xml | 5 ++ src/main/scala/code/api/OBPRestHelper.scala | 95 ++++++++++++++++++++ src/main/scala/code/api/v1_2/OBPAPI1.2.scala | 19 ++++ 3 files changed, 119 insertions(+) diff --git a/pom.xml b/pom.xml index f4a4b3ae0..69c2f8f9d 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,11 @@ selenium-java 2.29.0 + + org.mockito + mockito-all + 1.9.5 + diff --git a/src/main/scala/code/api/OBPRestHelper.scala b/src/main/scala/code/api/OBPRestHelper.scala index c6b7ae2f0..a01ccacbc 100644 --- a/src/main/scala/code/api/OBPRestHelper.scala +++ b/src/main/scala/code/api/OBPRestHelper.scala @@ -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]] { diff --git a/src/main/scala/code/api/v1_2/OBPAPI1.2.scala b/src/main/scala/code/api/v1_2/OBPAPI1.2.scala index 3c7db061d..7347ef26c 100644 --- a/src/main/scala/code/api/v1_2/OBPAPI1.2.scala +++ b/src/main/scala/code/api/v1_2/OBPAPI1.2.scala @@ -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 => {