Skip to content
This repository was archived by the owner on Jul 25, 2022. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package etude.epice.sel.auth

/**
*
*/
trait AuthResult {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package etude.epice.sel.auth.oauth

case class OAuthApp(apiKey: String, apiSecret: String, scope: OAuthScope)
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package etude.epice.sel.auth.oauth

import etude.epice.sel.auth.AuthResult

trait OAuthToken {
val token: String
val secret: String
Expand All @@ -8,7 +10,7 @@ trait OAuthToken {

case class OAuthAccessToken(token: String,
secret: String,
raw: String) extends OAuthToken
raw: String) extends OAuthToken with AuthResult

case class OAuthRequestToken(token: String,
secret: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package etude.epice.sel.client

import etude.epice.sel.request.Request
import etude.epice.sel.response.Response

trait AuthSession[RQ <: Request[RS], RS <: Response] extends Session[RQ, RS] {
}
17 changes: 10 additions & 7 deletions etude-epice-sel/src/main/scala/etude/epice/sel/client/Client.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package etude.epice.sel.client

import etude.epice.sel.recipe.Recipe
import etude.epice.sel.request.{Verb, Request}
import etude.epice.sel.signature.Signer
import etude.epice.sel.request.Request
import etude.epice.sel.response.Response

trait Client {
val context: Context

def currentSession(): Session = ???
}

object Client {
def of(recipe: Recipe): Client = ???
}
def apply(): Client = ???

def ofContext(context: Context): Client = ???

def ifAuthorized(request: Request): Response = ???

def execute(request: Request): Response = ???
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package etude.epice.sel.client

import etude.epice.sel.request.Request
import etude.epice.sel.response.Response
import etude.epice.sel.signature.Signer

class OAuthSession extends Session {
class OAuthSession[RQ <: Request[RS], RS <: Response] extends AuthSession[RQ, RS] {
val context: Context = ???

def signer(): Signer = ???
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package etude.epice.sel.client

class PlainSession extends Session {
val context: Context = ???
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package etude.epice.sel.client

import etude.epice.sel.policy.Policy
import etude.epice.sel.request.{Request, Verb}
import etude.epice.sel.response.Response

trait Session {
trait Session[RQ <: Request[RS], RS <: Response] {
val context: Context

def prepareRequest(verb: Verb): Request = ???
def prepareRequest(verb: Verb): RQ = ???

def withPolicy[S <: Session[RQ, RS]](policy: Policy): S = ???
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package etude.epice.sel.decomposer

trait DecomposedResponse {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package etude.epice.sel.decomposer

import etude.epice.sel.response.Response

import scala.util.Try

trait Decomposer[RS <: Response, NRS <: DecomposedResponse] {
def extract(response: RS): Try[NRS]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package etude.epice.sel.policy

case class Concurrency(concurrency: Int) extends Policy {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package etude.epice.sel.policy

trait Timeout {

}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package etude.epice.sel.recipe

import etude.epice.sel.client.{Session, Client}
import etude.epice.sel.policy.Policy

trait Recipe {
def withPolicy(policy: Policy): Recipe = ???

def ofSession[S <: Session[_, _]](client: Client): S = ???
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,4 @@ trait Request {
def withHeader(header: Header): Request = withHeaders(Seq(header))

def withHeaders(headers: Seq[Header]): Request = ???

def execute(): Try[Response]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package etude.epice.sel.response

trait Response {
def expectXML(): ResponseXML

def expectJSON(): ResponseJSON
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package etude.epice.sel.workflow

import java.net.URI

import etude.epice.sel.auth.oauth.OAuthAccessToken

object Workflow {

trait Stage {
val name: String
}

case class StagePlain() extends Stage {
val name: String = "Plain"
}

case class StageAuthorized() extends Stage {
val name: String = "Authorized"
}

case class StageSpecial() extends Stage {
val name: String = "Special"
}

case class Transition[FS <: Stage, TS <: Stage](from: FS, to: TS)

def main (args: Array[String]) {
val plainToAuthorized: PartialFunction[(Stage, Stage), Stage] = {
case (f: StagePlain, t: StageAuthorized) =>
println(s"${f.name} -> ${t.name}")
t
}
val authorizedToSpecial: PartialFunction[(Stage, Stage), Stage] = {
case (f: StageAuthorized, t: StageSpecial) =>
println(s"${f.name} -> ${t.name}")
t
}

val workflow = List[PartialFunction[(Stage, Stage), Stage]](
plainToAuthorized,
authorizedToSpecial
)

val stagePlain = StagePlain()
val stageAuthorized = StageAuthorized()
val stageSpecial = StageSpecial()

val prepare: PartialFunction[Transition, String] = {
case t: Transition[StagePlain, StageAuthorized] =>
s"${t.from.name} -> ${t.to.name}"
}

}
}
101 changes: 49 additions & 52 deletions etude-epice-sel/src/test/scala/etude/epice/sel/Design.scala
Original file line number Diff line number Diff line change
@@ -1,73 +1,70 @@
package etude.epice.sel

import java.awt.Desktop
import java.io.File
import java.net.URI
import java.util.Scanner

import etude.epice.sel.auth.oauth.OAuthScope
import etude.epice.sel.client.{OAuthSession, CommonSession, Session, Client}
import etude.epice.sel.policy.execution.Concurrency
import etude.epice.sel.auth.oauth.{OAuthApp, OAuthScope, OAuthVerifier}
import etude.epice.sel.client.{Client, OAuthSession}
import etude.epice.sel.recipe.Recipe
import etude.epice.sel.request.{Multipart, Verb}
import etude.epice.sel.response.ResponseXML
import etude.epice.sel.request.{Request, Multipart, Verb}
import etude.epice.sel.response.{Response, ResponseXML}

object Design {

trait OAuthRecipe extends Recipe {
val workflow: (URI) => OAuthVerifier

val appInfo: OAuthApp
}

/**
* service recipe for flickr.
* - authentication mechanism
* - REST styles like header, query string, etc.
* - recipe should know qos/limitation/recovery method for the service.
*/
case class FlickrRecipe(apiKey: String, apiSecret: String, scope: OAuthScope) extends Recipe {
case class FlickrRecipe(appInfo: OAuthApp,
workflow: (URI) => OAuthVerifier) extends OAuthRecipe {

}

def useCase1(): Unit = {
val recipe: Recipe = FlickrRecipe("<api-key>", "<api-secret>", OAuthScope("write")).withPolicy(Concurrency(2)) // customize policy
val client: Client = Client.of(recipe)
client.currentSession() match {
case session: CommonSession =>
// restore token from storage

// acquire requestToken

// user approval

// verify + acquire access Token


case session: OAuthSession =>
// biz logic

session.prepareRequest(Verb.GET)
.withQueryString("method", "flickr.test.echo")
.execute() map {
case r: ResponseXML =>
// do something
case _ =>
throw new UnsupportedOperationException()
}

val imagePayload = new Multipart()

imagePayload.withText("title", "Barbecue")
imagePayload.withText("description", "Summer vacation")
imagePayload.withText("is_public", "0")
imagePayload.withText("is_family", "1")
imagePayload.withText("is_friend", "1")

session.signer().signature(imagePayload)

// "photo" should not include for signature
imagePayload.withBinary("photo", new File("barbecue.jpg"))

session.prepareRequest(Verb.POST)
.withPayload(imagePayload)
.execute() map {
case r: ResponseXML =>
// do something
case _ =>
throw new UnsupportedOperationException()
}
val client: Client = Client()
val app: OAuthApp = OAuthApp("<api-key>", "<api-secret>", OAuthScope("write"))
val recipe: Recipe = FlickrRecipe(app, {
authUri =>
Desktop.getDesktop.browse(authUri)
OAuthVerifier(new Scanner(System.in).nextLine())
})

val session = recipe.ofSession[OAuthSession[Request[Response], Response]](client)

session.prepareRequest(Verb.GET)
.withQueryString("method", "flickr.test.echo")
.execute()

val imagePayload = new Multipart()

imagePayload.withText("title", "Barbecue")
imagePayload.withText("description", "Summer vacation")
imagePayload.withText("is_public", "0")
imagePayload.withText("is_family", "1")
imagePayload.withText("is_friend", "1")

session.signer().signature(imagePayload)

// "photo" should not include for signature
imagePayload.withBinary("photo", new File("barbecue.jpg"))

session.prepareRequest(Verb.POST)
.withPayload(imagePayload)
.execute() map {
case r: ResponseXML =>
// do something
case _ =>
throw new UnsupportedOperationException()
}
}
}