start implementing models for Mi API integration

This commit is contained in:
Cadey Ratio 2020-11-04 17:16:39 -05:00
parent a3d722e2ed
commit b06a90ccc5
6 changed files with 224 additions and 5 deletions

1
frontend/.gitignore vendored
View File

@ -1,2 +1,3 @@
index.html
index.js
elm-stuff

View File

@ -11,14 +11,14 @@
"elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
},
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/parser": "1.1.0",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.2"
}
},

View File

@ -1,7 +1,79 @@
module Main exposing (main)
import Html exposing (Html, text)
import Browser exposing (sandbox)
import Html exposing (Html, button, div, ul, li, span, input, text)
import Html.Attributes exposing (autofocus, class, value)
import Html.Events exposing (onClick, onInput)
import Mi
import Mi.Switch
main : Html msg
-- We added a new AddTodo message type.
type Msg
= UpdateText String
| AddTodo
| RemoveTodo Int
-- We added a new property called todos, which is a list of strings.
type alias Model =
{ text : String
, todos : List String
}
-- We added (autofocus True), which is like the native HTML autofocus attribute.
-- We also added a button that triggers an onClick event when clicked which
-- passes an AddTodo message to the update function.
view : Model -> Html Msg
view model =
div [ class "text-center" ]
[ input [ onInput UpdateText, value model.text, autofocus True ] []
, button [ onClick AddTodo, class "btn btn-primary" ] [ text "Add Todo" ]
, ul [] (List.indexedMap (\index todo -> li [] [ text todo, span [onClick (RemoveTodo index)] [text " X"] ]) model.todos)
]
update : Msg -> Model -> Model
update msg model =
case msg of
UpdateText newText ->
{ model | text = newText }
-- We append the model.text value to the end of our list of todo strings.
AddTodo ->
{ model | text = "", todos = model.todos ++ [ model.text ] }
RemoveTodo index ->
let
beforeTodos =
List.take index model.todos
afterTodos =
List.drop (index + 1) model.todos
newTodos =
beforeTodos ++ afterTodos
in
{ model | todos = newTodos }
-- We set the todos property so that it's initially an empty list.
main : Program () Model Msg
main =
text "Hello, World!"
sandbox
{ init = { text = "", todos = [] }
, view = view
, update = update
}

78
frontend/src/Mi.elm Normal file
View File

@ -0,0 +1,78 @@
module Mi exposing (TokenData, errorToString, expectJson, request, tokenDecoder)
import Http exposing (Error(..))
import Json.Decode as D
type alias TokenData =
{ sub : String
, jti : String, aud: String, iss: String
}
tokenDecoder : D.Decoder TokenData
tokenDecoder =
D.map4 TokenData
(D.field "sub" D.string)
(D.field "jti" D.string)
(D.field "aud" D.string)
(D.field "iss" D.string)
request method token path body expect =
Http.request
{ method = method
, body = body
, headers =
[ Http.header "Authorization" token
]
, url = path
, expect = expect
, timeout = Nothing
, tracker = Nothing
}
expectJson : (Result Http.Error a -> msg) -> D.Decoder a -> Http.Expect msg
expectJson toMsg decoder =
Http.expectStringResponse toMsg <|
\response ->
case response of
Http.BadUrl_ url ->
Err (Http.BadUrl url)
Http.Timeout_ ->
Err Http.Timeout
Http.NetworkError_ ->
Err Http.NetworkError
Http.BadStatus_ metadata body ->
Err (Http.BadStatus metadata.statusCode)
Http.GoodStatus_ metadata body ->
case D.decodeString decoder body of
Ok value ->
Ok value
Err err ->
Err (Http.BadBody (D.errorToString err))
errorToString : Http.Error -> String
errorToString err =
case err of
Timeout ->
"Timeout exceeded"
NetworkError ->
"Network error"
BadStatus st ->
"Bad status code: " ++ String.fromInt st
BadBody text ->
"Unexpected response from api: " ++ text
BadUrl url ->
"Malformed url: " ++ url

View File

@ -0,0 +1,56 @@
module Mi.Switch exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Iso8601
import Json.Decode exposing (Decoder, field, int, map5, nullable, string)
import Time exposing (..)
import Url.Builder as UB
type alias Switch =
{ id : String
, who : String
, started_at : Posix
, ended_at : Maybe Posix
, duration : Maybe Int
}
decoder : Decoder Switch
decoder =
map5 Switch
(field "id" string)
(field "who" string)
(field "started_at" Iso8601.decoder)
(field "ended_at" (nullable Iso8601.decoder))
(field "duration" (nullable int))
switchURL : String
switchURL =
UB.absolute
[ "api", "switches", "switch" ]
[]
idURL : String -> String
idURL id =
UB.absolute
[ "api","switches", "id", id ]
[]
frontURL : String
frontURL =
UB.absolute
["api", "switches", "current" ]
[]
listURL : Int -> Int -> String
listURL limit page =
UB.absolute
["api", "switches", "" ]
[ UB.int "limit" limit
, UB.int "page" page
]

View File

@ -1,4 +1,16 @@
{
"gruvbox-css": {
"branch": "master",
"description": "My minimal Gruvbox CSS file I've been keeping multiple places",
"homepage": null,
"owner": "Xe",
"repo": "gruvbox-css",
"rev": "6e1841c94190a1e06e63a2596767e66c35671320",
"sha256": "0s7whnin63558i70wp3by3rydsdx0qdzh5553km1s29w81npmgj6",
"type": "tarball",
"url": "https://github.com/Xe/gruvbox-css/archive/6e1841c94190a1e06e63a2596767e66c35671320.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"naersk": {
"branch": "master",
"description": "Build rust crates in Nix. No configuration, no code generation, no IFD. Sandbox friendly.",