I am exploring ELM and trying to access web api. I followed the this link.
I am able to hit my service and getting the response (Showing in browser network tab), but failure code portion is executing from elm update.
Implementation
---Model
type alias Model =
{
message : String,
}
model:Model
model = {
message = "Hello"
}
--update
postRequest : Http.Request
postRequest =
{ verb = "POST"
, headers =
[("Content-Type", "application/json")
]
, url = "http://xyz/events/list"
, body = Http.string """{ "domainId": 1 }"""
}
getEventList: Cmd Msg
getEventList =
Task.perform Messages.FetchFail Messages.FetchSucceed (Http.fromJson decodeString (Http.send Http.defaultSettings postRequest))
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Messages.NoOp ->
( model, Cmd.none )
Messages.FetchSucceed xy->
({model| message = "success"},Cmd.none)
Messages.FetchFail _->
({model |message = "fail"} ,Cmd.none)
API response :
{ "message": "", "data": [
{
"eventId": 104,
"title": "private Events",
"description": "abc",
"businessId": 51,
"businessTitle": "VampireDA_Adda",
"startDate": "2016-07-08"
},
{
"eventId": 107,
"title": "Event weekly Midnight",
"description": "xyz",
"businessId": 44,
"businessTitle": "Spanish Scotch",
"startDate": "2016-07-08"
}] }
Please help me, if i have implement any thing wrong.
and also how i can decode json to model (fill model with response json)?
you are probably getting the error because the decoding is not working out. But first try to get used to piping
Http.send Http.defaultSettings postRequest
|> Http.fromJson decodeString
|> Task.perform Messages.FetchFail Messages.FetchSucceed
To decode you need an Elm record
type alias DataItem =
{ eventId: Int
, title: String
, description : String
, businessId : Int
, businessTitle : String
, startDate: String
}
The decoder will then look something like
dataDecoder =
object6 DataItem
("eventId" := int)
("title" := string)
("description" := string)
("businessId" := int)
("businessTitle" := string)
("startDate" := string)
decoder = at ["data"] (list dataDecoder)
Decoders take a bit of getting used to, so I created a site to help you practise: http://simonh1000.github.io/decoder/
Related
I wish to read google spreadsheets conditional format information with script. I do it as below:
function readConditionalFormatInfo() {
var url = "https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId?fields=sheets(properties(title,sheetId),conditionalFormats)"
var response = UrlFetchApp.fetch(url)
Logger.log(response)
}
But error happen:
Request failed for https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId?fields=sheets(properties(title,sheetId),conditionalFormats) returned code 403. Truncated server response: { "error": { "code": 403, "message": "The request is missing a valid API key.", "status": "PERMISSION_DENIED" } } (use muteHttpExceptions option to examine full response) (line 214, file "Code")
Maybe I should not run REST API in GAS but I don't know how to do it in script!
Appreciate if anyone can help!
Something like below:
function readConditionalFormat() {
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var rules = sheet.getConditionalFormatRules();
if (rules != null) {
for (var i=0;i<rules.length;i++) {
var rule = rules[i].getBooleanCondition()
var criteria = rule.getCriteriaType()
var value = rule.getCriteriaValues()
var bakcolor = rule.getBackground()
Logger.log(criteria+ " -> " + value + ":" + bakcolor);
}
} else {
Logger.log('Conditional Format rule null')
}
}
I have used the http post in elm in following manner but it is not working and on server side I am getting the empty body.
Can anybody tell me what is wrong with this code.this is the code of my command module
module SignupForm.Commands exposing (..)
import Http exposing(..)
import Json.Decode as Decode exposing (field)
import SignupForm.Models exposing (..)
import SignupForm.Messages exposing (..)
import Json.Encode as Encode
import Debug exposing (log)
memberDecoder : Decode.Decoder UserDetails
memberDecoder =
Decode.map4 UserDetails
(field "fname" Decode.string)
(field "lname" Decode.string)
(field "email" Decode.string)
(field "password" Decode.string)
saveUrl : String
saveUrl =
"http://localhost:4000/userSignup"
saveRequest : UserDetails -> Http.Request UserDetails
saveRequest d =
Http.request
{ body = memberEncoded d |> Http.jsonBody
, expect = Http.expectJson memberDecoder
, headers = defaultRequestHeaders
, method = "POST"
, timeout = Nothing
, url = saveUrl
, withCredentials = False
}
defaultRequestHeaders : List Header
defaultRequestHeaders =
[ Http.header "Content-Type" "application/x-www-form-urlencoded"
]
fetchAll : UserDetails -> Cmd Msg
fetchAll data =
saveRequest data
|> Http.send OnSignUp
memberEncoded : UserDetails -> Encode.Value
memberEncoded data =
let
list =
[ ( "fname", Encode.string data.fname )
, ( "lname", Encode.string data.lname )
, ( "email", Encode.string data.email )
, ( "password", Encode.string data.password )
]
in
list
|> Encode.object
You are posting json, but declaring that you are sending formdata.
Try removing:
defaultRequestHeaders : List Header
defaultRequestHeaders =
[ Http.header "Content-Type" "application/x-www-form-urlencoded"
]
(Note that expectJson will add Http.header "Content-Type" "application/json" automatically for you
i have the following type User:
type alias User =
{ id : Int
, name : String
, age : Maybe Int
, deleted : Bool
}
User is a type used in my Model:
type alias Model =
{ users : List User
, name : String
, age : String
, message : String
}
When I iterate over "List User" using List.map like this...
Delete id ->
let
newUserList =
List.map
(\user ->
if user.id == id then
{ user | deleted = True }
else
user
)
model.users
in
( { model | users = newUserList }, Cmd.none )
... the Compiler tells me:
The 2nd argument to function `map` is causing a mismatch.
List.map
(\user ->
if user.id == id then
{ user | deleted = True }
else
user
)
model.users
Function `map` is expecting the 2nd argument to be:
List { a | id : Int, name : String }
But it is:
List (Bool -> User)
That is pretty strange for me.
Why does my map function change the Type User...?
I do not change it, I just iterate over, map each user and if I found the right one, by its id, I change deleted value to True...
I am a bit confused...
Can anyone help?
kind regards :)
UPDATE: It does not seem to me a problem of the List.map function but of the type alias User declaration.
As soon as I add another value this breaks...
Here is the whole code for it. It is kept pretty simple.
Note: As soon as you uncomment the Users property "deleted" the compiler throws an error
module Main exposing (..)
import Html exposing (Html, text, h1, div, img, input, form, ul, li, i, hr, br)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.App as App
import String
import Random
--import Debug
--import Uuid
main : Program Never
main =
App.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
--MODEL
type alias Model =
{ users : List User
, name : String
, age : String
, message : String
}
type alias User =
{ id : Int
, name : String
, age :
Maybe Int
-- , deleted : Bool
}
init : ( Model, Cmd Msg )
init =
( initModel, Cmd.none )
initModel : Model
initModel =
{ users = []
, name = ""
, age = ""
, message = ""
}
--UPDATE
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
InsertName username ->
( { model | name = username }, Cmd.none )
InsertAge age ->
let
newAge =
case String.toInt age of
Err err ->
""
Ok value ->
toString value
newMessage =
case String.toInt age of
Err err ->
"Age must be a number!"
Ok int ->
""
in
( { model | age = newAge, message = newMessage }, Cmd.none )
InitNewUser ->
( model, Random.generate AddNewUser (Random.int 1 9999) )
AddNewUser randomId ->
if String.isEmpty model.name then
( { model | message = "Please give a name" }, Cmd.none )
else
let
ageAsInt =
case String.toInt model.age of
Err err ->
Nothing
Ok int ->
Just int
newUser =
User randomId model.name ageAsInt
newUserList =
newUser :: model.users
in
( { model | users = newUserList, name = "", age = "" }, Cmd.none )
Delete id ->
let
newUserList =
List.map
(\user ->
if user.id == id then
{ user | name = "--deleted--" }
else
user
)
model.users
in
( { model | users = newUserList }, Cmd.none )
--VIEW
type Msg
= InsertName String
| InsertAge String
| AddNewUser Int
| InitNewUser
| Delete Int
userListView : Model -> Html Msg
userListView model =
let
newList =
List.filter (\user -> (user.name /= "--deleted--")) model.users
in
newList
|> List.sortBy .name
|> List.map userView
|> ul []
userView : User -> Html Msg
userView user =
let
ageAsString =
case user.age of
Just val ->
val |> toString
Nothing ->
"-"
in
li []
[ div [] [ text ("ID: " ++ toString user.id) ]
, div [] [ text ("Name: " ++ user.name) ]
, div [] [ text ("Age: " ++ ageAsString) ]
, input [ type' "button", value "Delete", onClick (Delete user.id) ] []
]
view : Model -> Html Msg
view model =
div [ class "wrapper" ]
[ h1 [] [ text ("We have " ++ toString (List.length model.users) ++ " Users") ]
, Html.form []
[ input [ type' "text", onInput InsertName, placeholder "Name", value model.name ] []
, input [ type' "text", onInput InsertAge, placeholder "Age", value model.age ] []
, input [ type' "button", onClick InitNewUser, value "Add new user" ] []
]
, div [] [ text model.message ]
, userListView model
, hr [] []
, div [] [ text (toString model) ]
]
The problem is this part of the AddNewUser message:
newUser =
User randomId model.name ageAsInt
It is missing False as the forth argument when you use the deleted property.
If you do not include it, the User function will return a partially applied function that still needs a Bool to return a proper user. The compiler seems to get thrown off by this even though all your types and functions have proper annotations.
Another way that leads to a better error message would be to define newUser like this:
newUser =
{ id = randomId
, name = model.name
, age = ageAsInt
, deleted = False
}
I have the following scenario:
when a user stops typing in the text area, I want to wait for 2 seconds and if the user didn't change anything in the textarea within those 2 seconds, I want to save the content of the textarea to the server. If the user changes something in the textarea within those 2 seconds, I want to restart the wait timeout.
In JavaScript, I would implement it something like this
http://codepen.io/ondrejsevcik/pen/LRxWQP
// Html
<textarea id="textarea"></textarea>
<pre id="server"></pre>
// JavaScript
var textarea = document.querySelector('#textarea');
var textValue = "";
textarea.oninput = function (e) {
textValue = e.target.value;
setSaveTimeout();
}
let saveTimeout;
function setSaveTimeout() {
if (saveTimeout) {
clearTimeout(saveTimeout);
}
saveTimeout = setTimeout(saveToServer, 2000);
}
function saveToServer() {
document.querySelector('#server').innerText =
'Value saved to server: ' + textValue;
}
One way to achieve the behavior is to..
Hook onInput event
Within onInput handler,
create a task which fires 2 sec later,
with a current value of textarea.
Also store the textarea contents.
check if the value has
changed or not, and save it if value did not change.
Here, it doesn't cancel the Task, so it may not be efficient.
-- MODEL
type alias Model =
{ str : String
, saved : String
}
init : (Model, Cmd Msg)
init =
(Model "" "", Cmd.none)
-- UPDATE
type Msg
= ChangeText String
| Save String
| NoOp ()
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
NoOp _ -> (model, Cmd.none)
Save str ->
let
_ = Debug.log "save" str
newmodel =
if model.str /= str
then model
else { model | saved = str }
in (newmodel, Cmd.none)
ChangeText str ->
let
_ = Debug.log "textarea" str
cmd = Task.perform NoOp Save <|
Process.sleep (2 * Time.second)
`Task.andThen`
\_ -> Task.succeed str
in ({ model | str = str }, cmd)
-- VIEW
view : Model -> Html Msg
view model =
Html.div []
[ Html.textarea [ onInput ChangeText ] []
, Html.div [] [ Html.text <| "saved: " ++ model.saved ]
]
I am trying to pull data from an API in power bi using power query formula language.
My code is:
let
obj= "{ ""dataset"": ""mydataset"",""queries"": [ { ""type"": ""single_measurement"", ""measure"": { ""aggregator"": ""unique_count"", ""column"": ""visitor_id"" } } ], ""start"":1451638800000,""end"":1468430640000,""max_groups"":1000,""group_by"":[""extrhike""]}",
authKey = "Token js+JG/FaGiZcFZPVAsAXmN+d20000",
url = "https:// myhostaddress.cloudapp.azure.com/api/v1/query?query=obj",
GetJson = Json.Document(Web.Contents(url,[Headers = [#"Authorization"=authKey, #"Content-Type"="application/json"]]))
in
GetJson
I want to pass the data request object(obj) as a query string like following
https://myhostaddress.cloudapp.azure.com/api/v1/query?query={ "dataset": "mydataset","queries": [ { "type": "single_measurement", "measure": { "aggregator": "unique_count", "column": "visitor_id" } } ], "start":1451638800000,"end":1468430640000,"max_groups":1000,"group_by":["extrhike"]}
I am unable to pass obj as a querystring value in power query.I am getting following error
DataFormat.Error: Invalid URI: The hostname could not be parsed.
Details:
https:// myhostaddress.cloudapp.azure.com/api/v1/query?query=obj
How do I pass the request object(obj) as query string in Power Query Formula Language.
Thanks in advance
Have you tried passing in your query parameters with the Query argument of Web.Contents ?
For e.g
let
Url = "https:// myhostaddress.cloudapp.azure.com"
Request = Json.Document(Web.Contents(url,
[Headers =
[#"Authorization"=authKey,
#"Content-Type"="application/json"],
RelativePath = "/api/v1/query",
Query = [
<insert record here of your query object>
]
]
An example of the Query record might be:
Query = [#"Argument 1" = "Foo", #"Argument 2" = "Bar", limit = "1000", skip="500"]
You can read about the options here: https://msdn.microsoft.com/en-us/library/mt260892.aspx
You need to add the strings together. It should be url = "https:// myhostaddress.cloudapp.azure.com/api/v1/query?query=" & obj,.
Try to use this
It POSTs content instead of GETting query
let
obj = "{ ""dataset"": ""mydataset"",""queries"": [ { ""type"": ""single_measurement"", ""measure"": { ""aggregator"": ""unique_count"", ""column"": ""visitor_id"" } } ], ""start"":1451638800000,""end"":1468430640000,""max_groups"":1000,""group_by"":[""extrhike""]}",
authKey = "Token js+JG/FaGiZcFZPVAsAXmN+d20000",
url = "https:// myhostaddress.cloudapp.azure.com/api/v1/query",
GetJson = Json.Document(Web.Contents(url,[
Headers = [#"Authorization"=authKey, #"Content-Type"="application/json"],
Content = Text.ToBinary(obj)
]))
in
GetJson
You also may use this
It escapes obj to percent-string and appends to "query" parameter
let
obj= "{ ""dataset"": ""mydataset"",""queries"": [ { ""type"": ""single_measurement"", ""measure"": { ""aggregator"": ""unique_count"", ""column"": ""visitor_id"" } } ], ""start"":1451638800000,""end"":1468430640000,""max_groups"":1000,""group_by"":[""extrhike""]}",
authKey = "Token js+JG/FaGiZcFZPVAsAXmN+d20000",
url = "https:// myhostaddress.cloudapp.azure.com/api/v1/query?query="&Uri.EscapeDataString(obj),
GetJson = Json.Document(Web.Contents(url,[Headers = [#"Authorization"=authKey, #"Content-Type"="application/json"]]))
in
GetJson