In elm's Http 1.0.0 package, I could send a custom request like:
post : Endoint -> List (Http.Header) -> Http.Body -> Decoder a -> Http.Request a
post url headers body decoder =
Http.request
{ method = "POST"
, headers = headers
, url = url
, body = body
, expect = Http.expectJson decoder
, timeout = Nothing
, withCredentials = False
}
With the post function I wrote above, I can simply call it with, say, a Decoder String, and after the Http request sends, the response string will be decoded and returned. period. No need to create a Msg like:
type Msg
= GotText (Result Http.Error String)
And no need to write a branch in update to handle this Msg.
However, as of Http 2.0.0, the expect argument is of type Expect msg, not Expect a, meaning that writing the Msg variation and additional branch to update will now be required.
I am writing an Api.elm file which makes Http requests. However, this means that now it will have to have its own Msg type and update function to run after these requests respond.
I used to think that Home.elm should only respond to messages from Home.Msg and Home.update not Api.Msg and Api.update. Am I wrong? Should Api.elm have its own Msg type and update function that changes other pages? Or would there be a better way to do this?
To clarify what I was explaining in my question:
A custom request in Elm's HTTP 2.0.0 package looks like this:
request :
{ method : String
, headers : List Header
, url = String
, body = Body
, expect = Expect msg
, timeout = Maybe Float
, withCredentials = Maybe String
}
-> Cmd msg
Wheras in Http 1.0.0 it looked like this:
request :
{ method : String
, headers : List Header
, url : String
, body : Body
, expect : Expect a
, timeout : Maybe Float
, withCredentials : Bool
}
-> Request a
The difference is that using the custom request from HTTP 2.0.0, I needed to pass-in a Msg to use this request.
Now, my problem was: I was using an Api.elm file. Every time I needed to issue an HTTP request, I would call Api.request arg1 arg2... from, say, Login.elm.
Since the request function in Api.elm demanded a Msg type, for, in this case, a login request I thought I would have to define a Msg of GotLogin within Api.elm and then handle how GotLogin would update Login.elm by writing an update branch for GotLogin within Api.elm.
However, I could just define a GotLogin Msg in Login.elm and pass that into Api.request. Since I've defined GotLogin in Login.elm, I would put a GotLogin branch in the update function of Login.elm, instead of Api.elm.
This also applies for any other request type from any other page (Signup.elm, Home.elm,...), meaning that Api.elm, should not have its own update function that updates other pages.
The whole point of Login.elm having its own update function, is that it should only be affected by the branches of its own update function, not ones from Api.elm.
Related
I have a Reactive Rest Controller that makes a POST call along with Flux<> Request Body.
I want to do sequential steps in this Post call
Validate Request Body input
Delete all rows based on one field inside Request Body.
Save all data from this Flux Request Body
Return all updated saved data from Flux to Controller
Sample Code
public Mono<Boolean> update(Flux<RequestBody> body, Long id) {.
return body.
.flatMap(d -> this.repo.deleteCriteria(id, d.getValue);
.thenMany(body)
.flatMap(m-> {mapper to convert from DTO to Entity});
.flatMap(s -> this.repo.save(s.name, s.data)
.then(c -> this.repo.findById(id).count)
.then(n-> n.intValue() > 0 ? Mono.just(true) : Mono.just(false));
}.
Controller code -
#ResponseStatus(HttpStatus.CREATED).
#PostMapping("/abc").
public Mono<ResponseEntity<Void> update(Long id, Flux<Body> body) {
Mono<Boolean> onSuccess = this.service.update(id, body);
onSuccess
.log()
.flatMap(b -> b ? Mono.just(new ResponseEntity<>(HttpStatus.CREATED) :
Mono.just(new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR));
}
I see in Logs that Delete statement getting executed n times. Ideally I wanted it only one time to run but I only know flatMap().
But after delete statement I see below error in logs.
"Error - 400 Bad Request - Request body is missing ....."
FYI - Before when I only had a delete statement and I was not fetching value from Flux Request Body everything was working fine.
Please let me know if there is a way to
read values from Request Body and do validations.
perform multiple operations on this Request body like Delete, save and find by reading some values from the RequestBody.
Any help appreciated :)
I'm trying to pass parameters in the request body, the documentation says :
The request to be sent was defined in a prior call to the Open method. The calling application can provide data to be sent to the server through the Body parameter. If the HTTP verb of the object's Open is "GET", this method sends the request without Body, even if it is provided by the calling application.
So, I need to use POST with body. But when I use use POST with body I have error "Bad Request: message text is empty" and when I use GET with body result is ok. Why?
My code:
WinHttp = NEW COMObject("WinHttp.WinHttpRequest.5.1");
WinHttp.Open("GET", "http://api.telegram.org/botbotname/sendMessage", 0);
WinHttp.setRequestHeader("Content-type", "application/json");
JSONWr = New JSONWriter();
JSONWr.ValidateStructure = False;
JSONParams = New JSONWriterSettings( , Chars.Tab);
JSONWr.SetString(JSONParams);
JSONWr.WriteStartObject();
JSONWr.WritePropertyName("chat_id");
JSONWr.WriteValue(UserId);
JSONWr.WritePropertyName("text");
JSONWr.WriteValue(Text);
JSONWr.WriteEndObject();
JSONString = JSONWr.Close();
WinHttp.Send(JSONString);
work, but how? And why the same with POST doesn`t work?
I'm trying to check the data received after sending a request and for that I need to write a test to make sure of it.
These are the three functions handling the data.
fetchConfigs : Cmd Msg
fetchConfigs =
Http.get
{ url = "http://localhost:8080/api/sso/configs"
, expect = Http.expectJson GotConfigs responseDecoder
}
responseDecoder : Decoder Config
responseDecoder =
map2 Config
(field "identifier" string)
(field "ssoType" int)
type Msg
= GotConfigs (Result Http.Error Config)
Have a look at https://package.elm-lang.org/packages/avh4/elm-program-test/latest/ProgramTest. From the package description:
This module allows you to interact with your program by simulating
user interactions and external events (like HTTP responses and ports),
and making assertions about the HTML it renders and the external
requests it makes.
I'm trying to make an authenticated api call to VALR crypto exchange as first step towards automated trading. They provide most of the code so I thought it would be easy even as a non coding techie. The code below does actually create the correct HMAC SHA512 signature using the API Secret provided for testing but I have a problem in passing this result along to the next section of code to request balances (starting at line 17). If I cut and paste the result/displayed 'signature' and 'timestamp' (after running the code) back into the code it does in fact work. So what changes do I need to make the code automatically pick up the signature and timestamp. The user defined function appears to keep all parameters "secret" from the rest of the code, especially after using return.
import time
import hashlib
import hmac
def sign_request( api_key_secret,timestamp, verb,path,body=""):
payload = "{}{}{}{}".format(timestamp, verb.upper(), path, body)
message = bytearray(payload, 'utf-8')
signature = hmac.new(bytearray(api_key_secret, 'utf-8'), message, digestmod=hashlib.sha512).hexdigest()
print("Signature=",signature)
print ("Timestamp",timestamp)
return signature
sign_request( verb = "GET", timestamp = int(time.time()*1000),path="/v1/account/balances",api_key_secret="4961b74efac86b25cce8fbe4c9811c4c7a787b7a5996660afcc2e287ad864363" )
import requests
url = "https://api.valr.com/v1/account/balances"
payload = {}
headers = {
'X-VALR-API-KEY': '2589fb273e86aeee10bac1445232aa302feb37e27d32c1c599abc3757599139e',
'X-VALR-SIGNATURE': 'signature',
'X-VALR-TIMESTAMP': 'timestamp'
}
response = requests.request("GET", url, headers=headers, data = payload)
print(response.text.encode('utf8'))
Well after some hard thinking I decided to change to using global variables. The hmac still worked fine and gave me a signature. Then I removed the quotes around signature and timestamp and realised they were both integers. I was then able to convert that signature and timestamp to a string and everything started to work perfectly. Maybe someone else will make use of this. If you want to make a POST request remember to put single quotes around anything in the {body} statement to make it a string.
Here is the code that I am currently using for a GET request from VALR. It's been working fine for many months. You will need to change the path and the url to correspond to whatever you are trying to get, and obviously you will need to add your_api_key and your_api_secret.
If you need to send through other request parameters like transaction types etc. then you will ned to include them in the path and the url e.g. https://api.valr.com/v1/account/transactionhistory?skip=0&limit=100&transactionTypes=MARKET_BUY¤cy=ZAR
def get_orders(): # to get open orders from valr
timestamp = int(time.time()*1000)
verb = "GET"
path = "/v1/orders/open"
body = ""
api_key_secret = 'your_api_secret'
payload = "{}{}{}".format(timestamp, verb.upper(), path)
message = bytearray(payload, 'utf-8')
signature = hmac.new(bytearray(api_key_secret, 'utf-8'), message, digestmod=hashlib.sha512).hexdigest()
timestamp_str = str(timestamp)
url = "https://api.valr.com/v1/orders/open"
headers = {
'Content-Type': 'application/json',
'X-VALR-API-KEY': 'your_api_key',
'X-VALR-SIGNATURE': signature,
'X-VALR-TIMESTAMP': timestamp_str,
}
response = requests.request("GET", url, headers=headers, data=body)
dict = json.loads(response.text)
dict = pd.DataFrame.from_dict(dict)
print(dict)
I started on my first, simple web app in Elm. Most of my code is currently adapted from https://github.com/rtfeldman/elm-spa-example. I am working against a API that will give me a authToken in the response header. I have a AuthToken type that is supposed to represent that token. Taking the value out of the header and converting it to a result that's either a error String or a AuthToken is causing trouble. I expected that I could just say I am returning a AuthToken, return a String and it would be fine because my AuthTokens right now are just Strings. It seems like there clearly is something about Elm types I am not understanding.
Here is the definition of AuthToken:
type AuthToken
= AuthToken String
and my way too complicated function that for now just tries to do some type changes (later I want to also do work on the body in here):
authTokenFromHeader : String -> Http.Response String -> Result String AuthToken
authTokenFromHeader name resp =
let
header = extractHeader name resp
in
case header of
Ok header ->
let
token : Result String AuthToken
token = Ok (AuthToken header)
in
token
Err error -> Err error
I expected the happy case would return a Ok result with the string from the response header converted to a AuthToken as its value. Instead I am getting Cannot find variable 'AuthToken'. From the documentation I expected to get a constructor with the same name as the type. If I just use Ok header, the compiler is unhappy because I am returning Result String String instead of the promised Result String AuthToken.
What's the right approach here?
The code looks fine as is. The error message indicates that type AuthToken has been defined in a different module and not imported completely to the module that defines authTokenFromHeader. You can read about Elm's module system in the Elm guide: Modules.
A possible fix, assuming that type AuthToken is defined in module Types, and authTokenFromHeader is defined in module Net, is:
Types.elm:
module Types exposing (AuthToken(..))
type AuthToken = AuthToken String
Net.elm:
module Net exposing (authTokenFromHeader)
import Types exposing (AuthToken(..))
authTokenFromHeader : String -> Http.Response String -> Result String AuthToken
authTokenFromHeader name resp =
...
Note the use of AuthToken(..) instead of just AuthToken, which ensures that the type as well as the type constructors are imported/exported.
Or just move the definition of type AuthToken into the same file as the definition of authTokenFromHeader.