Last active
October 31, 2022 15:42
-
-
Save bslatner/4dbc9d7f892560cc6e6ad54a44ad9113 to your computer and use it in GitHub Desktop.
Code for making rest API requests in F# using the RestSharp library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// StartResult would be the type of the response. Must be marked with [<CLIMutable>] | |
let response = | |
restWithResponse<StartResult> ( | |
POST >> toResource "stopwatch/{type}/{key}/start" >> atUrl config.Url | |
>> withUrlSegment "type" stopwatchType | |
>> withUrlSegment "key" key | |
>> withFormValue "owner" owner | |
>> withExpectedStatusOk | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module RestClient | |
open System.Net | |
open RestSharp | |
type RestApiMethod = GET | POST | PUT | DELETE | |
type RestHttpStatusCodeExpectation = | |
| Exact of HttpStatusCode | |
| Several of HttpStatusCode list | |
| Range of HttpStatusCode * HttpStatusCode | |
type RestApiBody = | |
| JsonString of string | |
| JsonObject of obj | |
| Xml of string | |
| XmlWithNamespace of string * string | |
type RestApiRequest = | |
{ | |
Url : string option | |
Resource : string option | |
Method : RestApiMethod option | |
Body : RestApiBody option | |
QueryStrings : (string * string) list option | |
FormValues : (string * string) list option | |
UrlSegments : (string * string) list option | |
Headers : (string * string) list option | |
Cookies : (string * string) list option | |
ExpectedStatusCode : RestHttpStatusCodeExpectation option | |
} | |
let newRestRequest = | |
{ | |
Url = None | |
Resource = None | |
Method = None | |
Body = None | |
QueryStrings = None | |
FormValues = None | |
UrlSegments = None | |
Headers = None | |
Cookies = None | |
ExpectedStatusCode = None | |
} | |
let addToKvpList (kvpList : (string * string) list option) k v = | |
match kvpList with | |
| Some l -> Some ((k,v)::l) | |
| None -> Some [(k,v)] | |
let GET request = | |
{ request with Method = Some RestApiMethod.GET } | |
let POST request = | |
{ request with Method = Some RestApiMethod.POST } | |
let PUT request = | |
{ request with Method = Some RestApiMethod.PUT } | |
let DELETE request = | |
{ request with Method = Some RestApiMethod.DELETE } | |
let setUrl url request = { request with Url = Some url } | |
let fromUrl = setUrl | |
let atUrl = setUrl | |
let setResource resource request = { request with Resource = Some resource } | |
let theResource = setResource | |
let toResource = setResource | |
let withJsonStringBody body request = | |
{ request with Body = Some (JsonString(body)) } | |
let withJsonObjectBody body request = | |
{ request with Body = Some (JsonObject(body)) } | |
let withXmlBody body request = | |
{ request with Body = Some (Xml(body)) } | |
let withXmlBodyWithNamespace body ns request = | |
{ request with Body = Some (XmlWithNamespace(body,ns)) } | |
let withQueryString key value request = | |
{ request with QueryStrings = addToKvpList request.QueryStrings key value } | |
let withFormValue key value request = | |
{ request with FormValues = addToKvpList request.FormValues key value } | |
let withUrlSegment key value request = | |
{ request with UrlSegments = addToKvpList request.UrlSegments key value } | |
let withHeader key value request = | |
{ request with Headers = addToKvpList request.Headers key value } | |
let withCookie key value request = | |
{ request with Cookies = addToKvpList request.Cookies key value } | |
let withExpectedStatus (code : HttpStatusCode) request = | |
{ request with ExpectedStatusCode = Some (Exact(code)) } | |
let withExpectedStatuses (codes : seq<HttpStatusCode>) request = | |
{ request with ExpectedStatusCode = Some (Several(codes |> List.ofSeq)) } | |
let withExpectedStatusRange (code1 : HttpStatusCode) (code2 : HttpStatusCode) request = | |
{ request with ExpectedStatusCode = Some (Range(code1,code2)) } | |
let withExpectedStatusOk request = | |
request |> withExpectedStatus HttpStatusCode.OK | |
let buildRestSharpRequest request = | |
let translateMethod = function | |
| GET -> Method.GET | |
| POST -> Method.POST | |
| PUT -> Method.PUT | |
| DELETE -> Method.DELETE | |
let addListValues (f : string * string -> IRestRequest) (l : (string * string) list option) = | |
match l with | |
| None -> () | |
| Some l -> | |
for k,v in l do | |
f(k,v) |> ignore | |
let client = | |
match request.Url with | |
| Some url -> RestClient(url) | |
| _ -> failwith "No URL specified" | |
let restRequest = | |
match request.Method,request.Resource with | |
| Some mthd,Some res -> RestRequest(res, translateMethod mthd) | |
| _ -> failwith "Missing method or resource" | |
match request.Body with | |
| None -> () | |
| Some body -> | |
match body with | |
| JsonString s -> restRequest.AddJsonBody(s) |> ignore | |
| JsonObject o -> restRequest.AddJsonBody(o) |> ignore | |
| Xml x -> restRequest.AddXmlBody(x) |> ignore | |
| XmlWithNamespace (x,ns) -> restRequest.AddXmlBody(x,ns) |> ignore | |
request.QueryStrings |> addListValues restRequest.AddQueryParameter | |
request.FormValues |> addListValues restRequest.AddParameter | |
request.UrlSegments |> addListValues restRequest.AddUrlSegment | |
request.Headers |> addListValues restRequest.AddHeader | |
request.Cookies |> addListValues restRequest.AddCookie | |
client,restRequest | |
let evalRestSharpResponse req (rsp : IRestResponse) = | |
let goodStatusCode = | |
match req.ExpectedStatusCode with | |
| None -> true | |
| Some expectation -> | |
match expectation with | |
| Exact exact -> exact = rsp.StatusCode | |
| Several several -> several |> List.contains rsp.StatusCode | |
| Range (l,h) -> rsp.StatusCode >= l && rsp.StatusCode <= h | |
if not goodStatusCode then failwith (sprintf "Unexpected HTTP status code: %A" rsp.StatusCode) | |
if not (isNull rsp.ErrorException) then | |
raise rsp.ErrorException | |
elif not (System.String.IsNullOrWhiteSpace(rsp.ErrorMessage)) then | |
failwith (sprintf "Error returned by client: %s" rsp.ErrorMessage) | |
let rest (f : RestApiRequest -> RestApiRequest) = | |
let request = f newRestRequest | |
let client,restRequest = buildRestSharpRequest request | |
let restResponse = client.Execute(restRequest) | |
evalRestSharpResponse request restResponse | |
let restWithResponse<'a when 'a : ( new : unit -> 'a)> (f : RestApiRequest -> RestApiRequest) : 'a = | |
let request = f newRestRequest | |
let client,restRequest = buildRestSharpRequest request | |
let restResponse = client.Execute<'a>(restRequest) | |
evalRestSharpResponse request restResponse | |
restResponse.Data | |
let restAsync (f : RestApiRequest -> RestApiRequest) = async { | |
let request = f newRestRequest | |
let client,restRequest = buildRestSharpRequest request | |
let! restResponse = Async.AwaitTask(client.ExecuteTaskAsync(restRequest)) | |
evalRestSharpResponse request restResponse | |
} | |
let restWithResponseAsync<'a when 'a : ( new : unit -> 'a)> (f : RestApiRequest -> RestApiRequest) : Async<'a> = async { | |
let request = f newRestRequest | |
let client,restRequest = buildRestSharpRequest request | |
let! restResponse = Async.AwaitTask(client.ExecuteTaskAsync<'a>(restRequest)) | |
evalRestSharpResponse request restResponse | |
return restResponse.Data | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment