Bot-Man-Toe/elm/Api.elm

168 lines
4.0 KiB
Elm

module Api exposing
( GameDetails
, Player
, gameDetails
, profile
, startGame
)
import Dict exposing (Dict)
import Http
import Json.Decode as D
import Json.Encode as E
endpointGameDetails =
"/game-details"
endpointGetProfile =
"/profile"
endpointStartGame =
"/start-game"
{-| Full report on how a game is going.
-}
type alias GameDetails gameState =
{ name : String
, turns : List { player : Int, action : E.Value, state : gameState }
, winner : Maybe Int
}
{-| General format of a player who's allowed to participate.
-}
type alias Player =
{ name : String
, games : Dict String (Dict String E.Value)
, profile : Dict String E.Value
, url : String
}
{-| Builds a generalized API call to the webclient server.
-}
callWebClient :
{ body : Maybe D.Value
, decoder : D.Decoder a
, method : String
, toMsg : Result Http.Error a -> msg
, url : String
}
-> Cmd msg
callWebClient data =
Http.request
{ method = data.method
, headers = []
, url = data.url
, body =
data.body
|> Maybe.map Http.jsonBody
|> Maybe.withDefault Http.emptyBody
, expect = Http.expectJson data.toMsg data.decoder
, timeout = Nothing
, tracker = Nothing
}
{-| Retrieves all the latest details about a game. A game might still be
ongoing and therefore might be incomplete.
-}
gameDetails :
{ baseUrl : String
, decoder : D.Decoder gameState
, gameId : String
, toMsg : Result Http.Error (GameDetails gameState) -> msg
}
-> Cmd msg
gameDetails data =
callWebClient
{ body = Just (E.object [ ( "game_id", E.string data.gameId ) ])
, decoder = gameDetailsDecoder data.decoder
, method = "GET"
, toMsg = data.toMsg
, url = data.baseUrl ++ endpointGameDetails
}
{-| Decodes a game's details from JSON.
-}
gameDetailsDecoder : D.Decoder gameState -> D.Decoder (GameDetails gameState)
gameDetailsDecoder decoder =
D.map3 GameDetails
(D.field "name" D.string)
(D.map3
(\action player state ->
{ player = player, action = action, state = state }
)
(D.field "action" D.value)
(D.field "player" D.int)
(D.field "state" decoder)
|> D.list
|> D.field "turns"
)
(D.field "winner" <| D.oneOf [ D.map Just D.int, D.null Nothing ])
{-| Decodes a Player from JSON.
-}
playerDecoder : D.Decoder Player
playerDecoder =
D.map4 Player
(D.field "name" D.string)
(D.field "games" <| D.dict <| D.dict D.value)
(D.field "profile" <| D.dict D.value)
(D.field "url" D.string)
{-| Gets the profile of a given player with a given URL.
-}
profile :
{ baseUrl : String
, playerUrl : String
, toMsg : Result Http.Error Player -> msg
}
-> Cmd msg
profile data =
callWebClient
{ body = Just (E.object [ ( "url", E.string data.playerUrl ) ])
, decoder = playerDecoder
, method = "GET"
, toMsg = data.toMsg
, url = data.baseUrl ++ endpointGetProfile
}
{-| Instructs the server to start a game with the PyClient. The players list
provides a set of URLs that should be considered as its players, even if the
players haven't been verified (yet).
The server responds with a unique identifier for the game. This allows the
front-end to query updates about the game while it's still being processed.
-}
startGame :
{ baseUrl : String
, game : String
, players : List String
, toMsg : Result Http.Error String -> msg
}
-> Cmd msg
startGame data =
callWebClient
{ body =
Just
(E.object
[ ( "game", E.string data.game )
, ( "players", E.list E.string data.players )
]
)
, decoder = D.string
, method = "POST"
, toMsg = data.toMsg
, url = data.baseUrl ++ endpointStartGame
}