How to mock test python requests - python

I want to know how to write mock python request tests.
def request_headers():
payload = {
'client_id': settings.STAT_SERVER['CLIENT_ID'],
'client_secret': settings.STAT_SERVER['CLIENT_SECRET'],
'username': settings.STAT_SERVER['USERNAME'],
'password': settings.STAT_SERVER['PASSWORD'],
'grant_type': 'password',
}
How can I mock test all of this?
token_url = settings.STAT_SERVER['URL'] + 'o/token/'
r = requests.request(
method="POST", url=token_url, data=payload, verify=False)
if r.status_code != 200:
msg = "Failed to authenticate. Error code {}: {}"
msg = msg.format(r.status_code, r.text)
LOGGER.error(msg)
credentials = r.json()
Here's the base_headers
base_headers = {
'Content-Type': 'application/json',
'Accept': 'application/json, */*'
}
base_headers['Authorization'] = "{} {}".format(
credentials['token_type'], credentials['access_token']
)
LOGGER.debug('Get token: credentials={}'.format(credentials))
return base_headers

The unittest.mock.patch decorator is an awesome tool for solving these things.
So you would do something like:
from unittest.mock import Mock, patch
#patch("requests.request")
def test_request(request_mock: Mock):
response_mock = Mock(status_code=200)
response_mock.json.return_value = {"foo": "bar"}
request_mock.return_value = response_mock

Related

Cloud function require authentication error

