Python REST API gives Internal Server Error - python

I'm trying to create a REST API in python .. I have decent experience in python but relatively new to REST APIs ... When I run my python script I get a "Internal Server Error" on my browser ..
Some thing like this:
Error on the browser
and on my console I see this:
Error as displayed on my console
Here's my code:
from flask import Flask, request
from flask_restful import Resource, Api
import firebase_admin
# For connecting to firestore database and authentication
from firebase_admin import credentials, firestore
# For Data base Connectivity
from firebase_admin import db
from flask import jsonify
app = Flask(__name__)
api = Api(app)
class Firebase_Data(Resource):
def getData(self):
# Setting up credentials to connect
cred = credentials.Certificate(../Path)
# Setting up secure connection to firestore Real time database
app = firebase_admin.initialize_app(cred, {
'projectId' : 'project_ID'
})
# Connecting to the firestore client
db_ref = firestore.client()
# Referring to the '** section of the data
ref_inc = db_ref.collection(u'name of column')
# Fetching all the records under that particular section and
converting them to list of dictionaries
docs = list( ref_inc.get() )
lat_long = []
for doc in docs:
data = doc.to_dict()
lat_long.append(
{ 'Latitude:' : data['latitude'], 'Longitude' :
data['longitude'] } )
return jsonify(lat_long)
api.add_resource(Firebase_Data, '/Firebase_Data') # Route_1
if __name__ == '__main__':
app.run(port=5002)
I'm basically trying to fetch some data from a Fire store database and display it on the browser. I don't think the fire store part has anything to do with my error, I think I'm missing something on executing the "get" function of my python class which I'm not able to figure out .. Any help is highly appreciated.. Thanks in advance

Related

Connecting Twilio API to Google Cloud Functions

I am trying to deploy a python script in Google Cloud Functions where the user submits a yes-or-no answer from Whatsapp and it reaches the Twilio API. If this function receives a 'Yes' it activates a query in Google Big Query table and reply with a report. My point is: if I create this function with no authentication, it works fine. However, it doesn't work for me, because it gives private information about my company.
So, in order to avoid problems, I create a function authenticating with Cloud IAM. When I pass the service account to this function, I give permission to invoke cloud function, the account inherit all permissions to read and execute jobs in Big Query and I give permission to service agents. Even following all these steps, I'm still receiving a 403 error.
Here is the code I'm trying to deploy in Google Cloud Functions:
from datetime import datetime, timedelta
from dotenv import load_dotenv
import os
from sales import Sales
from flask import Flask, request
from functools import wraps
from utils import format_currency, emoji_alerts
load_dotenv()
app = Flask(__name__)
def message_sales():
data = (datetime.now()-timedelta(days=1)).strftime('%d/%m/%Y')
resultado = Sales()
return f""" message """
#here is where the message is generated, this f-string queries results in bigquery
#app.route('/reply', methods=['GET','POST'])
def send_message(request):
from twilio.rest import Client
from twilio.twiml.messaging_response import MessagingResponse
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
incoming_msg = request.values.get('Body', '').lower()
resp = MessagingResponse()
msg = resp.message()
responded = False
report = message_sales()
if 'yes' in incoming_msg:
msg.body(report)
responded = True
elif 'no' in incoming_msg:
msg.body('Ok')
responded = True
return str(resp)
if __name__ == "__main__":
app.run(debug=False, host='0.0.0.0', port=2020)
And here is the connection function to big query and a example of a query:
def run_google_query(query):
credentials = service_account.Credentials.from_service_account_file(GOOGLE_APPLICATION_CREDENTIALS)
client = bigquery.Client(project='project-id', credentials=credentials)
return [row[0] for row in client.query(query)][0]
def get_resultado_dia(self):
return f"""
SELECT RESULTADO_ATUAL FROM `table`
WHERE DATA_VENDA = '{self.ontem.strftime('%Y-%m-%d')}'
"""
This is my last deploy. I have tried to use Secret Manager library, I have created a service account with the necessary permissions, I've given more permission to actual service accounts and nothing worked.
I believe that I need to authenticate the Twilio API with Google Cloud, but I can't find a clear explanation on how to procedure with that. Anyway, create a unauthenticated http request won't be an option, since the information shouldn't be open.

