Commit c416e2fa authored by Cédric F.'s avatar Cédric F.

Refactor to separate concerns better

parent 15cd7c82
......@@ -137,7 +137,11 @@
break;
case 'title':
document.title = params[0] + " - " + coin + " blockchain - Trappisto";
if (params[0] === "") {
document.title = coin + " blockchain - Trappisto";
} else {
document.title = params[0] + " - " + coin + " blockchain - Trappisto";
}
break;
default:
......
......@@ -7,6 +7,7 @@ import Time exposing (Time)
import Json.Encode as Encode
import Json.Decode as Decode
import Lib.JsonRpc as JsonRpc
import Trappisto.Config exposing (..)
import Trappisto.Helpers exposing (..)
import Components.Transaction as Transaction
......@@ -17,29 +18,29 @@ type alias Model =
, tickets : List String
, fetching : Bool
, error : Maybe String
, coin : Coin
, config : Config
}
initialModel : Coin -> Model
initialModel coin =
initialModel : Config -> Model
initialModel config =
{ address = ""
, transactions = []
, tickets = []
, fetching = False
, error = Nothing
, coin = coin
, config = config
}
modelFromJson : JsonModel -> Coin -> Model
modelFromJson jsonModel coin =
modelFromJson : JsonModel -> Config -> Model
modelFromJson jsonModel config =
{ address = jsonModel.address
, transactions = List.map (\tx -> Transaction.modelFromJson tx coin) jsonModel.transactions
, transactions = List.map (\tx -> Transaction.modelFromJson tx config) jsonModel.transactions
, tickets = []
, fetching = False
, error = Nothing
, coin = coin
, config = config
}
......@@ -138,7 +139,7 @@ update msg model =
updatedModel =
{ model | fetching = True }
in
case model.coin of
case model.config.coin of
DCR ->
( updatedModel, searchRawTransactions updatedModel address )
......@@ -158,7 +159,7 @@ update msg model =
SearchRawTransactionsResult result ->
case result of
Ok jsonModel ->
( modelFromJson jsonModel model.coin, Cmd.none )
( modelFromJson jsonModel model.config, Cmd.none )
Err error ->
( { model
......@@ -196,7 +197,9 @@ searchRawTransactions model address =
, Encode.bool True
]
in
JsonRpc.post "searchrawtransactions"
JsonRpc.send
model.config.rpcEndpoint
"searchrawtransactions"
params
SearchRawTransactionsResult
(decodeSearchRawTransactions address)
......
......@@ -9,6 +9,7 @@ import Json.Decode.Pipeline as Pipeline
import Time exposing (Time)
import Lib.TimeExtra as TimeExtra
import Lib.JsonRpc as JsonRpc
import Trappisto.Config exposing (..)
import Trappisto.Helpers exposing (..)
......@@ -25,12 +26,12 @@ type alias Model =
, nextBlockHash : Maybe String
, fetching : Bool
, error : Maybe String
, coin : Coin
, config : Config
}
initialModel : Coin -> Model
initialModel coin =
initialModel : Config -> Model
initialModel config =
{ hash = ""
, height = -1
, time = -1
......@@ -43,12 +44,12 @@ initialModel coin =
, nextBlockHash = Nothing
, fetching = False
, error = Nothing
, coin = coin
, config = config
}
modelFromJson : JsonModel -> Coin -> Model
modelFromJson jsonModel coin =
modelFromJson : JsonModel -> Config -> Model
modelFromJson jsonModel config =
{ height = jsonModel.height
, hash = jsonModel.hash
, time = Time.second * (toFloat jsonModel.time)
......@@ -61,7 +62,7 @@ modelFromJson jsonModel coin =
, nextBlockHash = jsonModel.nextBlockHash
, fetching = False
, error = Nothing
, coin = coin
, config = config
}
......@@ -126,7 +127,7 @@ view model now =
|> Just
allTransactions =
case model.coin of
case model.config.coin of
DCR ->
[ ( "stake transactions", transactions model.tickets "secondary" )
, ( "regular transactions", transactions model.transactions "light" )
......@@ -199,7 +200,7 @@ update msg model =
GetBlockResult result ->
case result of
Ok jsonModel ->
( modelFromJson jsonModel model.coin, Cmd.none )
( modelFromJson jsonModel model.config, Cmd.none )
Err error ->
( { model
......@@ -229,7 +230,11 @@ getBlock model hash =
params =
Encode.list [ Encode.string hash ]
in
JsonRpc.post "getblock" params GetBlockResult decodeGetBlock
JsonRpc.send model.config.rpcEndpoint
"getblock"
params
GetBlockResult
decodeGetBlock
getBlockHash : Model -> Int -> Cmd Msg
......@@ -238,7 +243,12 @@ getBlockHash model height =
params =
Encode.list [ Encode.int height ]
in
JsonRpc.post "getblockhash" params GetBlockHashResult decodeGetBlockHash
JsonRpc.send
model.config.rpcEndpoint
"getblockhash"
params
GetBlockHashResult
decodeGetBlockHash
decodeGetBlock : Decode.Decoder JsonModel
......
......@@ -10,6 +10,7 @@ import Time exposing (Time)
import Regex
import Lib.TimeExtra as TimeExtra
import Lib.JsonRpc as JsonRpc
import Trappisto.Config exposing (..)
import Trappisto.Helpers exposing (..)
......@@ -25,12 +26,12 @@ type alias Model =
, vOut : List VOut
, fetching : Bool
, error : Maybe String
, coin : Coin
, config : Config
}
initialModel : Coin -> Model
initialModel coin =
initialModel : Config -> Model
initialModel config =
{ hash = ""
, type_ = "?"
, size = -1
......@@ -42,12 +43,12 @@ initialModel coin =
, vOut = []
, fetching = False
, error = Nothing
, coin = coin
, config = config
}
modelFromJson : JsonModel -> Coin -> Model
modelFromJson jsonModel coin =
modelFromJson : JsonModel -> Config -> Model
modelFromJson jsonModel config =
{ hash = jsonModel.hash
, type_ = computeType jsonModel
, size = computeSize jsonModel
......@@ -59,7 +60,7 @@ modelFromJson jsonModel coin =
, vOut = List.sortBy .value jsonModel.vOut |> List.reverse
, fetching = False
, error = Nothing
, coin = coin
, config = config
}
......@@ -129,17 +130,21 @@ view model now =
queryLink hash (toString height) []
formatFees model =
case model.coin of
case model.config.coin of
DCR ->
[ ( "total sent", Just <| formatAmount (totalSent model) )
, ( "fee"
, Just <|
span []
[ formatAmount (fee model)
, span [] [ text " (" ]
, formatAmount (feePerKb model)
, span [] [ text "/kB)" ]
]
(if fee model == 0 then
[ formatAmount (fee model) ]
else
[ formatAmount (fee model)
, span [] [ text " (" ]
, formatAmount (feePerKb model)
, span [] [ text "/kB)" ]
]
)
)
]
......@@ -267,7 +272,7 @@ update msg model =
GetRawTransactionResult result ->
case result of
Ok jsonModel ->
( modelFromJson jsonModel model.coin, Cmd.none )
( modelFromJson jsonModel model.config, Cmd.none )
Err error ->
( { model
......@@ -284,7 +289,9 @@ getRawTransaction model hash =
params =
Encode.list [ Encode.string hash, Encode.int 1 ]
in
JsonRpc.post "getrawtransaction"
JsonRpc.send
model.config.rpcEndpoint
"getrawtransaction"
params
GetRawTransactionResult
(decodeGetRawTransaction True)
......
module Lib.JsonRpc exposing (post, parseError)
module Lib.JsonRpc exposing (send, sendWithoutParams, parseError)
{-
This allows to send JSONRPC requests to the `/rpc` endpoint and decode any errors.
-}
import Http
import Json.Decode
......@@ -17,19 +21,44 @@ type alias RequestParams a =
}
decodeError : String -> String
decodeError json =
send :
String
-> String
-> Json.Encode.Value
-> (Result Http.Error a -> msg)
-> Json.Decode.Decoder a
-> Cmd msg
send endpoint method params result decoder =
let
decode =
Json.Decode.at [ "error", "message" ] Json.Decode.string
|> Json.Decode.decodeString
json =
Json.Encode.object
[ ( "jsonrpc", Json.Encode.string "2.0" )
, ( "id", Json.Encode.int 0 )
, ( "method", Json.Encode.string method )
, ( "params", params )
]
request =
{ method = "POST"
, url = endpoint
, body = Http.jsonBody json
, expect = Http.expectJson decoder
, headers = []
, timeout = Nothing
, withCredentials = False
}
in
case decode json of
Ok result ->
result
request |> Http.request |> Http.send result
Err err ->
"Cannot decode error: " ++ err ++ " | " ++ json
sendWithoutParams :
String
-> String
-> (Result Http.Error a -> msg)
-> Json.Decode.Decoder a
-> Cmd msg
sendWithoutParams endpoint method result decoder =
send endpoint method (Json.Encode.list []) result decoder
parseError : Http.Error -> Maybe String
......@@ -48,34 +77,16 @@ parseError error =
Just (toString error)
post :
String
-> Json.Encode.Value
-> (Result Http.Error a -> msg)
-> Json.Decode.Decoder a
-> Cmd msg
post method params result decoder =
Http.send result <|
Http.request <|
makeRequest method params decoder
makeRequest : String -> Json.Encode.Value -> Json.Decode.Decoder a -> RequestParams a
makeRequest method params decoder =
decodeError : String -> String
decodeError json =
let
json =
Json.Encode.object
[ ( "jsonrpc", Json.Encode.string "2.0" )
, ( "id", Json.Encode.int 0 )
, ( "method", Json.Encode.string method )
, ( "params", params )
]
decode =
Json.Decode.at [ "error", "message" ] Json.Decode.string
|> Json.Decode.decodeString
in
{ method = "POST"
, url = "/rpc"
, body = Http.jsonBody json
, expect = Http.expectJson decoder
, headers = []
, timeout = Nothing
, withCredentials = False
}
case decode json of
Ok result ->
result
Err err ->
"Cannot decode error: " ++ err ++ " | " ++ json
module Lib.WebSocket exposing (send, isSuccess, isMethod)
module Lib.WebSocket exposing (listen, send, isSuccess, isMethod)
{-
This allows to send web socket messages to the `/ws` endpoint and parse notification messages.
-}
import WebSocket
import Json.Decode as Decode
import Json.Encode as Encode
listen : String -> (String -> msg) -> Sub msg
listen endpoint msg =
WebSocket.listen endpoint msg
send : String -> String -> List Encode.Value -> Cmd msg
send endpoint method params =
let
......
module Trappisto.Config exposing (..)
type Coin
= BCH
| BTC
| DCR
type alias Config =
{ coin : Coin
, rpcEndpoint : String
, wsEndpoint : String
}
module Trappisto.Decoder exposing (..)
import Keyboard
import Json.Decode as Decode
import Trappisto.Model exposing (Keys, BestBlock, BasicTransaction)
decodeGetBestBlock : Decode.Decoder BestBlock
decodeGetBestBlock =
Decode.map2 BestBlock
(Decode.at [ "result", "hash" ] Decode.string)
(Decode.at [ "result", "height" ] Decode.int)
decodeTxAccepted : String -> Maybe BasicTransaction
decodeTxAccepted json =
let
decodeParams =
Decode.map2 BasicTransaction
(Decode.index 0 Decode.string)
(Decode.index 1 Decode.float)
in
Decode.decodeString (Decode.field "params" decodeParams) json |> Result.toMaybe
decodeKeys : Bool -> Keyboard.KeyCode -> Keys -> Keys
decodeKeys bool keyCode keys =
case keyCode of
13 ->
{ keys | enter = bool }
27 ->
{ keys | esc = bool }
68 ->
{ keys | d = bool }
73 ->
{ keys | i = bool }
74 ->
{ keys | j = bool }
75 ->
{ keys | k = bool }
_ ->
keys
module Trappisto.Helpers exposing (..)
{-
Generic helpers to help with formatting and creating views. Those should not
be specific to Trappisto and could be reused in other projects. If multiple
helpers fit the same "category", they should be extracted into a library and
moved to src/Lib.
-}
import Html exposing (..)
import Html.Attributes exposing (..)
import Lib.HtmlAttributesExtra as HtmlAttributesExtra
type Coin
= BCH
| BTC
| DCR
pluralize : Int -> String -> String
pluralize count singular =
let
......
......@@ -5,18 +5,15 @@ import Window
import Keyboard
import Time exposing (Time)
import Http exposing (Error)
import Trappisto.Config exposing (..)
import Components.Address as AddressComponent
import Components.Block as BlockComponent
import Components.Transaction as TransactionComponent
import Trappisto.Helpers exposing (Coin)
type alias Flags =
{ coin : String }
type alias Config =
{ coin : Coin }
{ coin : String
}
type Template
......@@ -36,40 +33,38 @@ type alias Model =
, query : String
, template : Template
, error : Maybe String
, wsEndpoint : String
, vimMode : Bool
, debug : Bool
, now : Time
, fetching : Bool
, webSocketConnected : Bool
, lastWebSocketPong : Time
, lastBlockHash : String
, lastBlockHeight : Int
, lastTransactions : List BasicTransaction
, fetching : Bool
, webSocketConnected : Bool
}
initialModel : Coin -> String -> String -> Model
initialModel coin wsEndpoint query =
{ config = Config coin
initialModel : Config -> String -> Model
initialModel config query =
{ config = config
, keys = Keys False False False False False False
, window = Window.Size 0 0
, addressModel = AddressComponent.initialModel coin
, blockModel = BlockComponent.initialModel coin
, transactionModel = TransactionComponent.initialModel coin
, addressModel = AddressComponent.initialModel config
, blockModel = BlockComponent.initialModel config
, transactionModel = TransactionComponent.initialModel config
, query = query
, template = Home
, error = Nothing
, wsEndpoint = wsEndpoint
, vimMode = False
, debug = False
, fetching = False
, webSocketConnected = False
, now = -1
, lastWebSocketPong = -1
, lastBlockHash = ""
, lastBlockHeight = -1
, lastTransactions = []
, fetching = False
, webSocketConnected = False
}
......@@ -79,12 +74,12 @@ type Msg
| BlockMsg BlockComponent.Msg
| TransactionMsg TransactionComponent.Msg
| JsMsg (List String)
| WebSocketMsg String
| Query String
| KeyChange Bool Keyboard.KeyCode
| Resize Window.Size
| Tick Time
| WSMsg String
| GetBestBlock
| GetBestBlockx
| GetBestBlockResult (Result Http.Error BestBlock)
......
This diff is collapsed.
......@@ -197,10 +197,10 @@ view model =
formatTransaction tx =
let
size =
toString (Basics.min 9 (logBase 10 tx.amount * 4)) ++ "rem"
toString (Basics.clamp 3 9 (logBase 10 tx.amount * 4)) ++ "rem"
fontSize =
toString (Basics.min 3 (logBase 10 tx.amount * 1.5)) ++ "rem"
toString (Basics.clamp 1 3 (logBase 10 tx.amount * 1.5)) ++ "rem"
amount =
toString (round tx.amount) ++ "<br>DCR"
......@@ -222,18 +222,7 @@ view model =
else
case model.template of
Home ->
case model.query of
"" ->
[ searchView model, errorView model, ascii, lastTransactions ]
"particles" ->
[ div [ class "row" ]
[ div [ class "col" ] [ text "Reticulating splines..." ]
]
]
_ ->
[ searchView model, errorView model ]
[ searchView model, errorView model, ascii, lastTransactions ]
Address ->
if isError model then
......
......@@ -9,9 +9,17 @@ import Lib.WebSocket as WebSocket
import Components.Address as Address
import Components.Block as Block
import Components.Transaction as Transaction
import Trappisto.Config as Config
import Trappisto.Model exposing (..)
import Trappisto.Update
import Trappisto.Helpers as Coin exposing (Coin)
import Trappisto.Decoder as Decoder
testConfig : Config.Config
testConfig =
{ coin = Config.DCR
, rpcEndpoint = "https://localhost.test:8000/rpc"
, wsEndpoint = "wss://localhost.test:8000/ws"
}
suite : Test
......@@ -31,7 +39,7 @@ suite =
model =
case result of
Ok jsonModel ->
Address.modelFromJson jsonModel Coin.DCR
Address.modelFromJson jsonModel testConfig
Err error ->
Debug.crash error
......@@ -58,7 +66,9 @@ suite =
model =
case result of
Ok jsonModel ->
Transaction.modelFromJson jsonModel Coin.DCR
Transaction.modelFromJson
jsonModel
testConfig
Err error ->
Debug.crash error
......@@ -96,6 +106,22 @@ suite =
in
Expect.equal "v4" <| Transaction.computeVote jsonModel
]
, describe "Decoder"
[ test "decodeTxAccepted" <|
\() ->
let
txAcceptedFixture =
"{\"jsonrpc\":\"1.0\",\"method\":\"txaccepted\",\"params\":[\"e943704526165772229307a3e2406f5805160a9a0d33d702691044daef0ecbb2\",72.31055621],\"id\":null}"
maybeTransaction =
Decoder.decodeTxAccepted txAcceptedFixture
in
Expect.equal maybeTransaction <|
Just <|
BasicTransaction
"e943704526165772229307a3e2406f5805160a9a0d33d702691044daef0ecbb2"
72.31055621
]
, describe "WebSocket"
[ test "isSuccess" <|
\() ->
......@@ -129,27 +155,6 @@ suite =
WebSocket.isMethod [ "txaccepted" ] txAcceptedFixture
in
Expect.equal result True
, test "decodeTxAccepted" <|
\() ->
let
txAcceptedFixture =
"{\"jsonrpc\":\"1.0\",\"method\":\"txaccepted\",\"params\":[\"e943704526165772229307a3e2406f5805160a9a0d33d702691044daef0ecbb2\",72.31055621],\"id\":null}"
result =
Trappisto.Update.decodeTxAccepted txAcceptedFixture
model =
case result of
Ok model ->
model
Err error ->
Debug.crash error
in
Expect.equal model <|
BasicTransaction
"e943704526165772229307a3e2406f5805160a9a0d33d702691044daef0ecbb2"
72.31055621
]
, describe "Fuzz test examples, using randomly generated input"
-- XXX: keping them as examples
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment