Azure Functions with Multiple Python Files - python

I am unable to import my other python file in the init.py file. I have tried importing that according to the same way as mentioned in the documentation.
https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-python#import-behavior
This is my init.py file:
import logging
import azure.functions as func
from . import test
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
print("test")
num = sum_num(2, 3)
print(num)
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
)
this is my test.py file with very basic sum function:
def sum_num(val1, val2):
print("inside function")
return val1 + val2
Please help me out how to import this sum_num function in my init.py file.
the folder structure is also fine as you can see:
Error i am getting in the logs

You have already imported the module using
from . import test
so in order to access the function from the module you should use
num = test.sum_num(2, 3)
Alternatively you can import only the function from the module.
from .test import sum_num

Related

Azure Function App using python: How to access user groups for authorization

I am very new to Azure Function Apps and OAuth so please bear with me.
My Setup
I have an Azure Function App with a simple python-function doing nothing else but printing out the request headers:
import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
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:
aadIdToken = req.headers.get('X-MS-TOKEN-AAD-ID-TOKEN')
aadAccessToken = req.headers.get('X-MS-TOKEN-AAD-ACCESS-TOKEN')
principalID = req.headers.get('X-MS-CLIENT-PRINCIPAL-ID')
principalName = req.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
idProviderId = req.headers.get('X-MS-CLIENT-PRINCIPAL-IDP')
aadRefreshToken = req.headers.get('X-MS-TOKEN-AAD-REFRESH-TOKEN')
clientPrincipal = req.headers.get('X-MS-CLIENT-PRINCIPAL')
result = "\n"
myDict = sorted(dict(req.headers))
for key in myDict:
result += f"{key} = {dict(req.headers)[key]}\n"
return func.HttpResponse(
f"Hello, {name}. How are you ? Doing well ?"\
f"\n\nHere is some data concerning your Client principal:"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-ID: {principalID}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-NAME: {principalName}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-IDP: {idProviderId}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL: {clientPrincipal}"\
f"\n\nHere is some data concerning your AAD-token:"\
f"\nThis is your X-MS-TOKEN-AAD-ID-TOKEN: {aadIdToken}"\
f"\nThis is your X-MS-TOKEN-AAD-ACCESS-TOKEN: {aadAccessToken}"\
f"\nThis is your X-MS-TOKEN-AAD-REFRESH-TOKEN: {aadRefreshToken}"\
f"\n\n\nresult: {result}"\
)
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 followed this guide to let the user authenticate via EasyAuth before calling the function.
This seems to work fine. When accessing the function via browser I am redirected to sign-in. After successful sign-in I am then redirected again and the HTTP response is printed out in the browser. As I am able to access X-MS-CLIENT-PRINCIPAL-ID and X-MS-CLIENT-PRINCIPAL-NAME I suppose the authentication was successful. However when printing out the whole request header I did not find a X-MS-TOKEN-AAD-REFRESH-TOKEN, X-MS-TOKEN-AAD-ACCESS-TOKEN or X-MS-TOKEN-AAD-ID-TOKEN.
This is the output (output too large; below the output shown in the screenshot I can see the header content):
First half of my output
My question
What I am trying to do now is to access the groups assigned to the logged-in user via the python code of the function to further authorize his request (e.g. "user can only execute the function when group xyz is assigned, else he will be prompted 'not allowed'").
To achieve this I added the "groups"-claim to the Token Configuration of my App Registration.
From what I understand accessing the user groups via a function coded in .NET is easily possible by using the ClaimsPrinciple object (source).
How would I be able to access the user assigned groups via python code?
Is that possible?
Am I understanding something completely wrong?
Followup:
One thing that I do not understand by now, is that I can see an id_token in the callback-http-request of the browser-debuggger when accessing the function via browser for the first time (to trigger sign in):
Browser debugger: id_token in callback-request
When I decrypted that token using jwt.io I was able to see some IDs of assigned user groups which seems to be exactly what I want to access via the python code.
Re-loading the page (I suppose the request then uses the already authenticated browser session) makes the callback disappear.
The header X-MS-CLIENT-PRINCIPAL contains the same claims as the id_token. So if we want to get the group claim, we can base64 decode the header.
For example
My code
import logging
import azure.functions as func
import base64
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
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:
aadAccessToken = req.headers.get('X-MS-TOKEN-AAD-ACCESS-TOKEN')
principalID = req.headers.get('X-MS-CLIENT-PRINCIPAL-ID')
principalName = req.headers.get('X-MS-CLIENT-PRINCIPAL-NAME')
idProviderId = req.headers.get('X-MS-CLIENT-PRINCIPAL-IDP')
aadRefreshToken = req.headers.get('X-MS-TOKEN-AAD-REFRESH-TOKEN')
clientPrincipal = req.headers.get('X-MS-CLIENT-PRINCIPAL')
clientPrincipal= base64.b64decode(clientPrincipal)
result = "\n"
myDict = sorted(dict(req.headers))
for key in myDict:
result += f"{key} = {dict(req.headers)[key]}\n"
return func.HttpResponse(
f"Hello, {name}. How are you ? Doing well ?"\
f"\n\nHere is some data concerning your Client principal:"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-ID: {principalID}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-NAME: {principalName}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL-IDP: {idProviderId}"\
f"\nThis is your X-MS-CLIENT-PRINCIPAL: {clientPrincipal}"\
f"\n\nHere is some data concerning your AAD-token:"\
f"\nThis is your X-MS-TOKEN-AAD-ID-TOKEN: {aadIdToken}"\
f"\nThis is your X-MS-TOKEN-AAD-ACCESS-TOKEN: {aadAccessToken}"\
f"\nThis is your X-MS-TOKEN-AAD-REFRESH-TOKEN: {aadRefreshToken}"\
f"\n\n\nresult: {result}"\
)
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
)

azure functions return http response from a custom util function

Suppose I have an API for signup. I want to get data from the request and validate it in a Util function that I wrote and placed in another directory (see signup API and my Util directory in the picture). If validation goes fail, I want to return the HTTP response directly from that Util function. How can I achieve this?
In the init.py
from utils.common_utils import validate
def main(req: func.HttpRequest) -> func.HttpResponse:
req_body = validate(req)
# other stuff
return func.HttpResponse("ok", status_code=200)
and in my util function (validate function)
def validate(input):
if ...:
return func.HttpResponse("check your input", status_code=406)
As far as I know, I don't think it can return HttpResponse directly from that Util function and exit the main function. In your code, you can just return HttpResponse to req_body and then execute the following code of main function.
But I think if you write the code like below, it can also meet your requirement. It's the same thing.
def main(req: func.HttpRequest) -> func.HttpResponse:
req_body = validate(req)
if req_body.status_code == 406:
return req_body
return func.HttpResponse("ok", status_code=200)

What is the best way to create PDF files in Azure storage blob using Python?

I am new to Python and I came up with a requirement for created PDF files with data available in SQL server using Python script. As I researched, there are many libraries which can be used for this purpose but most of their approach is to generate HTML string then convert it into PDF file in a local directory. But none of them suggested how to do it on Azure storage. I am having an Azure function app which connects to DB and reads data, now I need to create PDF using this data. Since Azure function app is a serverless source and can't relay on a physical directory at all. So I should be able to create a string with data and convert them into PDF and upload them directly to Azre storage.
import logging
import azure.functions as func
from fpdf import FPDF
def generate_PDF():
pdf = FPDF()
pdf.add_page()
pdf.set_font("Arial", 12)
pdf.cell(w=0,h=0,txt="This is sample pdf",align="L")
pdf.output('demo.pdf')
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name')
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
c_pdf = req_body.get('cpdf')
if c_pdf == 'Y':
generate_PDF()
if name:
return func.HttpResponse(f"Hello {name}!, create PDF (cpdf) is set to {c_pdf}")
else:
return func.HttpResponse("Please pass a name on the query string or in the request body", status_code=400)
I am using FPDF library, and it creates pdf file in the current working directory.
Suggest me the best way for my approach. Thanks in advance.
Regarding the issue, please refer to the following code
sdk
pip install azure-storage-blob fpdf aiohttp
code
from azure.storage.blob import ContentSettings
from azure.storage.blob.aio import BlobServiceClient
from fpdf import FPDF
async def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
connection_string = 'DefaultEndpointsProtocol=https;AccountName=jimtestdiag924;AccountKey=uxz4AtF0A4tWBcPHwgbFAfdinvLEZpJtAu1MYVWD/xYCYDcLLRb8Zhp5lxR2/2rQ2P1OrxZwWarEoWyDSZ7Q+A==;EndpointSuffix=core.windows.net'
pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 16)
pdf.cell(40, 10, 'Hello World!')
s = pdf.output(dest='S').encode('latin-1')
logging.info(s)
blob_service_client = BlobServiceClient.from_connection_string(connection_string)
async with blob_service_client:
container_client = blob_service_client.get_container_client('testupload')
try:
# Create new Container in the Service
await container_client.create_container()
except Exception as ex:
pass
# Get a new BlobClient
blob_client = container_client.get_blob_client('demo.pdf')
await blob_client.upload_blob(s, blob_type="BlockBlob",content_settings=ContentSettings( content_type='application/pdf'))
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}!")
else:
return func.HttpResponse(
"Please pass a name on the query string or in the request body",
status_code=400
)

