Sending emails from Google Appengine fails because of missing api proxy

Viewed 126

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.

2 Answers

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