Sending emails from Google Appengine fails because of missing api proxy - python

I am trying to send e-mails from a GAE application using this code:
from google.appengine.api.mail import send_mail
send_mail(
"sender#nowhere.com",
["user#example.com"],
"Subject",
"Body",
)
I have configured usage of the apis in app.yaml with:
app_engine_apis: true
And deploy to App Engine is done with gcloud beta app deploy.
However, I get this error:
Traceback (most recent call last):
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/flask/app.py", line 2073, in wsgi_app response = self.full_dispatch_request()
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/flask/app.py", line 1518, in full_dispatch_request rv = self.handle_user_exception(e)
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/flask/app.py", line 1516, in full_dispatch_request rv = self.dispatch_request()
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/flask/app.py", line 1502, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "/srv/infrastructure/view_modifiers.py", line 12, in view_method response_val = f(*args, **kwargs)
File "/srv/views/orders.py", line 25, in create_order vm.create_order()
File "/srv/viewmodels/orders/order_viewmodel.py", line 74, in create_order self._send_order_email()
File "/srv/viewmodels/orders/order_viewmodel.py", line 54, in _send_order_email send_mail(
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/mail.py", line 401, in send_mail message.send(make_sync_call=make_sync_call)
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/mail.py", line 1209, in send make_sync_call('mail', self._API_CALL, message, response)
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/apiproxy_stub_map.py", line 96, in MakeSyncCall return stubmap.MakeSyncCall(service, call, request, response)
File "/layers/google.python.pip/pip/lib/python3.9/site-packages/google/appengine/api/apiproxy_stub_map.py", line 348, in MakeSyncCall assert stub, 'No api proxy found for service "%s"' % service AssertionError: No api proxy found for service "mail"
This seems to suggest that even for the default behavior of the mail service, some kind of proxy needs to be configured. However, I cannot find any information about the setup of this proxy.
And, my initial understanding was that setting up a proxy is only needed for unit-testing or local development.

Here is an example that works with fastapi. (answer to anton's comment)
from google.appengine.api import mail
from fastapi.middleware.wsgi import WSGIMiddleware
from google.appengine.api import wrap_wsgi_app
from flask import Flask
app = create_app() # this is just app = FastAPI() and somes middleware but but no relevance here
app_flask = Flask(__name__)
app_flask.wsgi_app = wrap_wsgi_app(app_flask.wsgi_app, use_deferred=True)
def send_approved_mail(sender_address):
# [START send_message]
message = mail.EmailMessage(
sender=sender_address,
subject="Your account has been approved")
message.to = "test#gmail.com"
message.body = """Dear Albert:
Your example.com account has been approved. You can now visit
http://www.example.com/ and sign in using your Google Account to
access new features.
Please let us know if you have any questions.
The example.com Team
"""
message.send()
#app_flask.route("/send_email", methods=['GET'])
def send_email():
send_approved_mail("registred_email#domain.com")
return "message sent"
app.mount("/v1", WSGIMiddleware(app_flask))
here app.yaml
runtime: python39
entrypoint: gunicorn -k uvicorn.workers.UvicornWorker app.main:app
instance_class: F1
app_engine_apis: true
inbound_services:
- mail
- mail_bounce
You will not see flask route on swagger.
and for some reason, i could not sent email with app engine's default service account. I had to register my email ( with same domain) here : https://console.cloud.google.com/appengine/settings/emailsenders?project=your_project

I just tested this and it worked for me (i.e. I received the email I supplied for recipient_email_address in the code below). Note that sender must be a value specified under Email Senders in App Engine settings page
requirements.txt file
Flask
appengine-python-standard>=1.0.0
app.yaml file
runtime: python39
app_engine_apis: true
handlers:
- url: /static
static_dir: static/
- url: /.*
script: auto
main.py
from flask import Flask
from google.appengine.api import wrap_wsgi_app
from google.appengine.api.mail import send_mail
app = Flask(__name__)
app.wsgi_app = wrap_wsgi_app(app.wsgi_app)
#app.route("/")
def sendMail():
send_mail(sender= <authorized_email_sender>,
to= <recipient_email_address>,
subject="Testing Python3 sending mails",
body="""Dear Albert:
Your example.com account has been approved. You can now visit
http://www.example.com/ and sign in using your Google Account to
access new features.
Please let us know if you have any questions.
The example.com Team
""")
return "Mail was sent"

Related

JIRA API not able to run on gunicron for flask app

I am trying to deploy my JiraAPI flask app on gunicorn in centos VM.
i have used JIRA module for it.
Here is my class
class Jira:
#initilizing the class
def __init__(self):
# private variables
self.logger = logging.getLogger('jiraUtil')
self.__email = environ.get('email')
self.__secret = environ.get('secret')
self.logger.debug("Connecting to Jira API")
jira_options = {
'server': environ.get('baseUrl'),
}
self.jira_con = JIRA(jira_options, basic_auth=(self.__email, self.__secret),max_retries=0)
if not self.jira_con:
self.logger.error("Error connecting to Jira")
raise ValueError("Error connecting to Jia")
def createIssue(self,issue_dict) -> dict:
self.logger.info("inside jira ticket creation")
new_issue_body = {}
try:
new_issue = self.jira_con.create_issue(fields=issue_dict)
print('new_issue')
new_issue_body['id'] = new_issue.id
new_issue_body['key'] = new_issue.key
new_issue_body['self'] = new_issue.self
return new_issue_body
except Exception as e:
self.logger.error("Error creating SSD ticket: ",e)
return new_issue_body
In my app.py
#app.route('/api/createTicket/', methods=['POST'])
def createTicket():
app.logger.info('Creating Issue Ticket')
request_data = request.get_json()
fields_dict = request_data.get('fields',None)
if fields_dict == None :
return jsonify({"result":"failure","status":"400", "message":"Bad Request"}),400
jiraObj = JiraUtil.Jira()
newIssueKey = jiraObj.createIssue(fields_dict)
if newIssueKey == None:
return jsonify({"result":"failure","status":"503", "message":"service Unavailable"}),500
return newIssueKey,200
if __name__ == '__main__':
app.run()
When i am running the app using flask run -h 0.0.0.0 -p 5000it is working fine and I am able to connect with my local postman
the logs got generated :
INFO:werkzeug:_[31m_[1mWARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead._[0m
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://192.168.113.24:5000
INFO:werkzeug:_[33mPress CTRL+C to quit_[0m
INFO:app:Creating Issue Ticket
DEBUG:jiraUtil:Connecting to Jira API
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): codecharlee.atlassian.net:443
DEBUG:urllib3.connectionpool:https://codecharlee.atlassian.net:443 "GET /rest/api/2/serverInfo HTTP/1.1" 200 None
INFO:jiraUtil:inside jira ticket creation
but when i am running
gunicorn --bind 0.0.0.0:5000 app:app
it starts, but gets an error when hitting the api
INFO:app:Creating Issue Ticket
DEBUG:jiraUtil:Connecting to Jira API
ERROR:app:Exception on /api/createTicket/ [POST]
Traceback (most recent call last):
File "/opt/ctier/JenkinsPythonAPI/venv/lib/python3.8/site-packages/flask/app.py", line 2525, in wsgi_app
response = self.full_dispatch_request()
File "/opt/ctier/JenkinsPythonAPI/venv/lib/python3.8/site-packages/flask/app.py", line 1822, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/opt/ctier/JenkinsPythonAPI/venv/lib/python3.8/site-packages/flask/app.py", line 1820, in full_dispatch_request
rv = self.dispatch_request()
File "/opt/ctier/JenkinsPythonAPI/venv/lib/python3.8/site-packages/flask/app.py", line 1796, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
File "/opt/ctier/JenkinsPythonAPI/app.py", line 25, in createTicket
jiraObj = JiraUtil.Jira()
File "/opt/ctier/JenkinsPythonAPI/JiraUtil.py", line 21, in __init__
self.jira_con = JIRA(jira_options, basic_auth=(self.__email, self.__secret),max_retries=0)
File "/opt/ctier/JenkinsPythonAPI/venv/lib/python3.8/site-packages/jira/client.py", line 516, in __init__
assert isinstance(self._options["server"], str) # to help mypy
AssertionError
Please help
The problem is that gunicorn is not able to read the environment variables that you have declared in the .env file.
So, just configure a service file app.service in /etc/systemd/system and add EnvironmentFile=/path_to_env_file under [service] and start the service.
For more info on configuring the env file in gunicorn service, you can see
here.

Python Flask: CoInitialize has not been called when using Waitress

Here is my script
"""
Concerto library
"""
import win32com.client
def concerto_authenticate(encrypted_request, expiry, mac):
"""
Authenticate request
"""
concerto_request = win32com.client.Dispatch(
"Concerto4xSecurity.ValidatedRequest")
secret_key = "XXX"
concerto_request.SetSecretKey(secret_key)
concerto_request.setEncryptedRequest(encrypted_request)
concerto_request.SetExpiry(expiry)
concerto_request.SetMAC(mac)
concerto_request.Validate()
return concerto_request
I call it from another page as so:
concerto_request = concerto.concerto_authenticate(encrypted_request, expiry, mac)
This works fine in my DEV environment. I am using Windows 2016 and IIS. It's using the inbuilt server that comes with Flask. Nothing fancy.
I moved all my files to PROD. Both DEV and PROD sit on the same server so have access to the exact same libraries. The only difference is that PROD uses Waitress.
No, when I run my code in PROD I get the following. This does not happen on the DEV URL, just the PROD one. The only difference being PROD uses Waitress and DEV does not.
Any suggestions?
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\flask\app.py", line 2077, in wsgi_app
response = self.full_dispatch_request()
File "C:\Python310\lib\site-packages\flask\app.py", line 1525, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Python310\lib\site-packages\flask\app.py", line 1523, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Python310\lib\site-packages\flask\app.py", line 1509, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "E:\Apps\prod\digital\gp_clinicals\gp_clinicals.py", line 43, in gp_clinicals
concerto_request = concerto.concerto_authenticate(encrypted_request, expiry, mac)
File "E:\Apps\prod\digital\concerto.py", line 11, in concerto_authenticate
concerto_request = win32com.client.Dispatch(
File "C:\Python310\lib\site-packages\win32com\client\__init__.py", line 117, in Dispatch
dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch, userName, clsctx)
File "C:\Python310\lib\site-packages\win32com\client\dynamic.py", line 106, in _GetGoodDispatchAndUserName
return (_GetGoodDispatch(IDispatch, clsctx), userName)
File "C:\Python310\lib\site-packages\win32com\client\dynamic.py", line 88, in _GetGoodDispatch
IDispatch = pythoncom.CoCreateInstance(
pywintypes.com_error: (-2147221008, 'CoInitialize has not been called.', None, None)
2022-08-01 13:15:12,592 - ERROR - __init__.py - handle_error_500 - 88 - 500 Internal Server Error: The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
prod wsgi.py
"""
Application entry point
"""
from digital import init_app
from waitress import serve
app = init_app()
if __name__ == "__main__":
serve(app, host="internal.prod", url_scheme='https')
dev wsgi.py
"""
Application entry point
"""
from digital import init_app
app = init_app()
if __name__ == "__main__":
app.run(host="internal.dev",threaded=True)

Python Flask - Session Not Being Set with Waitress

I am using waitress on IIS with Flask.
I have a route that sets a session as follows
#gp_clinicals_bp.route("/setting", methods=["POST","GET"])
def gp_clinicals():
session["gp_clinicals_meds_repeat"] = "123456"
return session["gp_clinicals_meds_repeat"]
This returns, unsurprisingly:
123456
I have another route as follows:
#gp_clinicals_bp.route("/testing", methods=["POST","GET"])
def gp_clinicals_test():
return session["gp_clinicals_meds_repeat"]
Now, I get nothing back and the following in the error log
2022-08-02 20:03:51,126 - ERROR - app.py - log_exception - 1455 - Exception on /gp-clinicals/medication-repeat [GET]
Traceback (most recent call last):
File "C:\Python310\lib\site-packages\flask\app.py", line 2077, in wsgi_app
response = self.full_dispatch_request()
File "C:\Python310\lib\site-packages\flask\app.py", line 1525, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Python310\lib\site-packages\flask\app.py", line 1523, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Python310\lib\site-packages\flask\app.py", line 1509, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "E:\Apps\dev\digital\gp_clinicals\gp_clinicals.py", line 158, in gp_clinicals_medication_repeat
return str(session["gp_clinicals_meds_repeat"])
KeyError: 'gp_clinicals_meds_repeat'
2022-08-02 20:03:51,128 - ERROR - __init__.py - handle_error_500 - 92 - 500 Internal Server Error: The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
I'm the only one on the server. Below is a snippet from my initilisation file:
"""Initialize Flask app."""
import logging
import os
from logging.handlers import RotatingFileHandler
from flask import Flask, render_template, flash,request
from flask_session import Session
from flask_assets import Environment
from config import Config
if Config.FLASK_ENV == "development" and Config.DD_SERVICE:
patch_all()
sess = Session()
def init_app():
"""Create Flask application."""
app = Flask(__name__, instance_relative_config=False)
app.config["CACHE_TYPE"] = "null"
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config["SESSION_TYPE"] = "filesystem"
app.config.from_object("config.Config")
assets = Environment()
assets.init_app(app)
sess.init_app(app)
What's going wrong? In the flask_session folder I am seeing files being created
Any advice? I tried using single apostophes instead of quotes but no go there either
The site in IIS is running through a reverse proxy in IIS - could that be doing it?

How to properly configure a Service Account in Google AppEngine Flex w/ Python?

I am using Google AppEngine (GAE) Flexible environment with a custom runtime (python2.7). I am using custom so I can just manage a Dockerfile and have easy parity between my laptop and when deployed in GAE.
I have a python 2.7 Flask app that is doing a query against DataStore. I get the following (when starting python main.py in my local docker):
root#24dcf9b8712d:/home/vmagent/app# curl localhost:5000/things
'/home/vmagent/app/clientid-searchapp.json'
[2018-09-04 14:54:19,969] ERROR in app: Exception on /things [GET]
Traceback (most recent call last):
File "/env/local/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/env/local/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/env/local/lib/python2.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/env/local/lib/python2.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/env/local/lib/python2.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "main.py", line 55, in products
for rec in query.fetch(limit=100):
File "/env/lib/python2.7/site-packages/google/api_core/page_iterator.py", line 199, in _items_iter
for page in self._page_iter(increment=False):
File "/env/lib/python2.7/site-packages/google/api_core/page_iterator.py", line 230, in _page_iter
page = self._next_page()
File "/env/local/lib/python2.7/site-packages/google/cloud/datastore/query.py", line 517, in _next_page
query=query_pb,
File "/env/local/lib/python2.7/site-packages/google/cloud/datastore_v1/gapic/datastore_client.py", line 294, in run_query
request, retry=retry, timeout=timeout, metadata=metadata)
File "/env/lib/python2.7/site-packages/google/api_core/gapic_v1/method.py", line 139, in __call__
return wrapped_func(*args, **kwargs)
File "/env/lib/python2.7/site-packages/google/api_core/retry.py", line 260, in retry_wrapped_func
on_error=on_error,
File "/env/lib/python2.7/site-packages/google/api_core/retry.py", line 177, in retry_target
return target()
File "/env/lib/python2.7/site-packages/google/api_core/timeout.py", line 206, in func_with_timeout
return func(*args, **kwargs)
File "/env/lib/python2.7/site-packages/google/api_core/grpc_helpers.py", line 56, in error_remapped_callable
six.raise_from(exceptions.from_grpc_error(exc), exc)
File "/env/local/lib/python2.7/site-packages/six.py", line 737, in raise_from
raise value
PermissionDenied: 403 Missing or insufficient permissions.
127.0.0.1 - - [04/Sep/2018 14:54:19] "GET /things HTTP/1.1" 500 -
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
Snippets from main.py
app = Flask(__name__)
ds = datastore.Client()
<snip>
#app.route("/things")
def things():
global ds
pprint(os.environ['GOOGLE_APPLICATION_CREDENTIALS'])
query = ds.query(kind='Things', order=('thing_name',))
results = list()
for rec in query.fetch(limit=100):
results.append(rec)
return jsonify(results)
Output of gcloud auth list:
root#24dcf9b8712d:/home/vmagent/app# gcloud auth list
Credentialed Accounts
ACTIVE ACCOUNT
* <myapp>#<project-name>.iam.gserviceaccount.com
This is the configured service account. I have over-privileged it with excessive Role membership; I'm pretty certain the service account isn't lacking in permissions. In fact, it has 'DataStore Owner'
In main.py I am outputting:
pprint(os.environ['GOOGLE_APPLICATION_CREDENTIALS'])
And this outputs the path to my client-<myapp>.json file for the service account. It is the same file supplied in the Dockerfile as:
RUN gcloud auth activate-service-account --key-file=clientid-<myapp>.json --project <project-name> --quiet
I get the same results when running the Flask app in my local Docker environment and when I deploy to GAE Flexible. The Flask app has other endpoints that work as they don't call any other services. Only /things uses an API to call DataStore and it receives the above error.
I've been through all the docs. I'm sure it's something basic and obvious but afaict, everything looks right.
UPDATE:
I've tried creating the DataStore client with an explicit pass of project name with no change in result. Also, I've run this command from within the Docker container and this seems like unusual output to me but I don't know that it's incorrect for a Service Account.
root#e02b74cd269d:/home/vmagent/app# gcloud projects list
API [cloudresourcemanager.googleapis.com] not enabled on project
[<project id>]. Would you like to enable and retry (this will take a
few minutes)? (y/N)? y
When I respond w/ Y,:
ERROR: (gcloud.projects.list) PERMISSION_DENIED: Not allowed to get project settings for project <project id>
Output of gcloud config list:
root#d18c83cae166:/home/vmagent/app# gcloud config list
[core]
account = <myapp>#<myproject>.iam.gserviceaccount.com
disable_usage_reporting = False
project = <myproject>
Your active configuration is: [default]

error with flask session during youtube api v3 authentication [duplicate]

This question already has answers here:
demystify Flask app.secret_key
(2 answers)
Closed 7 years ago.
I'm developing an app in python that uses YT API. I decided to move it to the web and i'm using Flask. I took the auth example from Google guides. For testing purposes i'm trying to create a playlist after authentication. It looks like this:
#app.route('/')
def index():
if 'credentials' not in flask.session:
return flask.redirect(flask.url_for('oauth2callback'))
credentials = client.OAuth2Credentials.from_json(flask.session['credentials'])
if credentials.access_token_expired:
return flask.redirect(flask.url_for('oauth2callback'))
else:
http_auth = credentials.authorize(httplib2.Http())
yt_service = discovery.build('youtube', 'v3', http_auth)
playlists_insert_response = yt_service.playlists().insert(
part="snippet,status",
body=dict(
snippet=dict(
title="Test Playlist",
description="A private playlist created with the YouTube API v3"
),
status=dict(
privacyStatus="private"
)
)
).execute()
return playlists_insert_response["id"]
#app.route('/oauth2callback')
def oauth2callback():
flow = client.flow_from_clientsecrets('youtube/client_secret.json',scope='https://www.googleapis.com/auth/youtube',redirect_uri=flask.url_for('oauth2callback', _external=True))
if 'code' not in flask.request.args:
auth_uri = flow.step1_get_authorize_url()
return flask.redirect(auth_uri)
else:
auth_code = flask.request.args.get('code')
credentials = flow.step2_exchange(auth_code)
flask.session['credentials'] = credentials.to_json()
return flask.redirect(flask.url_for('index'))
So in browser I authenticate, accept app's permisions and then I see 500 internal error with ouath2callback URI and code parameter. In the error log I see this:
2015-09-07 10:23:08,319 :Successfully retrieved access token
2015-09-07 10:23:08,330 :Exception on /oauth2callback [GET]
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1687, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1360, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1358, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1344, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/Kraxi/youtube/playlist.py", line 63, in oauth2callback
flask.session['credentials'] = credentials.to_json()
File "/usr/local/lib/python2.7/dist-packages/werkzeug/local.py", line 339, in __setitem__
self._get_current_object()[key] = value
File "/usr/local/lib/python2.7/dist-packages/flask/sessions.py", line 57, in _fail
raise RuntimeError('the session is unavailable because no secret '
RuntimeError: the session is unavailable because no secret key was set. Set the secret_key on the application to something unique and secret.
I tihnk it might be something wrong with Flask session - maybe seassion type?
Using Flask 0.10
Any ideas, please?
In order to use sessions you need to set a SECRET_KEY.
Extracted from the docs:
# set the secret key. keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

Categories