diff --git a/PureGym.App/Authenticate.fs b/PureGym.App/Authenticate.fs index 7bb4e32..ee2e5a9 100644 --- a/PureGym.App/Authenticate.fs +++ b/PureGym.App/Authenticate.fs @@ -1,5 +1,6 @@ namespace PureGym.App +open System.Threading open Argu open System open PureGym @@ -50,7 +51,7 @@ module Authenticate = let run (creds : UsernamePin) = task { - let! cred = AuthToken.get creds + let! cred = AuthToken.get creds CancellationToken.None Console.WriteLine cred.AccessToken match cred.ExpiryTime with diff --git a/PureGym.App/Fullness.fs b/PureGym.App/Fullness.fs index 749498c..2e0743a 100644 --- a/PureGym.App/Fullness.fs +++ b/PureGym.App/Fullness.fs @@ -1,5 +1,6 @@ namespace PureGym.App +open System.Threading open Argu open PureGym @@ -48,7 +49,7 @@ module Fullness = let run (args : FullnessArgs) = task { - let! client = Api.make args.Creds + let! client = Api.makeWithoutRefresh CancellationToken.None args.Creds let! id = GymSelector.canonicalId client args.Gym let! attendance = client.GetGymAttendance id diff --git a/PureGym.App/LookupGym.fs b/PureGym.App/LookupGym.fs index 2ca9a82..e1233fe 100644 --- a/PureGym.App/LookupGym.fs +++ b/PureGym.App/LookupGym.fs @@ -1,5 +1,6 @@ namespace PureGym.App +open System.Threading open Argu open PureGym @@ -44,7 +45,7 @@ module LookupGym = let run (args : LookupGymArgs) = task { - let! client = Api.make args.Creds + let! client = Api.makeWithoutRefresh CancellationToken.None args.Creds let! s = client.GetGym 19 System.Console.WriteLine (string s) return 0 diff --git a/PureGym.App/MemberActivity.fs b/PureGym.App/MemberActivity.fs index 98138e7..14d8d67 100644 --- a/PureGym.App/MemberActivity.fs +++ b/PureGym.App/MemberActivity.fs @@ -1,5 +1,6 @@ namespace PureGym.App +open System.Threading open Argu open PureGym @@ -31,7 +32,7 @@ module MemberActivity = let run (args : MemberActivityArgs) = task { - let! client = Api.make args.Creds + let! client = Api.makeWithoutRefresh CancellationToken.None args.Creds let! activity = client.GetMemberActivity () let activity = activity.ToMemberActivity () System.Console.WriteLine (string activity) diff --git a/PureGym.App/Sessions.fs b/PureGym.App/Sessions.fs index 243cd14..e71dfb7 100644 --- a/PureGym.App/Sessions.fs +++ b/PureGym.App/Sessions.fs @@ -1,5 +1,6 @@ namespace PureGym.App +open System.Threading open Argu open PureGym open System @@ -41,7 +42,7 @@ module Sessions = let run (args : SessionsArgs) = task { - let! client = Api.make args.Creds + let! client = Api.makeWithoutRefresh CancellationToken.None args.Creds let! activity = client.GetSessions (args.FromDate, args.ToDate) System.Console.WriteLine (string activity) diff --git a/PureGym/Api.fs b/PureGym/Api.fs index 1e8ea7e..3d1bbc3 100644 --- a/PureGym/Api.fs +++ b/PureGym/Api.fs @@ -2,26 +2,42 @@ namespace PureGym open System open System.Net.Http +open System.Threading open System.Threading.Tasks /// Methods for interacting with the PureGym REST API. [] module Api = - /// Create a REST client, authenticated as the specified user. - let make (auth : Auth) : IPureGymApi Task = + + /// Create a REST client, authenticated as the specified user. Creds will be refreshed if possible as long as + /// the returned disposable is not disposed. + let make (auth : Auth) : (IPureGymApi * IDisposable) Task = + let cache, getToken = + match auth with + | Auth.Token t -> + { new IDisposable with + member _.Dispose () = () + }, + fun () -> t + | Auth.User cred -> + let cache = new Cache<_> (AuthToken.get cred, _.ExpiryTime) + cache :> _, (fun () -> Async.RunSynchronously (cache.GetCurrentValue ())) + + task { + let client = new HttpClient () + + return PureGymApi.make (getToken >> _.AccessToken >> sprintf "Bearer %s") client, cache + } + + /// Create a REST client, authenticated as the specified user. Do not refresh creds. + let makeWithoutRefresh (ct : CancellationToken) (auth : Auth) : IPureGymApi Task = task { let! token = match auth with | Auth.Token t -> Task.FromResult<_> t - | Auth.User cred -> AuthToken.get cred + | Auth.User cred -> AuthToken.get cred ct let client = new HttpClient () - client.BaseAddress <- Uri "https://capi.puregym.com/api/" - client.DefaultRequestHeaders.Authorization <- - Headers.AuthenticationHeaderValue ("Bearer", token.AccessToken) - - client.DefaultRequestHeaders.Add ("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0") - - return PureGymApi.make client + return PureGymApi.make (fun () -> $"Bearer %s{token.AccessToken}") client } diff --git a/PureGym/Auth.fs b/PureGym/Auth.fs index c63c16a..9894dd8 100644 --- a/PureGym/Auth.fs +++ b/PureGym/Auth.fs @@ -5,6 +5,7 @@ open System.Collections.Generic open System.Net.Http open System.Text.Json open System.Text.Json.Serialization +open System.Threading open System.Threading.Tasks // System.Text.Json does not support internal F# records as of .NET 8, presumably because it can't find the constructor. @@ -72,8 +73,9 @@ module AuthToken = let private options = JsonSerializerOptions (IncludeFields = true) /// Get an AuthToken for the given user email address with the given eight-digit PureGym PIN. - let get (creds : UsernamePin) : Task = - task { + let get (creds : UsernamePin) (ct : CancellationToken) : Task = + async { + let! ct = Async.CancellationToken use client = new HttpClient () client.BaseAddress <- Uri "https://auth.puregym.com" client.DefaultRequestHeaders.Add ("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0") @@ -89,14 +91,21 @@ module AuthToken = |> List.map KeyValuePair use content = new FormUrlEncodedContent (request) - let! response = client.PostAsync (Uri "https://auth.puregym.com/connect/token", content) + + let! response = + Async.AwaitTask ( + client.PostAsync (Uri "https://auth.puregym.com/connect/token", content, cancellationToken = ct) + ) if response.IsSuccessStatusCode then - let! content = response.Content.ReadAsStreamAsync () - let! response = JsonSerializer.DeserializeAsync (content, options) - // let! response = JsonSerializer.DeserializeAsync (content, options) + let! content = Async.AwaitTask (response.Content.ReadAsStreamAsync ct) + + let! response = + Async.AwaitTask (JsonSerializer.DeserializeAsync(content, options, ct).AsTask ()) + return AuthToken.Parse response else - let! content = response.Content.ReadAsStringAsync () + let! content = Async.AwaitTask (response.Content.ReadAsStringAsync ct) return failwithf $"bad status code: %+A{response.StatusCode}\n%s{content}" } + |> fun a -> Async.StartAsTask (a, cancellationToken = ct) diff --git a/PureGym/Cache.fs b/PureGym/Cache.fs new file mode 100644 index 0000000..0625483 --- /dev/null +++ b/PureGym/Cache.fs @@ -0,0 +1,85 @@ +namespace PureGym + +open System +open System.Threading +open System.Threading.Tasks + +type private CacheMessage<'a> = + | TriggerUpdate + | UpdateStored of 'a Task + | Get of AsyncReplyChannel<'a> + | Quit of AsyncReplyChannel + +type internal Cache<'a> (obtainNew : CancellationToken -> 'a Task, expiry : 'a -> DateTime option) = + let cts = new CancellationTokenSource () + + let initialValue = obtainNew cts.Token + + let rec handle (value : 'a Task) (mailbox : MailboxProcessor>) : Async = + async { + let! message = mailbox.Receive () + + match message with + | Quit channel -> + channel.Reply () + return () + | CacheMessage.UpdateStored newValue -> return! handle newValue mailbox + | CacheMessage.TriggerUpdate -> + async { + let! a = Async.AwaitTask (obtainNew cts.Token) + let expiry = expiry a + + match expiry with + | None -> return () + | Some expiry -> + + // a bit sloppy but :shrug: + do! Async.Sleep ((expiry - DateTime.Now) - TimeSpan.FromMinutes 1.0) + + try + mailbox.Post CacheMessage.TriggerUpdate + with _ -> + // Post during shutdown sequence: drop it on the floor + () + + return () + } + |> fun a -> Async.Start (a, cancellationToken = cts.Token) + + return! handle value mailbox + | CacheMessage.Get reply -> + let! valueAwaited = Async.AwaitTask value + reply.Reply valueAwaited + return! handle value mailbox + } + + let mailbox = new MailboxProcessor<_> (handle initialValue) + + do + mailbox.Start () + mailbox.Post CacheMessage.TriggerUpdate + + let isDisposing = ref 0 + let hasDisposed = TaskCompletionSource () + + member this.GetCurrentValue () = + try + mailbox.PostAndAsyncReply CacheMessage.Get + with + // TODO I think this is the right exception... + | :? InvalidOperationException -> + raise (ObjectDisposedException (nameof (Cache))) + + interface IDisposable with + member _.Dispose () = + if Interlocked.Increment isDisposing = 1 then + mailbox.PostAndReply CacheMessage.Quit + (mailbox :> IDisposable).Dispose () + // We can't terminate the CTS until the mailbox has processed all client requests. + // Otherwise we terminate the mailbox's state Task before it has finished querying that + // task on behalf of clients. + cts.Cancel () + cts.Dispose () + hasDisposed.SetResult () + else + hasDisposed.Task.Result diff --git a/PureGym/Client.fs b/PureGym/Client.fs index 19fb745..61311f9 100644 --- a/PureGym/Client.fs +++ b/PureGym/Client.fs @@ -9,7 +9,11 @@ open RestEase /// The PureGym REST API. You probably want to instantiate one of these with `Api.make`. [] [] +[] type IPureGymApi = + [
] + abstract AuthHeader : string + /// Get the complete list of all gyms known to PureGym. [] abstract GetGyms : ?ct : CancellationToken -> Task diff --git a/PureGym/GeneratedClient.fs b/PureGym/GeneratedClient.fs index 732902a..1b95858 100644 --- a/PureGym/GeneratedClient.fs +++ b/PureGym/GeneratedClient.fs @@ -4,6 +4,8 @@ //------------------------------------------------------------------------------ + + namespace PureGym open System @@ -16,23 +18,19 @@ open RestEase [] [] module PureGymApi = - /// Create a REST client. - let make (client : System.Net.Http.HttpClient) : IPureGymApi = + /// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties. + let make (authHeader : unit -> string) (client : System.Net.Http.HttpClient) : IPureGymApi = { new IPureGymApi with - member _.GetGyms (ct : CancellationToken option) = + member _.AuthHeader : string = authHeader () + + member this.GetGyms (ct : CancellationToken option) = async { let! ct = Async.CancellationToken let uri = System.Uri ( (match client.BaseAddress with - | null -> - raise ( - System.ArgumentNullException ( - nameof (client.BaseAddress), - "No base address was supplied on the type, and no BaseAddress was on the HttpClient." - ) - ) + | null -> System.Uri "https://capi.puregym.com/api/" | v -> v), System.Uri ("v1/gyms/", System.UriKind.Relative) ) @@ -43,32 +41,28 @@ module PureGymApi = RequestUri = uri ) + do httpMessage.Headers.Add ("Authorization", this.AuthHeader.ToString ()) + do httpMessage.Headers.Add ("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0") let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask let response = response.EnsureSuccessStatusCode () - let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask - let! node = - System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct) + let! jsonNode = + System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct) |> Async.AwaitTask - return node.AsArray () |> Seq.map (fun elt -> Gym.jsonParse elt) |> List.ofSeq + return jsonNode.AsArray () |> Seq.map (fun elt -> Gym.jsonParse elt) |> List.ofSeq } |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) - member _.GetMember (ct : CancellationToken option) = + member this.GetMember (ct : CancellationToken option) = async { let! ct = Async.CancellationToken let uri = System.Uri ( (match client.BaseAddress with - | null -> - raise ( - System.ArgumentNullException ( - nameof (client.BaseAddress), - "No base address was supplied on the type, and no BaseAddress was on the HttpClient." - ) - ) + | null -> System.Uri "https://capi.puregym.com/api/" | v -> v), System.Uri ("v1/member", System.UriKind.Relative) ) @@ -79,32 +73,28 @@ module PureGymApi = RequestUri = uri ) + do httpMessage.Headers.Add ("Authorization", this.AuthHeader.ToString ()) + do httpMessage.Headers.Add ("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0") let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask let response = response.EnsureSuccessStatusCode () - let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask - let! node = - System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct) + let! jsonNode = + System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct) |> Async.AwaitTask - return Member.jsonParse node + return Member.jsonParse jsonNode } |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) - member _.GetGymAttendance (gymId : int, ct : CancellationToken option) = + member this.GetGymAttendance (gymId : int, ct : CancellationToken option) = async { let! ct = Async.CancellationToken let uri = System.Uri ( (match client.BaseAddress with - | null -> - raise ( - System.ArgumentNullException ( - nameof (client.BaseAddress), - "No base address was supplied on the type, and no BaseAddress was on the HttpClient." - ) - ) + | null -> System.Uri "https://capi.puregym.com/api/" | v -> v), System.Uri ( "v1/gyms/{gym_id}/attendance" @@ -119,32 +109,28 @@ module PureGymApi = RequestUri = uri ) + do httpMessage.Headers.Add ("Authorization", this.AuthHeader.ToString ()) + do httpMessage.Headers.Add ("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0") let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask let response = response.EnsureSuccessStatusCode () - let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask - let! node = - System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct) + let! jsonNode = + System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct) |> Async.AwaitTask - return GymAttendance.jsonParse node + return GymAttendance.jsonParse jsonNode } |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) - member _.GetGym (gymId : int, ct : CancellationToken option) = + member this.GetGym (gymId : int, ct : CancellationToken option) = async { let! ct = Async.CancellationToken let uri = System.Uri ( (match client.BaseAddress with - | null -> - raise ( - System.ArgumentNullException ( - nameof (client.BaseAddress), - "No base address was supplied on the type, and no BaseAddress was on the HttpClient." - ) - ) + | null -> System.Uri "https://capi.puregym.com/api/" | v -> v), System.Uri ( "v1/gyms/{gym_id}" @@ -159,32 +145,28 @@ module PureGymApi = RequestUri = uri ) + do httpMessage.Headers.Add ("Authorization", this.AuthHeader.ToString ()) + do httpMessage.Headers.Add ("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0") let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask let response = response.EnsureSuccessStatusCode () - let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask - let! node = - System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct) + let! jsonNode = + System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct) |> Async.AwaitTask - return Gym.jsonParse node + return Gym.jsonParse jsonNode } |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) - member _.GetMemberActivity (ct : CancellationToken option) = + member this.GetMemberActivity (ct : CancellationToken option) = async { let! ct = Async.CancellationToken let uri = System.Uri ( (match client.BaseAddress with - | null -> - raise ( - System.ArgumentNullException ( - nameof (client.BaseAddress), - "No base address was supplied on the type, and no BaseAddress was on the HttpClient." - ) - ) + | null -> System.Uri "https://capi.puregym.com/api/" | v -> v), System.Uri ("v1/member/activity", System.UriKind.Relative) ) @@ -195,32 +177,28 @@ module PureGymApi = RequestUri = uri ) + do httpMessage.Headers.Add ("Authorization", this.AuthHeader.ToString ()) + do httpMessage.Headers.Add ("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0") let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask let response = response.EnsureSuccessStatusCode () - let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask - let! node = - System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct) + let! jsonNode = + System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct) |> Async.AwaitTask - return MemberActivityDto.jsonParse node + return MemberActivityDto.jsonParse jsonNode } |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) - member _.GetSessions (fromDate : DateOnly, toDate : DateOnly, ct : CancellationToken option) = + member this.GetSessions (fromDate : DateOnly, toDate : DateOnly, ct : CancellationToken option) = async { let! ct = Async.CancellationToken let uri = System.Uri ( (match client.BaseAddress with - | null -> - raise ( - System.ArgumentNullException ( - nameof (client.BaseAddress), - "No base address was supplied on the type, and no BaseAddress was on the HttpClient." - ) - ) + | null -> System.Uri "https://capi.puregym.com/api/" | v -> v), System.Uri ( ("v2/gymSessions/member" @@ -238,15 +216,17 @@ module PureGymApi = RequestUri = uri ) + do httpMessage.Headers.Add ("Authorization", this.AuthHeader.ToString ()) + do httpMessage.Headers.Add ("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0") let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask let response = response.EnsureSuccessStatusCode () - let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask + let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask - let! node = - System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct) + let! jsonNode = + System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct) |> Async.AwaitTask - return Sessions.jsonParse node + return Sessions.jsonParse jsonNode } |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) } diff --git a/PureGym/GeneratedDto.fs b/PureGym/GeneratedDto.fs index d97aa14..4452ee9 100644 --- a/PureGym/GeneratedDto.fs +++ b/PureGym/GeneratedDto.fs @@ -3,6 +3,8 @@ // Changes to this file will be lost when the code is regenerated. //------------------------------------------------------------------------------ + + namespace PureGym /// Module containing JSON parsing methods for the GymOpeningHours type @@ -253,9 +255,41 @@ module Gym = .AsValue() .GetValue () - let Location = GymLocation.jsonParse node.["location"] - let AccessOptions = GymAccessOptions.jsonParse node.["accessOptions"] - let GymOpeningHours = GymOpeningHours.jsonParse node.["gymOpeningHours"] + let Location = + GymLocation.jsonParse ( + match node.["location"] with + | null -> + raise ( + System.Collections.Generic.KeyNotFoundException ( + sprintf "Required key '%s' not found on JSON object" ("location") + ) + ) + | v -> v + ) + + let AccessOptions = + GymAccessOptions.jsonParse ( + match node.["accessOptions"] with + | null -> + raise ( + System.Collections.Generic.KeyNotFoundException ( + sprintf "Required key '%s' not found on JSON object" ("accessOptions") + ) + ) + | v -> v + ) + + let GymOpeningHours = + GymOpeningHours.jsonParse ( + match node.["gymOpeningHours"] with + | null -> + raise ( + System.Collections.Generic.KeyNotFoundException ( + sprintf "Required key '%s' not found on JSON object" ("gymOpeningHours") + ) + ) + | v -> v + ) let EmailAddress = (match node.["emailAddress"] with @@ -281,7 +315,17 @@ module Gym = .AsValue() .GetValue () - let Address = GymAddress.jsonParse node.["address"] + let Address = + GymAddress.jsonParse ( + match node.["address"] with + | null -> + raise ( + System.Collections.Generic.KeyNotFoundException ( + sprintf "Required key '%s' not found on JSON object" ("address") + ) + ) + | v -> v + ) let Status = (match node.["status"] with @@ -856,7 +900,17 @@ namespace PureGym module Visit = /// Parse from a JSON node. let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Visit = - let Gym = VisitGym.jsonParse node.["Gym"] + let Gym = + VisitGym.jsonParse ( + match node.["Gym"] with + | null -> + raise ( + System.Collections.Generic.KeyNotFoundException ( + sprintf "Required key '%s' not found on JSON object" ("Gym") + ) + ) + | v -> v + ) let Duration = (match node.["Duration"] with @@ -909,8 +963,29 @@ namespace PureGym module SessionsSummary = /// Parse from a JSON node. let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsSummary = - let ThisWeek = SessionsAggregate.jsonParse node.["ThisWeek"] - let Total = SessionsAggregate.jsonParse node.["Total"] + let ThisWeek = + SessionsAggregate.jsonParse ( + match node.["ThisWeek"] with + | null -> + raise ( + System.Collections.Generic.KeyNotFoundException ( + sprintf "Required key '%s' not found on JSON object" ("ThisWeek") + ) + ) + | v -> v + ) + + let Total = + SessionsAggregate.jsonParse ( + match node.["Total"] with + | null -> + raise ( + System.Collections.Generic.KeyNotFoundException ( + sprintf "Required key '%s' not found on JSON object" ("Total") + ) + ) + | v -> v + ) { Total = Total @@ -937,7 +1012,17 @@ module Sessions = |> Seq.map (fun elt -> Visit.jsonParse elt) |> List.ofSeq - let Summary = SessionsSummary.jsonParse node.["Summary"] + let Summary = + SessionsSummary.jsonParse ( + match node.["Summary"] with + | null -> + raise ( + System.Collections.Generic.KeyNotFoundException ( + sprintf "Required key '%s' not found on JSON object" ("Summary") + ) + ) + | v -> v + ) { Summary = Summary diff --git a/PureGym/PureGym.fsproj b/PureGym/PureGym.fsproj index 5b0be4d..5b72b9e 100644 --- a/PureGym/PureGym.fsproj +++ b/PureGym/PureGym.fsproj @@ -6,20 +6,21 @@ true FS3559 - 1.1.13 + 1.4.8 - - Dto.fs + + Dto.fs - Client.fs + Client.fs + @@ -35,8 +36,7 @@ - - + diff --git a/PureGym/SurfaceBaseline.txt b/PureGym/SurfaceBaseline.txt index b77519a..c707f17 100644 --- a/PureGym/SurfaceBaseline.txt +++ b/PureGym/SurfaceBaseline.txt @@ -1,3 +1,6 @@ +PureGym.Api inherit obj +PureGym.Api.make [static method]: PureGym.Auth -> (PureGym.IPureGymApi * IDisposable) System.Threading.Tasks.Task +PureGym.Api.makeWithoutRefresh [static method]: System.Threading.CancellationToken -> PureGym.Auth -> PureGym.IPureGymApi System.Threading.Tasks.Task PureGym.Auth inherit obj, implements PureGym.Auth System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Auth System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 2 cases PureGym.Auth+Tags inherit obj PureGym.Auth+Tags.Token [static field]: int = 1 @@ -33,10 +36,8 @@ PureGym.AuthToken.ExpiryTime [property]: [read-only] System.DateTime option PureGym.AuthToken.get_AccessToken [method]: unit -> string PureGym.AuthToken.get_ExpiryTime [method]: unit -> System.DateTime option PureGym.AuthTokenModule inherit obj -PureGym.AuthTokenModule.get [static method]: PureGym.UsernamePin -> PureGym.AuthToken System.Threading.Tasks.Task +PureGym.AuthTokenModule.get [static method]: PureGym.UsernamePin -> System.Threading.CancellationToken -> PureGym.AuthToken System.Threading.Tasks.Task PureGym.AuthTokenModule.ofBearerToken [static method]: string -> PureGym.AuthToken -PureGym.Api inherit obj -PureGym.Api.make [static method]: PureGym.Auth -> PureGym.IPureGymApi System.Threading.Tasks.Task PureGym.Gym inherit obj, implements PureGym.Gym System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Gym System.IComparable, System.IComparable, System.Collections.IStructuralComparable PureGym.Gym..ctor [constructor]: (string, int, int, PureGym.GymAddress, string, string, PureGym.GymOpeningHours, PureGym.GymAccessOptions, PureGym.GymLocation, string, string) PureGym.Gym.AccessOptions [property]: [read-only] PureGym.GymAccessOptions @@ -150,7 +151,9 @@ PureGym.GymSelector.NewName [static method]: string -> PureGym.GymSelector PureGym.GymSelector.Tag [property]: [read-only] int PureGym.GymSelectorModule inherit obj PureGym.GymSelectorModule.canonicalId [static method]: PureGym.IPureGymApi -> PureGym.GymSelector -> int System.Threading.Tasks.Task -PureGym.IPureGymApi - interface with 6 member(s) +PureGym.IPureGymApi - interface with 8 member(s) +PureGym.IPureGymApi.AuthHeader [property]: [read-only] string +PureGym.IPureGymApi.get_AuthHeader [method]: unit -> string PureGym.IPureGymApi.GetGym [method]: (int, System.Threading.CancellationToken option) -> PureGym.Gym System.Threading.Tasks.Task PureGym.IPureGymApi.GetGymAttendance [method]: (int, System.Threading.CancellationToken option) -> PureGym.GymAttendance System.Threading.Tasks.Task PureGym.IPureGymApi.GetGyms [method]: System.Threading.CancellationToken option -> PureGym.Gym list System.Threading.Tasks.Task @@ -223,7 +226,7 @@ PureGym.MemberActivityThisMonth.TotalVisits [property]: [read-only] int PureGym.MemberModule inherit obj PureGym.MemberModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.Member PureGym.PureGymApiModule inherit obj -PureGym.PureGymApiModule.make [static method]: System.Net.Http.HttpClient -> PureGym.IPureGymApi +PureGym.PureGymApiModule.make [static method]: (unit -> string) -> System.Net.Http.HttpClient -> PureGym.IPureGymApi PureGym.Sessions inherit obj, implements PureGym.Sessions System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Sessions System.IComparable, System.IComparable, System.Collections.IStructuralComparable PureGym.Sessions..ctor [constructor]: (PureGym.SessionsSummary, PureGym.Visit list) PureGym.Sessions.get_Summary [method]: unit -> PureGym.SessionsSummary @@ -277,4 +280,4 @@ PureGym.VisitGym.Status [property]: [read-only] string PureGym.VisitGymModule inherit obj PureGym.VisitGymModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.VisitGym PureGym.VisitModule inherit obj -PureGym.VisitModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.Visit +PureGym.VisitModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.Visit \ No newline at end of file diff --git a/PureGym/version.json b/PureGym/version.json index f3c770d..d0e7d61 100644 --- a/PureGym/version.json +++ b/PureGym/version.json @@ -1,5 +1,5 @@ { - "version": "3.0", + "version": "4.0", "publicReleaseRefSpec": [ "^refs/heads/main$" ], diff --git a/nix/deps.nix b/nix/deps.nix index ff4c444..3ec8cae 100644 --- a/nix/deps.nix +++ b/nix/deps.nix @@ -438,7 +438,7 @@ }) (fetchNuGet { pname = "WoofWare.Myriad.Plugins"; - version = "1.1.13"; - sha256 = "sha256-TjR+3spu9vXb2kOoXM9p6yzjpt2rja1Tu7cAOmIMhog= "; + version = "1.4.8"; + sha256 = "sha256-HdqiXz4pSQ+pPkj82RsWR7zn3JePQm5/IFl4dlR2O5Y="; }) ]