Aggregating info from request in Suave

Viewed 227

I am building an authenticated web API using Suave, and I often stumble on the problem of aggregating infos throughout different functions

pathScan Navigation.playersAvailables GetGame >>= getInSession >>= (fun (gameId,playerInSession) -> //access to both gameId and player in session)

signatures :

getGame : HttpContext -> Async<HttpContext option>
getInSession : HttpContext -> Async<HttpContext option> 
  • getGame take id from httpContext.request.querystring getInSession
  • take sessionId from httpContext.cookie

The only thing I found in order to do that was to store infos in the userDataDictionnary :

Writers.setUserData "player"  { playerId= playersId; socialId=socialId; username = username}

And to retrieve it in the other function but it looks pretty nasty to me :

let player = x.userState.["player"] :?> PlayerSession
//do some other stuff now that we have the current player

Is there another way of doing that? I would like to have pure functions like getGameId and get Session, etc.. and to be able to compose them as I wish to process my different routes :

pathScan Navigation.playersAvailables GetGame >>= getInSession >>= (fun (gameId,playerInSession) -> //access to both gameId and player in session)
pathScan Navigation.otherRoute GetGame >>= (fun (gameId) -> //process gameId)
pathScan Navigation.otherRoute2 getInSession >>= (fun (sessionId) -> //process sessionId to do some other stuff)

I am afraid that what I need is a day talk with some real functionnal programmer..

1 Answers

setUserData is a pure function - src.

Not sure if this is still current but it says pathScan and >>= cannot be nicely chained. However I think the Writers.setUserData you are using may be able to accomplish it.

Accessing an object bag to pull things out isn't lovely.

how about:

let (|ParseInt|_|) =
    function
    | "" | null -> None
    | x ->
        match Int32.TryParse x with
        | true, i -> Some i
        | _ -> None


let (|HasParam|_|) name (ctx:HttpContext) =
    ctx.request.queryParam name
    |> function
        |Choice1Of2 value ->
            Some value
        | _ -> None

let playersAvailablePart:WebPart =
    function
    //access to both gameId and player in session
    |HasParam "inSession" playerInSession & HasParam "gameId" gameId as ctx ->
        // do your processing here, sample return:
        OK "we had all the required important parts" ctx
    // or an example of something more strongly typed
    | HasParam "inSession" (ParseInt playerInSession) & HasParam "gameId" (ParseInt gameId) as ctx ->
        // do your processing here, sample return:
        OK "we had all the required important parts" ctx
    | ctx -> never ctx

This doesn't exactly work if the values aren't in the queryParameters, but you can adapt it to where they are

Related