From d504c4f67d2da3aaecabe5bdf780ca6b15025ef2 Mon Sep 17 00:00:00 2001 From: Darko Draskovic Date: Wed, 20 Mar 2019 14:30:38 +0100 Subject: [PATCH] MF-571 - Add Env.elm to set custom base URL (#654) * Add Env module Signed-off-by: Darko Draskovic * Modify README.md Signed-off-by: Darko Draskovic * Remove literal vals from Env.elm Signed-off-by: Darko Draskovic * Replace protocol, host and port env setting by url Signed-off-by: Darko Draskovic * Remove default env.url value and update README.md Signed-off-by: Darko Draskovic * Add http://localhost:80/ as a default baseURL val Signed-off-by: Darko Draskovic * Replace Gateflux by Mainflux Signed-off-by: Darko Draskovic --- ui/README.md | 76 ++++++++++++++++++++----------------------- ui/src/Channel.elm | 20 ++++++------ ui/src/Connection.elm | 4 +-- ui/src/Env.elm | 12 +++++++ ui/src/HttpMF.elm | 48 ++++++++++++++++++--------- ui/src/Main.elm | 2 +- ui/src/Message.elm | 4 +-- ui/src/Thing.elm | 16 ++++----- ui/src/User.elm | 54 +++++++----------------------- ui/src/Version.elm | 10 +++--- 10 files changed, 120 insertions(+), 126 deletions(-) create mode 100644 ui/src/Env.elm diff --git a/ui/README.md b/ui/README.md index a83013b1..e9c0a93a 100644 --- a/ui/README.md +++ b/ui/README.md @@ -3,42 +3,7 @@ Dashboard made with [elm-bootstrap](http://elm-bootstrap.info/). ## Install -### Install GUI as a part of Mainflux build - -Install Elm (https://guide.elm-lang.org/install.html) and then - -``` -git clone https://github.com/mainflux/mainflux -cd mainflux/ui -make -``` - -This will produce `index.html` in the _ui_ directory. In order to use it, `cd` -to _ui_ and do - -`make run` - -### Build a standalone native GUI - -Install Elm (https://guide.elm-lang.org/install.html), `cd` to _ui_ and then - -`elm make --optimize src/Main.elm` - -This will produce `index.html` in the _ui_ directory. In order to use it do - -`make run` - -### About Elm `make` - -`make` does `elm make src/Main.elm`. - -`make run` just executes `elm reactor`. You can execute `elm reactor` in other -terminal window and keep it running, and then see changes as you change-compile -in the first window. You can even use something as -[entr](http://eradman.com/entrproject/) to have your source compiled -automatically when you change and save some files. - -### Build as a part of Docker composition +### Docker container GUI build Install Docker (https://docs.docker.com/install/) and Docker compose (https://docs.docker.com/compose/install/), `cd` to Mainflux root directory and @@ -46,15 +11,44 @@ then `docker-compose -f docker/docker-compose.yml up` -if you want to launch a whole Mainflux docker composition or just +if you want to launch a whole Mainflux docker composition, or just `docker-compose -f docker/docker-compose.yml up ui` if you want to launch just GUI. -### Contribute to the GUI development +### Native GUI build -Install GUI as a part of Mainflux build or as a standalone native GUI and run -it. Launch Mainflux without ui service, either natively or as a Docker -composition. Follow the guidelines for Mainflux contributors found here +Install Elm (https://guide.elm-lang.org/install.html) and then run the following +commands: + +``` +git clone https://github.com/mainflux/mainflux +cd mainflux/ui +make +``` + +This will produce `index.html` in the _ui_ directory. Still in the _mainflux/ui_ +folder, enter + +`make run` + +and follow the instructions on screen. + +**NB:** `make` does `elm make src/Main.elm` and `make run` executes `elm +reactor`. Cf. _Makefile_ for more options. + +## Configuration + +Open the _src/Env.elm_ file and edit the values of the `env` record. + +## Contribute to the GUI development + +Follow the instructions above to install and run GUI as a native build. Instead +of `make run` you can install `elm-live` (https://github.com/wking-io/elm-live) +and execute `elm-live src/Main.elm` to get a live reload when your `.Elm` pages +change. + +Launch Mainflux without ui service, either natively or as a Docker composition. +Please follow the guidelines for Mainflux contributors found here https://mainflux.readthedocs.io/en/latest/CONTRIBUTING/. diff --git a/ui/src/Channel.elm b/ui/src/Channel.elm index 6fd12bba..e62cc4bb 100644 --- a/ui/src/Channel.elm +++ b/ui/src/Channel.elm @@ -24,7 +24,7 @@ import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) import Http -import HttpMF exposing (path) +import HttpMF exposing (paths) import Json.Decode as D import Json.Encode as E import ModalMF @@ -123,7 +123,7 @@ update msg model token = ProvisionChannel -> ( resetEdit model , HttpMF.provision - (B.relative [ path.channels ] []) + (B.relative [ paths.channels ] []) token { emptyChannel | name = Just model.name @@ -160,7 +160,7 @@ update msg model token = UpdateChannel -> ( resetEdit { model | editMode = False } , HttpMF.update - (B.relative [ path.channels, model.channel.id ] []) + (B.relative [ paths.channels, model.channel.id ] []) token { emptyChannel | name = Just model.name @@ -181,7 +181,7 @@ update msg model token = RetrieveChannel channelid -> ( model , HttpMF.retrieve - (B.relative [ path.channels, channelid ] []) + (B.relative [ paths.channels, channelid ] []) token RetrievedChannel channelDecoder @@ -198,7 +198,7 @@ update msg model token = RetrieveChannels -> ( model , HttpMF.retrieve - (B.relative [ path.channels ] (Helpers.buildQueryParamList model.offset model.limit)) + (B.relative [ paths.channels ] (Helpers.buildQueryParamList model.offset model.limit)) token RetrievedChannels channelsDecoder @@ -207,7 +207,7 @@ update msg model token = RetrieveChannelsForThing thingid -> ( model , HttpMF.retrieve - (B.relative [ path.things, thingid, path.channels ] (Helpers.buildQueryParamList model.offset model.limit)) + (B.relative [ paths.things, thingid, paths.channels ] (Helpers.buildQueryParamList model.offset model.limit)) token RetrievedChannels channelsDecoder @@ -224,7 +224,7 @@ update msg model token = RemoveChannel id -> ( resetEdit model , HttpMF.remove - (B.relative [ path.channels, id ] []) + (B.relative [ paths.channels, id ] []) token RemovedChannel ) @@ -433,12 +433,12 @@ updateChannelList model token = ( model , Cmd.batch [ HttpMF.retrieve - (B.relative [ path.channels ] (Helpers.buildQueryParamList model.offset model.limit)) + (B.relative [ paths.channels ] (Helpers.buildQueryParamList model.offset model.limit)) token RetrievedChannels channelsDecoder , HttpMF.retrieve - (B.relative [ path.channels, model.channel.id ] []) + (B.relative [ paths.channels, model.channel.id ] []) token RetrievedChannel channelDecoder @@ -450,7 +450,7 @@ updateChannelListForThing : Model -> String -> String -> ( Model, Cmd Msg ) updateChannelListForThing model token thingid = ( model , HttpMF.retrieve - (B.relative [ path.things, thingid, path.channels ] (Helpers.buildQueryParamList model.offset model.limit)) + (B.relative [ paths.things, thingid, paths.channels ] (Helpers.buildQueryParamList model.offset model.limit)) token RetrievedChannels channelsDecoder diff --git a/ui/src/Connection.elm b/ui/src/Connection.elm index 090bd66a..1f4a49da 100644 --- a/ui/src/Connection.elm +++ b/ui/src/Connection.elm @@ -24,7 +24,7 @@ import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) import Http -import HttpMF exposing (path) +import HttpMF exposing (paths) import List.Extra import Thing import Url.Builder as B @@ -207,7 +207,7 @@ connect checkedThingsIds checkedChannelsIds method token = List.map (\channelid -> HttpMF.request - (B.relative [ path.channels, channelid, path.things, thingid ] []) + (B.relative [ paths.channels, channelid, paths.things, thingid ] []) method token Http.emptyBody diff --git a/ui/src/Env.elm b/ui/src/Env.elm new file mode 100644 index 00000000..4c5eb650 --- /dev/null +++ b/ui/src/Env.elm @@ -0,0 +1,12 @@ +-- Copyright (c) 2019 +-- Mainflux +-- +-- SPDX-License-Identifier: Apache-2.0 + + +module Env exposing (env) + + +env = + { url = "http://localhost:80/" + } diff --git a/ui/src/HttpMF.elm b/ui/src/HttpMF.elm index 9dd9f73d..097208aa 100644 --- a/ui/src/HttpMF.elm +++ b/ui/src/HttpMF.elm @@ -4,9 +4,10 @@ -- SPDX-License-Identifier: Apache-2.0 -module HttpMF exposing (expectID, expectRetrieve, expectStatus, path, provision, remove, request, retrieve, update, url) +module HttpMF exposing (baseURL, expectID, expectRetrieve, expectStatus, paths, provision, remove, request, retrieve, update, user, version) import Dict +import Env exposing (env) import Helpers import Http import Json.Decode as D @@ -14,12 +15,11 @@ import Json.Encode as E import Url.Builder as B -url = - { base = "http://localhost" - } +baseURL = + env.url -path = +paths = { users = "users" , tokens = "tokens" , things = "things" @@ -107,12 +107,30 @@ expectRetrieve toMsg decoder = -- REQUEST +version : String -> (Result Http.Error String -> msg) -> D.Decoder String -> Cmd msg +version path msg decoder = + Http.get + { url = baseURL ++ path + , expect = Http.expectJson msg decoder + } + + +user : String -> String -> String -> E.Value -> Http.Expect msg -> Cmd msg +user email password u value expect = + Http.post + { url = baseURL ++ u + , body = + value |> Http.jsonBody + , expect = expect + } + + request : String -> String -> String -> Http.Body -> (Result Http.Error String -> msg) -> Cmd msg -request u method token b msg = +request path method token b msg = Http.request { method = method , headers = [ Http.header "Authorization" token ] - , url = u + , url = baseURL ++ path , body = b , expect = expectStatus msg , timeout = Nothing @@ -121,11 +139,11 @@ request u method token b msg = retrieve : String -> String -> (Result Http.Error a -> msg) -> D.Decoder a -> Cmd msg -retrieve u token msg decoder = +retrieve path token msg decoder = Http.request { method = "GET" , headers = [ Http.header "Authorization" token ] - , url = u + , url = baseURL ++ path , body = Http.emptyBody , expect = expectRetrieve msg decoder , timeout = Nothing @@ -134,11 +152,11 @@ retrieve u token msg decoder = provision : String -> String -> entity -> (entity -> E.Value) -> (Result Http.Error String -> msg) -> String -> Cmd msg -provision u token e encoder msg prefix = +provision path token e encoder msg prefix = Http.request { method = "POST" , headers = [ Http.header "Authorization" token ] - , url = u + , url = baseURL ++ path , body = encoder e |> Http.jsonBody @@ -149,11 +167,11 @@ provision u token e encoder msg prefix = update : String -> String -> entity -> (entity -> E.Value) -> (Result Http.Error String -> msg) -> Cmd msg -update u token e encoder msg = +update path token e encoder msg = Http.request { method = "PUT" , headers = [ Http.header "Authorization" token ] - , url = u + , url = baseURL ++ path , body = encoder e |> Http.jsonBody @@ -164,11 +182,11 @@ update u token e encoder msg = remove : String -> String -> (Result Http.Error String -> msg) -> Cmd msg -remove u token msg = +remove path token msg = Http.request { method = "DELETE" , headers = [ Http.header "Authorization" token ] - , url = u + , url = baseURL ++ path , body = Http.emptyBody , expect = expectStatus msg , timeout = Nothing diff --git a/ui/src/Main.elm b/ui/src/Main.elm index 2f378dd8..4a4d9909 100644 --- a/ui/src/Main.elm +++ b/ui/src/Main.elm @@ -299,7 +299,7 @@ mfStylesheet = view : Model -> Browser.Document Msg view model = - { title = "Gateflux" + { title = "Mainflux" , body = let buttonAttrs = diff --git a/ui/src/Message.elm b/ui/src/Message.elm index 8932ab8a..0fe9a9f7 100644 --- a/ui/src/Message.elm +++ b/ui/src/Message.elm @@ -23,7 +23,7 @@ import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) import Http -import HttpMF exposing (path) +import HttpMF exposing (paths) import List.Extra import Thing import Url.Builder as B @@ -217,7 +217,7 @@ genChannelRows checkedChannelsIds channels = send : String -> String -> String -> Cmd Msg send channelid thingkey message = HttpMF.request - (B.relative [ "http", path.channels, channelid, path.messages ] []) + (B.relative [ "http", paths.channels, channelid, paths.messages ] []) "POST" thingkey (Http.stringBody "application/json" message) diff --git a/ui/src/Thing.elm b/ui/src/Thing.elm index b2fcd34b..0fd13ca8 100644 --- a/ui/src/Thing.elm +++ b/ui/src/Thing.elm @@ -27,7 +27,7 @@ import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) import Http -import HttpMF exposing (path) +import HttpMF exposing (paths) import Json.Decode as D import Json.Encode as E import ModalMF @@ -143,7 +143,7 @@ update msg model token = ProvisionThing -> ( resetEdit model , HttpMF.provision - (B.relative [ path.things ] []) + (B.relative [ paths.things ] []) token { emptyThing | name = Just model.name @@ -181,7 +181,7 @@ update msg model token = UpdateThing -> ( resetEdit { model | editMode = False } , HttpMF.update - (B.relative [ path.things, model.thing.id ] []) + (B.relative [ paths.things, model.thing.id ] []) token { emptyThing | name = Just model.name @@ -203,7 +203,7 @@ update msg model token = RetrieveThing thingid -> ( model , HttpMF.retrieve - (B.relative [ path.things, thingid ] []) + (B.relative [ paths.things, thingid ] []) token RetrievedThing thingDecoder @@ -220,7 +220,7 @@ update msg model token = RetrieveThings -> ( model , HttpMF.retrieve - (B.relative [ path.things ] (Helpers.buildQueryParamList model.offset model.limit)) + (B.relative [ paths.things ] (Helpers.buildQueryParamList model.offset model.limit)) token RetrievedThings thingsDecoder @@ -237,7 +237,7 @@ update msg model token = RemoveThing id -> ( model , HttpMF.remove - (B.relative [ path.things, id ] []) + (B.relative [ paths.things, id ] []) token RemovedThing ) @@ -491,12 +491,12 @@ updateThingList model token = ( model , Cmd.batch [ HttpMF.retrieve - (B.relative [ path.things ] (Helpers.buildQueryParamList model.offset model.limit)) + (B.relative [ paths.things ] (Helpers.buildQueryParamList model.offset model.limit)) token RetrievedThings thingsDecoder , HttpMF.retrieve - (B.relative [ path.things, model.thing.id ] []) + (B.relative [ paths.things, model.thing.id ] []) token RetrievedThing thingDecoder diff --git a/ui/src/User.elm b/ui/src/User.elm index 667b9a45..1d3e40fa 100644 --- a/ui/src/User.elm +++ b/ui/src/User.elm @@ -19,7 +19,7 @@ import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) import Http -import HttpMF exposing (path) +import HttpMF exposing (baseURL, paths) import Json.Decode as D import Json.Encode as E import Url.Builder as B @@ -66,10 +66,12 @@ update msg model = Create -> ( model - , create + , HttpMF.user model.email model.password - (B.relative [ path.users ] []) + (B.relative [ paths.users ] []) + (encode (User model.email model.password)) + (HttpMF.expectStatus Created) ) Created result -> @@ -82,10 +84,15 @@ update msg model = GetToken -> ( model - , getToken + , HttpMF.user model.email model.password - (B.relative [ path.tokens ] []) + (B.relative [ paths.tokens ] []) + (encode (User model.email model.password)) + (HttpMF.expectRetrieve + GotToken + (D.field "token" D.string) + ) ) GotToken result -> @@ -182,43 +189,6 @@ decoder = -- HTTP -create : String -> String -> String -> Cmd Msg -create email password u = - Http.request - { method = "POST" - , headers = [] - , url = u - , body = - encode (User email password) - |> Http.jsonBody - , expect = HttpMF.expectStatus Created - , timeout = Nothing - , tracker = Nothing - } - - -getToken : String -> String -> String -> Cmd Msg -getToken email password u = - Http.request - { method = "POST" - , headers = [] - , url = u - , body = - encode (User email password) - |> Http.jsonBody - , expect = - HttpMF.expectRetrieve - GotToken - (D.field "token" D.string) - , timeout = Nothing - , tracker = Nothing - } - - - --- Helpers - - loggedIn : Model -> Bool loggedIn model = if String.length model.token > 0 then diff --git a/ui/src/Version.elm b/ui/src/Version.elm index bcfba25f..26958621 100644 --- a/ui/src/Version.elm +++ b/ui/src/Version.elm @@ -10,7 +10,7 @@ import Error import Html exposing (..) import Html.Attributes exposing (..) import Http -import HttpMF exposing (path) +import HttpMF exposing (paths) import Json.Decode as D import Json.Encode as E import Url.Builder as B @@ -35,10 +35,10 @@ update msg model = case msg of GetVersion -> ( model - , Http.get - { url = B.relative [ path.version ] [] - , expect = Http.expectJson GotVersion (D.field "version" D.string) - } + , HttpMF.version + (B.relative [ paths.version ] []) + GotVersion + (D.field "version" D.string) ) GotVersion result ->