I want to trigger the cloud functions with a self-signed JWT for a Google-signed ID token. However, it produces the response Your client does not have permission to the requested URL. I am sure that that account has "Cloud Functions Invoker" access and I also get the token successfully.
import time
import jwt
import json
# import http.client
# payload
private_key = "\n-----END PRIVATE KEY-----\n"
token_dict = {
"iss": "het-query-function#smarter-poc.iam.gserviceaccount.com",
"scope": "https://www.googleapis.com/auth/cloud-platform",
"aud": "https://www.googleapis.com/oauth2/v4/token",
# "aud": "https://europe-central2-smarter-poc.cloudfunctions.net/query_soc_ota_information",
"exp": time.time()+3600,
'iat': time.time(), # 时间戳
"sub":"het-query-function#smarter-poc.iam.gserviceaccount.com"
}
"""payload 中一些固定参数名称的意义, 同时可以在payload中自定义参数"""
# iss 【issuer】发布者的url地址
# sub 【subject】该JWT所面向的用户,用于处理特定应用,不是常用的字段
# aud 【audience】接受者的url地址
# exp 【expiration】 该jwt销毁的时间;unix时间戳
# nbf 【not before】 该jwt的使用时间不能早于该时间;unix时间戳
# iat 【issued at】 该jwt的发布时间;unix 时间戳
# jti 【JWT ID】 该jwt的唯一ID编号
# headers
headers = {
'alg': "RS256", # 声明所使用的算法
'typ': 'JWT'
}
# 调用jwt库,生成json web token
jwt_token = jwt.encode(token_dict, # payload, 有效载体
private_key, # 进行加密签名的密钥
algorithm="RS256", # 指明签名算法方式, 默认也是HS256
headers=headers # json web token 数据结构包含两部分, payload(有效载体), headers(标头)
).decode('ascii') # python3 编码后得到 bytes, 再进行解码(指明解码的格式), 得到一个str
print(jwt_token)
import http.client
conn = http.client.HTTPSConnection("www.googleapis.com")
# payload = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjllN2Q5ZTk1YjZkNTdiY2NiZjBhYmM2NzgzYzc2N2RhYjE2MzFjOWIiLCJ0eXAiOiJKV1QifQ.eyJpYXQiOjE2NDMwMDUwMzAsImV4cCI6MTY0MzAwNTYzMCwic3ViIjoiZ2NwLXN0b3JhZ2VAc21hcnRlci1wb2MuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJpc3MiOiJnY3Atc3RvcmFnZUBzbWFydGVyLXBvYy5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsInNjb3BlIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vYXV0aC9jbG91ZC1wbGF0Zm9ybSJ9.eAgCd5JbliFLoggcE9z7Ybhrd0GO3vb3zBW9N48iDe9tGBkQKaFdgAeANutoYLmLuvQM4m4NSavIKFHWYJHiZ6-ioehUvrs0qHZFe2bBkbNYAMQTW73ERr1XjufnZkgK6u1TUTpcX9u2EiJyMHIuku4PBYlhv8aniIsYojVfA_wVcmKhN0dVeBQzixZ_mhJsIZRPKYDPkJKn4H4oOXgy_ymbvKmguZyYLuPGezgycZpKwhFOvQQTbVSuoKikow9v4JIISXlt0fuspFLlsaEVWRx4468GUJ1SNyYThkXARRFxQAWMsgAJ2Z25I38Z3i-owWDnFKJl8KrtjSGG52sa8w'
payload = 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion='+jwt_token
headers = {
'Authorization': 'Bearer '+jwt_token,
'Content-Type': 'application/x-www-form-urlencoded'
}
conn.request("POST", "/oauth2/v4/token", payload, headers)
res = conn.getresponse()
data = res.read()
jwt = data.decode("utf-8")
print(jwt)
jwt_list = json.loads(jwt)
print(jwt_list['access_token'])
for v in range(len(jwt_list['access_token'])-1,-1,-1):
# print(jwt_list['access_token'][v])
if jwt_list['access_token'][v]!='.':
break
print(jwt_list['access_token'][v])
print("!!!!!!!")
jwt_str = jwt_list['access_token'][0:v+1]
print(jwt_str)
conn = http.client.HTTPSConnection("europe-central2-smarter-poc.cloudfunctions.net")
headers = {
'Authorization': 'bearer '+jwt_str
}
payload = ""
conn.request("GET", "/query_soc_ota_information/?hello=hello", payload, headers)
res = conn.getresponse()
data = res.read()
re = data.decode("utf-8")
print(re)
OK, your code works for me but with several changes.
It would be helpful to include which JWT you're library; I assume PyJWT
You need to include target_audience
ALG="RS256"
PROJECT=os.getenv("PROJECT")
REGION=os.getenv("REGION")
ACCOUNT=os.getenv("ACCOUNT")
FUNCTION=os.getenv("FUNCTION")
EMAIL="{account}#{project}.iam.gserviceaccount.com".format(
account=ACCOUNT,
project=PROJECT,
)
ROOT="{region}-{project}.cloudfunctions.net".format(
region=REGION,
project=PROJECT,
)
ENDPOINT="https://{root}/{function}".format(
root=ROOT,
function=FUNCTION,
)
token_dict = {
"target_audience": ENDPOINT,
"iss": EMAIL,
"sub": EMAIL,
"exp": time.time()+3600,
"iat": time.time(),
"aud": "https://www.googleapis.com/oauth2/v4/token",
}
NOTE I think exp and iat should be integers but your code works as-is.
The .decode("ascii") appended to jwt.encode().decode("ascii") is incorrect.
The payload should not be URL-encoded:
payload = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion={jwt}".format(
jwt=jwt_token,
)
The /oauth2/v4/token endpoint does not return an access_token per #john-hanley comment. It returns an id_token (as described by Google in the doc you reference).
jwt = data.decode("utf-8")
jwt_list = json.loads(jwt)
id_token=jwt_list["id_token"]
conn = http.client.HTTPSConnection(ROOT)
headers = {
"Authorization": "Bearer {token}".format(token=id_token),
}
conn.request("GET", "/{function}".format(function=FUNCTION), headers=headers)
NOTE No payload (null) not ""
main.py:
import http.client
import json
import jwt
import os
import time
ALG="RS256"
# Use GOOGLE_APPLICATION_CREDENTIALS for consistency
CREDENTIALS=os.getenv("GOOGLE_APPLICATION_CREDENTIALS")
f=open(CREDENTIALS)
key=json.load(f)
PRIVATE_KEY=key["private_key"]
PROJECT=os.getenv("PROJECT")
REGION=os.getenv("REGION")
ACCOUNT=os.getenv("ACCOUNT")
FUNCTION=os.getenv("FUNCTION")
EMAIL="{account}#{project}.iam.gserviceaccount.com".format(
account=ACCOUNT,
project=PROJECT,
)
ROOT="{region}-{project}.cloudfunctions.net".format(
region=REGION,
project=PROJECT,
)
ENDPOINT="https://{root}/{function}".format(
root=ROOT,
function=FUNCTION,
)
token_dict = {
"target_audience": ENDPOINT,
"iss": EMAIL,
"sub": EMAIL,
"exp": time.time()+3600,
"iat": time.time(),
"aud": "https://www.googleapis.com/oauth2/v4/token",
}
headers = {
'alg': ALG,
'typ': 'JWT'
}
jwt_token = jwt.encode(
token_dict,
PRIVATE_KEY,
algorithm=ALG,
headers=headers,
)
payload = "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion={jwt}".format(
jwt=jwt_token,
)
headers = {
"Authorization": "Bearer {token}".format(
token=jwt_token,
),
"Content-Type": 'application/x-www-form-urlencoded'
}
conn = http.client.HTTPSConnection("www.googleapis.com")
conn.request("POST", "/oauth2/v4/token", payload, headers)
res = conn.getresponse()
data = res.read()
jwt = data.decode("utf-8")
jwt_list = json.loads(jwt)
id_token=jwt_list["id_token"]
conn = http.client.HTTPSConnection(ROOT)
headers = {
"Authorization": "Bearer {token}".format(token=id_token),
}
conn.request("GET", "/{function}".format(function=FUNCTION), headers=headers)
res = conn.getresponse()
data = res.read()
re = data.decode("utf-8")
print(re)

