Elm updating model with http - http

I am trying to update my bitcoin price field in my List Token Model in my update function. This is my code I cannot seem to get it to only update the price field. Do I need to access the list elements seeing as my model is list? Is this possible to do using record syntax in elm?
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Bass exposing (style, center, h1)
import Http
import Json.Decode as Decode
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
------------- MODEL
type alias Model =
{ tokens : List Token
}
init : (Model, Cmd Msg)
init =
(initialModel , Cmd.none)
initialModel : Model
initialModel =
{ tokens = [Token "Bitcoin" "150" "11000.00"]
}
type alias Token =
{ name : String
, holdings : String
, price : String
}
------------- UPDATE
type Msg
= FetchDatabasePrice | FetchLivePrice (Result Http.Error String)
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
FetchDatabasePrice ->
(model, getPrice )
FetchLivePrice (Ok newPrice) ->
( { model | price = newPrice }, Cmd.none )
FetchLivePrice (Err _) ->
(model,Cmd.none)
getPrice : Cmd Msg
getPrice =
let
url = "https://api.coinmarketcap.com/v1/ticker/bitcoin/"
request = Http.get url decodedUrl
in
Http.send FetchLivePrice request
decodedUrl : Decode.Decoder String
decodedUrl = Decode.at ["price_usd"] Decode.string
------------- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
------------- VIEW
view : Model -> Html Msg
view model =
div []
[nav
, div [] [list model.tokens]
, div [] [ button [onClick (FetchDatabasePrice) ] [text "Fetch Price"] ]
]
------BROKEN INTO PIECES---------
nav : Html Msg
nav = div [Bass.style
[center
, Bass.h1
, [("background-color", "black")
, ("color", "white")
]
]
]
[ div [] [text "Crypto Nutshell"]]
list : List Token -> Html Msg
list tokens =
div [Bass.style
[Bass.left_align
]
]
[div [ class "p1"]
[ table []
[ thead []
[ tr []
[ th [] [text "Name"]
, th [] [text "Holdings"]
, th [] [text "Price"]
, th [] [text "Actions"]
]
]
, tbody [] (List.map tokenRow tokens)
]
]
]
tokenRow : Token -> Html Msg
tokenRow token =
tr []
[ td [] [ text token.name ]
, td [] [ text token.holdings ]
, td [] [ text token.price ]
]
This is my error:
-- TYPE MISMATCH ------------------------------------------------------ test.elm
The definition of `update` does not match its type annotation.
50| update : Msg -> Model -> (Model, Cmd Msg)
51| update msg model =
52|> case msg of
53|> FetchDatabasePrice ->
54|> (model, getPrice )
55|> FetchLivePrice (Ok newPrice) ->
56|> ( { model | price = newPrice }, Cmd.none )
57|> FetchLivePrice (Err _) ->
58|> (model,Cmd.none)
The type annotation for `update` says it always returns:
( Model, Cmd Msg )
But the returned value (shown above) is a:
( { b | price : String }, Cmd Msg )
Hint: The record fields do not match up. One has tokens. The other has price.
-- TYPE MISMATCH ------------------------------------------------------ test.elm
`model` is being used in an unexpected way.
56| ( { model | price = newPrice }, Cmd.none )
^^^^^
Based on its definition, `model` has this type:
Model
But you are trying to use it as:
{ b | price : a }
Hint: The record fields do not match up. One has tokens. The other has price.