Returning a generator in Azure Function

Is it possible to return a generator in an Azure Function? I am trying to stream data while it is being created. I tried the following with no luck:
def main(req: func.HttpRequest) -> func.HttpResponse:
def gen():
for i in range(10):
yield i
return func.HttpResponse(gen(), mimetype='text/html')
The above yielded TypeError: reponse is expected to be either of str, bytes, or bytearray, got generator. Is it possible to do something like this in an Azure Function? I don't necessarily require it to be in Python, but it would be preferred.
I guess this should be possible, May be have a look at this example, you might need to convert the response to a Object/Disctionary.
If you want to return the number in the generator suppose you could return the list(gen()). It will act like the below pic.
I return the return func.HttpResponse(str(list(gen())),status_code=200), hope this is what you want, if you still have other problem please feel free to let me know.
If your purpose is use flask to return a generator you could refer to the below code.
import logging
import azure.functions as func
import json, os
from flask import stream_with_context, request, Response
from flask.app import Flask
app = Flask(__name__)
#app.route('/')
def hello_world():
def generate():
yield 'Hello '
yield 'world11'
return Response(stream_with_context(generate()))
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
uri=req.params['uri']
with app.test_client() as c:
doAction = {
"GET": c.get(uri).data,
"POST": c.post(uri).data
}
resp = doAction.get(req.method).decode()
return func.HttpResponse(resp, mimetype='text/html')
Here is the test result pic.

