From 3656b0f3f078bf165837e81f10e34775d7e1dc80 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Sat, 8 Dec 2018 13:49:30 -0800 Subject: [PATCH] Begin working on Http requests. --- elm.json | 3 +++ src/Main.elm | 16 +++++----------- src/Scylla/Api.elm | 15 +++++++++++++++ src/Scylla/Http.elm | 37 +++++++++++++++++++++++++++++++++++++ src/Scylla/Login.elm | 20 ++++++++++++++++++++ src/Scylla/Model.elm | 21 +++++++++++++++++++++ src/Scylla/Sync.elm | 27 +++++++++++++++++++++++++++ 7 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 src/Scylla/Api.elm create mode 100644 src/Scylla/Http.elm create mode 100644 src/Scylla/Login.elm create mode 100644 src/Scylla/Model.elm diff --git a/elm.json b/elm.json index 5564502..6c4b220 100644 --- a/elm.json +++ b/elm.json @@ -10,10 +10,13 @@ "elm/browser": "1.0.1", "elm/core": "1.0.2", "elm/html": "1.0.0", + "elm/http": "2.0.0", "elm/json": "1.1.2", "elm/url": "1.0.0" }, "indirect": { + "elm/bytes": "1.0.7", + "elm/file": "1.0.1", "elm/time": "1.0.0", "elm/virtual-dom": "1.0.2" } diff --git a/src/Main.elm b/src/Main.elm index 022f638..82a1ce5 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -1,28 +1,22 @@ import Browser exposing (application) import Browser.Navigation as Nav +import Scylla.Sync exposing (..) +import Scylla.Model exposing (..) +import Scylla.Http exposing (..) import Url exposing (Url) -import Html exposing (div) +import Html exposing (div, text) type alias Flags = { token : Maybe String } -type alias Model = - { key : Nav.Key - , token : Maybe String - } - -type Msg = - None - | TryUrl Browser.UrlRequest - | ChangeUrl Url - init : Flags -> Url -> Nav.Key -> (Model, Cmd Msg) init flags url key = let model = { key = key , token = flags.token + , apiUrl = "https://matrix.org" } cmd = case flags.token of Just _ -> Cmd.none diff --git a/src/Scylla/Api.elm b/src/Scylla/Api.elm new file mode 100644 index 0000000..2689c50 --- /dev/null +++ b/src/Scylla/Api.elm @@ -0,0 +1,15 @@ +module Scylla.Api exposing (..) +import Http exposing (Header, header) +import Json.Encode as Encode + +type alias ApiToken = String +type alias ApiUrl = String + +basicHeaders : List Header +basicHeaders = + [ header "Content-Type" "application/json" + ] + +authenticatedHeaders : ApiToken -> List Header +authenticatedHeaders token = + [ header "Authorization" ("Bearer " ++ token)] ++ basicHeaders diff --git a/src/Scylla/Http.elm b/src/Scylla/Http.elm new file mode 100644 index 0000000..613978e --- /dev/null +++ b/src/Scylla/Http.elm @@ -0,0 +1,37 @@ +module Scylla.Http exposing (..) +import Scylla.Model exposing (..) +import Scylla.Api exposing (..) +import Scylla.Sync exposing (syncResponseDecoder) +import Scylla.Login exposing (loginResponseDecoder, Username, Password) +import Json.Encode exposing (object, string) +import Http exposing (request, jsonBody, expectJson) + +-- Http Requests +firstSync : ApiUrl -> ApiToken -> Cmd Msg +firstSync apiUrl token = request + { method = "GET" + , headers = authenticatedHeaders token + , url = apiUrl ++ "/sync" + , body = jsonBody <| object [] + , expect = expectJson ReceiveSyncResponse syncResponseDecoder + , timeout = Nothing + , tracker = Nothing + } + +login : ApiUrl -> Username -> Password -> Cmd Msg +login apiUrl username password = request + { method = "POST" + , headers = basicHeaders + , url = apiUrl ++ "/login" + , body = jsonBody <| object + [ ("type", string "m.login.password") + , ("identifier", object + [ ("type", string "m.id.user") + , ("user", string username) + ] ) + , ("password", string password) + ] + , expect = expectJson ReceiveLoginResponse loginResponseDecoder + , timeout = Nothing + , tracker = Nothing + } diff --git a/src/Scylla/Login.elm b/src/Scylla/Login.elm new file mode 100644 index 0000000..6758950 --- /dev/null +++ b/src/Scylla/Login.elm @@ -0,0 +1,20 @@ +module Scylla.Login exposing (..) +import Scylla.Api exposing (ApiToken) +import Json.Decode as Decode exposing (Decoder, int, string, float, list, value, dict, bool) +import Json.Decode.Pipeline exposing (required, optional) + +type alias Username = String +type alias Password = String + +type alias LoginResponse = + { userId : String + , accessToken : ApiToken + , deviceId : String + } + +loginResponseDecoder : Decoder LoginResponse +loginResponseDecoder = + Decode.succeed LoginResponse + |> required "user_id" string + |> required "access_token" string + |> required "device_id" string diff --git a/src/Scylla/Model.elm b/src/Scylla/Model.elm new file mode 100644 index 0000000..ecac1dc --- /dev/null +++ b/src/Scylla/Model.elm @@ -0,0 +1,21 @@ +module Scylla.Model exposing (..) +import Scylla.Api exposing (..) +import Scylla.Sync exposing (SyncResponse) +import Scylla.Login exposing (LoginResponse) +import Browser.Navigation as Nav +import Browser +import Http +import Url exposing (Url) + +type alias Model = + { key : Nav.Key + , token : Maybe ApiToken + , apiUrl : ApiUrl + } + +type Msg = + TryUrl Browser.UrlRequest + | ChangeUrl Url + | ReceiveSyncResponse (Result Http.Error SyncResponse) + | ReceiveLoginResponse (Result Http.Error LoginResponse) + diff --git a/src/Scylla/Sync.elm b/src/Scylla/Sync.elm index 3bdab58..34a385c 100644 --- a/src/Scylla/Sync.elm +++ b/src/Scylla/Sync.elm @@ -1,8 +1,10 @@ module Scylla.Sync exposing (..) +import Scylla.Api exposing (..) import Dict exposing (Dict) import Json.Decode as Decode exposing (Decoder, int, string, float, list, value, dict, bool) import Json.Decode.Pipeline exposing (required, optional) +-- Special Decoding decodeJust : Decoder a -> Decoder (Maybe a) decodeJust = Decode.map Just @@ -227,3 +229,28 @@ leftRoomDecoder = |> maybeDecode "state" stateDecoder |> maybeDecode "timeline" timelineDecoder |> maybeDecode "account_data" accountDataDecoder + +-- General Sync Response +type alias SyncResponse = + { nextBatch : String + , rooms : Maybe Rooms + , presence : Maybe Presence + , accountData : Maybe AccountData + } + +syncResponseDecoder : Decoder SyncResponse +syncResponseDecoder = + Decode.succeed SyncResponse + |> required "next_batch" string + |> maybeDecode "rooms" roomsDecoder + |> maybeDecode "presence" presenceDecoder + |> maybeDecode "account_data" accountDataDecoder + +type alias Presence = + { events : Maybe (List Event) + } + +presenceDecoder : Decoder Presence +presenceDecoder = + Decode.succeed Presence + |> maybeDecode "events" (list eventDecoder)