How do I call another API within an Azure Function? - python

I'm trying to create an httptrigger function that calls a Google Chat API webhook. I'm doing this so I can provide another API with a simple callback URL.
Basically I've written the following but getting the usual unhelpful 500 error
import logging
import azure.functions as func
# for chat API call
import http.client
import json
# end for chat API call
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
# call the other API
conn = http.client.HTTPSConnection("chat.googleapis.com")
payload = "{'text':'hello world, from my azure function.'\r\n}"
headers = {'Content-Type': 'application/json'}
conn.request("POST", "/v1/spaces/Ajdf9u4jf4/messages?key=f03j4fij43", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
# end call
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
else:
return func.HttpResponse(
"This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
status_code=200
)
I'm getting a 200 on the function but I don't see a message in Google Chat. The python I have added to this function to post to the Chat webhook works in Postman, but not here. Any suggestions?

Related

bingx api How to get user balance with python

This is the code I wrote to get user balance from BingX API.
I think I do everything correct but it doesn't work properly.
import urllib.request
import json
import base64
import hmac
import time
APIURL = "https://open-api.bingx.com"
APIKEY = "MyApiKey"
SECRETKEY = "MySecretKey"
def genSignature(paramsStr):
return hmac.new(SECRETKEY.encode("utf-8"),
paramsStr.encode("utf-8"), digestmod="sha256").digest()
def post(url, body):
req = urllib.request.Request(url, headers={
'User-Agent': 'Mozilla/5.0',
'X-BX-APIKEY': APIKEY,
}, method="GET")
return urllib.request.urlopen(req).read()
def getBalance():
paramsMap = {
"timestamp": int(time.time()*1000)
}
paramsStr = "&".join(["%s=%s" % (k, paramsMap[k]) for k in paramsMap])
paramsStr += "&signature=" + genSignature(paramsStr).hex()
url = "%s/openApi/swap/v2/user/balance?%s" % (APIURL, paramsStr)
return post(url, paramsStr)
def main():
print(getBalance())
if __name__ == "__main__":
main()
But when I run it I get this:
b'{"code":100001,"msg":"","success":false,"timestamp":1675069039381}'
This is the doc link
The response from the API is indicating that the request was unsuccessful and returned a code of 100001 with a success value of false. This means that there was some sort of signature authentication error in the request that was made.
The 100001 error code means that the signature authentication has failed. The signature is used to verify the authenticity of the request, so if the signature is incorrect, the request will fail.
There are a few things that could be causing the signature to fail:
Incorrect calculation of the signature: Make sure the code for generating the signature is correct and follows the requirements of the BingX API.
Incorrect encoding: Make sure the signature is properly encoded before being added to the request as a query parameter.
Incorrect secret key: Make sure the secret key used to generate the signature is correct and up-to-date.
Incorrect time stamp: Make sure the time stamp included in the request is correct and in the correct format.
You should carefully review the code and the API documentation to ensure that the signature is being generated correctly and that all required information is included in the request. If the issue persists, you may also want to reach out to the BingX API support team for additional assistance.
import requests
# Replace YOUR_API_KEY with your actual Binance API key
headers = {'X-MBX-APIKEY': 'YOUR_API_KEY'}
# Make a GET request to the Binance account endpoint
response = requests.get('https://api.binance.com/api/v3/account', headers=headers)
# Check if the request was successful
if response.status_code == 200:
# Parse the JSON response
data = response.json()
# Get the user's available balance for the specified asset
asset_balance = [balance for balance in data['balances'] if balance['asset'] == 'BTC'][0]['free']
print('User balance:', asset_balance)
else:
# Handle the error
print('Error:', response.text)

aiohttp : Authorisation denied, ERROR : Not logged in

I am currently trying to authorise myself via an HTTP API using aiohttp. However, I am running into some problems.
Docs : https://docs.ftx.com/#overview
The reply I am receiving :
Exception: Not logged in
Code:
async def test():
url = 'https://ftx.com/api/account'
ts = int(time.time() * 1000)
signature_payload = f'{ts}{"GET"}{url}'
signature_payload = signature_payload.encode()
signature =hmac.new("secret_key".encode(),signature_payload,'sha256').hexdigest()
headers = {'FTX-KEY':'public key',
'FTX-SIGN':signature,
'FTX-TS':str(ts)}
raw = await ClientSession(headers=headers).get(url=url)
response =await process_response(raw)
print(response)
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
EDIT: The specific problem I am having is with authentication. I am currently unable to authorise private requests via this way. A request like GET/markets will result in a dictionary however a request as GET/account_info does not since I need to be authorised via API for this.

Migrate Python ADAL Custom Metrics Azure Function to support Managed Identity

I have a Python function using the preview option of sending custom metrics to Azure using the REST API https://learn.microsoft.com/en-us/azure/azure-monitor/platform/metrics-store-custom-rest-api, previously this was a C# function where authorisation and getting a bearer token was handled automagically by:
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string bearerToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://monitoring.azure.com/").ConfigureAwait(false);
This worked in VS Code using the logged in user and in Azure when a Managed Identity was assigned to the Function.
I needed to convert this to Python but so far the best (working) I've been able to come up with is:
import logging, requests, os, adal
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
regional_monitoring_url = "https://eastus.monitoring.azure.com"
monitored_resource_id = os.environ['RESOURCE_ID']
full_endpoint = f"{regional_monitoring_url}{monitored_resource_id}/metrics"
tenant_id = os.environ['AZURE_TENANT_ID']
context = adal.AuthenticationContext(f'https://login.microsoftonline.com/{tenant_id}')
token = context.acquire_token_with_client_credentials("https://monitoring.azure.com/", os.environ['AZURE_CLIENT_ID'], os.environ['AZURE_CLIENT_SECRET'] )
bearer_token = token['accessToken']
json = req.get_json()
headers = {"Authorization": 'Bearer ' + bearer_token}
result = requests.post(url = full_endpoint, headers = headers, json = json)
return func.HttpResponse(f"Done - {result.status_code} {result.text}", status_code=200)
This obviously relies on me creating a Service Principal with the relevant permissions. I'm trying to work out how to use the automatic Managed Identity authorisation that the C# libraries have.
I know ADAL should be replaced by MSAL but I can't work out how/if that automagically handles Managed Identities so I tried azure-identity:
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
token = credential.get_token("https://monitoring.azure.com/.default")
bearer_token = token.token
This gets me a token but because it requires a scope rather than a resource, which means adding .default to the resource URL, when I send the bearer token to the monitoring endpoint it complains the resource doesn't match and must be exactly "https://monitoring.azure.com/"
Is this just not currently possible or am I missing something with either azure-identity or the MSAL Python modules?
According to my research, when werequest an Azure AD token to emit custom metrics, ensure that the audience the token is requested for is https://monitoring.azure.com/. For more details, please refer to here. So we should update scope as https://monitoring.azure.com//.default
For example
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
credential = DefaultAzureCredential()
token = credential.get_token("https://monitoring.azure.com//.default")
bearer_token = token.token
#full_endpoint=""
json = req.get_json()
headers = {"Authorization": 'Bearer ' + bearer_token}
#result = requests.post(url = full_endpoint, headers = headers, json = json)
return func.HttpResponse(f"Done - {bearer_token}", status_code=200)

oauth2 POST - twitter

i created a script that will get the users friend list (GET request) and i was successful. Now i am attempting to make a script that will follow a particular user (POST request) and i've been unsuccessful.
here is my oauth function (where the problem lies):
def augment_POST(url,**kwargs) :
secrets = hidden.oauth()
consumer = oauth2.Consumer(secrets['consumer_key'], secrets['consumer_secret'])
token = oauth2.Token(secrets['token_key'],secrets['token_secret'])
oauth_request = oauth2.Request.from_consumer_and_token(consumer, token= token, http_method='POST', http_url=url, parameters=kwargs)
oauth_request.to_postdata() # this returns post data, where should i put it?
oauth_request.sign_request(oauth2.SignatureMethod_HMAC_SHA1(), consumer, token)
return oauth_request.to_url()
my augment_GET function is the exact same thing except http_mehtod='GET'
for clarity:
def follow_user(id):
seedurl="https://api.twitter.com/1.1/friendships/create.json"
print 'Attempting to follow: %d' % (id,)
url = augment_POST(seedurl,user_id=id)
connection = urllib.urlopen(url)
data = connection.read()
headers = connection.info().dict
any help will be greatly appreciated.
First it seems you need to import urllib2 to make a POST request.
You have to send the POST data that you get from the to_postdata method
using the data argument of urlopen:
def augment_POST(url, **kwargs) :
secrets = hidden.oauth()
consumer = oauth2.Consumer(secrets['consumer_key'],
secrets['consumer_secret'])
token = oauth2.Token(secrets['token_key'],
secrets['token_secret'])
oauth_request = oauth2.Request.from_consumer_and_token(
consumer,
token= token,
http_method='POST',
http_url=url,
parameters=kwargs
)
oauth_request.sign_request(oauth2.SignatureMethod_HMAC_SHA1(),
consumer, token)
# this is the data that must be sent with you POST request
return oauth_request.to_postdata()
def follow_user(id):
url = "https://api.twitter.com/1.1/friendships/create.json"
print 'Attempting to follow: %d' % id
postdata = augment(url, method='GET', user_id=id)
# Send the POST request with the data argument
# The url is the same as the data is sent in the body of the request
connection = urllib2.urlopen(url, data=postdata)
data = connection.read()
headers = connection.info().dict
I would recommend to use the requests_oauthlib module which makes all this really easy:
from requests_oauthlib import OAuth1Session
tokens = hidden.oauth()
client = OAuth1Session(tokens['consumer_key'],
tokens['consumer_secret'],
tokens['token_key'],
tokens['token_secret'])
def follow_user(id):
url = "https://api.twitter.com/1.1/friendships/create.json"
print 'Attempting to follow: %d' % id
# for GET requests use client.get and the `params` argument
# instead of the `data` argument
response = client.post(url, data={'user_id': id})
data = response.text
# or even `data = response.json()` to decode the data
headers = response.headers

Python httplib2.Http not sending post parameters

I have been trying to make an API request to Twilio using the httplib2 Http class and no matter how I try to setup the request, it doesn't send my post DATA. I know this, because I posted to a local URL and the post arguments are empty. Here is my code:
_TWILIO_URL = 'https://api.twilio.com/2010-04-01/Accounts/%s/%s'
class Api(object):
'''
A Python interface to the Twilio API
'''
def __init__(self, AccountSid, AuthToken, From):
self.AccountSid = AccountSid
self.AuthToken = AuthToken
self.From = From
def _get_from(self, From):
"""Use the provided From number or the one defined on initialization"""
if From:
return From
else:
return self.From
def call(self, To, Url, From=None):
"""Sends a request to Twilio having it call a number; the provided URL will indicate how to handle the call"""
url = _TWILIO_URL % (self.AccountSid, 'Calls')
data = dict(From=self._get_from(From), To=To, Url=Url)
return self.request(url, body=urlencode(data))
def request(self, url, method='POST', body=None, headers={'content-type':'text/plain'}):
"""Send the actual request"""
h = Http()
h.add_credentials(self.AccountSid, self.AuthToken)
resp, content = h.request(url, method=method, body=body, headers=headers)
print content
if resp['status'] == '200':
return loads(content)
else:
raise TwilioError(resp['status'], content)
def sms(self, To, Body, From=None):
"""Sends a request to Twilio having it call a number; the provided URL will indicate how to handle the call"""
url = _TWILIO_URL % (self.AccountSid, 'SMS/Messages')
data = dict(From=self._get_from(From), To=To, Body=Body)
return self.request(url, body=urlencode(data))
I can't find anything on google talking about troubleshooting
Twilio mentions this requirement in their API docs concerning POST requests:
But be sure to set the HTTP
Content-Type header to
"application/x-www-form-urlencoded"
for your requests if you are
writing your own client.
It turns out that the 'content-type' has to be set to 'application/x-www-form-urlencoded'. If anyone knows why, please let me know.

Categories

Resources