HttpTrigger Execute one after another using python

I have created one HttpTrigger Azure functions using python. But I want to create another one HttpTrigger Azure Functions using python in the same project. In this application I want to execute first HttpTrigger Azure functions after that second HttpTrigger Azure functions execute. How can I implement that?
Because in Python Azure Functions there is no Durable Functions. That's why I am not able to understood how can I execute one after another azure function executes.
Assumed there are two HttpTrigger functions HttpTriggerA and HttpTriggerB, a direct solution in my mind is to request the public url of HttpTriggerA from HttpTriggerB function via a HTTP client request using requests, as the code below in my first version of HttpTriggerB code.
import logging
import azure.functions as func
import requests
from urllib.parse import urlparse
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name') or 'Peter Pan'
codeA = req.params.get('codeA') or ''
o = urlparse(req.url)
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
print("B be invoked.")
resp = requests.get(f"{o.scheme}://{o.netloc}/api/HttpTriggerA?name={name}&code={codeA}")
return func.HttpResponse(f"Hello {name}! from B {resp.status_code == 200 and resp.text or ''}")
else:
return func.HttpResponse(
"Please pass a name on the query string or in the request body",
status_code=400
)
However, it will not work after I test the code above on local or on Azure. It will hang at the code line resp = requests.get(f"{o.scheme}://{o.netloc}/api/HttpTriggerA?name={name}&code={codeA}"), when I access the url http(s)://<the host of local or Azure>:<7071 or 80>/api/HttpTriggerB?name=Peter%20Pan&code=<code for HttpTriggerB>&codeA=<code for HttpTriggerA>. The reason for the hang issue seems to be caused by functions running in singleton or in single thread.
So I switched to the other solution to use Ajax request from the html content of HttpTriggerB. It works as I wish as the figure below.
Here is my code for HttpTriggerB function, the HttpTriggerA function is simply generated by func new.
import logging
import azure.functions as func
#import requests
from urllib.parse import urlparse
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
name = req.params.get('name') or 'Peter Pan'
codeA = req.params.get('codeA') or ''
o = urlparse(req.url)
if not name:
try:
req_body = req.get_json()
except ValueError:
pass
else:
name = req_body.get('name')
if name:
print("B be invoked.")
#resp = requests.get(f"{o.scheme}://{o.netloc}/api/httptriggera?name={name}&code={req.params.get('codeA')}")
html = """
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
Hello """+name+"""! from B<br/>
<span id="A"></span>
<script>
$.when(
$.get('"""+o.scheme+'://'+o.netloc+'/api/HttpTriggerA?name='+name+'&code='+codeA+"""'),
$.ready
).done(function( data ) {
$( "#A" ).html( data[0] );
});
</script>
"""
return func.HttpResponse(html, mimetype="text/html")
else:
return func.HttpResponse(
"Please pass a name on the query string or in the request body",
status_code=400
)
Hope it helps.

Categories

Resources