How to get a stream of a ndjson response - http

I am trying to connect to a http API. This API responses with a ndjson, that is a newline separated json strings. I need to consume these lines one by one, before I download them all (in fact even before the server knows what it will output on the future lines).
In Python, I can achieve this by:
import requests, json
lines = requests.get("some url", stream=True).iter_lines()
for line in lines:
#parse line as JSON and do whatever
and it works like charm.
I want the same effect done in Nim, but the program blocks. For example, I tried to load just the first line of the response:
import httpclient, json, streams
var stream = newHttpClient().get("some url").bodyStream
var firstLine = ""
discard stream.readLine(firstLine )
echo firstLine
but with no luck - that is, the program never echoes.
I also tried streams.lines iterator, but that didn't help either.
Is there some idiom similar to the Python snipet that would allow me to easily work with the http reponse stream line by line?

The solution is to use the net module as in the question linked by #pietroppeter. That initially didn't work for me, because I didn't construct the HTTP request correctly.
The resulting code:
import net, json
const HOST = "host"
const TOKEN = "token"
iterator getNdjsonStream(path: string): JsonNode =
let s = newSocket()
wrapSocket(newContext(), s)
s.connect(HOST, Port(443))
var req = &"GET {path} HTTP/1.1\r\nHost:{HOST}\r\nAuthorization: {TOKEN}\r\n\r\n"
s.send(req)
while true:
var line = ""
while line == "" or line[0] != '{':
line = s.recvLine
yield line.parseJson
I think this can't be achieved using the httpClient module. The async versions might look like they can do it but it seems to me that you can only work with the received data once the Future is completed, that is after all data is downloaded.
The fact that such a simple think cannot be done simply and the lack of examples I could find lead to a couple of days of frustration and the need of opening a stackoverflow account after 10 years of programming.

Related

How to encode URLs in Dart?

I'm trying to port some code from Python to Dart (for a Flutter application). However, I'm having a bit of trouble encoding URLs. I'm trying to do the equivalent of parsedData = urllib.parse.quote(STR_DATA). The closest I've gotten is with the Uri Dart class, with:
parsedData = Uri(queryParameters: STR_DATA);
parsedData = Uri.encodeComponent(parsedData.toString());
This gets close to what I'm trying to get but not quite. The result I get using Python is something like this (side note: it's only encoding after the period):
ig_sig_key_version=4&signed_body=efbcf4ac8577da5eb43f33f369cda4248dba52a407e88a565038b53933737bba.%7B%22phone_id%22%3A%20%22ee79227a-cf89-41c8-9598-d0c6f3931fa4%22%2C%20%22_csrftoken%22%3A%20%22BB1PRXVV1y6FgU0Rfmcda3jJG5eVFSPd%22%2C%20%22username%22%3A%20%22USERNAME%22%2C%20%22guid%22%3A%20%22c668814c-a1e9-487c-97f0-8491b2c07c1c%22%2C%20%22device_id%22%3A%20%22android-7eb57ab90e1e2c3e%22%2C%20%22password%22%3A%20%22PASSWORD%22%2C%20%22login_attempt_count%22%3A%20%220%22%7D
While with Dart, I get something like this:
ig_sig_key_version=4&signed_body=d1c26c132b536b3f4ffdd7f5c0524503e48216fb7f638b5f4cab65d74a9834de.%3Fphone_id%3D112626ab-1946-4ad0-bc63-b233f57033f9%26_csrftoken%3DhiPh0jSvjd0eP2dP4VUr83t3htYF7xci%26username%3DUSERNAME%26guid%3D999d2242-f52a-4044-96d9-2245c6757fbc%26device_id%3Dandroid-5daff5c3029f414c%26password%3DPASSWORD%26login_attempt_count%3D0
By the way, the reason why I need this to encode in the same way is because otherwise my HTTP request returns 400. Anyway, any help is appreciated; thank you in advance.
OTHER SIDE NOTE: I think this is what's causing my request to be rejected but I don't know, I haven't done a lot of web stuff. If you think it might be something else, feel free to correct me.
You are doing too much.
In this case, you only need to do: parsedData = Uri.encodeComponent(STR_DATA); to get the same result as the Python code.
Use Dart Uri Class
var uri = 'http://example.com/path/to/page?name=ferret john';
var encoded = Uri.encodeFull(uri);
assert(encoded == 'http://example.com/path/to/page?name=ferret%20john');
var decoded = Uri.decodeFull(encoded);
assert(uri == decoded);