How to access firebase database without having 'Realtime Database'?

Im trying to connect my python script to a Firebase database.
Here is the code:
import firebase_admin
from firebase_admin import credentials, db
cred = credentials.Certificate("irebase-adminsdk-ofcmd-bwd7fbcz2c.json")
firebase_admin.initialize_app(cred, {'databaseURL':'https://company.firebaseio.com/'})
ref = db.reference('/users').get()
The ERROR Im facing looks like this:
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://company.firebaseio.com/users.json
I did a lot of research and everyone says that I have to find the right URL in the 'Realtime Database' Section. I wonder if there is any way to access the Firebase db without having realtime one enabled.
if you are looking to access the Firebase Database without having the realtime one enabled, you can try the Cloud Firestore which is a different database service provided by Firebase which doesn't require realtime database to be enabled but you may need to change the way you are accessing the data
import firebase_admin
from firebase_admin import credentials, firestore
# Initialize the Firebase Admin SDK with your service account credentials
cred = credentials.Certificate("firebase-adminsdk-that-cred-file.json")
firebase_admin.initialize_app(cred)
# Create a reference to the Firestore database
db = firestore.client()
# Read data from a specific document in the 'users' collection
doc_ref = db.collection('users').document('user1')
doc = doc_ref.get()
# Print the data from the document
print(doc.to_dict())

Failed to use auth code flow with python MSAL

I simply can't get acquire_token_by_auth_code_flow() from the MSAL package to work outside a flask app using the basic example giving in the MSAL documentation.
I think the problem comes from using the wrong authentication response which must be a "dict of the query string received from auth server" according to the documentation. In a flask app, I can simply use request.args which I'm not quite sure how to use outside of flask.
I've already tried using requests.request as well as urlsplit. The device flow is working fine as well as using the MSAL package in Java and connecting via R. So the app seems to be set up correctly.
The basic example code from the MSAL app below produces the error:
state mismatch: XXXXXXXXXXXX vs None
(so auth_response is wrong).
Any thoughts?
import requests
import msal
CLIENT_ID = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" # Application (client) ID of app registration
CLIENT_SECRET = "XX-XXXXXXXX-XXXXXXXX.XX~XXXXX~XXXX" # Placeholder - for use ONLY during testing.
AUTHORITY = "https://login.microsoftonline.com/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXX"
REDIRECT_PATH = "/getAToken" # Used for forming an absolute URL to your redirect URI.
# The absolute URL must match the redirect URI you set
# in the app's registration in the Azure portal.
ENDPOINT = 'https://graph.microsoft.com/v1.0/me'
SCOPE = ["https://graph.microsoft.com/.default"]
# Cache
cache = msal.SerializableTokenCache()
# Build msal app
app = msal.ConfidentialClientApplication(
CLIENT_ID, authority=AUTHORITY,
client_credential=CLIENT_SECRET, token_cache=cache)
# Initiate auth code flow
session = requests.Session()
session.flow = app.initiate_auth_code_flow(scopes=SCOPE, redirect_uri=REDIRECT_PATH)
# Aquire token
result = app.acquire_token_by_auth_code_flow(auth_code_flow=session.flow, auth_response = dict(parse.parse_qsl(parse.urlsplit(REDIRECT_PATH).query)))
The equivalent code for the last bit from the flask app looks like this with REDIRECT_PATH = "/getAToken":
#app.route(app_config.REDIRECT_PATH) # Its absolute URL must match your app's redirect_uri set in AAD
def authorized():
result = _build_msal_app(cache=cache).acquire_token_by_auth_code_flow(
session.get("flow", {}), request.args)
return redirect(url_for("index"))
Getting a token requires few requests according to documentation. To make it possible you need to create flow and store it inside session before navigating to microsoft login page.
session["flow"] = _build_auth_code_flow(authority=app_config.AUTHORITY, scopes=app_config.SCOPE)
After navigation back to your application you should use this flow object as you did in your example
result = _build_msal_app(cache=cache).acquire_token_by_auth_code_flow(
session.get("flow", {}), request.args)
Make sure that you didn't create it twice. In this case error will be similar, but state mismatch: XXXXXXXXXXXX vs XXXXXXXXXXXX. It may happened if you route called twice.
auth_response must be a dictionary built from the current HTTP request query params.
If this is a desktop application you must switch to PublicClientApplication. You can find a sample here.

