REST API works with curl but not requests - python-3.x

I have this command:
import requests
url = "https://api.opensubtitles.com/api/v1/download"
payload = {"file_id": id_to_download}
headers = {
"Content-Type": "application/json",
"Api-Key": "myApiKey",
"Authorization": "Bearer myApiKey"
}
response = requests.request("POST", url, json=payload, headers=headers)
print(response.text)
That returns
{
"message":"You cannot consume this service"
}
When the console version works perfectly:
curl --request POST --header 'Api-Key: myApiKey' --url https://api.opensubtitles.com/api/v1/download --header 'Content-Type: application/json, Authorization: Bearer undefined' --data '{"file_id": 934267}'
{"link":"https://www.opensubtitles.com/download/901E4D16AF81FF191D37B3D10AD6517A2ECE30F77679205199EF4742C5595022275ADBA60A53E73F444E251FA5B71825CA101C199D469B02264AFCCC46F1AAAF966A8197FA479E70CC58EE2D1D89FFCB04226FB33DCECBBB3BFF04F888E5CAC73C8D9813FCF84245B7AC80F9B5B18E386524F881292F0EFE45A534879E2AC7D6B92BB55BF6F5E948F6D1A586809E5723BFDA861BB0E6E842AAFB71D5A74ADC9BFB95C067D7B853C9BA2C5819726E5D90536DA0AC9EBB282602133CBECF24E1DDC1337731FEB652A384059CA4D5452F62FC4325C7D75BDA6B9AE06CCE34A1DA872B15DD28BD0D90C548BB122C38ADF8267DA29F7418C8C5F6BDD3A423F8CC20904BC2D8960A1C0C9B30A9CE0EFDC65CCBC696EE74666CE631B17F1139C7D95507CFCAAF65B5D4370C/subfile/Magic.Mike.XXL.2015.720p.BluRay.x264-GECKOS.srt","file_name":"Magic.Mike.XXL.2015.720p.BluRay.x264-GECKOS.srt","requests":8,"remaining":92,"message":"Your quota will be renewed in 16 hours and 06 minutes (2022-10-24 01:25:09 UTC) ","reset_time":"16 hours and 06 minutes","reset_time_utc":"2022-10-24T01:25:09.000Z"}%
Notice the "requests":8,"remaining":92,"message":"Your quota will be renewed in 16 hours and 06 minutes (2022-10-24 01:25:09 UTC) ","reset_time":"16 hours and 06 minutes","reset_time_utc":"2022-10-24T01:25:09.000Z" part, so apparently this is not quota-related. All the other requests work, typically this one:
url = "https://api.opensubtitles.com/api/v1/subtitles"
querystring = {"query": movie_name,
"languages": "en"}
headers = {
"Content-Type": "application/json",
"Api-Key": "myApiKey"
}
response = requests.request("GET", url, headers=headers, params=querystring)
print(response.text)
Works perfectly. Any idea about what could make the POST request fail?

Related

DocuSign, Node SDK, JWT auth, Envelope fetch, "The custom error module does not recognize this error."