What is the Correct Endpoint for the Form Recognizer API?

Docs about using the Azure's Form Recognizer seem unclear. What is the correct ENDPOINT to send my request?
I'm using python and have followed the docs to use the prebuilt receipt model of Form Recognizer but don't get the expected output. I'm unsure if I'm using the correct endpoint. I tried two things:
Reading this tutorial, it is stated that I need to look up the <ENDPOINT> in my resource's overview page. In my case it is: formextractor.cognitiveservices.azure.com. So I tried this:
import http.client, urllib.request, urllib.parse, urllib.error, base64
params = urllib.parse.urlencode({
})
# key = '...'
headers = {
# Request headers
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': key,
}
source = r"https://www.w3.org/WAI/WCAG20/Techniques/working-examples/PDF20/table.pdf"
body = {"url":source}
body = json.dumps(body)
try:
conn = http.client.HTTPSConnection('formextractor.cognitiveservices.azure.com')
conn.request("POST", "/formrecognizer/v1.0-preview/prebuilt/receipt/asyncBatchAnalyze?s" % params, f"{body}", headers)
response = conn.getresponse()
data = response.read()
operationURL = "" + response.getheader("Operation-Location")
print ("Operation-Location header: " + operationURL)
conn.close()
except Exception as e:
print(e)
This returns:
[Errno 8] nodename nor servname provided, or not known
But in the API Docs the ENDPOINT is already fixed to westeurope.api.cognitive.microsoft.com, which is where my resource is located. So I tried this:
# ... same headers, body and params as before
try:
conn = http.client.HTTPSConnection('westeurope.api.cognitive.microsoft.com')
conn.request("POST", "/formrecognizer/v1.0-preview/prebuilt/receipt/asyncBatchAnalyze?%s" % params, f"{body}", headers)
response = conn.getresponse()
data = response.read()
operationURL = "" + response.getheader("Operation-Location")
print ("Operation-Location header: " + operationURL)
conn.close()
except Exception as e:
print("[Errno {0}] {1}".format(e.errno, e.strerror))
Outputs an URL where I see:
{"error":{"code":"401","message": "Access denied due to invalid subscription key or wrong API endpoint. Make sure to provide a valid key for an active subscription and use a correct regional API endpoint for your resource."}}
I am certain I'm using the correct key. But in any case neither of the two seems to work. Could you help me? Thank you.
It looks like the initial documentation you are mentioning is misleading. You can find the endpoint of your resource in Azure portal, in the resource overview. Sample for mine:
Form Recognizer API is (at the time of writing this answer) hosted in the following Azure regions:
West US 2 - westus2.api.cognitive.microsoft.com
West Europe - westeurope.api.cognitive.microsoft.com
So in my case it's WestEurope, and as you mentioned it is the same on your resource. Once you got it, you then got a 401 Unauthorized
Several possibilities:
You made an error in the way you are passing the header (wrong key name, wrong key value), but it looks okay based on your code above (but I'm not used to Python)
Your resource is not from the same region as the endpoint you are querying (please double check)
You are using the right root, but something is wrong in how you are calling it
Once you have checked your region / key values, can you remove your ?%s"%params from your query? The Analyze Receipt method don't have params in query string (given documentation)

Cannot download with right format an excel file F#

I have the following part of code:
let client = new WebClient()
let url = "https://..."
client.DownloadFile(Url, filename)
client.Dispose()
In which code i am performing a HttpGet method in which method i get a file excel with some data.
The method is executed correctly because i get my excel file.
The problem is that the content of my file excel is like this:
I think its because i don't pass ContentType:"application/vnd.ms-excel"
So anyone can help how can I pass that ContentType in my Client in F# ?
If you want to add HTTP headers to a request made using WebClient, use the Headers property:
let client = new WebClient()
let url = "https://..."
client.Headers.Add(HttpRequestHeader.Accept, "application/vnd.ms-excel")
client.DownloadFile(Url, filename)
In your case, I think you need the Accept header (Content-Type is what the response should contain to tell you what you got).
That said, I'm not sure if this is the problem you are actually having - as noted in the comments, your screenshot shows a different file, so it is hard to tell what's wrong with the file you get from the download (maybe it's just somewhere else? or maybe the encoding is wrong?)

How to access trailing metadata from python gRPC client

Here is how I am sending the metadata from server.
def DoSomething(self, request, context):
response = detection2g_pb2.SomeResponse()
response.message = 'done'
_SERVER_TRAILING_METADATA = (
('method_status', '1010'),
('error', 'No Error')
)
context.set_trailing_metadata(_SERVER_TRAILING_METADATA)
return response
Here is what I tried:
res = _stub.DoSomething(req)
print (res.trailing_metadata())
In this case I get Attribute Error object has no attribute 'trailing_metadata'. I want to know way to access the trailing metadata in the client side.
I apologize that we don't yet have an example illustrating metadata but you can see here how getting the trailing metadata on the invocation side requires using with_call (or future, but that may change the control flow in a way that you don't want changed, so I think that with_call should be your first choice). I think your invocation-side code should look like
response, call = _stub.DoSomething.with_call(request)
print(call.trailing_metadata())
.

How to pass parameter to Url with Python urlopen

I'm currently new to python programming. My problem is that my python program doesn't seem to pass/encode the parameter properly to the ASP file that I've created. This is my sample code:
import urllib.request
url = 'http://www.sample.com/myASP.asp'
full_url = url + "?data='" + str(sentData).replace("'", '"').replace(" ", "%20").replace('"', "%22") + "'"
print (full_url)
response = urllib.request.urlopen(full_url)
print(response)
the output would give me something like:
http://www.sample.com/myASP.asp?data='{%22mykey%22:%20[{%22idno%22:%20%22id123%22,%20%22name%22:%20%22ej%22}]}'
The asp file is suppose to insert the acquired querystring to a database.. But whenever I check my database, no record is saved. Though if I do copy and paste the printed output on my browser url, the record is saved. Any input on this? TIA
Update:
Is it possible the python calls my ASP File A but it doesn't call my ASP File B? ASP File A is called by python while ASP File B is called by ASP File A. Because whenever I run the url on a browser, the saving goes well. But in python, no saving of database occurs even though the data passed from python is read by ASP File A..
Use firebug with Firefox and watch the network traffic when the page is loaded. If it is actually an HTTP POST, which I suspect it is, check the post parameters on that post and do something like this:
from BeautifulSoup import BeautifulSoup
import urllib
post_params = {
'param1' : 'val1',
'param2' : 'val2',
'param3' : 'val3'
}
post_args = urllib.urlencode(post_params)
url = 'http://www.sample.com/myASP.asp'
fp = urllib.urlopen(url, post_args)
soup = BeautifulSoup(fp)
If its actually HTTP POST, this will work.
In case anybody stumbles upon this, this is what I've come up with:
py file:
url = "my.url.com"
data = {'sample': 'data'}
encodeddata = urllib.parse.urlencode(data).encode('UTF-8')
req = urllib.request.Request(url, encodeddata)
response = urllib.request.urlopen(req)
and in my asp file, I used json2.js:
jsondata = request.form("data")
jsondata = replace(jsondata,"'","""")
SET jsondata = JSON.parse(jsontimecard)
Note: use requests instead. ;)
First off, I don't know Python.
But from this : doc on urllib.request
the HTTP request will be a POST instead of a GET when the data
parameter is provided
Let me make a really wild guess, you are accessing the form values as Request.Querystring(..) in the asp page, so your post wont pass any values. But when you paste the url in the address bar, it is a GET and it works.
just guessing, you could show the .asp page for further check.

Resources