elm-matrix-sdk-beta/src/Internal/Values/Context.elm

453 lines
12 KiB
Elm
Raw Normal View History

2023-12-18 13:26:57 +00:00
module Internal.Values.Context exposing
2024-05-26 11:12:03 +00:00
( Context, AccessToken, init, coder, encode, decoder
, mostPopularToken
2024-05-22 17:12:34 +00:00
, APIContext, apiFormat, fromApiFormat
2023-12-18 13:26:57 +00:00
, setAccessToken, getAccessToken
, setBaseUrl, getBaseUrl
, setNow, getNow
2023-12-18 13:26:57 +00:00
, setTransaction, getTransaction
, Versions, setVersions, getVersions
2024-05-25 15:03:42 +00:00
, reset
2023-12-18 13:26:57 +00:00
)
{-| The Context is the set of variables that the user (mostly) cannot control.
The Context contains tokens, values and other bits that the Vault receives from
the Matrix API.
## Context
2024-05-26 11:12:03 +00:00
@docs Context, AccessToken, init, coder, encode, decoder
Some functions are present to influence the general Context type itself.
@docs mostPopularToken
2023-12-18 13:26:57 +00:00
## APIContext
Once the API starts needing information, that's when we use the APIContext type
to build the right environment for the API communication to work with.
2024-05-22 17:12:34 +00:00
@docs APIContext, apiFormat, fromApiFormat
2023-12-18 13:26:57 +00:00
Once the APIContext is ready, there's helper functions for each piece of
information that can be inserted.
### Access token
@docs setAccessToken, getAccessToken
### Base URL
@docs setBaseUrl, getBaseUrl
### Timestamp
@docs setNow, getNow
2023-12-18 13:26:57 +00:00
### Transaction id
@docs setTransaction, getTransaction
### Versions
@docs Versions, setVersions, getVersions
2023-12-18 13:26:57 +00:00
2024-05-25 15:03:42 +00:00
### Reset
@docs reset
2023-12-18 13:26:57 +00:00
-}
import Internal.Config.Leaks as L
import Internal.Config.Text as Text
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
import Internal.Tools.Json as Json
import Internal.Tools.Timestamp as Timestamp exposing (Timestamp)
2024-05-22 17:12:34 +00:00
import Json.Encode as E
import Set exposing (Set)
import Time
{-| The Access Token is a combination of access tokens, values and refresh
tokens that contain and summarizes all properties of a known access token.
-}
type alias AccessToken =
{ created : Timestamp
, expiryMs : Maybe Int
, lastUsed : Timestamp
, refresh : Maybe String
, value : String
}
2023-12-18 13:26:57 +00:00
{-| The Context type stores all the information in the Vault. This data type is
static and hence can be passed on easily.
-}
type alias Context =
{ accessTokens : Hashdict AccessToken
2023-12-18 13:26:57 +00:00
, baseUrl : Maybe String
2024-05-24 13:15:44 +00:00
, deviceId : Maybe String
2024-07-08 22:08:46 +00:00
, nextBatch : Maybe String
, now : Maybe Timestamp
2023-12-18 13:26:57 +00:00
, password : Maybe String
, refreshToken : Maybe String
2024-05-22 17:12:34 +00:00
, serverName : String
2024-05-26 11:12:03 +00:00
, suggestedAccessToken : Maybe String
2023-12-18 13:26:57 +00:00
, transaction : Maybe String
2024-05-22 17:12:34 +00:00
, username : Maybe String
, versions : Maybe Versions
2023-12-18 13:26:57 +00:00
}
{-| The APIContext is a separate type that uses a phantom type to trick the
compiler into requiring values to be present. This data type is used to gather
the right variables (like an access token) before accessing the Matrix API.
-}
type APIContext ph
= APIContext
{ accessToken : String
, baseUrl : String
, context : Context
, now : Timestamp
2023-12-18 13:26:57 +00:00
, transaction : String
, versions : Versions
2023-12-18 13:26:57 +00:00
}
type alias Versions =
{ versions : List String, unstableFeatures : Set String }
2023-12-18 13:26:57 +00:00
{-| Create an unformatted APIContext type.
-}
apiFormat : Context -> APIContext {}
apiFormat context =
APIContext
{ accessToken =
mostPopularToken context |> Maybe.withDefault L.accessToken
2023-12-18 13:26:57 +00:00
, baseUrl = context.baseUrl |> Maybe.withDefault L.baseUrl
, context = context
, now = context.now |> Maybe.withDefault (Time.millisToPosix 0)
2023-12-18 13:26:57 +00:00
, transaction = context.transaction |> Maybe.withDefault L.transaction
2024-05-22 17:12:34 +00:00
, versions = context.versions |> Maybe.withDefault L.versions
2023-12-18 13:26:57 +00:00
}
2024-05-22 17:12:34 +00:00
{-| Get the original context that contains all values from before any were
gotten from the Matrix API.
-}
fromApiFormat : APIContext a -> Context
fromApiFormat (APIContext c) =
c.context
2024-01-22 17:09:08 +00:00
{-| Define how a Context can be encoded to and decoded from a JSON object.
-}
coder : Json.Coder Context
coder =
2024-07-08 22:08:46 +00:00
Json.object12
{ name = Text.docs.context.name
, description = Text.docs.context.description
, init = Context
}
(Json.field.required
{ fieldName = "accessTokens"
, toField = .accessTokens
, description = Text.fields.context.accessToken
, coder = Hashdict.coder .value coderAccessToken
}
)
(Json.field.optional.value
{ fieldName = "baseUrl"
, toField = .baseUrl
, description = Text.fields.context.baseUrl
, coder = Json.string
}
)
2024-05-24 13:15:44 +00:00
(Json.field.optional.value
{ fieldName = "deviceId"
, toField = .deviceId
2024-05-28 16:20:01 +00:00
, description = Text.fields.context.deviceId
2024-05-24 13:15:44 +00:00
, coder = Json.string
}
)
2024-07-08 22:08:46 +00:00
(Json.field.optional.value
{ fieldName = "nextBatch"
, toField = .nextBatch
, description = Text.fields.context.nextBatch
, coder = Json.string
}
)
(Json.field.optional.value
{ fieldName = "now"
, toField = .now
2024-05-28 16:20:01 +00:00
, description = Text.fields.context.now
, coder = Timestamp.coder
}
)
(Json.field.optional.value
{ fieldName = "password"
, toField = .password
, description = Text.fields.context.password
, coder = Json.string
}
)
(Json.field.optional.value
{ fieldName = "refreshToken"
, toField = .refreshToken
, description = Text.fields.context.refreshToken
, coder = Json.string
}
)
2024-05-22 17:12:34 +00:00
(Json.field.required
{ fieldName = "serverName"
, toField = .serverName
, description = Text.fields.context.serverName
, coder = Json.string
}
)
2024-05-26 11:12:03 +00:00
(Json.field.optional.value
{ fieldName = "suggestedAccessToken"
, toField = always Nothing -- Do not save
2024-05-28 16:20:01 +00:00
, description = Text.fields.context.suggestedAccessToken
2024-05-26 11:12:03 +00:00
, coder = Json.string
}
)
(Json.field.optional.value
{ fieldName = "transaction"
, toField = .transaction
, description = Text.fields.context.transaction
, coder = Json.string
}
)
2024-05-22 17:12:34 +00:00
(Json.field.optional.value
{ fieldName = "username"
, toField = .username
, description = Text.fields.context.username
, coder = Json.string
}
)
(Json.field.optional.value
{ fieldName = "versions"
, toField = .versions
, description = Text.fields.context.versions
2024-05-22 17:12:34 +00:00
, coder = versionsCoder
}
)
{-| JSON coder for an Access Token.
-}
coderAccessToken : Json.Coder AccessToken
coderAccessToken =
Json.object5
2024-05-28 16:20:01 +00:00
{ name = Text.docs.accessToken.name
, description = Text.docs.accessToken.description
, init = AccessToken
}
(Json.field.required
{ fieldName = "created"
, toField = .created
2024-05-28 16:20:01 +00:00
, description = Text.fields.accessToken.created
, coder = Timestamp.coder
}
)
(Json.field.optional.value
{ fieldName = "expiryMs"
, toField = .expiryMs
2024-05-28 16:20:01 +00:00
, description = Text.fields.accessToken.expiryMs
, coder = Json.int
}
)
(Json.field.required
{ fieldName = "lastUsed"
, toField = .lastUsed
2024-05-28 16:20:01 +00:00
, description = Text.fields.accessToken.lastUsed
, coder = Timestamp.coder
}
)
(Json.field.optional.value
{ fieldName = "refresh"
, toField = .refresh
2024-05-28 16:20:01 +00:00
, description = Text.fields.accessToken.refresh
, coder = Json.string
}
)
(Json.field.required
{ fieldName = "value"
, toField = .value
2024-05-28 16:20:01 +00:00
, description = Text.fields.accessToken.value
, coder = Json.string
}
)
2023-12-18 13:26:57 +00:00
{-| Decode a Context type from a JSON value.
-}
decoder : Json.Decoder Context
2023-12-18 13:26:57 +00:00
decoder =
Json.decode coder
2023-12-18 13:26:57 +00:00
{-| Encode a Context type into a JSON value.
-}
encode : Json.Encoder Context
encode =
Json.encode coder
2023-12-18 13:26:57 +00:00
{-| A basic, untouched version of the Context, containing no information.
-}
2024-05-22 17:12:34 +00:00
init : String -> Context
init sn =
{ accessTokens = Hashdict.empty .value
2023-12-18 13:26:57 +00:00
, baseUrl = Nothing
2024-05-24 13:15:44 +00:00
, deviceId = Nothing
2024-07-08 22:08:46 +00:00
, nextBatch = Nothing
, now = Nothing
2023-12-18 13:26:57 +00:00
, refreshToken = Nothing
, password = Nothing
2024-05-22 17:12:34 +00:00
, serverName = sn
2024-05-26 11:12:03 +00:00
, suggestedAccessToken = Nothing
2023-12-18 13:26:57 +00:00
, transaction = Nothing
2024-05-22 17:12:34 +00:00
, username = Nothing
2023-12-18 13:26:57 +00:00
, versions = Nothing
}
{-| Get the most popular access token available, if any.
-}
mostPopularToken : Context -> Maybe String
mostPopularToken c =
case c.suggestedAccessToken of
Just _ ->
c.suggestedAccessToken
Nothing ->
c.accessTokens
|> Hashdict.values
|> List.sortBy
(\token ->
case token.expiryMs of
Nothing ->
( 0, Timestamp.toMs token.created )
Just e ->
( 1
, token.created
|> Timestamp.add e
|> Timestamp.toMs
)
)
|> List.head
|> Maybe.map .value
2024-05-25 15:03:42 +00:00
{-| Reset the phantom type of the Context, effectively forgetting all values.
-}
reset : APIContext a -> APIContext {}
reset (APIContext c) =
APIContext c
2023-12-18 13:26:57 +00:00
{-| Get an inserted access token.
-}
getAccessToken : APIContext { a | accessToken : () } -> String
getAccessToken (APIContext c) =
c.accessToken
{-| Insert an access token into the APIContext.
-}
setAccessToken : String -> APIContext a -> APIContext { a | accessToken : () }
setAccessToken value (APIContext c) =
APIContext { c | accessToken = value }
{-| Get an inserted base URL.
-}
getBaseUrl : APIContext { a | baseUrl : () } -> String
getBaseUrl (APIContext c) =
c.baseUrl
{-| Insert a base URL into the APIContext.
-}
setBaseUrl : String -> APIContext a -> APIContext { a | baseUrl : () }
setBaseUrl value (APIContext c) =
APIContext { c | baseUrl = value }
{-| Get an inserted timestamp.
-}
getNow : APIContext { a | now : () } -> Timestamp
getNow (APIContext c) =
c.now
{-| Insert a Timestamp into the APIContext.
-}
setNow : Timestamp -> APIContext a -> APIContext { a | now : () }
setNow t (APIContext c) =
APIContext { c | now = t }
2023-12-18 13:26:57 +00:00
{-| Get an inserted transaction id.
-}
getTransaction : APIContext { a | transaction : () } -> String
getTransaction (APIContext c) =
c.transaction
{-| Insert a transaction id into the APIContext.
-}
setTransaction : String -> APIContext a -> APIContext { a | transaction : () }
setTransaction value (APIContext c) =
APIContext { c | transaction = value }
{-| Get an inserted versions list.
-}
getVersions : APIContext { a | versions : () } -> Versions
2023-12-18 13:26:57 +00:00
getVersions (APIContext c) =
c.versions
{-| Insert a versions list into the APIContext.
-}
setVersions : Versions -> APIContext a -> APIContext { a | versions : () }
2023-12-18 13:26:57 +00:00
setVersions value (APIContext c) =
APIContext { c | versions = value }
2024-05-22 17:12:34 +00:00
versionsCoder : Json.Coder Versions
versionsCoder =
Json.object2
2024-05-28 16:20:01 +00:00
{ name = Text.docs.versions.name
, description = Text.docs.versions.description
2024-05-22 17:12:34 +00:00
, init = Versions
}
(Json.field.required
{ fieldName = "versions"
, toField = .versions
2024-05-28 16:20:01 +00:00
, description = Text.fields.versions.versions
2024-05-22 17:12:34 +00:00
, coder = Json.list Json.string
}
)
(Json.field.optional.withDefault
{ fieldName = "unstableFeatures"
, toField = .unstableFeatures
2024-05-28 16:20:01 +00:00
, description = Text.fields.versions.unstableFeatures
2024-05-22 17:12:34 +00:00
, coder = Json.set Json.string
, default = ( Set.empty, [] )
}
)