The type errors are fundamentally telling you your issue - you are trying to work on a Token, but you don't have one - you have a Model.
How do we get from one to the other? Well. We start with a model, and we can do model.tokens to get a List Token. We then want to modify that list to contain the new tokens, updated. The normal way to do this is with List.map. This operates on each Token and gives us the updated list. Following these steps:
FetchLivePrice (Ok newPrice) ->
let
updatePrice = (\token -> { token | price = newPrice })
updated = List.map updatePrice model.tokens
in
({ model | tokens = updated }, Cmd.none )
Now, the solution I've given is a simple one that will fall apart when you have multiple different tokens (they will all get changed at the same time). As you only have one right now, the same thing could be achieved just by simplifying the model to take only a single token, not a list.
You will need to start identifying which token you are getting the price for so you can update the right one, if you want to be able to use the ability to have multiple tokens.
In reality, you probably want this to end up looking something like:
FetchLivePrice tokenId (Ok newPrice) ->
({ model | tokens = tokenUpdatePrice tokenId newPrice model.tokens, Cmd.none)
Where tokenUpdatePrice is a function that manipulates your list (or other data structure - a dictionary might be appropriate, although you might need to store a separate order for presentation) to update the appropriate records. tokenId will be something used to identify the token.

Related

Kusto: Permission based display of columns

I am trying to access function parameters within the 'case' statement in that function and displaying data/"filtered" based on the permission flag..Is it possible?
Usecase: TypeCast the value based on the columnType and check if the user has the permission to view the column based on which you display either the value or say something like "filtered"
Here is what I tried
function rls_columnCheck
.create-or-alter function rls_columnCheck(tableName:string, columnName: string, value:string, columnType:string, IsInGroupPII:bool, IsInGroupFinance:bool) {
let PIIColumns = rls_getTablePermissions(tableName, "PII");
let FinanceColumns = rls_getTablePermissions(tableName, "Finance");
let val= case(columnType=="bool", tobool(value),
columnType=="datetime", todatetime(value),
columnType=="int", toint(value),
value);
iif(columnName in (PIIColumns),
iif(columnName in (FinanceColumns),
iif(IsInGroupPII == true and IsInGroupFinance == true,
val,
"filtered"), // PII True, Fin True
iif(IsInGroupPII == true,
val,
"filtered") // PII True, Fin False
),
iif(columnName in (FinanceColumns),
iif(IsInGroupFinance == true,
val,
"filtered"), // PII False, Fin True
val // PII False, Fin False
)
);
}
Error:
Call to iff(): #then data type (int) must match the #else data type (string)
val in your function must have a single and well-defined data type, that is known at "compile" time of the query.
you can't have different cases, where in each it has a different type (bool, datetime, int, string - in your case statement) - hence the error.
if it makes sense in your use case, you can try to always have val typed as string.
This is not a good approach to use RLS because this will actually cause the engine to run a function for every column of every record. It has many downsides:
Performance of displaying the table’s contents (even if you have full permissions)
Queries on the table won’t benefit from the indexes Kusto stores (suppose you query PermissionTesting2 | where Col1 has “blablabla” - instead of checking the index for “blablabla”, the engine will have to scan all the data, because it has to apply a function for every single cell)
A better approach is to do something like this:
let UserCanSeePII = current_principal_is_member_of('aadgroup=group1#domain.com');
let UserCanSeeFinance = current_principal_is_member_of('aadgroup=group2#domain.com');
let ResultWithPII = YourTable | where UserCanSeePII and (not UserCanSeeFinance) | where ... | extend ...;
let ResultWithFinance = YourTable | where UserCanSeeFinance and (not UserCanSeePII) | where ... | extend ...;
let ResultWithPIIandFinance = YourTable | where UserCanSeeFinance and UserCanSeePII | where ... | extend ...;
let ResultWithoutPIIandFinance = YourTable | where (not UserCanSeePII) and (not UserCanSeeFinance) | where ... | extend ...;
union ResultWithPII, ResultWithFinance, ResultWithPIIandFinance, ResultWithoutPIIandFinance

How to delete and output a new string in a text field?

I want to clear the text field and want to create a new placeholder when pressing a button. Additionally I want an Increment in the placeholder that counts every new text field that is created (player 1, player 2, player 3, etc.) I tried to use the Increment button from the elm docs. But I get an error:
Something is off with the 5th branch of this `case` expression:
78| model "Player" + 1
^^^^^^^^^^^^^^^^^^
The 5th branch is:
number
But the type annotation on `update` says it should be:
Model
I tried to create a new string player that adds every time the "+add" button is pressed +1. So with an onClick event + the name(Clear), how it is also done in the docs.
Here is my Elm Code:
--imports
...
-- MAIN
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Player =
{ player : String
, strength : Int
, number : Int
}
type alias Model =
{ content : String
, teams : List Player
, currentNumber : Int
, currentPlayer : String
, currentStrength : Int
}
init : Model
init =
{ content = ""
, teams = []
, currentNumber = 0
, currentPlayer = ""
, currentStrength = 0
}
-- UPDATE
type Msg
= Change String
| Clear
update : Msg -> Model -> Model
update msg model =
case msg of
Change newContent ->
{ model | content = newContent }
Clear ->
model "Player" + 1
-- VIEW
view : Model -> Html Msg
view model =
div []
[ h1 [style "font-family" "impact"] [ text "Team Creator" ]
, h2 [style "font-family" "impact"] [ text "Name and Strength:" ]
, div[] [ input [ placeholder "🏅 Player 1", style "width" "300px"] [] ]
, input [ placeholder "💪🏼 Strength", style "width" "300px"] []
, div [] [ button [ style "background-color" "#66cc81", style "color" "white", onClick Clear] [ text "+ADD" ] ]
, h2 [style "font-family" "impact"] [ text "Teams:" ]
, div [] [ text (model.currentPlayer)]
]
As the error message states, you are returning a type String from a function that returns Model by its type signature. The problem is this line
Clear ->
model "Player" + 1
It is a bit unclear what this is supposed to do, but in the case expression above, the code creates a new model where it makes the update content = newContent. If you want to update some other field in the model, you need a similar pattern. For example (not 100% sure about the syntax, but you'll hopefully get the idea)
Clear ->
{ model | currentNumber = currentNumber + 1 }

ELM update two fields in model

How can i update two fields in model at once?
Now i have request that return me an alias with {price: Float, productId: Int}
I need to update two fields in model like model.price and model.productId
I looking for something like that, but its not work (ofc)
case maybeProduct of
Just product ->
( { model | price = product.price &&
model | productId = product.productId}
, Cmd.none
)
Nothing ->
( model
, Cmd.none
)
I found some info where advice that I can create two functions
(Model -> Product -> Model) and do something like:
setPrice : Model -> Product -> Model
setPrice model product =
{ model | price = product.price }
setProductId : Model -> Product -> Model
setProductId model product =
{ model | companyId = product.productId }
Just product ->
let
newModel =
product
|> setPrice model
|> setProductId model
in
( newModel
, Cmd.none
)
Nothing ->
( model
, Cmd.none
)
but something dont work. It looks like product dont passing in each function
i recieve
The argument is:
Model
But (|>) is piping it a function that expects:
{ companyId : Int, price : Float }
Where i mistake? Or maybe there is different way to update two fields in model?
How to update two fields at once:
Do something like this (this excludes commands by the way - you can add that in to suit your requirements):
{ model | price = new_price, productId = newProductId}
If you want to add Commands then you can do this:
({ model | price = new_price, productId = newProductId}, nameOfYourCommand)
Documentation:
Here is a link that #JackLeow very kindly posted: https://elm-lang.org/docs/records#updating-records

Http DELETE with empty body

In Elm (0.18) I'm calling one http DELETE endpoint that if successful responds with 200 and an empty body.
In this case (of success) I need to pass back a message with the initial id (OnDelete playerId). But as the body is empty i can't parse it from there.
Currently I'm doing it like this, but is there a more elegant way to write the expect part of Http.Request:
Http.expectStringResponse (\response -> Ok playerId)
?
This reflects my current code:
deletePlayer : PlayerId -> Cmd Msg
deletePlayer playerId =
deleteRequest playerId
|> Http.send OnDelete
deleteRequest : PlayerId -> Http.Request PlayerId
deleteRequest playerId =
Http.request
{ body = Http.emptyBody
, expect = Http.expectStringResponse (\response -> Ok playerId)
, headers = []
, method = "DELETE"
, timeout = Nothing
, url = "http://someHost/players/" ++ playerId
, withCredentials = False
}
type alias PlayerId =
String
Elm v0.19 added expectWhatever. It behaves slightly different with the Result being checked for errors, but a similar effect.
I've created a helper expectUnit for "empty" 200 responses.
expectUnit : Expect ()
expectUnit =
Http.expectStringResponse << always <| Ok ()
deleteThing : String -> Request ()
deleteThing path =
Http.request
{ method = "DELETE"
, headers = []
, url = "http://localhost/api"
, body = Http.jsonBody <| Encode.object [ ( "path", Encode.string path ) ]
, expect = expectUnit
, timeout = Nothing
, withCredentials = False
}
But for you, the best you could get is.
{ ...
, expect = Http.expectStringResponse << always <| Ok playerId
...
}
Or you could create a helper (which is actually the singleton or pure for Expect)
alwaysExpect : a -> Expect a
alwaysExpect =
Http.expectStringResponse << always << Ok
Which could be used like
{ ...
, expect = alwaysExpect playerId
...
}

Printing the results from a select query with Genie

I have created the database in SQL lite and improved the little program to handle it (list, add, remove records). At this point I am trying to list the contents from the database using the prepared statement step() function. However, I can't iterate over the rows and columns on the database.
I suspect that the reason for that is that I am not handling the statement appropriately in this line:
stmt:Sqlite.Statement = null
If that is the case, how to pass the statement from the main (init) function to the children function?
This is the entire code so far:
// Trying to do a cookbook program
// raw_imput for Genie included, compile with valac --pkg sqlite3 cookbook.gs
[indent=4]
uses Sqlite
def raw_input (query:string = ""):string
stdout.printf ("%s", query)
return stdin.read_line ()
init
db : Sqlite.Database? = null
if (Sqlite.Database.open ("cookbook.db3", out db) != Sqlite.OK)
stderr.printf ("Error: %d: %s \n", db.errcode (), db.errmsg ())
Process.exit (-1)
loop:bool = true
while loop = true
print "==================================================="
print " RECIPE DATABASE "
print " 1 - Show All Recipes"
print " 2 - Search for a recipe"
print " 3 - Show a Recipe"
print " 4 - Delete a recipe"
print " 5 - Add a recipe"
print " 6 - Print a recipe"
print " 0 - Exit"
print "==================================================="
response:string = raw_input("Enter a selection -> ")
if response == "1" // Show All Recipes
PrintAllRecipes()
else if response is "2" // Search for a recipe
pass
else if response is "3" //Show a Recipe
pass
else if response is "4"//Delete a recipe
pass
else if response is "5" //Add a recipe
pass
else if response is "6" //Print a recipe
pass
else if response is "0" //Exit
print "Goodbye"
Process.exit (-1)
else
print "Unrecognized command. Try again."
def PrintAllRecipes ()
print "%-5s%-30s%-20s%-30s", "Item", "Name", "Serves", "Source"
print "--------------------------------------------------------------------------------------"
stmt:Sqlite.Statement = null
param_position:int = stmt.bind_parameter_index ("$UID")
//assert (param_position > 0)
stmt.bind_int (param_position, 1)
cols:int = stmt.column_count ()
while stmt.step () == Sqlite.ROW
for i:int = 0 to cols
i++
col_name:string = stmt.column_name (i)
val:string = stmt.column_text (i)
type_id:int = stmt.column_type (i)
stdout.printf ("column: %s\n", col_name)
stdout.printf ("value: %s\n", val)
stdout.printf ("type: %d\n", type_id)
/* while stmt.step () == Sqlite.ROW
col_item:string = stmt.column_name (1)
col_name:string = stmt.column_name (2)
col_serves:string = stmt.column_name (3)
col_source:string = stmt.column_name (4)
print "%-5s%-30s%-20s%-30s", col_item, col_name, col_serves, col_source */
Extra questions are:
Does the definitions of functions should come before or after init? I have noticed that they wouldn't be called if I left all of them after init. But by leaving raw_input in the beginning the error disappeared.
I was trying to define PrintAllRecipes() within a class, for didactic reasons. But I ended up making it "invisible" to the main routine.
Many thanks,
Yes, you need to assign a prepared statement, not null, to stmt. For example:
// Trying to do a cookbook program
// raw_input for Genie included, compile with
// valac --pkg sqlite3 --pkg gee-0.8 cookbook.gs
[indent=4]
uses Sqlite
init
db:Database
if (Database.open ("cookbook.db3", out db) != OK)
stderr.printf ("Error: %d: %s \n", db.errcode (), db.errmsg ())
Process.exit (-1)
while true
response:string = UserInterface.get_input_from_menu()
if response is "1" // Show All Recipes
PrintAllRecipes( db )
else if response is "2" // Search for a recipe
pass
else if response is "3" //Show a Recipe
pass
else if response is "4"//Delete a recipe
pass
else if response is "5" //Add a recipe
pass
else if response is "6" //Print a recipe
pass
else if response is "0" //Exit
print "Goodbye"
break
else
print "Unrecognized command. Try again."
namespace UserInterface
def get_input_from_menu():string
show_menu()
return raw_input("Enter a selection -> ")
def raw_input (query:string = ""):string
stdout.printf ("%s", query)
return stdin.read_line ()
def show_menu()
print """===================================================
RECIPE DATABASE
1 - Show All Recipes
2 - Search for a recipe
3 - Show a Recipe
4 - Delete a recipe
5 - Add a recipe
6 - Print a recipe
0 - Exit
==================================================="""
namespace PreparedStatements
def select_all( db:Database ):Statement
statement:Statement
db.prepare_v2( """
select name, servings as serves, source from Recipes
""", -1, out statement )
return statement
def PrintAllRecipes ( db:Database )
print "%-5s%-30s%-20s%-30s", "Item", "Name", "Serves", "Source"
print "--------------------------------------------------------------------------------------"
stmt:Statement = PreparedStatements.select_all( db )
cols:int = stmt.column_count ()
var row = new dict of string, string
item:int = 1
while stmt.step() == ROW
for i:int = 0 to (cols - 1)
row[ stmt.column_name( i ) ] = stmt.column_text( i )
stdout.printf( "%-5s", item.to_string( "%03i" ))
stdout.printf( "%-30s", row[ "name" ])
stdout.printf( "%-20s", row[ "serves" ])
stdout.printf( "%-30s\n", row[ "source" ])
item++
A few pointers
Generally you want to avoid assigning null. null is no value. For example a boolean can either be true or false and nothing else, but a variable that can have no value makes things more complicated.
a:bool? = null
if a == null
print "I'm a boolean variable, but I am neither true nor false???"
If you are looking to declare a variable in Genie before assigning a value, for example when calling a function with an out parameter, don't assign anything. I have changed db:Database to show this
Process.exit( -1 ) should probably be used sparingly and really only for error conditions that you want to signal to a calling command line script. I don't think a user selected exit from the program is such an error condition, so I have changed Process.exit( -1 ) to break for that
The definition of functions doesn't matter whether it is before or after init, I prefer to put them after so the first function that is called, i.e. init, is at the top and easy to read
A class is a data type and yes, it can have functions, but usually you need some data defined in the class and the function is written to act on that data. A function in a class is often called a 'method' and in the past with object oriented programming classes were defined to group methods together. These methods had no data to act on and are defined as 'static' methods. The modern practise is to mainly use static methods for creating more complex object constructors, look up 'factory' methods and creational design patterns. Instead to group functions, and other syntax, we use namespaces. I have used a couple of namespaces in the example. Usually a namespace is given its own file or files. If you are thinking of splitting your Genie project into more source files then take a look at https://wiki.gnome.org/Projects/Genie#A_Simple_Build_Script
A primary key should be internal to the database and would not be presented to a user, only a database administrator would be interested in such things. So I have changed 'item' in the output to be a count of the number of entries displayed
Genie and Vala bind the SQLite C interface. If you need more details on a particular function take a look at C-language Interface Specification for SQLite

Resources