Is that possible to create a class instance with webpart GET values - f#

I created a "Game" class and i'm trying to use values from my webpart path to create an instance of it.
My instance need a playerName so i tried to create one with the name value
let g:game.Game = new game.Game()
let php =
request (fun r ->
match r.queryParam "playerName" with
| Choice1Of2 name -> new game.Game(1,name,"hyy")//OK (sprintf "playerName: %s" name)
| Choice2Of2 msg -> BAD_REQUEST msg)
let webPart =
choose [
path "/" >=> (OK "Home")
path "/elm/api/create.php" >=> php
]
startWebServer defaultConfig webPart
but it doesn't work because this expression is supposed to be HttpContext type and not Game type.
I'd like to create an instance and call class's methods depending on my path values.

first: you cant return 2 different types from your function
let php =
request (fun r ->
match r.queryParam "playerName" with
| Choice1Of2 name -> new game.Game(1,name,"hyy")
^^^^^^^^^^^
//should probably be a OK
//OK (sprintf "playerName: %s" name)
| Choice2Of2 msg -> BAD_REQUEST msg)
Then you also should Jsonify your Game object. So probably your code should look somehow like this
| Choice1Of2 name ->
new game.Game(1,name,"hyy")
|> toJson
|> OK
please substitute toJson with a call of your chosen Json library

Related

How do I return conditional status codes using F# syntax in WebAPI

I'm struggling to figure out how to make the compiler happy when accounting for both happy and sad path scenarios of an ActionResult.
I thought I could do the following:
[<HttpGet>]
member x.GetQuery([<FromQuery>] restaurant:string) =
restaurant
|> Name
|> Query.menu
|> function
| Error _ -> (x.StatusCode 500) :> IActionResult
| Ok v -> ActionResult<DataTransfer.MenuItemTypes>(v) :> IActionResult
However, I receive the following error:
Error FS0193 Type constraint mismatch. The type
'ActionResult' is not compatible with type
'IActionResult'
I can get the code to compile if I do this:
[<HttpGet>]
member x.GetQuery([<FromQuery>] restaurant:string) =
restaurant
|> Name
|> Query.menu
|> function
| Error _ -> failwith "Internal Server Error"
| Ok v -> ActionResult<DataTransfer.MenuItemTypes>(v)
However, I don't want to throw an exception just to return a status code.
I always use the methods on the controller base class to create the return value. You should be able to make it work in your first example by changing the Ok case to use the Ok method:
[<HttpGet>]
member x.GetQuery([<FromQuery>] restaurant:string) =
restaurant
|> Name
|> Query.menu
|> function
| Error _ -> x.StatusCode(500) :> IActionResult
| Ok v -> x.Ok(v) :> IActionResult

Suave not showing static file

So I have my server set up very simply. If the path is of the form /article/something, it should serve up the static file something.html within the folder static. For some reason, the Files.file webpart is apparently returning None. I tacked on the OK "File Displayed" webpart to verify that this is the case. The OK never executes.
let app =
choose [
pathScan "/article/%s" (fun article ->
let name = sprintf "%s.html" article
Console.WriteLine name
Files.file name >=> OK "File Displayed")
]
let config =
{ defaultConfig with homeFolder = Some (Path.GetFullPath "./static") }
[<EntryPoint>]
let main args =
startWebServer config app
0
Interestingly enough, the Console.WriteLine name line executes perfectly and I see something.html in the console window when I execute this. It appears the problem is exclusively Files.file name returning None.
The file something.html definitely exists in the static folder, so that's not the problem .
Any ideas on what might be causing this?
Here are some parts to troubleshoot static file serving issues
let troubleShootExtensionPart extensionToCheck :WebPart =
fun ctx ->
match extensionToCheck with
| null | "" -> ServerErrors.INTERNAL_ERROR "Extension Error not supplied, part is not set up correctly"
| x when not <| x.StartsWith "." -> ServerErrors.INTERNAL_ERROR "Extensions start with a '.', part is not set up correctly"
| _ ->
let mtm = ctx.runtime.mimeTypesMap
match mtm extensionToCheck with
| None ->
sprintf "%s is not supported by the mime types map, compose your mime type with the `defaultMimeTypesMap`" extensionToCheck
|> RequestErrors.FORBIDDEN
| Some x ->
sprintf "%s is supported and uses '%s', compression on? : %A" extensionToCheck x.name x.compression
|> OK
|> fun wp -> wp ctx
example consumption with a wildcard so if no routes match you get some diagnostic info
#if DEBUG
pathScan "/checkExtension/%s" (fun name -> troubleShootExtensionPart name)
// catch all
(fun ctx -> sprintf "404, also homeFolder resolves to %s" (Path.GetFullPath ".") |> RequestErrors.NOT_FOUND |> fun wp -> wp ctx)
#endif

Aggregating info from request in Suave

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..
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

Filter and convert `list option` to `list`?

I have the following code which will return a seq of DownloadLink for these Urls that can be parsed.
type DownloadLink = { Url: string; Period: DateTime }
nodes |> Seq.map (fun n ->
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
{ Url = url; Period = period }
| _ ->
printfn "Cannot parse %s" url // Error
)
However, I got the following error at the printfn. What's right way to implement it? Should I make it a list option first and then filter out these None items?
Error 1 Type mismatch. Expecting a
string -> DownloadLink
but given a
string -> unit
The type 'DownloadLink' does not match the type 'unit'
The basic problem is that if you have something like
match x with
|true -> A
|false -> B
the type of A and B must be the same.
There is actually a build in function that combines the map and filter using Some that you had though of - use Seq.choose like so
nodes |> Seq.choose (fun n ->
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
Some ({ Url = url; Period = period })
| _ ->
printfn "Cannot parse %s" url // Error
None
)
Aside from Seq.choose, you can also nicely solve the problem using sequence expressions - where you can use yield to return result in one branch, but do not have to produce a value in another branch:
seq { for n in nodes do
let url = n.Attributes.["href"].Value
match url with
| Helper.ParseRegex "[a-zA-Z](?<period>\d{4})\.txt" [period] ->
yield { Url = url; Period = period }
| _ ->
printfn "Cannot parse %s" url }
Aside, I would not recommend doing a side effect (printing) as part of your processing code. If you want to report errors, it might be better to return an option (or define a type which is either Success or Error of string) so that the error reporting is separated from processing.

System.Linq.Enumerable.OfType<T> - is there a F# way?

I'm looking to use the F# WSDL Type Provider. To call the web service I am using, I need to attach my client credentials to the System.ServiceModel.Description.ClientCredentials.
This is the C# code I have:
var serviceClient = new InvestmentServiceV1Client.InvestmentServiceV1Client();
foreach (ClientCredentials behaviour in serviceClient.Endpoint.Behaviors.OfType<ClientCredentials>())
{
(behaviour).UserName.UserName = USERNAME;
(behaviour).UserName.Password = PASSWORD;
break;
}
This is the F# code I have so far:
let client = new service.ServiceTypes.InvestmentServiceV1Client()
let xxx = client.Endpoint.Behaviors
|> Seq.choose (fun p ->
match box p with
:? System.ServiceModel.Description.ClientCredentials as x -> Some(x)
_ -> None)
|> (System.ServiceModel.Description.ClientCredentials)p.UserName.UserName = USERNAME
Is there an F# equivalent of System.Linq.Enumerable.OfType<T> or should I just use raw OfType<T> ?
I suppose the question is mainly about the break construct, which is not available in F#. Well, the code really just sets the user name and password for the first element of the collection (or none, if the collection is empty). This can be done easily using pattern matching, if you turn the collection to an F# list:
// Get behaviours as in C# and convert them to list using 'List.ofSeq'
let sc = new InvestmentServiceV1Client.InvestmentServiceV1Client()
let behaviours = sc.Endpoint.Behaviors.OfType<ClientCredentials>() |> List.ofSeq
// Now we can use pattern matching to see if there is something in the list
match behaviours with
| behaviour::_ ->
// And if the list is non-empty, set the user name and password
behaviour.UserName.UserName <- USERNAME
behaviour.UserName.Password <- PASSWORD
| _ -> ()
I think you've already implemented the F# equivalent of .OfType(). For emulating the break statement you can do as Tomas does in his answer (matching on list), or you call Seq.head (throws if there are no elements left), or you can do this:
let xxx =
client.Endpoint.Behaviors
|> Seq.choose (function
| :? System.ServiceModel.Description.ClientCredentials as x -> Some x
| _ -> None )
|> Seq.tryPick Some
match xxx with
| Some behavior -> ... // First element of required type found
| None -> ... // No elements of required type at all in sequence

Resources