python requests not recognizing params

I am requesting to mindbodyapi to get token with the following code using requests library
def get_staff_token(request):
URL = "https://api.mindbodyonline.com/public/v6/usertoken/issue"
payload = {
'Api-Key': API_KEY,
'SiteId': "1111111",
'Username': 'user#xyz.com',
'Password': 'xxxxxxxx',
}
r = requests.post(url=URL, params=payload)
print(r.text)
return HttpResponse('Done')
gives a response as follows
{"Error":{"Message":"Missing API key","Code":"DeniedAccess"}}
But if I request the following way it works, anybody could tell me, what I am doing wrong on the above code.
conn = http.client.HTTPSConnection("api.mindbodyonline.com")
payload = "{\r\n\t\"Username\": \"username\",\r\n\t\"Password\": \"xxxxx\"\r\n}"
headers = {
'Content-Type': "application/json",
'Api-Key': API_KEY,
'SiteId': site_id,
}
conn.request("POST", "/public/v6/usertoken/issue", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
In the second one, you are passing the API Key in headers and the credentials in the body of the request. In the first, you are sending both the API Key and credentials together in the query string, not the request body. Refer to requests.request() docs
Just use two dictionaries like in your second code and the correct keywords, I think it should work:
def get_staff_token(request):
URL = "https://api.mindbodyonline.com/public/v6/usertoken/issue"
payload = {
'Username': 'user#xyz.com',
'Password': 'xxxxxxxx',
}
headers = {
'Content-Type': "application/json",
'Api-Key': API_KEY,
'SiteId': "1111111",
}
r = requests.post(url=URL, data=payload, headers=headers)
print(r.text)
return HttpResponse('Done')

python set returned value from another function as a variable to use in header

Trying to get token as a variable in auth header in request in submit_documents() function it works fine if it was hard coded ; however I need to get from the return from log_in_as_taxpayer() function :
import json
class ETax_Integration(object):
"""docstring for ETax_Integration"""
def __init__(self, arg):
super(ETax_Integration, self).__init__()
self.arg = arg
def log_in_as_taxpayer(self):
client_id='###'
# print(client_id)
url = "https://url/connect/token"
client_secret='###'
# print(client_secret)
payload={'grant_type':'client_credentials','client_id':{client_id},'client_secret':{client_secret},'scope':'InvoicingAPI'}
headers = {
'Content-Type':'application/x-www-form-urlencoded'
}
response = requests.request("POST", url, headers=headers, data=payload, verify=False)
values=json.loads(response.text)
# print(values['access_token'])
# print( response.raise_for_status())
return (values['access_token'])
def submit_document(self):
token=self.log_in_as_taxpayer()
print(token)
submit_payload = json.dumps({
"documents": []
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer {token}'
}
submit_url = "https://api/link"
response = requests.request("POST", submit_url, headers=headers, data=submit_payload,verify=False)
print(response.text)
print( response.raise_for_status())
test=ETax_Integration(object)
# test.log_in_as_taxpayer()
test.submit_document()
f'Bearer {token}', notice the leading f. See here and here for more info or search python f-string with your favorite search engine ;)

How to pass return as a value for argument with adding a string?

I prepared this method
def make_bearer_token(token_url):
body = {
"scope": "ingo_dev",
"client_id": "machine__dev",
"client_secret": "0c3b99",
"username": "test.user",
"password": "xxx",
"grant_type": "pass",
}
response = requests.post(token_url, data=body).json()
bearer_token = response["access_token"]
print({'authorization: ' + 'Bearer ' + str(response)})
return bearer_token
I want to pass that bearer_token to another method with string 'authorization':bearer_token
as a header parameter. The method is looking something like that.
#staticmethod
def post_request(endpoint, file, timeout=30):
response = requests.post(endpoint, files=file_dict, timeout=timeout)
return response
A simple solution would be to run your make_bearer_token() from within your post_request method.
Bearer is a header so make a new headers dict with your Authorization header in it, and pass it as a parameter to requests.post()
def post_request(endpoint, file, timeout=30):
headers = {
"Authorization": "Bearer {}".format(make_bearer_token(endpoint))
}
response = requests.post(endpoint, files=file_dict, headers=headers, timeout=timeout)
return response
Edit: Based on your comment below, you might want to make use of class variables
class Uploader:
def __init__(self, url):
self.headers = {
"Authorization": "Bearer {}".format(self.make_bearer_token(url))
}
def post_request(self, endpoint, file, timeout=30):
response = requests.post(endpoint, files=file_dict, headers=self.headers, timeout=timeout)
return response
Usually response access_token don't have prefix bearer. So you try this way.
import requests
bearer_token = response["access_token"]
hed = {'Authorization': 'Bearer ' + bearer_token}
data = {'key' : 'value'}
url = 'https://next.url.com'
response = requests.post(url, json=data, headers=hed)
print(response)
print(response.json())
Please try this. Let me know you feedback on the same.

Null response from dialogAction(Python)

I am getting null response while my code is successfully working. I am not able to get response from dialogAction, i am
import json
import requests
def getdata(intent_name, fulfillment_state, message):
response = {
'dialogAction': {
'type': 'Close',
'intentName': intent_name,
'fulfillmentState': fulfillment_state,
'message': message
}
}
return response
def lambda_handler(event,context):
payload = {'userId':4,'type':'PARENT'}
r = requests.post("http://ec2-54-226-57-153.compute-1.amazonaws.com:8080/Tracking/rest/api", data=json.dumps(payload), headers = {'Content-Type': 'application/json','Accept': 'application/json'})
print(r.content)
getdata(
'currentIntent',
'Fulfilled',
{
'contentType': 'PlainText',
'content': 'message'
}
)
From what I understand, your code should be:
import json
import requests
def getdata(message):
return {
'dialogAction':{
'type':'Close',
'fulfillmentState':'Fulfilled',
'message':{
'contentType':'PlainText',
'content':message
}
}
}
def lambda_handler(event, context):
payload = {'userId':4,'type':'PARENT'}
r = requests.post("http://ec2-54-226-57-153.compute-1.amazonaws.com:8080/Tracking/rest/api", data=json.dumps(payload), headers = {'Content-Type': 'application/json','Accept': 'application/json'})
print(r.content)
return getdata(r.content)
Let us know if you are getting any error.

Categories

Resources