check if a Firebase App is already initialized in python

I get the following error:
ValueError: The default Firebase app already exists. This means you called initialize_app() more than once without providing an app name as the second argument. In most cases you only need to call initialize_app() once. But if you do want to initialize multiple apps, pass a second argument to initialize_app() to give each app a unique name.
How Can I check if the default firebase app is already initialized or not in python?
The best way is to control your app workflow so the initialization is called only once. But of course, idempotent code is also a good thing, so here is what you can do to avoid that error:
import firebase_admin
from firebase_admin import credentials
if not firebase_admin._apps:
cred = credentials.Certificate('path/to/serviceAccountKey.json')
default_app = firebase_admin.initialize_app(cred)
Initialize the app in the constructor
cred = credentials.Certificate('/path/to/serviceAccountKey.json')
firebase_admin.initialize_app(cred)
then in your method you call
firebase_admin.get_app()
https://firebase.google.com/docs/reference/admin/python/firebase_admin
I've found the following to work for me.
For the default app:
import firebase_admin
from firebase_admin import credentials
if firebase_admin._DEFAULT_APP_NAME in firebase_admin._apps:
# do something.
I have been using it in this way with a named app:
import firebase_admin
from firebase_admin import credentials
if 'my_app_name' not in firebase_admin._apps:
cred = credentials.Certificate('path/to/serviceAccountKey.json')
firebase_admin.initialize_app(cred, {
'databaseURL': 'https://{}.firebaseio.com'.format(project_id),
'storageBucket': '{}.appspot.com'.format(project_id)}, name='my_app_name')
I use this try / except block to handle initialisation of the app
try:
app = firebase_admin.get_app()
except ValueError as e:
cred = credentials.Certificate(CREDENTIALS_FIREBASE_PATH)
firebase_admin.initialize_app(cred)
If you ended up here due to building Cloud Functions in GCP with Python that interacts with Firestore, then this is what worked for me:
The reason for using an exception for control flow here is that the firebase_admin._apps is a protected member of the module, so accessing it directly is not best practice either.
import firebase_admin
from firebase_admin import credentials, firestore
def init_with_service_account(file_path):
"""
Initialize the Firestore DB client using a service account
:param file_path: path to service account
:return: firestore
"""
cred = credentials.Certificate(file_path)
try:
firebase_admin.get_app()
except ValueError:
firebase_admin.initialize_app(cred)
return firestore.client()
def init_with_project_id(project_id):
"""
Initialize the Firestore DB client using a GCP project ID
:param project_id: The GCP project ID
:return: firestore
"""
cred = credentials.ApplicationDefault()
try:
firebase_admin.get_app()
except ValueError:
firebase_admin.initialize_app(cred)
return firestore.client()
You can use
firebase_admin.delete_app(firebase_admin.get_app())
And execute the code again
You can also use default credentials
if (not len(firebase_admin._apps)):
cred = credentials.ApplicationDefault()
firebase_admin.initialize_app(cred, {
'projectId': "yourprojetid"})
In my case, I faced a similar error. But my issue was, I have initialized the app twice in my python file. So it crashed my whole python file and returns a
ValueError: The default Firebase app already exists. This means you called initialize_app() more than once without providing an app name as the second argument. In most cases you only need to call initialize_app() once. But if you do want to initialize multiple apps, pass a second argument to initialize_app() to give each app a unique name.
I solved this by removing one of my firebase app initialization. I hope this will help someone!!
Reinitializing more than one app in firebase using python:
You need to give different name for different app
For every app we will be generating one object store those objects in list and access those object one by one later
def getprojectid(proj_url):
p = r'//(.*)\.firebaseio'
x = re.findall(p, url)
return x[0]
objects = []
count = 0
details = dict()
def addtofirebase(json_path, url):
global objects, count, details
my_app_name = getprojectid(url) # Function which returns project ID
if my_app_name not in firebase_admin._apps:
cred = credentials.Certificate(json_path)
obj = firebase_admin.initialize_app(cred,xyz , name=my_app_name) # create the object
objects.append(obj) # Store Initialized Objects in one list
details[my_app_name] = count # Storing index of object in dictionary to access it later using project id
count += 1
ref = db.reference('/',app= objects[details[my_app_name]) # using this reference, change database
else:
ref = db.reference('/',app= objects[details[my_app_name]) # from next time it will get update here. it will not get initialise again and again
Make the the app global, don't put the initialize_app() inside the function because whenever the function called it also calls the initialize_app() again.
CRED = credentials.Certificate('path/to/serviceAccountKey.json')
DEFAULT_APP = firebase_admin.initialize_app(cred)
def function():
"""call default_app and process data here"""
Use don't need key.json file. You can default gcloud credentials to authenticate.
gcloud auth application-default login --project="yourproject"
python code:
import firebase_admin
app_options = {'projectId': 'yourproject'}
default_app = firebase_admin.initialize_app(options=app_options)

Python app running on Google app engine. How to make calls to Google Maps API?

I've managed to write a simple Python app using webapp2 to make requests to Google maps. When I run a local server, the requests are valid and everything works. I uploaded my contents to google's app engine and deployed it. Because of the change in environment, when I make a request, the IP address isn't white listed in my app engine configurations. Every time I add an address, the address changes.
Long story short: python app running webapp2 makes calls to google maps locally but can't when deployed on app engine.
Is there a specific module/library I should be using? I've looked everywhere but most solutions are deprecated or ask to use google.appengine.api which doesn't seem like it has what I need.
EDIT
Here's the code. The API call is made in mapRequests.getMapRequests()
when you run a local server and visit
http://localhost:8080/map/nearby/?radius=5000&latitudeUser=40.905451&longitudeUser=-74.838134&query=italian
__init__.py
import webapp2
import json
import cgi
import config
from mapRequests import *
class MainPage(webapp2.RequestHandler):
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
self.response.write('**************************')
class mapHandler(webapp2.RequestHandler):
def get(self,requestType,latitudeUser=None,longitudeUser=None,radius=None,query=None,referenceId=None):
finder = mapRequests()
self.response.headers['Content-Type'] = 'text/plain'
latitudeUser = cgi.escape(self.request.get('latitudeUser'))
longitudeUser = cgi.escape(self.request.get('longitudeUser'))
radius = cgi.escape(self.request.get('radius'))
query = cgi.escape(self.request.get('query'))
referenceId = cgi.escape(self.request.get('referenceId'))
options = {
'nearby' : finder.getMapRequest(latitudeUser,longitudeUser,radius,query),
'location' : finder.getRestaurantQuery(referenceId)
}
self.response.write(options[requestType])
self.response.write(finder.getMapRequest(latitudeUser,longitudeUser,radius,query))
app = webapp2.WSGIApplication([
('/', MainPage),
('/map/(nearby|location)/',mapHandler)
], debug=True)
mapRequests.py
import json
import config
import urllib
class mapRequests():
def __init__(self):
self.error = {'status': 'FAIL'}
return
# Get nearby locations within specified radius
def getMapRequest(self,latitudeUser,longitudeUser,radius,query):
....
val = {
'location': latitudeUser+','+longitudeUser,
'radius': radius,
'name': query,
'type': 'food',
'key': config.GOOGLE_PLACES_KEY
}
params = urllib.urlencode(val)
resp = urllib.urlopen("https://maps.googleapis.com/maps/api/place/nearbysearch/json?%s" % params)
return resp.read()
# Get additional details about a particular location
def getRestaurantQuery(self,referenceId):
if not referenceId:
self.error['referenceId'] = 'MISSING'
return(json.dumps(self.error))
return "Looking for a place\n"
This is what Google responds with when this is deployed and run on the app engine
{
"error_message" : "This IP, site or mobile application is not authorized to use this API key. Request received from IP address 107.178.195.162, with empty referer",
"html_attributions" : [],
"results" : [],
"status" : "REQUEST_DENIED"
}
When I say the IP changes, what I mean is when I add the IP seen here to the white list in my developer console, and then run the request again, it changes.
Although the only similarity is the 107.178.().() subnet so I'll tinker with that as one person mentioned here. I'll get back to you on that.
Any other suggestions what it might be?
Change your API key to allow the entire subnet of google app engine servers to make requests to the Google APIs
add 107.178.0.0/16 to your credential in this case

Categories