Compare commits
18 Commits
989a4a7a33
...
main
Author | SHA1 | Date | |
---|---|---|---|
2ca8c5e43b | |||
6b2ebdffba | |||
541b69a853 | |||
5a75d01466 | |||
bf2a00a7f1 | |||
f679bc4328 | |||
e699f4d9ad | |||
a29133a443 | |||
eb1cc43a08 | |||
419f27053f | |||
f182c6ebad | |||
e96ae78665 | |||
58fdb23719 | |||
cdbc73b07f | |||
0be5485603 | |||
c8a29356b7 | |||
8ece87ff57 | |||
2741c5e36c |
@@ -3,7 +3,7 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"fantomas": {
|
||||
"version": "6.2.0",
|
||||
"version": "6.3.15",
|
||||
"commands": [
|
||||
"fantomas"
|
||||
]
|
||||
|
@@ -2,7 +2,6 @@ root=true
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=crlf
|
||||
trim_trailing_whitespace=true
|
||||
insert_final_newline=true
|
||||
indent_style=space
|
||||
|
1
.fantomasignore
Normal file
1
.fantomasignore
Normal file
@@ -0,0 +1 @@
|
||||
.direnv/
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,6 +9,7 @@ riderModule.iml
|
||||
.DS_Store
|
||||
result
|
||||
.profile*
|
||||
.direnv/
|
||||
|
||||
node_modules/
|
||||
package.json
|
||||
|
@@ -1,22 +0,0 @@
|
||||
steps:
|
||||
build:
|
||||
image: nixos/nix
|
||||
commands:
|
||||
- echo 'experimental-features = flakes nix-command' >> /etc/nix/nix.conf
|
||||
# Lint
|
||||
- "nix flake check"
|
||||
# Test
|
||||
- nix build
|
||||
- nix run . -- --help
|
||||
- nix run . -- auth --help
|
||||
- nix run . -- lookup-gym --help
|
||||
- nix run . -- fullness --help
|
||||
- nix run . -- activity --help
|
||||
- nix develop --command markdown-link-check README.md
|
||||
- nix develop --command dotnet test
|
||||
- nix develop --command dotnet test --configuration Release
|
||||
|
||||
when:
|
||||
- event: "push"
|
||||
evaluate: 'CI_COMMIT_BRANCH == CI_REPO_DEFAULT_BRANCH'
|
||||
- event: "pull_request"
|
29
.woodpecker/build.yaml
Normal file
29
.woodpecker/build.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# yaml-language-server: $schema=https://raw.githubusercontent.com/woodpecker-ci/woodpecker/main/pipeline/frontend/yaml/linter/schema/schema.json
|
||||
{
|
||||
"steps": {
|
||||
"build": {
|
||||
"image": "nixos/nix",
|
||||
"commands": [
|
||||
"echo 'experimental-features = flakes nix-command' >> /etc/nix/nix.conf",
|
||||
# Lint
|
||||
"nix flake check",
|
||||
# Test
|
||||
"nix build",
|
||||
# Reproducibility
|
||||
"nix build --rebuild",
|
||||
"nix run . -- --help",
|
||||
"nix run . -- auth --help",
|
||||
"nix run . -- lookup-gym --help",
|
||||
"nix run . -- fullness --help",
|
||||
"nix run . -- activity --help",
|
||||
"nix develop --command markdown-link-check README.md",
|
||||
"nix develop --command dotnet test",
|
||||
"nix develop --command dotnet test --configuration Release"
|
||||
],
|
||||
"when": [
|
||||
{ "event": "push", "evaluate": "CI_COMMIT_BRANCH == CI_REPO_DEFAULT_BRANCH" },
|
||||
{ "event": "pull_request" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
53
PureGym.App/AllGyms.fs
Normal file
53
PureGym.App/AllGyms.fs
Normal file
@@ -0,0 +1,53 @@
|
||||
namespace PureGym.App
|
||||
|
||||
open System.Threading
|
||||
open Argu
|
||||
open PureGym
|
||||
|
||||
type AllGymsArgsFragment =
|
||||
| [<Unique ; EqualsAssignmentOrSpaced>] Terse of bool
|
||||
|
||||
interface IArgParserTemplate with
|
||||
member s.Usage =
|
||||
match s with
|
||||
| Terse _ -> "print only 'id,gym-name'"
|
||||
|
||||
type AllGymsArgs =
|
||||
{
|
||||
Creds : Auth
|
||||
Terse : bool
|
||||
}
|
||||
|
||||
static member Parse
|
||||
(auth : Auth)
|
||||
(args : AllGymsArgsFragment ParseResults)
|
||||
: Result<AllGymsArgs, ArguParseException>
|
||||
=
|
||||
let terse =
|
||||
match args.TryGetResult AllGymsArgsFragment.Terse with
|
||||
| None -> false
|
||||
| Some x -> x
|
||||
|
||||
{
|
||||
Creds = auth
|
||||
Terse = terse
|
||||
}
|
||||
|> Ok
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module AllGyms =
|
||||
|
||||
let run (args : AllGymsArgs) =
|
||||
task {
|
||||
let! client = Api.makeWithoutRefresh CancellationToken.None args.Creds
|
||||
let! s = client.GetGyms ()
|
||||
|
||||
if args.Terse then
|
||||
for gym in s do
|
||||
System.Console.WriteLine $"%i{gym.Id},%s{gym.Name}"
|
||||
else
|
||||
for gym in s do
|
||||
System.Console.WriteLine (string<Gym> gym)
|
||||
|
||||
return 0
|
||||
}
|
@@ -5,8 +5,8 @@ open PureGym
|
||||
|
||||
type AuthArg =
|
||||
| [<Unique ; CustomAppSettings "PUREGYM_BEARER_TOKEN">] Bearer_Token of string
|
||||
| [<Unique>] User_Email of string
|
||||
| [<Unique>] Pin of string
|
||||
| [<Unique ; EqualsAssignmentOrSpaced>] User_Email of string
|
||||
| [<Unique ; EqualsAssignmentOrSpaced>] Pin of string
|
||||
| [<GatherUnrecognized>] Others of string
|
||||
|
||||
interface IArgParserTemplate with
|
||||
|
@@ -1,5 +1,6 @@
|
||||
namespace PureGym.App
|
||||
|
||||
open System.Threading
|
||||
open Argu
|
||||
open System
|
||||
open PureGym
|
||||
@@ -14,7 +15,7 @@ type GetTokenArg =
|
||||
match s with
|
||||
| GetTokenArg.Pin _ -> "Eight-digit PureGym user's PIN"
|
||||
| GetTokenArg.User_Email _ -> "PureGym user's email address"
|
||||
| GetTokenArg.StdIn _ -> "Read anything not specified on the command line from stdin"
|
||||
| GetTokenArg.StdIn -> "Read anything not specified on the command line from stdin"
|
||||
|
||||
static member Parse (args : ParseResults<GetTokenArg>) : Result<UsernamePin, ArguParseException> =
|
||||
let canUseStdin = args.TryGetResult(GetTokenArg.StdIn).IsSome
|
||||
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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<Gym> s)
|
||||
return 0
|
||||
|
@@ -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<MemberActivityThisMonth> activity)
|
||||
|
@@ -16,13 +16,21 @@ module Program =
|
||||
("Get information about the physical instantiation of a gym",
|
||||
RequiresAuth (fun auth -> ArgsCrate.make (LookupGymArgs.Parse auth) LookupGym.run))
|
||||
|
||||
"all-gyms",
|
||||
("List information about all gyms",
|
||||
RequiresAuth (fun auth -> ArgsCrate.make (AllGymsArgs.Parse auth) AllGyms.run))
|
||||
|
||||
"fullness",
|
||||
("Determine how full a gym is",
|
||||
RequiresAuth (fun auth -> ArgsCrate.make (FullnessArgs.Parse auth) Fullness.run))
|
||||
|
||||
"activity",
|
||||
("Get information about your gym usage",
|
||||
("Get information about your aggregate gym usage",
|
||||
RequiresAuth (fun auth -> ArgsCrate.make (MemberActivityArgs.Parse auth) MemberActivity.run))
|
||||
|
||||
"sessions",
|
||||
("Get information about your individual sessions",
|
||||
RequiresAuth (fun auth -> ArgsCrate.make (SessionsArgs.Parse auth) Sessions.run))
|
||||
|]
|
||||
|> Map.ofArray
|
||||
|
||||
|
@@ -1,8 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<NuGetAuditMode>all</NuGetAuditMode>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -13,7 +15,9 @@
|
||||
<Compile Include="Authenticate.fs" />
|
||||
<Compile Include="Fullness.fs" />
|
||||
<Compile Include="LookupGym.fs" />
|
||||
<Compile Include="AllGyms.fs" />
|
||||
<Compile Include="MemberActivity.fs" />
|
||||
<Compile Include="Sessions.fs" />
|
||||
<Compile Include="Program.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -22,7 +26,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Argu" Version="6.1.1" />
|
||||
<PackageReference Include="Argu" Version="6.2.5" />
|
||||
<PackageReference Include="System.Text.Json" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
50
PureGym.App/Sessions.fs
Normal file
50
PureGym.App/Sessions.fs
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace PureGym.App
|
||||
|
||||
open System.Threading
|
||||
open Argu
|
||||
open PureGym
|
||||
open System
|
||||
|
||||
type SessionsArgsFragment =
|
||||
| [<Mandatory ; EqualsAssignmentOrSpaced>] From_Date of string
|
||||
| [<Mandatory ; EqualsAssignmentOrSpaced>] To_Date of string
|
||||
|
||||
interface IArgParserTemplate with
|
||||
member s.Usage =
|
||||
match s with
|
||||
| From_Date _ -> "start of date range (inclusive) for query, which needs to parse as a DateTime"
|
||||
| To_Date _ -> "end of date range (inclusive) for query, which needs to parse as a DateTime"
|
||||
|
||||
type SessionsArgs =
|
||||
{
|
||||
Creds : Auth
|
||||
FromDate : DateOnly
|
||||
ToDate : DateOnly
|
||||
}
|
||||
|
||||
static member Parse
|
||||
(auth : Auth)
|
||||
(args : SessionsArgsFragment ParseResults)
|
||||
: Result<SessionsArgs, ArguParseException>
|
||||
=
|
||||
let fromDate = args.GetResult SessionsArgsFragment.From_Date
|
||||
let toDate = args.GetResult SessionsArgsFragment.To_Date
|
||||
|
||||
{
|
||||
Creds = auth
|
||||
FromDate = DateOnly.Parse fromDate
|
||||
ToDate = DateOnly.Parse toDate
|
||||
}
|
||||
|> Ok
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
module Sessions =
|
||||
|
||||
let run (args : SessionsArgs) =
|
||||
task {
|
||||
let! client = Api.makeWithoutRefresh CancellationToken.None args.Creds
|
||||
let! activity = client.GetSessions (args.FromDate, args.ToDate)
|
||||
|
||||
System.Console.WriteLine (string<Sessions> activity)
|
||||
return 0
|
||||
}
|
@@ -1,23 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<WarningsNotAsErrors>NU1901;NU1902;NU1903;NU1904</WarningsNotAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="TestSurface.fs" />
|
||||
<Compile Include="TestJson.fs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ApiSurface" Version="4.0.12" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0"/>
|
||||
<PackageReference Include="NUnit" Version="3.13.3"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2"/>
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.6.1"/>
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0"/>
|
||||
<PackageReference Include="ApiSurface" Version="5.0.1" />
|
||||
<PackageReference Include="FsCheck" Version="3.3.1" />
|
||||
<PackageReference Include="FsUnit" Version="7.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1"/>
|
||||
<PackageReference Include="NUnit" Version="4.4.0"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="5.1.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
317
PureGym.Test/TestJson.fs
Normal file
317
PureGym.Test/TestJson.fs
Normal file
@@ -0,0 +1,317 @@
|
||||
namespace PureGym.Test
|
||||
|
||||
open System
|
||||
open System.IO
|
||||
open System.Reflection.Metadata
|
||||
open System.Reflection.PortableExecutable
|
||||
open System.Text.Json
|
||||
open System.Text.Json.Nodes
|
||||
open System.Text.Json.Serialization
|
||||
open NUnit.Framework
|
||||
open FsUnitTyped
|
||||
open PureGym
|
||||
|
||||
[<TestFixture>]
|
||||
module TestJson =
|
||||
|
||||
let gymOpeningHoursCases =
|
||||
[
|
||||
"""{"openingHours": [], "isAlwaysOpen": false}""",
|
||||
{
|
||||
GymOpeningHours.OpeningHours = []
|
||||
IsAlwaysOpen = false
|
||||
}
|
||||
"""{"openingHours": ["something"], "isAlwaysOpen": false}""",
|
||||
{
|
||||
GymOpeningHours.OpeningHours = [ "something" ]
|
||||
IsAlwaysOpen = false
|
||||
}
|
||||
]
|
||||
|> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof (gymOpeningHoursCases))>]
|
||||
let ``GymOpeningHours JSON parse`` (json : string, expected : GymOpeningHours) =
|
||||
JsonNode.Parse json |> GymOpeningHours.jsonParse |> shouldEqual expected
|
||||
|
||||
let gymAccessOptionsCases =
|
||||
List.allPairs [ true ; false ] [ true ; false ]
|
||||
|> List.map (fun (a, b) ->
|
||||
let s = sprintf """{"pinAccess": %b, "qrCodeAccess": %b}""" a b
|
||||
|
||||
s,
|
||||
{
|
||||
GymAccessOptions.PinAccess = a
|
||||
QrCodeAccess = b
|
||||
}
|
||||
)
|
||||
|> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof (gymAccessOptionsCases))>]
|
||||
let ``GymAccessOptions JSON parse`` (json : string, expected : GymAccessOptions) =
|
||||
JsonNode.Parse json |> GymAccessOptions.jsonParse |> shouldEqual expected
|
||||
|
||||
let gymLocationCases =
|
||||
[
|
||||
"""{"latitude": 1.0, "longitude": 3.0}""",
|
||||
{
|
||||
GymLocation.Latitude = 1.0
|
||||
Longitude = 3.0
|
||||
}
|
||||
]
|
||||
|> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof (gymLocationCases))>]
|
||||
let ``GymLocation JSON parse`` (json : string, expected : GymLocation) =
|
||||
JsonNode.Parse json |> GymLocation.jsonParse |> shouldEqual expected
|
||||
|
||||
let gymAddressCases =
|
||||
[
|
||||
"""{"addressLine1": "", "postCode": "hi", "town": ""}""",
|
||||
{
|
||||
GymAddress.AddressLine1 = ""
|
||||
AddressLine2 = None
|
||||
AddressLine3 = None
|
||||
County = None
|
||||
Postcode = "hi"
|
||||
Town = ""
|
||||
}
|
||||
"""{"addressLine1": "", "addressLine2": null, "postCode": "hi", "town": ""}""",
|
||||
{
|
||||
GymAddress.AddressLine1 = ""
|
||||
AddressLine2 = None
|
||||
AddressLine3 = None
|
||||
County = None
|
||||
Postcode = "hi"
|
||||
Town = ""
|
||||
}
|
||||
]
|
||||
|> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof (gymAddressCases))>]
|
||||
let ``GymAddress JSON parse`` (json : string, expected : GymAddress) =
|
||||
JsonNode.Parse (json, Nullable (JsonNodeOptions (PropertyNameCaseInsensitive = true)))
|
||||
|> GymAddress.jsonParse
|
||||
|> shouldEqual expected
|
||||
|
||||
let gymCases =
|
||||
let ovalJson =
|
||||
"""{"name":"London Oval","id":19,"status":2,"address":{"addressLine1":"Canterbury Court","addressLine2":"Units 4, 4A, 5 And 5A","addressLine3":"Kennington Park","town":"LONDON","county":null,"postcode":"SW9 6DE"},"phoneNumber":"+44 3444770005","emailAddress":"info.londonoval@puregym.com","staffMembers":null,"gymOpeningHours":{"isAlwaysOpen":true,"openingHours":[]},"reasonsToJoin":null,"accessOptions":{"pinAccess":true,"qrCodeAccess":true},"virtualTourUrl":null,"personalTrainersUrl":null,"webViewUrl":null,"floorPlanUrl":null,"location":{"longitude":"-0.110252","latitude":"51.480401"},"timeZone":"Europe/London","reopenDate":"2021-04-12T00:00:00+01 Europe/London"}"""
|
||||
|
||||
let oval =
|
||||
{
|
||||
Gym.Name = "London Oval"
|
||||
Id = 19
|
||||
Status = 2
|
||||
Address =
|
||||
{
|
||||
AddressLine1 = "Canterbury Court"
|
||||
AddressLine2 = Some "Units 4, 4A, 5 And 5A"
|
||||
AddressLine3 = Some "Kennington Park"
|
||||
Town = "LONDON"
|
||||
County = None
|
||||
Postcode = "SW9 6DE"
|
||||
}
|
||||
PhoneNumber = "+44 3444770005"
|
||||
EmailAddress = "info.londonoval@puregym.com"
|
||||
GymOpeningHours =
|
||||
{
|
||||
IsAlwaysOpen = true
|
||||
OpeningHours = []
|
||||
}
|
||||
AccessOptions =
|
||||
{
|
||||
PinAccess = true
|
||||
QrCodeAccess = true
|
||||
}
|
||||
Location =
|
||||
{
|
||||
Longitude = -0.110252
|
||||
Latitude = 51.480401
|
||||
}
|
||||
TimeZone = "Europe/London"
|
||||
ReopenDate = Some "2021-04-12T00:00:00+01 Europe/London"
|
||||
}
|
||||
|
||||
[ ovalJson, oval ] |> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof (gymCases))>]
|
||||
let ``Gym JSON parse`` (json : string, expected : Gym) =
|
||||
JsonNode.Parse json |> Gym.jsonParse |> shouldEqual expected
|
||||
|
||||
let memberCases =
|
||||
let me =
|
||||
{
|
||||
Id = 1234567
|
||||
CompoundMemberId = "12A123456"
|
||||
FirstName = "Patrick"
|
||||
LastName = "Stevens"
|
||||
HomeGymId = 19
|
||||
HomeGymName = "London Oval"
|
||||
EmailAddress = "someone@somewhere"
|
||||
GymAccessPin = "00000000"
|
||||
DateOfBirth = DateOnly (1994, 01, 02)
|
||||
MobileNumber = "+44 1234567"
|
||||
Postcode = "W1A 1AA"
|
||||
MembershipName = "Corporate"
|
||||
MembershipLevel = 12
|
||||
SuspendedReason = 0
|
||||
MemberStatus = 2
|
||||
}
|
||||
|
||||
let meJson =
|
||||
"""{
|
||||
"id": 1234567,
|
||||
"compoundMemberId": "12A123456",
|
||||
"firstName": "Patrick",
|
||||
"lastName": "Stevens",
|
||||
"homeGymId": 19,
|
||||
"homeGymName": "London Oval",
|
||||
"emailAddress": "someone@somewhere",
|
||||
"gymAccessPin": "00000000",
|
||||
"dateofBirth": "1994-01-02",
|
||||
"mobileNumber": "+44 1234567",
|
||||
"postCode": "W1A 1AA",
|
||||
"membershipName": "Corporate",
|
||||
"membershipLevel": 12,
|
||||
"suspendedReason": 0,
|
||||
"memberStatus": 2
|
||||
}"""
|
||||
|
||||
[ meJson, me ] |> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof memberCases)>]
|
||||
let ``Member JSON parse`` (json : string, expected : Member) =
|
||||
json |> JsonNode.Parse |> Member.jsonParse |> shouldEqual expected
|
||||
|
||||
let gymAttendanceCases =
|
||||
let json =
|
||||
"""{
|
||||
"description": "65",
|
||||
"totalPeopleInGym": 65,
|
||||
"totalPeopleInClasses": 2,
|
||||
"totalPeopleSuffix": null,
|
||||
"isApproximate": false,
|
||||
"attendanceTime": "2023-12-27T18:54:09.5101697",
|
||||
"lastRefreshed": "2023-12-27T18:54:09.5101697Z",
|
||||
"lastRefreshedPeopleInClasses": "2023-12-27T18:50:26.0782286Z",
|
||||
"maximumCapacity": 0
|
||||
}"""
|
||||
|
||||
let expected =
|
||||
{
|
||||
Description = "65"
|
||||
TotalPeopleInGym = 65
|
||||
TotalPeopleInClasses = Some 2
|
||||
TotalPeopleSuffix = None
|
||||
IsApproximate = false
|
||||
AttendanceTime =
|
||||
DateTime (2023, 12, 27, 18, 54, 09, 510, 169, DateTimeKind.Utc)
|
||||
+ TimeSpan.FromTicks 7L
|
||||
LastRefreshed =
|
||||
DateTime (2023, 12, 27, 18, 54, 09, 510, 169, DateTimeKind.Utc)
|
||||
+ TimeSpan.FromTicks 7L
|
||||
LastRefreshedPeopleInClasses =
|
||||
DateTime (2023, 12, 27, 18, 50, 26, 078, 228, DateTimeKind.Utc)
|
||||
+ TimeSpan.FromTicks 6L
|
||||
MaximumCapacity = 0
|
||||
}
|
||||
|
||||
[ json, expected ] |> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof gymAttendanceCases)>]
|
||||
let ``GymAttendance JSON parse`` (json : string, expected : GymAttendance) =
|
||||
json |> JsonNode.Parse |> GymAttendance.jsonParse |> shouldEqual expected
|
||||
|
||||
let memberActivityDtoCases =
|
||||
let json =
|
||||
"""{"totalDuration":2217,"averageDuration":48,"totalVisits":46,"totalClasses":0,"isEstimated":false,"lastRefreshed":"2023-12-27T19:00:56.0309892Z"}"""
|
||||
|
||||
let value =
|
||||
{
|
||||
TotalDuration = 2217
|
||||
AverageDuration = 48
|
||||
TotalVisits = 46
|
||||
TotalClasses = 0
|
||||
IsEstimated = false
|
||||
LastRefreshed =
|
||||
DateTime (2023, 12, 27, 19, 00, 56, 030, 989, DateTimeKind.Utc)
|
||||
+ TimeSpan.FromTicks 2L
|
||||
}
|
||||
|
||||
[ json, value ] |> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof memberActivityDtoCases)>]
|
||||
let ``MemberActivityDto JSON parse`` (json : string, expected : MemberActivityDto) =
|
||||
json |> JsonNode.Parse |> MemberActivityDto.jsonParse |> shouldEqual expected
|
||||
|
||||
let sessionsCases =
|
||||
let json =
|
||||
"""{
|
||||
"Summary":{"Total":{"Activities":0,"Visits":10,"Duration":445},"ThisWeek":{"Activities":0,"Visits":0,"Duration":0}},
|
||||
"Visits":[
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-21T10:12:00","Duration":50,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-20T12:05:00","Duration":80,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-17T19:37:00","Duration":46,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-16T12:19:00","Duration":37,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-15T11:14:00","Duration":47,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-13T10:30:00","Duration":36,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-10T16:18:00","Duration":32,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-05T22:36:00","Duration":40,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-03T17:59:00","Duration":48,"Name":null},
|
||||
{"IsDurationEstimated":false,"Gym":{"Id":19,"Name":"London Oval","Status":"Blocked","Location":null,"GymAccess":null,"ContactInfo":null,"TimeZone":null},"StartTime":"2023-12-01T21:41:00","Duration":29,"Name":null}],
|
||||
"Activities":[]}
|
||||
"""
|
||||
|
||||
let singleVisit startTime duration =
|
||||
{
|
||||
IsDurationEstimated = false
|
||||
Gym =
|
||||
{
|
||||
Id = 19
|
||||
Name = "London Oval"
|
||||
Status = "Blocked"
|
||||
}
|
||||
StartTime = startTime
|
||||
Duration = duration
|
||||
}
|
||||
|
||||
let expected =
|
||||
{
|
||||
Summary =
|
||||
{
|
||||
Total =
|
||||
{
|
||||
Activities = 0
|
||||
Visits = 10
|
||||
Duration = 445
|
||||
}
|
||||
ThisWeek =
|
||||
{
|
||||
Activities = 0
|
||||
Visits = 0
|
||||
Duration = 0
|
||||
}
|
||||
}
|
||||
Visits =
|
||||
[
|
||||
singleVisit (DateTime (2023, 12, 21, 10, 12, 00)) 50
|
||||
singleVisit (DateTime (2023, 12, 20, 12, 05, 00)) 80
|
||||
singleVisit (DateTime (2023, 12, 17, 19, 37, 00)) 46
|
||||
singleVisit (DateTime (2023, 12, 16, 12, 19, 00)) 37
|
||||
singleVisit (DateTime (2023, 12, 15, 11, 14, 00)) 47
|
||||
singleVisit (DateTime (2023, 12, 13, 10, 30, 00)) 36
|
||||
singleVisit (DateTime (2023, 12, 10, 16, 18, 00)) 32
|
||||
singleVisit (DateTime (2023, 12, 05, 22, 36, 00)) 40
|
||||
singleVisit (DateTime (2023, 12, 03, 17, 59, 00)) 48
|
||||
singleVisit (DateTime (2023, 12, 01, 21, 41, 00)) 29
|
||||
]
|
||||
}
|
||||
|
||||
[ json, expected ] |> List.map TestCaseData
|
||||
|
||||
[<TestCaseSource(nameof sessionsCases)>]
|
||||
let ``Sessions JSON parse`` (json : string, expected : Sessions) =
|
||||
json
|
||||
|> fun o -> JsonNode.Parse (o, Nullable (JsonNodeOptions (PropertyNameCaseInsensitive = true)))
|
||||
|> Sessions.jsonParse
|
||||
|> shouldEqual expected
|
324
PureGym/Api.fs
324
PureGym/Api.fs
@@ -2,308 +2,44 @@ namespace PureGym
|
||||
|
||||
open System
|
||||
open System.Net.Http
|
||||
open System.Text.Json.Serialization
|
||||
open System.Threading
|
||||
open System.Threading.Tasks
|
||||
open RestEase
|
||||
|
||||
/// Describes the opening hours of a given gym.
|
||||
type GymOpeningHours =
|
||||
{
|
||||
/// If this is true, there should be no OpeningHours (but nothing enforces that).
|
||||
IsAlwaysOpen : bool
|
||||
/// This is a pretty unstructured list, which is in general not really parseable: it's human-readable only.
|
||||
OpeningHours : string list
|
||||
}
|
||||
|
||||
/// Human-readable representation
|
||||
override this.ToString () =
|
||||
if this.IsAlwaysOpen then
|
||||
"always open"
|
||||
else
|
||||
this.OpeningHours |> String.concat ", "
|
||||
|
||||
/// How a human can authenticate with a gym when they physically try to enter it
|
||||
type GymAccessOptions =
|
||||
{
|
||||
/// This gym has PIN entry pads
|
||||
PinAccess : bool
|
||||
/// This gym has a QR code scanner. QR codes can be generated with the PureGym app.
|
||||
QrCodeAccess : bool
|
||||
}
|
||||
|
||||
/// Human-readable representation
|
||||
override this.ToString () =
|
||||
$"Pin access: %c{Char.emoji this.PinAccess}; QR code access: %c{Char.emoji this.QrCodeAccess}"
|
||||
|
||||
/// Where a gym is on the Earth
|
||||
type GymLocation =
|
||||
{
|
||||
/// Measured in degrees
|
||||
Longitude : float
|
||||
/// Measured in degrees
|
||||
Latitude : float
|
||||
}
|
||||
|
||||
/// The postal address of a gym
|
||||
type GymAddress =
|
||||
{
|
||||
/// E.g. "Canterbury Court"
|
||||
[<JsonRequired>]
|
||||
AddressLine1 : string
|
||||
/// E.g. "Units 4, 4A, 5 And 5A"
|
||||
AddressLine2 : string
|
||||
/// E.g. "Kennington Park"
|
||||
AddressLine3 : string
|
||||
/// E.g. "LONDON"
|
||||
[<JsonRequired>]
|
||||
Town : string
|
||||
County : string
|
||||
/// E.g. "SW9 6DE"
|
||||
[<JsonRequired>]
|
||||
Postcode : string
|
||||
}
|
||||
|
||||
/// Human-readable statement of the address
|
||||
override this.ToString () =
|
||||
[
|
||||
yield Some this.AddressLine1
|
||||
yield this.AddressLine2 |> Option.ofObj
|
||||
yield this.AddressLine3 |> Option.ofObj
|
||||
match this.County with
|
||||
| null -> yield Some $"%s{this.Town} %s{this.Postcode}"
|
||||
| county ->
|
||||
yield Some this.Town
|
||||
yield Some $"%s{county} %s{this.Postcode}"
|
||||
]
|
||||
|> Seq.choose id
|
||||
|> String.concat "\n"
|
||||
|
||||
/// Metadata about a physical gym
|
||||
type Gym =
|
||||
{
|
||||
// The following fields are returned but are always null
|
||||
// ReasonsToJoin : string
|
||||
// VirtualTourUrl : Uri
|
||||
// PersonalTrainersUrl : Uri
|
||||
// WebViewUrl : Uri
|
||||
// FloorPlanUrl : Uri
|
||||
// StaffMembers : string
|
||||
|
||||
/// The name of this gym, e.g. "London Oval"
|
||||
[<JsonRequired>]
|
||||
Name : string
|
||||
/// This gym's ID in the PureGym system, e.g. 19
|
||||
[<JsonRequired>]
|
||||
Id : int
|
||||
/// I don't know what this status is. Please tell me if you know!
|
||||
[<JsonRequired>]
|
||||
Status : int
|
||||
/// Postal address of this gym
|
||||
[<JsonRequired>]
|
||||
Address : GymAddress
|
||||
/// Phone number of this gym, e.g. "+44 1234 567890"
|
||||
[<JsonRequired>]
|
||||
PhoneNumber : string
|
||||
/// Contact email address for this gym's staff
|
||||
[<JsonRequired>]
|
||||
EmailAddress : string
|
||||
/// When this gym is open
|
||||
[<JsonRequired>]
|
||||
GymOpeningHours : GymOpeningHours
|
||||
/// How a human can physically authenticate when they physically enter this gym
|
||||
[<JsonRequired>]
|
||||
AccessOptions : GymAccessOptions
|
||||
/// Where this gym is physically located
|
||||
[<JsonRequired>]
|
||||
Location : GymLocation
|
||||
/// The IANA time zone this gym observes, e.g. "Europe/London"
|
||||
[<JsonRequired>]
|
||||
TimeZone : string
|
||||
/// This is a date-time in the format yyyy-MM-ddTHH:mm:ss+01 Europe/London
|
||||
ReopenDate : string
|
||||
}
|
||||
|
||||
/// Human-readable representation of the most important information about this gym
|
||||
override this.ToString () =
|
||||
$"""%s{this.Name} (%i{this.Id})
|
||||
{this.Address}
|
||||
%s{this.EmailAddress} %s{this.PhoneNumber}
|
||||
Opening hours: %s{string<GymOpeningHours> this.GymOpeningHours}
|
||||
%s{string<GymAccessOptions> this.AccessOptions}
|
||||
"""
|
||||
|
||||
/// A human member of PureGym
|
||||
type Member =
|
||||
{
|
||||
/// This member's ID. This is a fairly large number.
|
||||
Id : int
|
||||
/// No idea what this is - please tell me if you know!
|
||||
CompoundMemberId : string
|
||||
/// First name, e.g. "Patrick"
|
||||
FirstName : string
|
||||
/// Last name, e.g. "Stevens"
|
||||
LastName : string
|
||||
/// ID of the gym designated as this user's home gym. This is also the "Id" field of the appropriate Gym object.
|
||||
HomeGymId : int
|
||||
/// The name of the gym designated as this user's home gym. This is also the "Name" field of the appropriate
|
||||
/// Gym object.
|
||||
HomeGymName : string
|
||||
/// This user's email address
|
||||
EmailAddress : string
|
||||
/// This user's gym access pin, probably 8 digits
|
||||
GymAccessPin : string
|
||||
/// This user's recorded date of birth
|
||||
DateOfBirth : DateOnly
|
||||
/// This user's phone number, human-readable
|
||||
MobileNumber : string
|
||||
/// This user's registered home postcode
|
||||
Postcode : string
|
||||
/// E.g. "Corporate"
|
||||
MembershipName : string
|
||||
MembershipLevel : int
|
||||
SuspendedReason : int
|
||||
MemberStatus : int
|
||||
}
|
||||
|
||||
/// Statistics for how many people are currently at a gym
|
||||
type GymAttendance =
|
||||
{
|
||||
/// This appears always to be just equal to TotalPeopleInGym, but a string.
|
||||
[<JsonRequired>]
|
||||
Description : string
|
||||
/// How many people are in the gym as of this statistics snapshot
|
||||
[<JsonRequired>]
|
||||
TotalPeopleInGym : int
|
||||
/// How many people are in classes at the gym as of this statistics snapshot
|
||||
[<JsonRequired>]
|
||||
TotalPeopleInClasses : int
|
||||
/// E.g. " or fewer"
|
||||
TotalPeopleSuffix : string
|
||||
[<JsonRequired>]
|
||||
IsApproximate : bool
|
||||
/// When the query was received (I think)
|
||||
AttendanceTime : DateTime
|
||||
/// When the "total people in gym" snapshot was taken that is reported here
|
||||
LastRefreshed : DateTime
|
||||
/// When the "number of people in classes" snapshot was taken that is reported here
|
||||
LastRefreshedPeopleInClasses : DateTime
|
||||
/// Maximum capacity of the gym, or 0 if no listed capacity
|
||||
MaximumCapacity : int
|
||||
}
|
||||
|
||||
/// Human-readable representation
|
||||
override this.ToString () =
|
||||
let totalPeopleSuffix =
|
||||
match this.TotalPeopleSuffix with
|
||||
| null -> ""
|
||||
| suffix -> suffix
|
||||
|
||||
let capacity =
|
||||
if this.MaximumCapacity = 0 then
|
||||
""
|
||||
else
|
||||
$" out of %i{this.MaximumCapacity} maximum"
|
||||
|
||||
let classes =
|
||||
if this.TotalPeopleInClasses = 0 then
|
||||
""
|
||||
else
|
||||
$"\n%i{this.TotalPeopleInClasses} in classes"
|
||||
|
||||
$"""%i{this.TotalPeopleInGym}%s{totalPeopleSuffix} in gym%s{capacity} (is exact: %c{Char.emoji (not this.IsApproximate)})%s{classes}
|
||||
Query made at %s{this.AttendanceTime.ToString "s"}%s{this.AttendanceTime.ToString "zzz"}
|
||||
Snapshot correct as of %s{this.LastRefreshed.ToString "s"}%s{this.LastRefreshed.ToString "zzz"}
|
||||
Classes info correct as of %s{this.LastRefreshedPeopleInClasses.ToString "s"}%s{this.LastRefreshedPeopleInClasses.ToString "zzz"}"""
|
||||
|
||||
/// The visit statistics for a particular human to a particular gym.
|
||||
/// The semantics of this class are basically unknown.
|
||||
type MemberActivityThisMonth =
|
||||
{
|
||||
/// How many minutes, including classes, have been logged so far this month
|
||||
TotalDurationMinutes : int
|
||||
/// How long, in minutes, each visit has been on average this month
|
||||
AverageDurationMinutes : int
|
||||
/// How many visits have been made this month, excluding classes
|
||||
TotalVisits : int
|
||||
/// How many classes have been attended this month
|
||||
TotalClasses : int
|
||||
/// Whether this block of statistics is estimated rather than exact
|
||||
IsEstimated : bool
|
||||
/// When this data was constructed
|
||||
LastRefreshed : DateTime
|
||||
}
|
||||
|
||||
/// Don't use this type. It's public because System.Text.Json can't do private types.
|
||||
type MemberActivityDto =
|
||||
{
|
||||
[<JsonRequired>]
|
||||
TotalDuration : int
|
||||
[<JsonRequired>]
|
||||
AverageDuration : int
|
||||
[<JsonRequired>]
|
||||
TotalVisits : int
|
||||
[<JsonRequired>]
|
||||
TotalClasses : int
|
||||
[<JsonRequired>]
|
||||
IsEstimated : bool
|
||||
[<JsonRequired>]
|
||||
LastRefreshed : DateTime
|
||||
}
|
||||
|
||||
member this.ToMemberActivity () =
|
||||
{
|
||||
TotalDurationMinutes = this.TotalDuration
|
||||
AverageDurationMinutes = this.AverageDuration
|
||||
TotalVisits = this.TotalVisits
|
||||
TotalClasses = this.TotalClasses
|
||||
IsEstimated = this.IsEstimated
|
||||
LastRefreshed = this.LastRefreshed
|
||||
}
|
||||
|
||||
/// The PureGym REST API. You probably want to instantiate one of these with `Api.make`.
|
||||
[<Header("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0")>]
|
||||
type IPureGymApi =
|
||||
/// Get the complete list of all gyms known to PureGym.
|
||||
[<Get "v1/gyms/">]
|
||||
abstract GetGyms : unit -> Task<Gym list>
|
||||
|
||||
/// Get information about the PureGym human whose credentials this client is authenticated with.
|
||||
[<Get "v1/member">]
|
||||
abstract GetMember : unit -> Task<Member>
|
||||
|
||||
/// Get information about how full the given gym currently is. The gym ID can be found from `GetGyms`.
|
||||
[<Get "v1/gyms/{gym_id}/attendance">]
|
||||
abstract GetGymAttendance : [<Path "gym_id">] gymId : int -> Task<GymAttendance>
|
||||
|
||||
/// Get information about a specific gym.
|
||||
[<Get "v1/gyms/{gym_id}">]
|
||||
abstract GetGym : [<Path "gym_id">] gymId : int -> Task<Gym>
|
||||
|
||||
/// Get information about the activities logged against the currently authenticated PureGym human.
|
||||
[<Get "v1/member/activity">]
|
||||
abstract GetMemberActivity : unit -> Task<MemberActivityDto>
|
||||
|
||||
// [<Get "v1/member/activity/history">]
|
||||
// abstract GetMemberActivityAll : unit -> Task<string>
|
||||
|
||||
/// Methods for interacting with the PureGym REST API.
|
||||
[<RequireQualifiedAccess>]
|
||||
module Api =
|
||||
/// Create a REST client, authenticated as the specified user.
|
||||
let make (auth : Auth) : IPureGymApi Task =
|
||||
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 ()))
|
||||
|
||||
async {
|
||||
let client = new HttpClient ()
|
||||
|
||||
return PureGymApi.make (getToken >> _.AccessToken >> sprintf "Bearer %s") client, cache
|
||||
}
|
||||
|> Async.StartAsTask
|
||||
|
||||
/// Create a REST client, authenticated as the specified user. Do not refresh creds.
|
||||
let makeWithoutRefresh (ct : CancellationToken) (auth : Auth) : IPureGymApi Task =
|
||||
async {
|
||||
let! token =
|
||||
match auth with
|
||||
| Auth.Token t -> Task.FromResult<_> t
|
||||
| Auth.User cred -> AuthToken.get cred
|
||||
| Auth.Token t -> async.Return t
|
||||
| Auth.User cred -> Async.AwaitTask (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 RestClient.For<IPureGymApi> client
|
||||
return PureGymApi.make (fun () -> $"Bearer %s{token.AccessToken}") client
|
||||
}
|
||||
|> Async.StartAsTask
|
||||
|
@@ -5,16 +5,22 @@ 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.
|
||||
// So we end up with this gruesome soup of C#.
|
||||
// TODO(net8): make this internal
|
||||
/// An internal type. Don't use it.
|
||||
type AuthResponseRaw [<JsonConstructor>] (access_token : string, expires_in : int, token_type : string, scope : string)
|
||||
=
|
||||
/// Don't use this internal type.
|
||||
member _.access_token = access_token
|
||||
/// Don't use this internal type.
|
||||
member _.expires_in = expires_in
|
||||
/// Don't use this internal type.
|
||||
member _.token_type = token_type
|
||||
/// Don't use this internal type.
|
||||
member _.scope = scope
|
||||
|
||||
/// A token which can be used to authenticate with the PureGym API.
|
||||
@@ -67,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<AuthToken> =
|
||||
task {
|
||||
let get (creds : UsernamePin) (ct : CancellationToken) : Task<AuthToken> =
|
||||
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")
|
||||
@@ -84,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<AuthResponseRaw> (content, options)
|
||||
// let! response = JsonSerializer.DeserializeAsync<AuthResponseRaw> (content, options)
|
||||
let! content = Async.AwaitTask (response.Content.ReadAsStreamAsync ct)
|
||||
|
||||
let! response =
|
||||
Async.AwaitTask (JsonSerializer.DeserializeAsync<AuthResponseRaw>(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)
|
||||
|
85
PureGym/Cache.fs
Normal file
85
PureGym/Cache.fs
Normal file
@@ -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<unit>
|
||||
|
||||
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<CacheMessage<'a>>) : Async<unit> =
|
||||
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<unit> ()
|
||||
|
||||
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
|
43
PureGym/Client.fs
Normal file
43
PureGym/Client.fs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace PureGym
|
||||
|
||||
open System
|
||||
open System.Net.Http
|
||||
open System.Threading
|
||||
open System.Threading.Tasks
|
||||
open RestEase
|
||||
|
||||
/// The PureGym REST API. You probably want to instantiate one of these with `Api.make`.
|
||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||
[<Header("User-Agent", "PureGym/1523 CFNetwork/1312 Darwin/21.0.0")>]
|
||||
[<BaseAddress "https://capi.puregym.com/api/">]
|
||||
type IPureGymApi =
|
||||
[<Header "Authorization">]
|
||||
abstract AuthHeader : string
|
||||
|
||||
/// Get the complete list of all gyms known to PureGym.
|
||||
[<Get "v1/gyms/">]
|
||||
abstract GetGyms : ?ct : CancellationToken -> Task<Gym list>
|
||||
|
||||
/// Get information about the PureGym human whose credentials this client is authenticated with.
|
||||
[<Get "v1/member">]
|
||||
abstract GetMember : ?ct : CancellationToken -> Task<Member>
|
||||
|
||||
/// Get information about how full the given gym currently is. The gym ID can be found from `GetGyms`.
|
||||
[<Get "v1/gyms/{gym_id}/attendance">]
|
||||
abstract GetGymAttendance : [<Path "gym_id">] gymId : int * ?ct : CancellationToken -> Task<GymAttendance>
|
||||
|
||||
/// Get information about a specific gym.
|
||||
[<Get "v1/gyms/{gym_id}">]
|
||||
abstract GetGym : [<Path "gym_id">] gymId : int * ?ct : CancellationToken -> Task<Gym>
|
||||
|
||||
/// Get information about the activities logged against the currently authenticated PureGym human.
|
||||
[<Get "v1/member/activity">]
|
||||
abstract GetMemberActivity : ?ct : CancellationToken -> Task<MemberActivityDto>
|
||||
|
||||
/// Get information about the individual visits to all PureGyms the currently-authenticated PureGym human has made.
|
||||
[<Get "v2/gymSessions/member">]
|
||||
abstract GetSessions :
|
||||
[<Query>] fromDate : DateOnly * [<Query>] toDate : DateOnly * ?ct : CancellationToken -> Task<Sessions>
|
||||
|
||||
// [<Get "v1/member/activity/history">]
|
||||
// abstract GetMemberActivityAll : ?ct: CancellationToken -> Task<string>
|
366
PureGym/Dto.fs
Normal file
366
PureGym/Dto.fs
Normal file
@@ -0,0 +1,366 @@
|
||||
namespace PureGym
|
||||
|
||||
open System
|
||||
open System.Text.Json.Serialization
|
||||
|
||||
/// Describes the opening hours of a given gym.
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type GymOpeningHours =
|
||||
{
|
||||
/// If this is true, there should be no OpeningHours (but nothing enforces that).
|
||||
IsAlwaysOpen : bool
|
||||
/// This is a pretty unstructured list, which is in general not really parseable: it's human-readable only.
|
||||
OpeningHours : string list
|
||||
}
|
||||
|
||||
/// Human-readable representation
|
||||
override this.ToString () =
|
||||
if this.IsAlwaysOpen then
|
||||
"always open"
|
||||
else
|
||||
this.OpeningHours |> String.concat ", "
|
||||
|
||||
/// How a human can authenticate with a gym when they physically try to enter it
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type GymAccessOptions =
|
||||
{
|
||||
/// This gym has PIN entry pads
|
||||
PinAccess : bool
|
||||
/// This gym has a QR code scanner. QR codes can be generated with the PureGym app.
|
||||
QrCodeAccess : bool
|
||||
}
|
||||
|
||||
/// Human-readable representation
|
||||
override this.ToString () =
|
||||
$"Pin access: %c{Char.emoji this.PinAccess}; QR code access: %c{Char.emoji this.QrCodeAccess}"
|
||||
|
||||
/// Where a gym is on the Earth
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type GymLocation =
|
||||
{
|
||||
/// Measured in degrees
|
||||
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
||||
Longitude : float
|
||||
/// Measured in degrees
|
||||
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
||||
Latitude : float
|
||||
}
|
||||
|
||||
/// The postal address of a gym
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type GymAddress =
|
||||
{
|
||||
/// E.g. "Canterbury Court"
|
||||
[<JsonRequired>]
|
||||
AddressLine1 : string
|
||||
/// E.g. "Units 4, 4A, 5 And 5A"
|
||||
AddressLine2 : string option
|
||||
/// E.g. "Kennington Park"
|
||||
AddressLine3 : string option
|
||||
/// E.g. "LONDON"
|
||||
[<JsonRequired>]
|
||||
Town : string
|
||||
/// Never seen this in the wild, sorry
|
||||
County : string option
|
||||
/// E.g. "SW9 6DE"
|
||||
[<JsonRequired>]
|
||||
Postcode : string
|
||||
}
|
||||
|
||||
/// Human-readable statement of the address
|
||||
override this.ToString () =
|
||||
[
|
||||
yield Some this.AddressLine1
|
||||
yield this.AddressLine2
|
||||
yield this.AddressLine3
|
||||
match this.County with
|
||||
| None -> yield Some $"%s{this.Town} %s{this.Postcode}"
|
||||
| Some county ->
|
||||
yield Some this.Town
|
||||
yield Some $"%s{county} %s{this.Postcode}"
|
||||
]
|
||||
|> Seq.choose id
|
||||
|> String.concat "\n"
|
||||
|
||||
/// Metadata about a physical gym
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type Gym =
|
||||
{
|
||||
// The following fields are returned but are always null
|
||||
// ReasonsToJoin : string
|
||||
// VirtualTourUrl : Uri
|
||||
// PersonalTrainersUrl : Uri
|
||||
// WebViewUrl : Uri
|
||||
// FloorPlanUrl : Uri
|
||||
// StaffMembers : string
|
||||
|
||||
/// The name of this gym, e.g. "London Oval"
|
||||
[<JsonRequired>]
|
||||
Name : string
|
||||
/// This gym's ID in the PureGym system, e.g. 19
|
||||
[<JsonRequired>]
|
||||
Id : int
|
||||
/// I don't know what this status is. Please tell me if you know!
|
||||
[<JsonRequired>]
|
||||
Status : int
|
||||
/// Postal address of this gym
|
||||
[<JsonRequired>]
|
||||
Address : GymAddress
|
||||
/// Phone number of this gym, e.g. "+44 1234 567890"
|
||||
[<JsonRequired>]
|
||||
PhoneNumber : string
|
||||
/// Contact email address for this gym's staff
|
||||
[<JsonRequired>]
|
||||
EmailAddress : string
|
||||
/// When this gym is open
|
||||
[<JsonRequired>]
|
||||
GymOpeningHours : GymOpeningHours
|
||||
/// How a human can physically authenticate when they physically enter this gym
|
||||
[<JsonRequired>]
|
||||
AccessOptions : GymAccessOptions
|
||||
/// Where this gym is physically located
|
||||
[<JsonRequired>]
|
||||
Location : GymLocation
|
||||
/// The IANA time zone this gym observes, e.g. "Europe/London"
|
||||
[<JsonRequired>]
|
||||
TimeZone : string
|
||||
/// This is a date-time in the format yyyy-MM-ddTHH:mm:ss+01 Europe/London
|
||||
ReopenDate : string option
|
||||
}
|
||||
|
||||
/// Human-readable representation of the most important information about this gym
|
||||
override this.ToString () =
|
||||
$"""%s{this.Name} (%i{this.Id})
|
||||
{this.Address}
|
||||
%s{this.EmailAddress} %s{this.PhoneNumber}
|
||||
Opening hours: %s{string<GymOpeningHours> this.GymOpeningHours}
|
||||
%s{string<GymAccessOptions> this.AccessOptions}
|
||||
"""
|
||||
|
||||
/// A human member of PureGym
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type Member =
|
||||
{
|
||||
/// This member's ID. This is a fairly large number.
|
||||
Id : int
|
||||
/// No idea what this is - please tell me if you know!
|
||||
CompoundMemberId : string
|
||||
/// First name, e.g. "Patrick"
|
||||
FirstName : string
|
||||
/// Last name, e.g. "Stevens"
|
||||
LastName : string
|
||||
/// ID of the gym designated as this user's home gym. This is also the "Id" field of the appropriate Gym object.
|
||||
HomeGymId : int
|
||||
/// The name of the gym designated as this user's home gym. This is also the "Name" field of the appropriate
|
||||
/// Gym object.
|
||||
HomeGymName : string
|
||||
/// This user's email address
|
||||
EmailAddress : string
|
||||
/// This user's gym access pin, probably 8 digits
|
||||
GymAccessPin : string
|
||||
/// This user's recorded date of birth
|
||||
[<JsonPropertyName "dateofBirth">]
|
||||
DateOfBirth : DateOnly
|
||||
/// This user's phone number, human-readable
|
||||
MobileNumber : string
|
||||
/// This user's registered home postcode
|
||||
[<JsonPropertyName "postCode">]
|
||||
Postcode : string
|
||||
/// E.g. "Corporate"
|
||||
MembershipName : string
|
||||
/// No idea what this is
|
||||
MembershipLevel : int
|
||||
/// No idea what this is
|
||||
SuspendedReason : int
|
||||
/// No idea what this is
|
||||
MemberStatus : int
|
||||
}
|
||||
|
||||
/// Statistics for how many people are currently at a gym
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type GymAttendance =
|
||||
{
|
||||
/// This appears always to be just equal to TotalPeopleInGym, but a string.
|
||||
[<JsonRequired>]
|
||||
Description : string
|
||||
/// How many people are in the gym as of this statistics snapshot
|
||||
[<JsonRequired>]
|
||||
TotalPeopleInGym : int
|
||||
/// How many people are in classes at the gym as of this statistics snapshot
|
||||
TotalPeopleInClasses : int option
|
||||
/// E.g. " or fewer"
|
||||
TotalPeopleSuffix : string option
|
||||
/// Whether the number of people in the gym is approximate. This appears to become true when the number
|
||||
/// of people in the gym is small enough (e.g. in Oval the threshold is 10).
|
||||
[<JsonRequired>]
|
||||
IsApproximate : bool
|
||||
/// When the query was received (I think)
|
||||
AttendanceTime : DateTime
|
||||
/// When the "total people in gym" snapshot was taken that is reported here
|
||||
LastRefreshed : DateTime
|
||||
/// When the "number of people in classes" snapshot was taken that is reported here
|
||||
LastRefreshedPeopleInClasses : DateTime
|
||||
/// Maximum capacity of the gym, or 0 if no listed capacity
|
||||
MaximumCapacity : int
|
||||
}
|
||||
|
||||
/// Human-readable representation
|
||||
override this.ToString () =
|
||||
let totalPeopleSuffix =
|
||||
match this.TotalPeopleSuffix with
|
||||
| None -> ""
|
||||
| Some suffix -> suffix
|
||||
|
||||
let capacity =
|
||||
if this.MaximumCapacity = 0 then
|
||||
""
|
||||
else
|
||||
$" out of %i{this.MaximumCapacity} maximum"
|
||||
|
||||
let classes =
|
||||
match this.TotalPeopleInClasses with
|
||||
| None
|
||||
| Some 0 -> ""
|
||||
| Some totalPeopleInClasses -> $"\n%i{totalPeopleInClasses} in classes"
|
||||
|
||||
$"""%i{this.TotalPeopleInGym}%s{totalPeopleSuffix} in gym%s{capacity} (is exact: %c{Char.emoji (not this.IsApproximate)})%s{classes}
|
||||
Query made at %s{this.AttendanceTime.ToString "s"}%s{this.AttendanceTime.ToString "zzz"}
|
||||
Snapshot correct as of %s{this.LastRefreshed.ToString "s"}%s{this.LastRefreshed.ToString "zzz"}
|
||||
Classes info correct as of %s{this.LastRefreshedPeopleInClasses.ToString "s"}%s{this.LastRefreshedPeopleInClasses.ToString "zzz"}"""
|
||||
|
||||
/// The visit statistics for a particular human to a particular gym.
|
||||
/// The semantics of this class are basically unknown.
|
||||
type MemberActivityThisMonth =
|
||||
{
|
||||
/// How many minutes, including classes, have been logged so far this month
|
||||
TotalDurationMinutes : int
|
||||
/// How long, in minutes, each visit has been on average this month
|
||||
AverageDurationMinutes : int
|
||||
/// How many visits have been made this month, excluding classes
|
||||
TotalVisits : int
|
||||
/// How many classes have been attended this month
|
||||
TotalClasses : int
|
||||
/// Whether this block of statistics is estimated rather than exact
|
||||
IsEstimated : bool
|
||||
/// When this data was constructed
|
||||
LastRefreshed : DateTime
|
||||
}
|
||||
|
||||
/// Don't use this type. It's public because System.Text.Json can't do private types.
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type MemberActivityDto =
|
||||
{
|
||||
[<JsonRequired>]
|
||||
TotalDuration : int
|
||||
[<JsonRequired>]
|
||||
AverageDuration : int
|
||||
[<JsonRequired>]
|
||||
TotalVisits : int
|
||||
[<JsonRequired>]
|
||||
TotalClasses : int
|
||||
[<JsonRequired>]
|
||||
IsEstimated : bool
|
||||
[<JsonRequired>]
|
||||
LastRefreshed : DateTime
|
||||
}
|
||||
|
||||
member this.ToMemberActivity () =
|
||||
{
|
||||
TotalDurationMinutes = this.TotalDuration
|
||||
AverageDurationMinutes = this.AverageDuration
|
||||
TotalVisits = this.TotalVisits
|
||||
TotalClasses = this.TotalClasses
|
||||
IsEstimated = this.IsEstimated
|
||||
LastRefreshed = this.LastRefreshed
|
||||
}
|
||||
|
||||
/// Aggregation of visits made to some particular gym in some defined time period.
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type SessionsAggregate =
|
||||
{
|
||||
/// Number of gym "activities" within some query-defined time period; presumably this is like classes?
|
||||
/// It's always 0 for me.
|
||||
[<JsonPropertyName "Activities">]
|
||||
Activities : int
|
||||
/// Number of visits to the gym within some query-defined time period.
|
||||
[<JsonPropertyName "Visits">]
|
||||
Visits : int
|
||||
/// In minutes: total time spent in gym during the query-defined time period.
|
||||
[<JsonPropertyName "Duration">]
|
||||
Duration : int
|
||||
}
|
||||
|
||||
/// The DTO for gym info returned from the Sessions endpoint.
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type VisitGym =
|
||||
{
|
||||
// Omitting Location, GymAccess, ContactInfo, TimeZone because these were all null for me
|
||||
/// The PureGym ID of this gym, e.g. 19
|
||||
[<JsonPropertyName "Id">]
|
||||
Id : int
|
||||
/// E.g. "London Oval", the canonical name of this gym
|
||||
[<JsonPropertyName "Name">]
|
||||
Name : string
|
||||
/// For some reason this always seems to be "Blocked"
|
||||
[<JsonPropertyName "Status">]
|
||||
Status : string
|
||||
}
|
||||
|
||||
/// Summary of a single visit to a gym.
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type Visit =
|
||||
{
|
||||
// Omitted Name because it always was null for me
|
||||
/// Whether the Duration field is estimated.
|
||||
[<JsonPropertyName "IsDurationEstimated">]
|
||||
IsDurationEstimated : bool
|
||||
/// When the visit began.
|
||||
[<JsonPropertyName "StartTime">]
|
||||
StartTime : DateTime
|
||||
/// In minutes.
|
||||
[<JsonPropertyName "Duration">]
|
||||
Duration : int
|
||||
/// Which gym was visited
|
||||
[<JsonPropertyName "Gym">]
|
||||
Gym : VisitGym
|
||||
}
|
||||
|
||||
/// Human-readable non-round-trip representation.
|
||||
override this.ToString () =
|
||||
let startTime = this.StartTime.ToString "yyyy-MM-dd HH:mm"
|
||||
$"%s{this.Gym.Name}: %s{startTime} (%i{this.Duration} minutes)"
|
||||
|
||||
/// Aggregate statistics for gym visits across a time period.
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type SessionsSummary =
|
||||
{
|
||||
/// Aggregate stats for gym visits within the query-dependent time period.
|
||||
[<JsonPropertyName "Total">]
|
||||
Total : SessionsAggregate
|
||||
/// Aggregate stats for gym visits "this week", whatever that means to PureGym.
|
||||
[<JsonPropertyName "ThisWeek">]
|
||||
ThisWeek : SessionsAggregate
|
||||
}
|
||||
|
||||
/// Human-readable non-round-trippable representation
|
||||
override this.ToString () =
|
||||
$"%i{this.Total.Visits} visits, totalling %i{this.Total.Duration} minutes"
|
||||
|
||||
/// Information about a particular user's visits to a particular gym.
|
||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||
type Sessions =
|
||||
{
|
||||
/// Aggregated summary over some time period.
|
||||
[<JsonPropertyName "Summary">]
|
||||
Summary : SessionsSummary
|
||||
/// List of all individual visits made within some time period.
|
||||
[<JsonPropertyName "Visits">]
|
||||
Visits : Visit list
|
||||
}
|
||||
|
||||
/// Human-readable non-round-trip representation.
|
||||
override this.ToString () =
|
||||
let summary = string<SessionsSummary> this.Summary
|
||||
let visits = this.Visits |> Seq.map string<Visit> |> String.concat "\n"
|
||||
|
||||
$"%s{summary}\n%s{visits}"
|
235
PureGym/GeneratedClient.fs
Normal file
235
PureGym/GeneratedClient.fs
Normal file
@@ -0,0 +1,235 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// This code was generated by myriad.
|
||||
// Changes to this file will be lost when the code is regenerated.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
namespace PureGym
|
||||
|
||||
open System
|
||||
open System.Net.Http
|
||||
open System.Threading
|
||||
open System.Threading.Tasks
|
||||
open RestEase
|
||||
|
||||
/// Module for constructing a REST client.
|
||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
|
||||
module PureGymApi =
|
||||
/// 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 _.AuthHeader : string = authHeader ()
|
||||
|
||||
member this.GetGyms (ct : CancellationToken option) =
|
||||
async {
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://capi.puregym.com/api/"
|
||||
| v -> v),
|
||||
System.Uri ("v1/gyms/", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
Method = System.Net.Http.HttpMethod.Get,
|
||||
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! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||
|
||||
let! jsonNode =
|
||||
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||
|> Async.AwaitTask
|
||||
|
||||
return jsonNode.AsArray () |> Seq.map (fun elt -> Gym.jsonParse elt) |> List.ofSeq
|
||||
}
|
||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||
|
||||
member this.GetMember (ct : CancellationToken option) =
|
||||
async {
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://capi.puregym.com/api/"
|
||||
| v -> v),
|
||||
System.Uri ("v1/member", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
Method = System.Net.Http.HttpMethod.Get,
|
||||
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! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||
|
||||
let! jsonNode =
|
||||
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||
|> Async.AwaitTask
|
||||
|
||||
return Member.jsonParse jsonNode
|
||||
}
|
||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||
|
||||
member this.GetGymAttendance (gymId : int, ct : CancellationToken option) =
|
||||
async {
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://capi.puregym.com/api/"
|
||||
| v -> v),
|
||||
System.Uri (
|
||||
"v1/gyms/{gym_id}/attendance"
|
||||
.Replace ("{gym_id}", gymId.ToString () |> System.Uri.EscapeDataString),
|
||||
System.UriKind.Relative
|
||||
)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
Method = System.Net.Http.HttpMethod.Get,
|
||||
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! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||
|
||||
let! jsonNode =
|
||||
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||
|> Async.AwaitTask
|
||||
|
||||
return GymAttendance.jsonParse jsonNode
|
||||
}
|
||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||
|
||||
member this.GetGym (gymId : int, ct : CancellationToken option) =
|
||||
async {
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://capi.puregym.com/api/"
|
||||
| v -> v),
|
||||
System.Uri (
|
||||
"v1/gyms/{gym_id}"
|
||||
.Replace ("{gym_id}", gymId.ToString () |> System.Uri.EscapeDataString),
|
||||
System.UriKind.Relative
|
||||
)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
Method = System.Net.Http.HttpMethod.Get,
|
||||
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! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||
|
||||
let! jsonNode =
|
||||
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||
|> Async.AwaitTask
|
||||
|
||||
return Gym.jsonParse jsonNode
|
||||
}
|
||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||
|
||||
member this.GetMemberActivity (ct : CancellationToken option) =
|
||||
async {
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://capi.puregym.com/api/"
|
||||
| v -> v),
|
||||
System.Uri ("v1/member/activity", System.UriKind.Relative)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
Method = System.Net.Http.HttpMethod.Get,
|
||||
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! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||
|
||||
let! jsonNode =
|
||||
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||
|> Async.AwaitTask
|
||||
|
||||
return MemberActivityDto.jsonParse jsonNode
|
||||
}
|
||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||
|
||||
member this.GetSessions (fromDate : DateOnly, toDate : DateOnly, ct : CancellationToken option) =
|
||||
async {
|
||||
let! ct = Async.CancellationToken
|
||||
|
||||
let uri =
|
||||
System.Uri (
|
||||
(match client.BaseAddress with
|
||||
| null -> System.Uri "https://capi.puregym.com/api/"
|
||||
| v -> v),
|
||||
System.Uri (
|
||||
("v2/gymSessions/member"
|
||||
+ (if "v2/gymSessions/member".IndexOf (char 63) >= 0 then
|
||||
"&"
|
||||
else
|
||||
"?")
|
||||
+ "fromDate="
|
||||
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Uri.EscapeDataString)
|
||||
+ "&toDate="
|
||||
+ ((toDate.ToString "yyyy-MM-dd") |> System.Uri.EscapeDataString)),
|
||||
System.UriKind.Relative
|
||||
)
|
||||
)
|
||||
|
||||
let httpMessage =
|
||||
new System.Net.Http.HttpRequestMessage (
|
||||
Method = System.Net.Http.HttpMethod.Get,
|
||||
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! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||
|
||||
let! jsonNode =
|
||||
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||
|> Async.AwaitTask
|
||||
|
||||
return Sessions.jsonParse jsonNode
|
||||
}
|
||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||
}
|
1003
PureGym/GeneratedDto.fs
Normal file
1003
PureGym/GeneratedDto.fs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,14 +21,17 @@ module GymSelector =
|
||||
let canonicalId (client : IPureGymApi) (gym : GymSelector) : int Task =
|
||||
match gym with
|
||||
| GymSelector.Home ->
|
||||
task {
|
||||
let! self = client.GetMember ()
|
||||
async {
|
||||
let! ct = Async.CancellationToken
|
||||
let! self = Async.AwaitTask (client.GetMember ct)
|
||||
return self.HomeGymId
|
||||
}
|
||||
|> Async.StartAsTask
|
||||
| GymSelector.Id i -> Task.FromResult<_> i
|
||||
| GymSelector.Name name ->
|
||||
task {
|
||||
let! allGyms = client.GetGyms ()
|
||||
async {
|
||||
let! ct = Async.CancellationToken
|
||||
let! allGyms = Async.AwaitTask (client.GetGyms ct)
|
||||
|
||||
if allGyms.IsEmpty then
|
||||
return failwith "PureGym API returned no gyms!"
|
||||
@@ -46,3 +49,4 @@ module GymSelector =
|
||||
|
||||
return bestGym.Id
|
||||
}
|
||||
|> Async.StartAsTask
|
||||
|
@@ -1,24 +1,45 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<WarningsNotAsErrors>NU1901;NU1902;NU1903;NU1904</WarningsNotAsErrors>
|
||||
<WarnOn>FS3559</WarnOn>
|
||||
|
||||
<WoofWareMyriadPluginVersion>4.0.9</WoofWareMyriadPluginVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="String.fs" />
|
||||
<Compile Include="Auth.fs" />
|
||||
<Compile Include="Dto.fs" />
|
||||
<Compile Include="GeneratedDto.fs">
|
||||
<MyriadFile>Dto.fs</MyriadFile>
|
||||
</Compile>
|
||||
<Compile Include="Client.fs" />
|
||||
<Compile Include="GeneratedClient.fs">
|
||||
<MyriadFile>Client.fs</MyriadFile>
|
||||
</Compile>
|
||||
<Compile Include="Cache.fs" />
|
||||
<Compile Include="Api.fs" />
|
||||
<Compile Include="GymSelector.fs" />
|
||||
<EmbeddedResource Include="SurfaceBaseline.txt" />
|
||||
<EmbeddedResource Include="version.json" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<MyriadSdkGenerator Include="$(NuGetPackageRoot)/woofware.myriad.plugins/$(WoofWareMyriadPluginVersion)/lib/net6.0/WoofWare.Myriad.Plugins.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RestEase" Version="1.6.4" />
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.0" />
|
||||
<PackageReference Include="System.Text.Json" Version="7.0.3" />
|
||||
<PackageReference Include="Fastenshtein" Version="1.0.0.8" />
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.1" />
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.0" />
|
||||
<PackageReference Include="Fastenshtein" Version="1.0.10" />
|
||||
<PackageReference Include="Myriad.Sdk" Version="0.8.3" PrivateAssets="all" />
|
||||
<PackageReference Include="WoofWare.Myriad.Plugins" Version="$(WoofWareMyriadPluginVersion)" PrivateAssets="all" />
|
||||
<PackageReference Include="WoofWare.Myriad.Plugins.Attributes" Version="3.6.6" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@@ -1,5 +1,6 @@
|
||||
PureGym.Api inherit obj
|
||||
PureGym.Api.make [static method]: PureGym.Auth -> PureGym.IPureGymApi System.Threading.Tasks.Task
|
||||
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
|
||||
@@ -10,6 +11,7 @@ PureGym.Auth+Token.Item [property]: [read-only] PureGym.AuthToken
|
||||
PureGym.Auth+User inherit PureGym.Auth
|
||||
PureGym.Auth+User.get_Item [method]: unit -> PureGym.UsernamePin
|
||||
PureGym.Auth+User.Item [property]: [read-only] PureGym.UsernamePin
|
||||
PureGym.Auth.Equals [method]: (PureGym.Auth, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.Auth.get_IsToken [method]: unit -> bool
|
||||
PureGym.Auth.get_IsUser [method]: unit -> bool
|
||||
PureGym.Auth.get_Tag [method]: unit -> int
|
||||
@@ -31,17 +33,19 @@ PureGym.AuthResponseRaw.token_type [property]: [read-only] string
|
||||
PureGym.AuthToken inherit obj, implements PureGym.AuthToken System.IEquatable, System.Collections.IStructuralEquatable, PureGym.AuthToken System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.AuthToken..ctor [constructor]: (string, System.DateTime option)
|
||||
PureGym.AuthToken.AccessToken [property]: [read-only] string
|
||||
PureGym.AuthToken.Equals [method]: (PureGym.AuthToken, System.Collections.IEqualityComparer) -> bool
|
||||
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.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..ctor [constructor]: (string, int, int, PureGym.GymAddress, string, string, PureGym.GymOpeningHours, PureGym.GymAccessOptions, PureGym.GymLocation, string, string option)
|
||||
PureGym.Gym.AccessOptions [property]: [read-only] PureGym.GymAccessOptions
|
||||
PureGym.Gym.Address [property]: [read-only] PureGym.GymAddress
|
||||
PureGym.Gym.EmailAddress [property]: [read-only] string
|
||||
PureGym.Gym.Equals [method]: (PureGym.Gym, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.Gym.get_AccessOptions [method]: unit -> PureGym.GymAccessOptions
|
||||
PureGym.Gym.get_Address [method]: unit -> PureGym.GymAddress
|
||||
PureGym.Gym.get_EmailAddress [method]: unit -> string
|
||||
@@ -50,7 +54,7 @@ PureGym.Gym.get_Id [method]: unit -> int
|
||||
PureGym.Gym.get_Location [method]: unit -> PureGym.GymLocation
|
||||
PureGym.Gym.get_Name [method]: unit -> string
|
||||
PureGym.Gym.get_PhoneNumber [method]: unit -> string
|
||||
PureGym.Gym.get_ReopenDate [method]: unit -> string
|
||||
PureGym.Gym.get_ReopenDate [method]: unit -> string option
|
||||
PureGym.Gym.get_Status [method]: unit -> int
|
||||
PureGym.Gym.get_TimeZone [method]: unit -> string
|
||||
PureGym.Gym.GymOpeningHours [property]: [read-only] PureGym.GymOpeningHours
|
||||
@@ -58,61 +62,78 @@ PureGym.Gym.Id [property]: [read-only] int
|
||||
PureGym.Gym.Location [property]: [read-only] PureGym.GymLocation
|
||||
PureGym.Gym.Name [property]: [read-only] string
|
||||
PureGym.Gym.PhoneNumber [property]: [read-only] string
|
||||
PureGym.Gym.ReopenDate [property]: [read-only] string
|
||||
PureGym.Gym.ReopenDate [property]: [read-only] string option
|
||||
PureGym.Gym.Status [property]: [read-only] int
|
||||
PureGym.Gym.TimeZone [property]: [read-only] string
|
||||
PureGym.GymAccessOptions inherit obj, implements PureGym.GymAccessOptions System.IEquatable, System.Collections.IStructuralEquatable, PureGym.GymAccessOptions System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.GymAccessOptions..ctor [constructor]: (bool, bool)
|
||||
PureGym.GymAccessOptions.Equals [method]: (PureGym.GymAccessOptions, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.GymAccessOptions.get_PinAccess [method]: unit -> bool
|
||||
PureGym.GymAccessOptions.get_QrCodeAccess [method]: unit -> bool
|
||||
PureGym.GymAccessOptions.PinAccess [property]: [read-only] bool
|
||||
PureGym.GymAccessOptions.QrCodeAccess [property]: [read-only] bool
|
||||
PureGym.GymAccessOptionsModule inherit obj
|
||||
PureGym.GymAccessOptionsModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.GymAccessOptions
|
||||
PureGym.GymAddress inherit obj, implements PureGym.GymAddress System.IEquatable, System.Collections.IStructuralEquatable, PureGym.GymAddress System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.GymAddress..ctor [constructor]: (string, string, string, string, string, string)
|
||||
PureGym.GymAddress..ctor [constructor]: (string, string option, string option, string, string option, string)
|
||||
PureGym.GymAddress.AddressLine1 [property]: [read-only] string
|
||||
PureGym.GymAddress.AddressLine2 [property]: [read-only] string
|
||||
PureGym.GymAddress.AddressLine3 [property]: [read-only] string
|
||||
PureGym.GymAddress.County [property]: [read-only] string
|
||||
PureGym.GymAddress.AddressLine2 [property]: [read-only] string option
|
||||
PureGym.GymAddress.AddressLine3 [property]: [read-only] string option
|
||||
PureGym.GymAddress.County [property]: [read-only] string option
|
||||
PureGym.GymAddress.Equals [method]: (PureGym.GymAddress, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.GymAddress.get_AddressLine1 [method]: unit -> string
|
||||
PureGym.GymAddress.get_AddressLine2 [method]: unit -> string
|
||||
PureGym.GymAddress.get_AddressLine3 [method]: unit -> string
|
||||
PureGym.GymAddress.get_County [method]: unit -> string
|
||||
PureGym.GymAddress.get_AddressLine2 [method]: unit -> string option
|
||||
PureGym.GymAddress.get_AddressLine3 [method]: unit -> string option
|
||||
PureGym.GymAddress.get_County [method]: unit -> string option
|
||||
PureGym.GymAddress.get_Postcode [method]: unit -> string
|
||||
PureGym.GymAddress.get_Town [method]: unit -> string
|
||||
PureGym.GymAddress.Postcode [property]: [read-only] string
|
||||
PureGym.GymAddress.Town [property]: [read-only] string
|
||||
PureGym.GymAddressModule inherit obj
|
||||
PureGym.GymAddressModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.GymAddress
|
||||
PureGym.GymAttendance inherit obj, implements PureGym.GymAttendance System.IEquatable, System.Collections.IStructuralEquatable, PureGym.GymAttendance System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.GymAttendance..ctor [constructor]: (string, int, int, string, bool, System.DateTime, System.DateTime, System.DateTime, int)
|
||||
PureGym.GymAttendance..ctor [constructor]: (string, int, int option, string option, bool, System.DateTime, System.DateTime, System.DateTime, int)
|
||||
PureGym.GymAttendance.AttendanceTime [property]: [read-only] System.DateTime
|
||||
PureGym.GymAttendance.Description [property]: [read-only] string
|
||||
PureGym.GymAttendance.Equals [method]: (PureGym.GymAttendance, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.GymAttendance.get_AttendanceTime [method]: unit -> System.DateTime
|
||||
PureGym.GymAttendance.get_Description [method]: unit -> string
|
||||
PureGym.GymAttendance.get_IsApproximate [method]: unit -> bool
|
||||
PureGym.GymAttendance.get_LastRefreshed [method]: unit -> System.DateTime
|
||||
PureGym.GymAttendance.get_LastRefreshedPeopleInClasses [method]: unit -> System.DateTime
|
||||
PureGym.GymAttendance.get_MaximumCapacity [method]: unit -> int
|
||||
PureGym.GymAttendance.get_TotalPeopleInClasses [method]: unit -> int
|
||||
PureGym.GymAttendance.get_TotalPeopleInClasses [method]: unit -> int option
|
||||
PureGym.GymAttendance.get_TotalPeopleInGym [method]: unit -> int
|
||||
PureGym.GymAttendance.get_TotalPeopleSuffix [method]: unit -> string
|
||||
PureGym.GymAttendance.get_TotalPeopleSuffix [method]: unit -> string option
|
||||
PureGym.GymAttendance.IsApproximate [property]: [read-only] bool
|
||||
PureGym.GymAttendance.LastRefreshed [property]: [read-only] System.DateTime
|
||||
PureGym.GymAttendance.LastRefreshedPeopleInClasses [property]: [read-only] System.DateTime
|
||||
PureGym.GymAttendance.MaximumCapacity [property]: [read-only] int
|
||||
PureGym.GymAttendance.TotalPeopleInClasses [property]: [read-only] int
|
||||
PureGym.GymAttendance.TotalPeopleInClasses [property]: [read-only] int option
|
||||
PureGym.GymAttendance.TotalPeopleInGym [property]: [read-only] int
|
||||
PureGym.GymAttendance.TotalPeopleSuffix [property]: [read-only] string
|
||||
PureGym.GymAttendance.TotalPeopleSuffix [property]: [read-only] string option
|
||||
PureGym.GymAttendanceModule inherit obj
|
||||
PureGym.GymAttendanceModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.GymAttendance
|
||||
PureGym.GymLocation inherit obj, implements PureGym.GymLocation System.IEquatable, System.Collections.IStructuralEquatable, PureGym.GymLocation System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.GymLocation..ctor [constructor]: (float, float)
|
||||
PureGym.GymLocation.Equals [method]: (PureGym.GymLocation, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.GymLocation.get_Latitude [method]: unit -> float
|
||||
PureGym.GymLocation.get_Longitude [method]: unit -> float
|
||||
PureGym.GymLocation.Latitude [property]: [read-only] float
|
||||
PureGym.GymLocation.Longitude [property]: [read-only] float
|
||||
PureGym.GymLocationModule inherit obj
|
||||
PureGym.GymLocationModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.GymLocation
|
||||
PureGym.GymModule inherit obj
|
||||
PureGym.GymModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.Gym
|
||||
PureGym.GymOpeningHours inherit obj, implements PureGym.GymOpeningHours System.IEquatable, System.Collections.IStructuralEquatable, PureGym.GymOpeningHours System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.GymOpeningHours..ctor [constructor]: (bool, string list)
|
||||
PureGym.GymOpeningHours.Equals [method]: (PureGym.GymOpeningHours, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.GymOpeningHours.get_IsAlwaysOpen [method]: unit -> bool
|
||||
PureGym.GymOpeningHours.get_OpeningHours [method]: unit -> string list
|
||||
PureGym.GymOpeningHours.IsAlwaysOpen [property]: [read-only] bool
|
||||
PureGym.GymOpeningHours.OpeningHours [property]: [read-only] string list
|
||||
PureGym.GymOpeningHoursModule inherit obj
|
||||
PureGym.GymOpeningHoursModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.GymOpeningHours
|
||||
PureGym.GymSelector inherit obj, implements PureGym.GymSelector System.IEquatable, System.Collections.IStructuralEquatable, PureGym.GymSelector System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 3 cases
|
||||
PureGym.GymSelector+Id inherit PureGym.GymSelector
|
||||
PureGym.GymSelector+Id.get_Item [method]: unit -> int
|
||||
@@ -124,6 +145,7 @@ PureGym.GymSelector+Tags inherit obj
|
||||
PureGym.GymSelector+Tags.Home [static field]: int = 2
|
||||
PureGym.GymSelector+Tags.Id [static field]: int = 0
|
||||
PureGym.GymSelector+Tags.Name [static field]: int = 1
|
||||
PureGym.GymSelector.Equals [method]: (PureGym.GymSelector, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.GymSelector.get_Home [static method]: unit -> PureGym.GymSelector
|
||||
PureGym.GymSelector.get_IsHome [method]: unit -> bool
|
||||
PureGym.GymSelector.get_IsId [method]: unit -> bool
|
||||
@@ -138,17 +160,21 @@ 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 5 member(s)
|
||||
PureGym.IPureGymApi.GetGym [method]: int -> PureGym.Gym System.Threading.Tasks.Task
|
||||
PureGym.IPureGymApi.GetGymAttendance [method]: int -> PureGym.GymAttendance System.Threading.Tasks.Task
|
||||
PureGym.IPureGymApi.GetGyms [method]: unit -> PureGym.Gym list System.Threading.Tasks.Task
|
||||
PureGym.IPureGymApi.GetMember [method]: unit -> PureGym.Member System.Threading.Tasks.Task
|
||||
PureGym.IPureGymApi.GetMemberActivity [method]: unit -> PureGym.MemberActivityDto System.Threading.Tasks.Task
|
||||
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
|
||||
PureGym.IPureGymApi.GetMember [method]: System.Threading.CancellationToken option -> PureGym.Member System.Threading.Tasks.Task
|
||||
PureGym.IPureGymApi.GetMemberActivity [method]: System.Threading.CancellationToken option -> PureGym.MemberActivityDto System.Threading.Tasks.Task
|
||||
PureGym.IPureGymApi.GetSessions [method]: (System.DateOnly, System.DateOnly, System.Threading.CancellationToken option) -> PureGym.Sessions System.Threading.Tasks.Task
|
||||
PureGym.Member inherit obj, implements PureGym.Member System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Member System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.Member..ctor [constructor]: (int, string, string, string, int, string, string, string, System.DateOnly, string, string, string, int, int, int)
|
||||
PureGym.Member.CompoundMemberId [property]: [read-only] string
|
||||
PureGym.Member.DateOfBirth [property]: [read-only] System.DateOnly
|
||||
PureGym.Member.EmailAddress [property]: [read-only] string
|
||||
PureGym.Member.Equals [method]: (PureGym.Member, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.Member.FirstName [property]: [read-only] string
|
||||
PureGym.Member.get_CompoundMemberId [method]: unit -> string
|
||||
PureGym.Member.get_DateOfBirth [method]: unit -> System.DateOnly
|
||||
@@ -179,6 +205,7 @@ PureGym.Member.SuspendedReason [property]: [read-only] int
|
||||
PureGym.MemberActivityDto inherit obj, implements PureGym.MemberActivityDto System.IEquatable, System.Collections.IStructuralEquatable, PureGym.MemberActivityDto System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.MemberActivityDto..ctor [constructor]: (int, int, int, int, bool, System.DateTime)
|
||||
PureGym.MemberActivityDto.AverageDuration [property]: [read-only] int
|
||||
PureGym.MemberActivityDto.Equals [method]: (PureGym.MemberActivityDto, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.MemberActivityDto.get_AverageDuration [method]: unit -> int
|
||||
PureGym.MemberActivityDto.get_IsEstimated [method]: unit -> bool
|
||||
PureGym.MemberActivityDto.get_LastRefreshed [method]: unit -> System.DateTime
|
||||
@@ -191,9 +218,12 @@ PureGym.MemberActivityDto.ToMemberActivity [method]: unit -> PureGym.MemberActiv
|
||||
PureGym.MemberActivityDto.TotalClasses [property]: [read-only] int
|
||||
PureGym.MemberActivityDto.TotalDuration [property]: [read-only] int
|
||||
PureGym.MemberActivityDto.TotalVisits [property]: [read-only] int
|
||||
PureGym.MemberActivityDtoModule inherit obj
|
||||
PureGym.MemberActivityDtoModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.MemberActivityDto
|
||||
PureGym.MemberActivityThisMonth inherit obj, implements PureGym.MemberActivityThisMonth System.IEquatable, System.Collections.IStructuralEquatable, PureGym.MemberActivityThisMonth System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.MemberActivityThisMonth..ctor [constructor]: (int, int, int, int, bool, System.DateTime)
|
||||
PureGym.MemberActivityThisMonth.AverageDurationMinutes [property]: [read-only] int
|
||||
PureGym.MemberActivityThisMonth.Equals [method]: (PureGym.MemberActivityThisMonth, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.MemberActivityThisMonth.get_AverageDurationMinutes [method]: unit -> int
|
||||
PureGym.MemberActivityThisMonth.get_IsEstimated [method]: unit -> bool
|
||||
PureGym.MemberActivityThisMonth.get_LastRefreshed [method]: unit -> System.DateTime
|
||||
@@ -205,9 +235,67 @@ PureGym.MemberActivityThisMonth.LastRefreshed [property]: [read-only] System.Dat
|
||||
PureGym.MemberActivityThisMonth.TotalClasses [property]: [read-only] int
|
||||
PureGym.MemberActivityThisMonth.TotalDurationMinutes [property]: [read-only] int
|
||||
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]: (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.Equals [method]: (PureGym.Sessions, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.Sessions.get_Summary [method]: unit -> PureGym.SessionsSummary
|
||||
PureGym.Sessions.get_Visits [method]: unit -> PureGym.Visit list
|
||||
PureGym.Sessions.Summary [property]: [read-only] PureGym.SessionsSummary
|
||||
PureGym.Sessions.Visits [property]: [read-only] PureGym.Visit list
|
||||
PureGym.SessionsAggregate inherit obj, implements PureGym.SessionsAggregate System.IEquatable, System.Collections.IStructuralEquatable, PureGym.SessionsAggregate System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.SessionsAggregate..ctor [constructor]: (int, int, int)
|
||||
PureGym.SessionsAggregate.Activities [property]: [read-only] int
|
||||
PureGym.SessionsAggregate.Duration [property]: [read-only] int
|
||||
PureGym.SessionsAggregate.Equals [method]: (PureGym.SessionsAggregate, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.SessionsAggregate.get_Activities [method]: unit -> int
|
||||
PureGym.SessionsAggregate.get_Duration [method]: unit -> int
|
||||
PureGym.SessionsAggregate.get_Visits [method]: unit -> int
|
||||
PureGym.SessionsAggregate.Visits [property]: [read-only] int
|
||||
PureGym.SessionsAggregateModule inherit obj
|
||||
PureGym.SessionsAggregateModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.SessionsAggregate
|
||||
PureGym.SessionsModule inherit obj
|
||||
PureGym.SessionsModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.Sessions
|
||||
PureGym.SessionsSummary inherit obj, implements PureGym.SessionsSummary System.IEquatable, System.Collections.IStructuralEquatable, PureGym.SessionsSummary System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.SessionsSummary..ctor [constructor]: (PureGym.SessionsAggregate, PureGym.SessionsAggregate)
|
||||
PureGym.SessionsSummary.Equals [method]: (PureGym.SessionsSummary, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.SessionsSummary.get_ThisWeek [method]: unit -> PureGym.SessionsAggregate
|
||||
PureGym.SessionsSummary.get_Total [method]: unit -> PureGym.SessionsAggregate
|
||||
PureGym.SessionsSummary.ThisWeek [property]: [read-only] PureGym.SessionsAggregate
|
||||
PureGym.SessionsSummary.Total [property]: [read-only] PureGym.SessionsAggregate
|
||||
PureGym.SessionsSummaryModule inherit obj
|
||||
PureGym.SessionsSummaryModule.jsonParse [static method]: System.Text.Json.Nodes.JsonNode -> PureGym.SessionsSummary
|
||||
PureGym.UsernamePin inherit obj, implements PureGym.UsernamePin System.IEquatable, System.Collections.IStructuralEquatable, PureGym.UsernamePin System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.UsernamePin..ctor [constructor]: (string, string)
|
||||
PureGym.UsernamePin.Equals [method]: (PureGym.UsernamePin, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.UsernamePin.get_Pin [method]: unit -> string
|
||||
PureGym.UsernamePin.get_Username [method]: unit -> string
|
||||
PureGym.UsernamePin.Pin [property]: [read-only] string
|
||||
PureGym.UsernamePin.Username [property]: [read-only] string
|
||||
PureGym.Visit inherit obj, implements PureGym.Visit System.IEquatable, System.Collections.IStructuralEquatable, PureGym.Visit System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.Visit..ctor [constructor]: (bool, System.DateTime, int, PureGym.VisitGym)
|
||||
PureGym.Visit.Duration [property]: [read-only] int
|
||||
PureGym.Visit.Equals [method]: (PureGym.Visit, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.Visit.get_Duration [method]: unit -> int
|
||||
PureGym.Visit.get_Gym [method]: unit -> PureGym.VisitGym
|
||||
PureGym.Visit.get_IsDurationEstimated [method]: unit -> bool
|
||||
PureGym.Visit.get_StartTime [method]: unit -> System.DateTime
|
||||
PureGym.Visit.Gym [property]: [read-only] PureGym.VisitGym
|
||||
PureGym.Visit.IsDurationEstimated [property]: [read-only] bool
|
||||
PureGym.Visit.StartTime [property]: [read-only] System.DateTime
|
||||
PureGym.VisitGym inherit obj, implements PureGym.VisitGym System.IEquatable, System.Collections.IStructuralEquatable, PureGym.VisitGym System.IComparable, System.IComparable, System.Collections.IStructuralComparable
|
||||
PureGym.VisitGym..ctor [constructor]: (int, string, string)
|
||||
PureGym.VisitGym.Equals [method]: (PureGym.VisitGym, System.Collections.IEqualityComparer) -> bool
|
||||
PureGym.VisitGym.get_Id [method]: unit -> int
|
||||
PureGym.VisitGym.get_Name [method]: unit -> string
|
||||
PureGym.VisitGym.get_Status [method]: unit -> string
|
||||
PureGym.VisitGym.Id [property]: [read-only] int
|
||||
PureGym.VisitGym.Name [property]: [read-only] string
|
||||
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
|
0
PureGym/myriad.toml
Normal file
0
PureGym/myriad.toml
Normal file
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"version": "6.0",
|
||||
"publicReleaseRefSpec": [
|
||||
"^refs/heads/main$"
|
||||
],
|
||||
|
13
flake.lock
generated
13
flake.lock
generated
@@ -5,11 +5,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694529238,
|
||||
"narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -20,15 +20,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1696981262,
|
||||
"narHash": "sha256-YaCOjdqhbjBeyMjxlgFWt4XD/b9pGKWURgS3uEwNLtc=",
|
||||
"lastModified": 1757068644,
|
||||
"narHash": "sha256-NOrUtIhTkIIumj1E/Rsv1J37Yi3xGStISEo8tZm3KW4=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a2b87a4f66f309d2f4b789fd0457f5fc5db0a9a6",
|
||||
"rev": "8eb28adfa3dc4de28e792e3bf49fcf9007ca8ac9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
60
flake.nix
60
flake.nix
@@ -1,12 +1,12 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs";
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
flake-utils = {
|
||||
url = "github:numtide/flake-utils";
|
||||
};
|
||||
};
|
||||
|
||||
outputs = inputs @ {
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
@@ -14,13 +14,14 @@
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
deps = builtins.fromJSON (builtins.readFile ./nix/deps.json);
|
||||
projectFile = "./PureGym.App/PureGym.App.fsproj";
|
||||
testProjectFile = "./PureGym.Test/PureGym.Test.fsproj";
|
||||
pname = "puregym";
|
||||
dotnet-sdk = pkgs.dotnet-sdk_7;
|
||||
dotnet-runtime = pkgs.dotnetCorePackages.runtime_7_0;
|
||||
dotnet-sdk = pkgs.dotnetCorePackages.sdk_9_0;
|
||||
dotnet-runtime = pkgs.dotnetCorePackages.runtime_9_0;
|
||||
version = "0.1";
|
||||
dotnetTool = toolName: toolVersion: sha256:
|
||||
dotnetTool = dllOverride: toolName: toolVersion: hash:
|
||||
pkgs.stdenvNoCC.mkDerivation rec {
|
||||
name = toolName;
|
||||
version = toolVersion;
|
||||
@@ -28,49 +29,32 @@
|
||||
src = pkgs.fetchNuGet {
|
||||
pname = name;
|
||||
version = version;
|
||||
sha256 = sha256;
|
||||
installPhase = ''mkdir -p $out/bin && cp -r tools/net6.0/any/* $out/bin'';
|
||||
hash = hash;
|
||||
installPhase = ''mkdir -p $out/bin && cp -r tools/*/any/* $out/bin'';
|
||||
};
|
||||
installPhase = ''
|
||||
installPhase = let
|
||||
dll =
|
||||
if isNull dllOverride
|
||||
then name
|
||||
else dllOverride;
|
||||
in ''
|
||||
runHook preInstall
|
||||
mkdir -p "$out/lib"
|
||||
cp -r ./bin/* "$out/lib"
|
||||
makeWrapper "${dotnet-runtime}/bin/dotnet" "$out/bin/${name}" --add-flags "$out/lib/${name}.dll"
|
||||
makeWrapper "${dotnet-runtime}/bin/dotnet" "$out/bin/${name}" --set DOTNET_HOST_PATH "${dotnet-sdk}/bin/dotnet" --add-flags "$out/lib/${dll}.dll"
|
||||
runHook postInstall
|
||||
'';
|
||||
};
|
||||
fantomas = dotnetTool "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version "sha256-83RodORaC3rkYfbFMHsYLEtl0+8+akZXcKoSJdgwuUo=";
|
||||
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") deps)).hash;
|
||||
in {
|
||||
packages = {
|
||||
fantomas = fantomas;
|
||||
fetchDeps = let
|
||||
flags = [];
|
||||
runtimeIds = ["win-x64"] ++ map (system: pkgs.dotnetCorePackages.systemToDotnetRid system) dotnet-sdk.meta.platforms;
|
||||
in
|
||||
pkgs.writeShellScriptBin "fetch-${pname}-deps" (builtins.readFile (pkgs.substituteAll {
|
||||
src = ./nix/fetchDeps.sh;
|
||||
pname = pname;
|
||||
binPath = pkgs.lib.makeBinPath [pkgs.coreutils dotnet-sdk (pkgs.nuget-to-nix.override {inherit dotnet-sdk;})];
|
||||
projectFiles = toString (pkgs.lib.toList projectFile);
|
||||
testProjectFiles = toString (pkgs.lib.toList testProjectFile);
|
||||
rids = pkgs.lib.concatStringsSep "\" \"" runtimeIds;
|
||||
packages = dotnet-sdk.packages;
|
||||
storeSrc = pkgs.srcOnly {
|
||||
src = ./.;
|
||||
pname = pname;
|
||||
version = version;
|
||||
};
|
||||
}));
|
||||
default = pkgs.buildDotnetModule {
|
||||
pname = pname;
|
||||
inherit pname version projectFile testProjectFile dotnet-sdk dotnet-runtime;
|
||||
name = "puregym";
|
||||
version = version;
|
||||
src = ./.;
|
||||
projectFile = projectFile;
|
||||
nugetDeps = ./nix/deps.nix;
|
||||
nugetDeps = ./nix/deps.json; # `nix build .#default.fetch-deps && ./result nix/deps.json` and put the result here
|
||||
doCheck = true;
|
||||
dotnet-sdk = dotnet-sdk;
|
||||
dotnet-runtime = dotnet-runtime;
|
||||
};
|
||||
};
|
||||
apps = {
|
||||
@@ -80,13 +64,7 @@
|
||||
};
|
||||
};
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs =
|
||||
[pkgs.alejandra pkgs.dotnet-sdk_7 pkgs.python3 pkgs.nodePackages.markdown-link-check]
|
||||
++ (
|
||||
if pkgs.stdenv.isDarwin
|
||||
then [pkgs.darwin.apple_sdk.frameworks.CoreServices]
|
||||
else []
|
||||
);
|
||||
buildInputs = [pkgs.alejandra dotnet-sdk pkgs.python3 pkgs.nodePackages.markdown-link-check];
|
||||
};
|
||||
checks = {
|
||||
alejandra = pkgs.stdenvNoCC.mkDerivation {
|
||||
|
377
nix/deps.json
Normal file
377
nix/deps.json
Normal file
@@ -0,0 +1,377 @@
|
||||
[
|
||||
{
|
||||
"pname": "ApiSurface",
|
||||
"version": "5.0.1",
|
||||
"hash": "sha256-0GMXEMFgWbbE2OGxW+6h4zGgQHg+IZy1aI13Dn97xSU="
|
||||
},
|
||||
{
|
||||
"pname": "Argu",
|
||||
"version": "6.2.5",
|
||||
"hash": "sha256-5HcZcvco4e8+hgLhzlxk7ZmFVLtZL9LVr7LbmXsLmNU="
|
||||
},
|
||||
{
|
||||
"pname": "fantomas",
|
||||
"version": "6.3.15",
|
||||
"hash": "sha256-Gjw7MxjUNckMWSfnOye4UTe5fZWnor6RHCls3PNsuG8="
|
||||
},
|
||||
{
|
||||
"pname": "Fantomas.Core",
|
||||
"version": "6.1.1",
|
||||
"hash": "sha256-FcTLHQFvKkQY/kV08jhhy/St/+FmXpp3epp/R3zUXMA="
|
||||
},
|
||||
{
|
||||
"pname": "Fantomas.FCS",
|
||||
"version": "6.1.1",
|
||||
"hash": "sha256-NuZ8msPEHYA8T3EYREB28F1RcNgUU8V54eg2+UttYxw="
|
||||
},
|
||||
{
|
||||
"pname": "Fastenshtein",
|
||||
"version": "1.0.10",
|
||||
"hash": "sha256-9qE1zKJhfRvx7X/66MAk2+F7pwrd/2EKKl7r5qjTPCk="
|
||||
},
|
||||
{
|
||||
"pname": "FsCheck",
|
||||
"version": "3.3.1",
|
||||
"hash": "sha256-k65ksdOSOGz+meRUUND+yuqJtm5ChaKuaxmRIdKzx2Y="
|
||||
},
|
||||
{
|
||||
"pname": "FSharp.Core",
|
||||
"version": "6.0.1",
|
||||
"hash": "sha256-Ehsgt3nCJijpaVuJguC1TPVEKSkJd6PSc07D2ZQSemI="
|
||||
},
|
||||
{
|
||||
"pname": "FsUnit",
|
||||
"version": "7.1.0",
|
||||
"hash": "sha256-HHuIEocJrm6PSiTJeMWaYDsPYow8A/NFthU7sgB88sk="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.ApplicationInsights",
|
||||
"version": "2.23.0",
|
||||
"hash": "sha256-5sf3bg7CZZjHseK+F3foOchEhmVeioePxMZVvS6Rjb0="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.AspNetCore.App.Ref",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-9jDkWbjw/nd8yqdzVTagCuqr6owJ/DUMi4BlUZT4hWU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.AspNetCore.App.Runtime.linux-arm64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-JQULJyF0ivLoUU1JaFfK/HHg+/qzpN7V2RR2Cc+WlQ4="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.AspNetCore.App.Runtime.linux-x64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-zUsVIpV481vMLAXaLEEUpEMA9/f1HGOnvaQnaWdzlyY="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.AspNetCore.App.Runtime.osx-arm64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-2seqZcz0JeUjkzh3QcGa9TcJ4LUafpFjTRk+Nm8T6T0="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.AspNetCore.App.Runtime.osx-x64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-yxLafxiBKkvfkDggPk0P9YZIHBkDJOsFTO7/V9mEHuU="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.CodeCoverage",
|
||||
"version": "17.14.1",
|
||||
"hash": "sha256-f8QytG8GvRoP47rO2KEmnDLxIpyesaq26TFjDdW40Gs="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NET.Test.Sdk",
|
||||
"version": "17.14.1",
|
||||
"hash": "sha256-mZUzDFvFp7x1nKrcnRd0hhbNu5g8EQYt8SKnRgdhT/A="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Host.linux-arm64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-9lC/LYnthYhjkWWz2kkFCvlA5LJOv11jdt59SDnpdy0="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Host.linux-x64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-VFRDzx7LJuvI5yzKdGmw/31NYVbwHWPKQvueQt5xc10="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Host.osx-arm64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-DaSWwYACJGolEBuMhzDVCj/rQTdDt061xCVi+gyQnuo="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Host.osx-x64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-FrRny9EI6HKCKQbu6mcLj5w4ooSRrODD4Vj2ZMGnMd4="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Ref",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-9LZgVoIFF8qNyUu8kdJrYGLutMF/cL2K82HN2ywwlx8="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Runtime.linux-arm64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-k3rxvUhCEU0pVH8KgEMtkPiSOibn+nBh+0zT2xIfId8="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Runtime.linux-x64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-U8wJ2snSDFqeAgDVLXjnniidC7Cr5aJ1/h/BMSlyu0c="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Runtime.osx-arm64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-UfLcrL2Gj/OLz0s92Oo+OCJeDpZFAcQLPLiSNND8D5Y="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.App.Runtime.osx-x64",
|
||||
"version": "6.0.36",
|
||||
"hash": "sha256-0xIJYFzxdMcnCj3wzkFRQZSnQcPHzPHMzePRIOA3oJs="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.Platforms",
|
||||
"version": "1.1.0",
|
||||
"hash": "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.Platforms",
|
||||
"version": "1.1.1",
|
||||
"hash": "sha256-8hLiUKvy/YirCWlFwzdejD2Db3DaXhHxT7GSZx/znJg="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.Targets",
|
||||
"version": "1.1.0",
|
||||
"hash": "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.NETCore.Targets",
|
||||
"version": "1.1.3",
|
||||
"hash": "sha256-WLsf1NuUfRWyr7C7Rl9jiua9jximnVvzy6nk2D2bVRc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Extensions.Telemetry",
|
||||
"version": "1.7.3",
|
||||
"hash": "sha256-Z6WsY2FCUbNnT5HJd7IOrfOvqknVXp6PWzTVeb0idVg="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Extensions.TrxReport.Abstractions",
|
||||
"version": "1.7.3",
|
||||
"hash": "sha256-PTee04FHyTHx/gF5NLckXuVje807G51MzkPrZ1gkgCw="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Extensions.VSTestBridge",
|
||||
"version": "1.7.3",
|
||||
"hash": "sha256-8d+wZmucfSO7PsviHjVxYB4q6NcjgxvnCUpLePq35sM="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Platform",
|
||||
"version": "1.7.3",
|
||||
"hash": "sha256-cavX11P5o9rooqC3ZHw5h002OKRg2ZNR/VaRwpNTQYA="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.Testing.Platform.MSBuild",
|
||||
"version": "1.7.3",
|
||||
"hash": "sha256-cREl529UQ/c5atT8KimMgrgNdy6MrAd0sBGT8sXRRPM="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.TestPlatform.AdapterUtilities",
|
||||
"version": "17.13.0",
|
||||
"hash": "sha256-Vr+3Tad/h/nk7f/5HMExn3HvCGFCarehFAzJSfCBaOc="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.TestPlatform.ObjectModel",
|
||||
"version": "17.13.0",
|
||||
"hash": "sha256-6S0fjfj8vA+h6dJVNwLi6oZhYDO/I/6hBZaq2VTW+Uk="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.TestPlatform.ObjectModel",
|
||||
"version": "17.14.1",
|
||||
"hash": "sha256-QMf6O+w0IT+16Mrzo7wn+N20f3L1/mDhs/qjmEo1rYs="
|
||||
},
|
||||
{
|
||||
"pname": "Microsoft.TestPlatform.TestHost",
|
||||
"version": "17.14.1",
|
||||
"hash": "sha256-1cxHWcvHRD7orQ3EEEPPxVGEkTpxom1/zoICC9SInJs="
|
||||
},
|
||||
{
|
||||
"pname": "Myriad.Core",
|
||||
"version": "0.8.3",
|
||||
"hash": "sha256-vBOxfq8QriX/yUtaXN69rEQaY/psRNJWxqATLidrt2g="
|
||||
},
|
||||
{
|
||||
"pname": "Myriad.Sdk",
|
||||
"version": "0.8.3",
|
||||
"hash": "sha256-7O397WKhskKOvE3MkJT37BvxorDWngDR6gTUogtDZ2M="
|
||||
},
|
||||
{
|
||||
"pname": "Newtonsoft.Json",
|
||||
"version": "13.0.1",
|
||||
"hash": "sha256-K2tSVW4n4beRPzPu3rlVaBEMdGvWSv/3Q1fxaDh4Mjo="
|
||||
},
|
||||
{
|
||||
"pname": "Newtonsoft.Json",
|
||||
"version": "13.0.3",
|
||||
"hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc="
|
||||
},
|
||||
{
|
||||
"pname": "NuGet.Common",
|
||||
"version": "6.14.0",
|
||||
"hash": "sha256-jDOwt3veI1GSG8CfBnf2+dJxD3E/Nmlc+vHtD4J76Ms="
|
||||
},
|
||||
{
|
||||
"pname": "NuGet.Configuration",
|
||||
"version": "6.14.0",
|
||||
"hash": "sha256-1PN9s6fhCw3wd2260U6hQ4vG3jIvcG8GIn1oQgxMXA0="
|
||||
},
|
||||
{
|
||||
"pname": "NuGet.Frameworks",
|
||||
"version": "6.14.0",
|
||||
"hash": "sha256-3ViM3R1ucQMEL2hQYsivT86kI6veMQK2xDsiAcFcVQk="
|
||||
},
|
||||
{
|
||||
"pname": "NuGet.Packaging",
|
||||
"version": "6.14.0",
|
||||
"hash": "sha256-Yafbnxs3maj55bJ1oKQiZ0QkntFUzXdhorL94YEUOhY="
|
||||
},
|
||||
{
|
||||
"pname": "NuGet.Protocol",
|
||||
"version": "6.14.0",
|
||||
"hash": "sha256-uLDKfs+QN1MdnuQtTES8qfNzzsmYKM6XB9pwJc4G+eo="
|
||||
},
|
||||
{
|
||||
"pname": "NuGet.Versioning",
|
||||
"version": "6.14.0",
|
||||
"hash": "sha256-DqdOJgsphKxSvqB8b60zNPCaiLfbiu3WnUJ/90feLrY="
|
||||
},
|
||||
{
|
||||
"pname": "NUnit",
|
||||
"version": "4.4.0",
|
||||
"hash": "sha256-5geF5QOF+X/WkuCEgkPVKH4AdKx4U0olpU07S8+G3nU="
|
||||
},
|
||||
{
|
||||
"pname": "NUnit3TestAdapter",
|
||||
"version": "5.1.0",
|
||||
"hash": "sha256-5z470sFjV67wGHaw8KfmSloIAYe81Dokp0f8I6zXsDc="
|
||||
},
|
||||
{
|
||||
"pname": "RestEase",
|
||||
"version": "1.6.4",
|
||||
"hash": "sha256-FFmqFwlHhIln46k56Z8KM1G+xuPEh/bceKCQnJcdcdc="
|
||||
},
|
||||
{
|
||||
"pname": "runtime.any.System.Runtime",
|
||||
"version": "4.3.0",
|
||||
"hash": "sha256-qwhNXBaJ1DtDkuRacgHwnZmOZ1u9q7N8j0cWOLYOELM="
|
||||
},
|
||||
{
|
||||
"pname": "runtime.native.System",
|
||||
"version": "4.3.0",
|
||||
"hash": "sha256-ZBZaodnjvLXATWpXXakFgcy6P+gjhshFXmglrL5xD5Y="
|
||||
},
|
||||
{
|
||||
"pname": "runtime.unix.System.Private.Uri",
|
||||
"version": "4.3.0",
|
||||
"hash": "sha256-c5tXWhE/fYbJVl9rXs0uHh3pTsg44YD1dJvyOA0WoMs="
|
||||
},
|
||||
{
|
||||
"pname": "System.Collections.Immutable",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-F7OVjKNwpqbUh8lTidbqJWYi476nsq9n+6k0+QVRo3w="
|
||||
},
|
||||
{
|
||||
"pname": "System.Configuration.ConfigurationManager",
|
||||
"version": "4.4.0",
|
||||
"hash": "sha256-+8wGYllXnIxRzy9dLhZFB88GoPj8ivYXS0KUfcivT8I="
|
||||
},
|
||||
{
|
||||
"pname": "System.Diagnostics.DiagnosticSource",
|
||||
"version": "5.0.0",
|
||||
"hash": "sha256-6mW3N6FvcdNH/pB58pl+pFSCGWgyaP4hfVtC/SMWDV4="
|
||||
},
|
||||
{
|
||||
"pname": "System.Diagnostics.DiagnosticSource",
|
||||
"version": "7.0.0",
|
||||
"hash": "sha256-9Wk8cHSkjKtqkN6xW7KnXoQVtF/VNbKeBq79WqDesMs="
|
||||
},
|
||||
{
|
||||
"pname": "System.Formats.Asn1",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8="
|
||||
},
|
||||
{
|
||||
"pname": "System.Memory",
|
||||
"version": "4.5.5",
|
||||
"hash": "sha256-EPQ9o1Kin7KzGI5O3U3PUQAZTItSbk9h/i4rViN3WiI="
|
||||
},
|
||||
{
|
||||
"pname": "System.Private.Uri",
|
||||
"version": "4.3.0",
|
||||
"hash": "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM="
|
||||
},
|
||||
{
|
||||
"pname": "System.Reflection.Metadata",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-dQGC30JauIDWNWXMrSNOJncVa1umR1sijazYwUDdSIE="
|
||||
},
|
||||
{
|
||||
"pname": "System.Runtime",
|
||||
"version": "4.3.1",
|
||||
"hash": "sha256-R9T68AzS1PJJ7v6ARz9vo88pKL1dWqLOANg4pkQjkA0="
|
||||
},
|
||||
{
|
||||
"pname": "System.Runtime.CompilerServices.Unsafe",
|
||||
"version": "6.0.0",
|
||||
"hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I="
|
||||
},
|
||||
{
|
||||
"pname": "System.Security.Cryptography.Pkcs",
|
||||
"version": "6.0.4",
|
||||
"hash": "sha256-2e0aRybote+OR66bHaNiYpF//4fCiaO3zbR2e9GABUI="
|
||||
},
|
||||
{
|
||||
"pname": "System.Security.Cryptography.ProtectedData",
|
||||
"version": "4.4.0",
|
||||
"hash": "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE="
|
||||
},
|
||||
{
|
||||
"pname": "System.Text.Encodings.Web",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-IUQkQkV9po1LC0QsqrilqwNzPvnc+4eVvq+hCvq8fvE="
|
||||
},
|
||||
{
|
||||
"pname": "System.Text.Json",
|
||||
"version": "8.0.0",
|
||||
"hash": "sha256-XFcCHMW1u2/WujlWNHaIWkbW1wn8W4kI0QdrwPtWmow="
|
||||
},
|
||||
{
|
||||
"pname": "System.Text.Json",
|
||||
"version": "8.0.5",
|
||||
"hash": "sha256-yKxo54w5odWT6nPruUVsaX53oPRe+gKzGvLnnxtwP68="
|
||||
},
|
||||
{
|
||||
"pname": "System.Text.Json",
|
||||
"version": "9.0.0",
|
||||
"hash": "sha256-aM5Dh4okLnDv940zmoFAzRmqZre83uQBtGOImJpoIqk="
|
||||
},
|
||||
{
|
||||
"pname": "TypeEquality",
|
||||
"version": "0.3.0",
|
||||
"hash": "sha256-V50xAOzzyUJrY+MYPRxtnqW5MVeATXCes89wPprv1r4="
|
||||
},
|
||||
{
|
||||
"pname": "WoofWare.Myriad.Plugins",
|
||||
"version": "4.0.9",
|
||||
"hash": "sha256-VWpStkuvdFZWsEs/tC0mjChneFgxWw+1YETH+3aCoz4="
|
||||
},
|
||||
{
|
||||
"pname": "WoofWare.Myriad.Plugins.Attributes",
|
||||
"version": "3.6.6",
|
||||
"hash": "sha256-68T5JQNp4V0DDad0I3snVh8BCe7rz11mLyvm60hxwaA="
|
||||
},
|
||||
{
|
||||
"pname": "WoofWare.Whippet.Fantomas",
|
||||
"version": "0.3.1",
|
||||
"hash": "sha256-i5oiqcrxzM90Ocuq5MIu2Ha5lV0aYu5nCvuwmFqp6NA="
|
||||
}
|
||||
]
|
364
nix/deps.nix
364
nix/deps.nix
@@ -1,364 +0,0 @@
|
||||
# This file was automatically generated by passthru.fetch-deps.
|
||||
# Please don't edit it manually, your changes might get overwritten!
|
||||
{fetchNuGet}: [
|
||||
(fetchNuGet {
|
||||
pname = "Fastenshtein";
|
||||
version = "1.0.0.8";
|
||||
sha256 = "1rvw27rz7qb2n68i0jvvcr224fcpy5yzzxaj1bp89jw41cpdabp2";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Argu";
|
||||
version = "6.1.1";
|
||||
sha256 = "1v996g0760qhiys2ahdpnvkldaxr2jn5f1falf789glnk4a6f3xl";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Configuration.ConfigurationManager";
|
||||
version = "4.4.0";
|
||||
sha256 = "1hjgmz47v5229cbzd2pwz2h0dkq78lb2wp9grx8qr72pb5i0dk7v";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "fantomas";
|
||||
version = "6.2.0";
|
||||
sha256 = "sha256-83RodORaC3rkYfbFMHsYLEtl0+8+akZXcKoSJdgwuUo=";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "ApiSurface";
|
||||
version = "4.0.12";
|
||||
sha256 = "0v56sv4cz8bgrfqjjg0q96619qs9dvvi0a6lp7hzz2mi82i1inmq";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "coverlet.collector";
|
||||
version = "3.2.0";
|
||||
sha256 = "1qxpv8v10p5wn162lzdm193gdl6c5f81zadj8h889dprlnj3g8yr";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "FSharp.Core";
|
||||
version = "6.0.0";
|
||||
sha256 = "1hjhvr39c1vpgrdmf8xln5q86424fqkvy9nirkr29vl2461d2039";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "FSharp.Core";
|
||||
version = "7.0.400";
|
||||
sha256 = "1pl6iqqcpm9djfn7f6ms5j1xbcyz00nb808qd6pmsjrnylflalgp";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Ref";
|
||||
version = "6.0.22";
|
||||
sha256 = "0fqpl1fr213b4fb3c6xw3fy6669yxqcp1bzcnayw80yrskw8lpxs";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
|
||||
version = "6.0.22";
|
||||
sha256 = "1xvqqc7bzj764g3scp0saqxlfiv866crgi8chz57vhjp9sgd61jw";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
|
||||
version = "7.0.11";
|
||||
sha256 = "0hmsqy4yc3023mcp5rg0h59yv3f8cnjhxw1g4i8md67vm5y04lfv";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "1gcv99y295fnhy12fyx8wqvbhbj6mz8p5bm66ppwdxb3zykjg2l8";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "18sk9wka8z5354ca77q43hi0615yjssdjbyi0hqq92w6zmg43vgc";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
|
||||
version = "6.0.22";
|
||||
sha256 = "1ib0x1w33wqy7lgzjf14dvgx981xpjffjqd800d7wgxisgmakrmr";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
|
||||
version = "7.0.11";
|
||||
sha256 = "1j0zbd4rmmd3ylgixsvyj145g2r6px6b9d9k4yxxg6d61x90c165";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "026r38a7by7wdfd3virjdaah3y2sjjmnabgf5l25vdnwpwc7c31d";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "0wxw7vgygg6hqzq479n0pfjizr69wq7ja03a0qh8bma8b9q2mn6f";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "0ygdqsd312kqpykwb0k2942n45q1w3yn1nia6m1ahf7b74926qb5";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "05ywwfn5lzx6y999f7gwmablkxi2zvska4sg20ihmjzp3xakcmk0";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.CodeCoverage";
|
||||
version = "17.6.0";
|
||||
sha256 = "02s98d8nwz5mg4mymcr86qdamy71a29g2091xg452czmd3s3x2di";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NET.Test.Sdk";
|
||||
version = "17.6.0";
|
||||
sha256 = "1bnwpwg7k72z06027ip4yi222863r8sv14ck9nj8h64ckiw2r256";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.linux-arm64";
|
||||
version = "6.0.22";
|
||||
sha256 = "0gri1gqznm5c8fsb6spqb3j88a3b0br0iy50y66fh4hz9wc4fwzm";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.linux-arm64";
|
||||
version = "7.0.11";
|
||||
sha256 = "03nkxjn4wq30rw0163rqi8sngfxmcvwgm0wg7sgyb1cdh0q1ai68";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.linux-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "0k1i74wn6j7nq0bd8m6jrpl65wda6qc9pglppvz4ybk0n2ab1rbi";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.linux-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "12hh69sr4wf8sjcw3q71vky51sn854ffahbq6rgz3njzvbvc0dbj";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.osx-arm64";
|
||||
version = "6.0.22";
|
||||
sha256 = "0166gwarhhnary19lf80ff33bkx00mkm24f17bc8j6v7g3a7zvq6";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.osx-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "038bjwk201p2kzs3jflrkhlnszf7cwalafq0nvs2v8bp7jlnx5ib";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.osx-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "1j1k735gkwba93n5yck87wppfpsbny979hppcygwrk81myf3fv03";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.win-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "1bjy3zmrmaq97xp0f3nzs3ax330ji632avrfpg8xz4vc5p8s1xpc";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Host.win-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "0ifshdx19bgnbgynbk6iy6gybnxmp63nylrn7068x66hvcavh7kh";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Ref";
|
||||
version = "6.0.22";
|
||||
sha256 = "0km8184kma8kgz7iyl3j6apj1n7vskzdhzmq3myy3y36ysqrb4wf";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
|
||||
version = "6.0.22";
|
||||
sha256 = "01gbl9dgky4h7ijxryz3527l39v23lkcvk4fs4w91ra4pris2n8p";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
|
||||
version = "7.0.11";
|
||||
sha256 = "1gzwc96fs222ddia0k1924cn7gxm2a4anqgcxhmavx56x76wsy6f";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "09gfqdxbh36bjx20fw9k94b9qa9bwffhrq0ldwn834mx31bgrfs8";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "0vxza49wwiia0d3m887yiaprp3xnax2bgzhj5bf080b4ayapzkf9";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
|
||||
version = "6.0.22";
|
||||
sha256 = "1x7wclv93q8wp7rip5nwnsxbqcami92yilvzbp0yn42ddkw177ds";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
|
||||
version = "7.0.11";
|
||||
sha256 = "15b62hxrpfy19xvyxlyligixxpa9sysfgi47xi4imx5055fhwphh";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "1sq1ygsrpv2sl85wrs8382wgkjic0zylaj1y8kcvhczcmkpk3wr5";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "018qf23b0jixfh3fm74zqaakk01qx6yq21gk2mdn68b0xhnvlzma";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.win-x64";
|
||||
version = "6.0.22";
|
||||
sha256 = "1nn254xv1hi5c4rg38fbfkln3031vv545lv9f4df31i8c1yfzz24";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.App.Runtime.win-x64";
|
||||
version = "7.0.11";
|
||||
sha256 = "12xmw2kcpf5rh8sv4y0mqzp917f7q8g4mfh5navqw4jmnxyb26qq";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.Platforms";
|
||||
version = "1.1.0";
|
||||
sha256 = "08vh1r12g6ykjygq5d3vq09zylgb84l63k49jc4v8faw9g93iqqm";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.NETCore.Platforms";
|
||||
version = "2.0.0";
|
||||
sha256 = "1fk2fk2639i7nzy58m9dvpdnzql4vb8yl8vr19r2fp8lmj9w2jr0";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.TestPlatform.ObjectModel";
|
||||
version = "17.6.0";
|
||||
sha256 = "1rz22chnis11dwjrqrcvvmfw80fi2a7756a7ahwy6jlnr250zr61";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Microsoft.TestPlatform.TestHost";
|
||||
version = "17.6.0";
|
||||
sha256 = "16vpicp4q2kbpgr3qwpsxg7srabxqszx23x6smjvvrvz7qmr5v8i";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NETStandard.Library";
|
||||
version = "2.0.0";
|
||||
sha256 = "1bc4ba8ahgk15m8k4nd7x406nhi0kwqzbgjk2dmw52ss553xz7iy";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "Newtonsoft.Json";
|
||||
version = "13.0.1";
|
||||
sha256 = "0fijg0w6iwap8gvzyjnndds0q4b8anwxxvik7y8vgq97dram4srb";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NuGet.Common";
|
||||
version = "6.6.1";
|
||||
sha256 = "1q7k5rqwchxgs5pnrn22d1rkdb7l2qblvsb9hy046ll69i71vv45";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NuGet.Configuration";
|
||||
version = "6.6.1";
|
||||
sha256 = "0pw4ikd8784iya920wxigacqn5g2v0zlpwxjlswyq5mnj2ha7gpk";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NuGet.Frameworks";
|
||||
version = "5.11.0";
|
||||
sha256 = "0wv26gq39hfqw9md32amr5771s73f5zn1z9vs4y77cgynxr73s4z";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NuGet.Frameworks";
|
||||
version = "6.6.1";
|
||||
sha256 = "1zq79mklzq7qyiyhcv3w8pznw6rq1ddcl8fvy7j1c6n8qh3mglhx";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NuGet.Packaging";
|
||||
version = "6.6.1";
|
||||
sha256 = "1lmx8kgpg220q8kic4wm8skccj53cbkdqggirq9js34gnxxi9b88";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NuGet.Protocol";
|
||||
version = "6.6.1";
|
||||
sha256 = "01n8cw114npvzfk3m3803lb8plk0wm1zg496gpq9az8hw20nmd8g";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NuGet.Versioning";
|
||||
version = "6.6.1";
|
||||
sha256 = "0n2p05y8ciw6jc5s238rlnx6q4dgxvm14v06pcd84ji5j1iirc30";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NUnit";
|
||||
version = "3.13.3";
|
||||
sha256 = "0wdzfkygqnr73s6lpxg5b1pwaqz9f414fxpvpdmf72bvh4jaqzv6";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NUnit.Analyzers";
|
||||
version = "3.6.1";
|
||||
sha256 = "16dw5375k2wyhiw9x387y7pjgq6zms30y036qb8z7idx4lxw9yi9";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "NUnit3TestAdapter";
|
||||
version = "4.4.2";
|
||||
sha256 = "1n2jlc16vjdd81cb1by4qbp75sq73zsjz5w3zc61ssmbdci1q2ri";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "RestEase";
|
||||
version = "1.6.4";
|
||||
sha256 = "1mvi3nbrr450g3fgd1y4wg3bwl9k1agyjfd9wdkqk12714bsln8l";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Formats.Asn1";
|
||||
version = "5.0.0";
|
||||
sha256 = "1axc8z0839yvqi2cb63l73l6d9j6wd20lsbdymwddz9hvrsgfwpn";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.IO.Abstractions";
|
||||
version = "4.2.13";
|
||||
sha256 = "0s784iphsmj4vhkrzq9q3w39vsn76w44zclx3hsygsw458zbyh4y";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.IO.FileSystem.AccessControl";
|
||||
version = "4.5.0";
|
||||
sha256 = "1gq4s8w7ds1sp8f9wqzf8nrzal40q5cd2w4pkf4fscrl2ih3hkkj";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Reflection.Metadata";
|
||||
version = "1.6.0";
|
||||
sha256 = "1wdbavrrkajy7qbdblpbpbalbdl48q3h34cchz24gvdgyrlf15r4";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Runtime.CompilerServices.Unsafe";
|
||||
version = "6.0.0";
|
||||
sha256 = "0qm741kh4rh57wky16sq4m0v05fxmkjjr87krycf5vp9f0zbahbc";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Security.AccessControl";
|
||||
version = "4.5.0";
|
||||
sha256 = "1wvwanz33fzzbnd2jalar0p0z3x0ba53vzx1kazlskp7pwyhlnq0";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Security.Cryptography.Cng";
|
||||
version = "5.0.0";
|
||||
sha256 = "06hkx2za8jifpslkh491dfwzm5dxrsyxzj5lsc0achb6yzg4zqlw";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Security.Cryptography.Pkcs";
|
||||
version = "5.0.0";
|
||||
sha256 = "0hb2mndac3xrw3786bsjxjfh19bwnr991qib54k6wsqjhjyyvbwj";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Security.Cryptography.ProtectedData";
|
||||
version = "4.4.0";
|
||||
sha256 = "1q8ljvqhasyynp94a1d7jknk946m20lkwy2c3wa8zw2pc517fbj6";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Security.Principal.Windows";
|
||||
version = "4.5.0";
|
||||
sha256 = "0rmj89wsl5yzwh0kqjgx45vzf694v9p92r4x4q6yxldk1cv1hi86";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Text.Encodings.Web";
|
||||
version = "6.0.0";
|
||||
sha256 = "06n9ql3fmhpjl32g3492sj181zjml5dlcc5l76xq2h38c4f87sai";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Text.Encodings.Web";
|
||||
version = "7.0.0";
|
||||
sha256 = "1151hbyrcf8kyg1jz8k9awpbic98lwz9x129rg7zk1wrs6vjlpxl";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Text.Json";
|
||||
version = "6.0.0";
|
||||
sha256 = "1si2my1g0q0qv1hiqnji4xh9wd05qavxnzj9dwgs23iqvgjky0gl";
|
||||
})
|
||||
(fetchNuGet {
|
||||
pname = "System.Text.Json";
|
||||
version = "7.0.3";
|
||||
sha256 = "0zjrnc9lshagm6kdb9bdh45dmlnkpwcpyssa896sda93ngbmj8k9";
|
||||
})
|
||||
]
|
@@ -1,73 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This file was adapted from
|
||||
# https://github.com/NixOS/nixpkgs/blob/b981d811453ab84fb3ea593a9b33b960f1ab9147/pkgs/build-support/dotnet/build-dotnet-module/default.nix#L173
|
||||
set -euo pipefail
|
||||
export PATH="@binPath@"
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--keep-sources|-k)
|
||||
keepSources=1
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
echo "usage: $0 [--keep-sources] [--help] <output path>"
|
||||
echo " <output path> The path to write the lockfile to. A temporary file is used if this is not set"
|
||||
echo " --keep-sources Don't remove temporary directories upon exit, useful for debugging"
|
||||
echo " --help Show this help message"
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
tmp=$(mktemp -td "@pname@-tmp-XXXXXX")
|
||||
export tmp
|
||||
HOME=$tmp/home
|
||||
exitTrap() {
|
||||
test -n "${ranTrap-}" && return
|
||||
ranTrap=1
|
||||
if test -n "${keepSources-}"; then
|
||||
echo -e "Path to the source: $tmp/src\nPath to the fake home: $tmp/home"
|
||||
else
|
||||
rm -rf "$tmp"
|
||||
fi
|
||||
# Since mktemp is used this will be empty if the script didnt succesfully complete
|
||||
if ! test -s "$depsFile"; then
|
||||
rm -rf "$depsFile"
|
||||
fi
|
||||
}
|
||||
trap exitTrap EXIT INT TERM
|
||||
dotnetRestore() {
|
||||
local -r project="${1-}"
|
||||
local -r rid="$2"
|
||||
dotnet restore "${project-}" \
|
||||
-p:ContinuousIntegrationBuild=true \
|
||||
-p:Deterministic=true \
|
||||
--packages "$tmp/nuget_pkgs" \
|
||||
--runtime "$rid" \
|
||||
--no-cache \
|
||||
--force
|
||||
}
|
||||
declare -a projectFiles=( @projectFiles@ )
|
||||
declare -a testProjectFiles=( @testProjectFiles@ )
|
||||
export DOTNET_NOLOGO=1
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
depsFile=$(realpath "${1:-$(mktemp -t "@pname@-deps-XXXXXX.nix")}")
|
||||
mkdir -p "$tmp/nuget_pkgs"
|
||||
storeSrc="@storeSrc@"
|
||||
src="$tmp/src"
|
||||
cp -rT "$storeSrc" "$src"
|
||||
chmod -R +w "$src"
|
||||
cd "$src"
|
||||
echo "Restoring project..."
|
||||
rids=("@rids@")
|
||||
for rid in "${rids[@]}"; do
|
||||
(( ${#projectFiles[@]} == 0 )) && dotnetRestore "" "$rid"
|
||||
for project in "${projectFiles[@]-}" "${testProjectFiles[@]-}"; do
|
||||
dotnetRestore "$project" "$rid"
|
||||
done
|
||||
done
|
||||
echo "Successfully restored project"
|
||||
echo "Writing lockfile..."
|
||||
echo -e "# This file was automatically generated by passthru.fetch-deps.\n# Please don't edit it manually, your changes might get overwritten!\n" > "$depsFile"
|
||||
nuget-to-nix "$tmp/nuget_pkgs" "@packages@" >> "$depsFile"
|
||||
echo "Successfully wrote lockfile to $depsFile"
|
Reference in New Issue
Block a user