[FINAL UPDATE]
Fixed per this thread: https://github.com/docusign/docusign-esign-node-client/issues/295
Apparently related to security additions made to DocuSign's API.
I haven't encountered this issue before today. I gave a demo to a client just last week and the same code was working fine. Today I get the following:
{
"status": 444,
"response": {
"req": {
"method": "GET",
"url": "https://demo.docusign.net/restapi...",
"data": {},
"headers": {
"user-agent": "node-superagent/3.8.2",
"x-docusign-sdk": "Node",
"authorization": "Bearer ABC...",
"content-type": "application/json",
"accept": "application/json"
}
},
"header": {
"content-type": "text/html",
"x-docusign-tracetoken": "2eeb8caa-8865-4898-bef9-d3611bfaa3f7",
"x-docusign-node": "DA2DFE5",
"date": "Fri, 17 Jun 2022 01:02:02 GMT",
"content-length": "54",
"connection": "close",
"strict-transport-security": "max-age=31536000; includeSubDomains"
},
"status": 444,
"text": "The custom error module does not recognize this error."
}
}
Through the node SDK I fetch a token using requestJWTUserToken, and set the apiclient's auth header, then I new-up an EnvelopesAPI instance. When I make a call to getEnvelope() or listStatusChanges(), then I get the error above. None of this code has changed in months, and I'm using the same integration key, account, private key, everything. I've demo'd it all a few times now - no issues.
An interesting observation: the error above gives me a URL and token. The token is valid, and if I make a request to the URL (the envelopes endpoint) via Postman using said token, then the request succeeds. So my calls through the SDK seem to be failing for some reason.
I can't seem to crack this one, and now I can't get around it given a couple demo systems that worked just last week.
I'm using the docusign-esign 5.17 module - upgraded from 5.15 in an attempt to fix the issue. No luck.
Where is this error coming from?
[Update 1]
I'm running my node app that is making requests through the DocuSign Node SDK against a proxy so I can see what the failing request actually look like:
They fail the same way.
HTTP/1.1 444
Content-Type: text/html
X-DocuSign-TraceToken: 338534c6-c8c3-4b01-9b66-35d697cd0053
X-DocuSign-Node: DA1DFE4
Date: Fri, 17 Jun 2022 03:55:07 GMT
Content-Length: 54
Vary: Accept-Encoding
Connection: close
Strict-Transport-Security: max-age=31536000; includeSubDomains
The custom error module does not recognize this error.
I'm using Proxyman to catch the request, and like Chrome or Firefox it will let you copy a request as a cURL command. If I copy the failing request as cURL, then run in at the terminal, it succeeds.
[MacBookPro0020]~/source/docusign/jwt-smoke-test:0 (master)
$ curl 'https://demo.docusign.net/restapi/v2.1/accounts/a0a4c81f-.../envelopes?envelope_ids=e750526f-...&envelope_ids=a38b794b...&envelope_ids=a5d8c586-...' \
-H 'Host: demo.docusign.net' \
-H 'User-Agent: node-superagent/3.8.2' \
-H 'X-DocuSign-SDK: Node' \
-H 'Node-Ver: v14.18.3' \
-H 'Authorization: Bearer ABCD...' \
-H 'Accept: application/json' \
-H 'Connection: close' \
-H 'Content-Type: application/json' \
--proxy http://localhost:9090
{"resultSetSize":"1","startPosition":"0","endPosition":"0","totalSetSize":"1","nextUri":"","previousUri":"","envelopes":[{"status":"created","documentsUri":"/envelopes/d97565c8...purgeState":"unpurged","envelopeIdStamping":"true","autoNavigation":"true","isSignatureProviderEnvelope":"false","allowComments":"true","anySigner":null,"envelopeLocation":"current_site"}]}
I'm using a JWT auth token, so again, I'm getting a valid token. Calls through the SDK consistently fail, but cURL'ing and manually requesting through Postman both succeed.
I'm at a loss.
Additional details: I see this same issue on MacOS and Windows (i.e., node app hosting docusign-esign). I'm using auth code grant to send envelopes and query envelope statuses and that works fine. I've used JWT Grant without issue up until this week (just demo'd automated functionality last week and it worked.) I haven't made any code changes to my DocuSign functionality, nor have my colleagues, at least according to the repo's history.
I don't recall ever encountering the error above before. I'd love to know why cURL'ing the same request succeeds. I'd rather not ditch the SDK and roll my own requests, but it wouldn't be difficult.
[Update 2]
Here's a simple repro - it's a quick and dirty copy of the QuickStart demo project for the node SDK. I'm using only docusign-esign.
Exact. same. issue.
Again, I can take that token and drop it into cURL or postman and the request will succeed. There's not a lot of code here. The token is valid.
async function main() {
// Data used
// dsConfig.dsClientId
// dsConfig.impersonatedUserGuid
// dsConfig.privateKey
// dsConfig.dsOauthServer
let dsConfig = dsConfig_customer; // defined globally
const jwtLifeSec = 10 * 60, // requested lifetime for the JWT is 10 min
dsApi = new docusign.ApiClient();
dsApi.setOAuthBasePath(dsConfig.dsOauthServer.replace('https://', '')); // it should be domain only.
const results = await dsApi.requestJWTUserToken(dsConfig.dsClientId,
dsConfig.impersonatedUserGuid, 'signature impersonation', dsConfig.privateKey,
jwtLifeSec);
console.log( results.body.access_token );
const userInfo = await dsApi.getUserInfo(results.body.access_token);
dsApi.setBasePath(userInfo.accounts[0].baseUri + '/restapi');
dsApi.addDefaultHeader( 'Authorization', 'Bearer ' + results.body.access_token );
const envelopesAPI = new docusign.EnvelopesApi(dsApi);
const res = await envelopesAPI.getEnvelope( dsConfig.accountID, 'e1917111-2900-48e8-9054-799169379c8a', null );
console.log(res);
return {
accessToken: results.body.access_token,
tokenExpirationTimestamp: expiresAt,
userInfo,
account: userInfo.accounts[0]
};
}
main().then(result => console.log(result)).catch(err=>console.error(err));
...
header: {
'content-type': 'text/html',
'x-docusign-tracetoken': '685b6226-a0d3-4547-94c7-df0216d884a3',
'x-docusign-node': 'DA2DFE188',
date: 'Fri, 17 Jun 2022 05:20:12 GMT',
'content-length': '54',
vary: 'Accept-Encoding',
connection: 'close',
'strict-transport-security': 'max-age=31536000; includeSubDomains'
},
statusCode: 444,
status: 444,
statusType: 4,
info: false,
ok: false,
redirect: false,
clientError: true,
serverError: false,
error: Error: cannot GET /restapi/v2.1/accounts/49754554-ABCD-.../envelopes/e1917111-2900-48e8-9054-799169379c8a (444)
...
I finally manage to resolve the issue by downgrading my Docusign SDK NodeJS version from 5.15.0 to 5.7.0 in my package.json file.
An issue relative to this can be find there, I really hope this issue will be resolved anytime soon.
Edit :
The Docusign support actually took action on this (the github's issue from above is now closed), it might be interesting to test again with the latest version of Docusign SDK NodeJS (or the one you were using previously).

Python call rest api to get data from url

I've created a Bash script to get the data from the url using rest API from a appliance using username, password and saving the Session ID into a Variable and then using the session ID to get the data into csv format which is working fine.
I want to change the bash code into python3 code as i'm parsing it using pandas.
Bash Code:
#!/bin/bash
sessionID=$(curl -k -H "accept: application/json" -H "content-type: application/json" -H "x-api-version: 120" -d '{"userName":"administrator","password":"adminpass"}' -X POST https://hpe.sysnergy.com/rest/login-sessions | jq -r ".sessionID")
curl -k -H 'accept: application/json' \
-H 'content-type: text/csv' \
-H 'x-api-version: 2' \
-H "auth: $sessionID" \
-X GET https://hpe.sysnergy.com/rest/resource-alerts
Python Version of tries code:
#!/usr/bin/python3
import requests
import json
url = "https://hpe.sysnergy.com/rest/login-sessions"
data = {'username': 'administrator', 'password': 'adminpass'}
headers = {'Content-type': 'text/csv', 'Accept': 'application/json', 'x-api-version': 2}
r = requests.post(url, data=json.dumps(data), headers=headers)
print(r)
I am getting below error:
Error:
requests.exceptions.InvalidHeader: Value for header {x-api-version: 2} must be of type str or bytes, not <class 'int'>
if i convert int to str as '2' then it gives another ssl error:
requests.exceptions.SSLError: HTTPSConnectionPool(host='hpe.synerg.com', port=443): Max retries exceeded with url: /rest/login-sessions (Caused by SSLError(SSLError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:877)'),))
EDIT:
I have tried little different approach to get the same code format as bash in python but now it returns new error with new response code.
import os
import requests
sessionID = os.getenv('sessionID')
headers = {
'accept': 'application/json',
'content-type': 'text/csv',
'x-api-version': '2',
'auth': f"{sessionID}",
}
data = '{"userName":"administrator","password":"adminpassword"}'
response = requests.post('https://hpe.synergy.com/rest/login-sessions', headers=headers, data=data, verify=False)
print(response)
Error:
/python3/lib64/python3.6/site-packages/urllib3/connectionpool.py:1020: InsecureRequestWarning: Unverified HTTPS request is being made to host 'hpe.synergy.com'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings
InsecureRequestWarning,
<Response [415]>
Please help or suggest the way to achieve same function in the python.
You first need to make a POST request to get the sessionID, then you need to make a GET request. Also note the headers are slightly different for the 2 requests. Something like this should work:
import requests
session = requests.Session()
url = "https://hpe.sysnergy.com/rest/login-sessions"
credentials = {"userName": "administrator", "password": "adminpass"}
headers = {"accept": "application/json",
"content-type": "application/json",
"x-api-version": "120",
}
response = session.post(url, headers=headers, json=credentials, verify=False)
session_id = response.json()["sessionID"]
url = "https://hpe.sysnergy.com/rest/resource-alerts"
headers = {"accept": "application/json",
"content-type": "text/csv",
"x-api-version": "2",
"auth": session_id,
}
response = session.get(url, headers=headers, verify=False)
print(response)
#print(response.content) # returns bytes
#print(response.text) # returns string

Configure correctly CORS with wai-cors

I am struggling with Servant and the CORS configuration: I am exposing and API through Servant and I have the following configuration:
-- Wai application initialization logic
initializeApplication :: IO Application
initializeApplication = do
let frontCors = simpleCorsResourcePolicy { corsOrigins = Just ([pack "https://xxxx.cloudfront.net"], True)
, corsMethods = ["OPTIONS", "GET", "PUT", "POST"]
, corsRequestHeaders = simpleHeaders }
return
$ cors (const $ Just $ frontCors)
$ serve (Proxy #API)
$ hoistServer (Proxy #API) toHandler server
When I perform a query like this through Chromium (by copying and pasting):
curl 'https://api.xxx/' \
-H 'Accept: application/json, text/plain, */*' \
-H 'Referer: https://xxx.cloudfront.net' \
-H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36' \
-H 'Authorization: Bearer XXX==' \
--compressed
It works but if I copy-paste the fetch query in the dev console:
fetch("https://api.xxx", {
"headers": {
"accept": "application/json, text/plain, */*",
"authorization": "Bearer XXX=="
},
"referrer": "https://xxx.cloudfront.net/",
"referrerPolicy": "no-referrer-when-downgrade",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
});
I get:
> Access to fetch at 'https://api.xxx' from origin 'https://xxx.cloudfront.net' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
polyfills-es2015.3eb4283ca820c86b1337.js:1 GET https://api.xxx net::ERR_FAILED
e.fetch # polyfills-es2015.3eb4283ca820c86b1337.js:1
> (anonymous) # VM20:1
> x:1 Uncaught (in promise) TypeError: Failed to fetch
Any hints regarding that? Especially why it works in cUrl and not in Chromium?
Thanks in advance.
It was a basic CORS issue, in fact, sending Authorization without having it in the corsRequestHeaders makes the request rejected.
I should have written:
, corsRequestHeaders = ["Authorization", "Content-Type"]

convert curl to python requests (pisignage API)

I am trying to convert a curl of pisignage into python requests. The curl is,
curl -X POST "https://swagger.piathome.com/api/files" -H "accept:
application/json" -H "x-access-token: login_session_token" -H
"Content-Type: multipart/form-data" -F "Upload
file=#test.jpg;type=image/jpeg"
My code is,
import requests
files = {'Upload file': open('test.jpg', 'rb'), 'type': 'image/jpeg'}
headers = {'Content-type': 'multipart/form-data', 'accept': 'application/json', 'x-access-token': 'login_session_token'}
file_response = requests.post(
'https://swagger.piathome.com/api/files',
files=files,
headers=headers
)
print(file_response)
It returns 404. I tried uncurl, the code is:
import uncurl
u = uncurl.parse('curl -X POST "https://swagger.piathome.com/api/files" -H "accept: application/json" -H "x-access-token: login_session_token" -H "Content-Type: multipart/form-data" -F "Upload file=test.jpg;type=image/jpeg"')
print(u)
The output is ,
error: unrecognized arguments: -F Upload file=test.jpg;type=image/jpeg
After a day of searching it turns out the swagger documentation is incorrect.
use
files = {
'assets': (open('test.jpg', 'rb'))
}
Try this
import requests
headers = {
'accept': 'application/json',
'x-access-token': 'login_session_token',
'Content-Type': 'multipart/form-data',
}
files = {
'Upload file': (None, 'test.jpg;type'),
}
response = requests.post('https://swagger.piathome.com/api/files', headers=headers, files=files)
link to parse curl to request python

replicate curl command python 3 urllib request API

This problem is kind of driving me crazy.
I'm doing a very simple python 3 script to manage an API in a public website.
I am able to do it with curl, but not in pyhton.
Can't use either requests library or curl in my real environment, just for tests
This is working:
curl -d "credential_0=XXXX&credential_1=XXXXXX" -c cookiefile.txt https://XXXXXXXXXXXXXXX/LOGIN
curl -d 'json={"devices" : ["00:1A:1E:29:73:B2","00:1A:1E:29:73:B2"]}' -b cookiefile.txt -v https://XXXXXXXXX/api-path --trace-ascii /dev/stdout
and we can see this in the curl debug:
Send header, 298 bytes (0x12a)
0000: POST /api-path HTTP/1.1
0034: Host: XXXXXXXXXXXXXXXX
0056: User-Agent: curl/7.47.0
006f: Accept: /
007c: Cookie: csrf_token=751b6bd9-0290-496b-820e-XXXXXXXX; session
00bc: =XXXXXX-6d29-4cf9-8907-XXXXXXXXXXXX
00e3: Content-Length: 60
00f7: Content-Type: application/x-www-form-urlencoded
0128:
=> Send data, 60 bytes (0x3c)
0000: json={"devices" : ["00:1A:1E:29:73:B2","00:1A:1E:29:73:B2"]}
== Info: upload completely sent off: 60 out of 60 bytes
This is the python code to replicate the second request, which is the problematic one
string_query={"devices" : [ "34:FC:B9:CE:14:7E","00:1A:1E:29:73:B2" ]}
jsonbody_url=urllib.parse.urlencode(string_query)
jsonbody_url=jsonbody_url.encode("utf-8")
req=urllib.request.Request(url,data=jsonbody_url,headers={"Cookie" :
cookie,"Content-Type": "application/x-www-form-urlencoded","User-
Agent":"curl/7.47.0","charset":"UTF-8","Content-
length":len(jsonbody_url),
"Connection": "Keep-Alive"},method='POST')
And the server is completely ignoring the Json content.
Everything else is working, login and other url parameters from the same API
Any ideas?
Try this:
import requests
string_query={"devices" : [ "34:FC:B9:CE:14:7E","00:1A:1E:29:73:B2" ]}
headers={
"Cookie" : cookie,
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent":"curl/7.47.0",
"charset":"UTF-8",
"Connection": "Keep-Alive"
}
response = requests.post(url,data=string_query,headers=headers)
print(response.content)

Resources