Skip to content

Commit e48d44b

Browse files
authored
Merge pull request #83 from tecladocode/jose/cou-100-write-section-on-task-queues
2 parents 6bc81d1 + eba8205 commit e48d44b

File tree

321 files changed

+10345
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

321 files changed

+10345
-5
lines changed

docs/docs/05_flask_smorest/02_data_model_improvements/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,14 @@ Next, let's create the `.flaskenv` file:
108108

109109
```txt title=".flaskenv"
110110
FLASK_APP=app
111-
FLASK_ENV=development
111+
FLASK_DEBUG=True
112112
```
113113

114114
If we have the `python-dotenv` library installed, when we run the `flask run` command, Flask will read the variables inside `.flaskenv` and use them to configure the Flask app.
115115

116-
The configuration that we'll do is to define the Flask app file (here, `app.py`). Then we'll also set the Flask environment to `development`, which does a couple things:
116+
The configuration that we'll do is to define the Flask app file (here, `app.py`). Then we'll also set the `FLASK_DEBUG` flag to `True`, which does a couple things:
117117

118-
- Sets debug mode to true, which makes the app give us better error messages
118+
- Makes the app give us better error messages and return a traceback when we make requests if there's an error.
119119
- Sets the app reloading to true, so the app restarts when we make code changes
120120

121121
We don't want debug mode to be enabled in production (when we deploy our app), but while we're doing development it's definitely a time-saving tool!
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# How to send emails with Python and Mailgun
2+
3+
To send e-mails using Python, we are going to use Mailgun, a third party service which actually delivers the messages.
4+
5+
You could use [your own personal account and the built-in `email` and `smtp` libraries](https://blog.teclado.com/learn-python-send-emails/), but most personal e-mail providers will limit how many e-mails you can send per day. Plus, you won't get analytics and a host of other features that you can get with an email service like Mailgun.
6+
7+
There are two ways to use the Mailgun service: [via SMTP or via their API](https://www.mailgun.com/blog/email/difference-between-smtp-and-api/). I'll show you how to use the API since it's a bit easier and has the same functionality.
8+
9+
Sending an e-mail with Mailgun is just a matter of sending a request to their API. To do this, we'll use the `requests` library:
10+
11+
```bash
12+
pip install requests
13+
```
14+
15+
Remember to add it to your `requirements.txt` as well:
16+
17+
```text title="requirements.txt"
18+
requests
19+
```
20+
21+
## Setting up for Mailgun
22+
23+
Before we can send any emails, we need to set up our Mailgun account. First, register over at [https://mailgun.com](https://mailgun.com).
24+
25+
Once you have registered, select your sandbox domain. It's in [your dashboard](https://app.mailgun.com/app/dashboard), at the bottom. It looks like this: `sandbox847487f8g78.mailgun.org`.
26+
27+
Then at the top right, enter your personal email address under "Authorized recipients".
28+
29+
You will get an email to confirm. Click the button that you see in that email to add your personal email to the list of authorized recipients.
30+
31+
Next up, grab your API key. You can find it by clicking on this button (my domain and API key are blurred in this screenshot):
32+
33+
![Click the 'Select' button to reveal your Mailgun API key](./assets/mailgun-api-key.png)
34+
35+
## Sending emails with Mailgun
36+
37+
To make the API request which sends an email, we'll use a function that looks very much like this one (taken from their documentation):
38+
39+
```py
40+
def send_simple_message():
41+
return requests.post(
42+
"https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages",
43+
auth=("api", "YOUR_API_KEY"),
44+
data={"from": "Excited User <mailgun@YOUR_DOMAIN_NAME>",
45+
"to": ["[email protected]", "YOU@YOUR_DOMAIN_NAME"],
46+
"subject": "Hello",
47+
"text": "Testing some Mailgun awesomness!"})
48+
```
49+
50+
So let's go into our User resource and add a couple of imports and this function. Make sure to replace "Your Name" with your actual name or that of your application:
51+
52+
```py title="resources/user.py"
53+
import os
54+
import requests
55+
56+
...
57+
58+
def send_simple_message(to, subject, body):
59+
domain = os.getenv("MAILGUN_DOMAIN")
60+
return requests.post(
61+
f"https://api.mailgun.net/v3/{domain}/messages",
62+
auth=("api", os.getenv("MAILGUN_API_KEY")),
63+
data={
64+
"from": f"Your Name <mailgun@{domain}>",
65+
"to": [to],
66+
"subject": subject,
67+
"text": body,
68+
},
69+
)
70+
```
71+
72+
Then let's go to the `.env` file and add your Mailgun API key and domain:
73+
74+
```text title=".env"
75+
MAILGUN_API_KEY="<insert your api key here>"
76+
MAILGUN_DOMAIN="<insert your domain here>"
77+
```
78+
79+
:::info
80+
The API Key should look something like this: `"1f1ahfjhf4878797887187j-5ac54n"`.
81+
82+
The Domain should look something like this: `"sandbox723b05d9.mailgun.org"`
83+
:::
84+
85+
With this, we're ready to actually send emails!
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DATABASE_URL=
2+
MAILGUN_API_KEY=
3+
MAILGUN_DOMAIN=
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FLASK_APP=app
2+
FLASK_ENV=development
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.env
2+
.venv
3+
.vscode
4+
__pycache__
5+
data.db
6+
*.pyc
7+
.DS_Store
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# CONTRIBUTING
2+
3+
## How to run the Dockerfile locally
4+
5+
```
6+
docker run -dp 5000:5000 -w /app -v "$(pwd):/app" IMAGE_NAME sh -c "flask run"
7+
```
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM python:3.10
2+
WORKDIR /app
3+
COPY requirements.txt .
4+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
5+
COPY . .
6+
CMD ["/bin/bash", "docker-entrypoint.sh"]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# REST APIs Recording Project
2+
3+
Nothing here yet!
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import os
2+
3+
from flask import Flask, jsonify
4+
from flask_smorest import Api
5+
from flask_jwt_extended import JWTManager
6+
from flask_migrate import Migrate
7+
from dotenv import load_dotenv
8+
9+
10+
from db import db
11+
from blocklist import BLOCKLIST
12+
import models
13+
14+
from resources.item import blp as ItemBlueprint
15+
from resources.store import blp as StoreBlueprint
16+
from resources.tag import blp as TagBlueprint
17+
from resources.user import blp as UserBlueprint
18+
19+
20+
def create_app(db_url=None):
21+
app = Flask(__name__)
22+
load_dotenv()
23+
24+
app.config["PROPAGATE_EXCEPTIONS"] = True
25+
app.config["API_TITLE"] = "Stores REST API"
26+
app.config["API_VERSION"] = "v1"
27+
app.config["OPENAPI_VERSION"] = "3.0.3"
28+
app.config["OPENAPI_URL_PREFIX"] = "/"
29+
app.config["OPENAPI_SWAGGER_UI_PATH"] = "/swagger-ui"
30+
app.config["OPENAPI_SWAGGER_UI_URL"] = "https://cdn.jsdelivr.net/npm/swagger-ui-dist/"
31+
app.config["SQLALCHEMY_DATABASE_URI"] = db_url or os.getenv("DATABASE_URL", "sqlite:///data.db")
32+
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
33+
db.init_app(app)
34+
migrate = Migrate(app, db)
35+
api = Api(app)
36+
37+
app.config["JWT_SECRET_KEY"] = "jose"
38+
jwt = JWTManager(app)
39+
40+
@jwt.token_in_blocklist_loader
41+
def check_if_token_in_blocklist(jwt_header, jwt_payload):
42+
return jwt_payload["jti"] in BLOCKLIST
43+
44+
@jwt.revoked_token_loader
45+
def revoked_token_callback(jwt_header, jwt_payload):
46+
return (
47+
jsonify(
48+
{"description": "The token has been revoked.", "error": "token_revoked"}
49+
),
50+
401,
51+
)
52+
53+
@jwt.needs_fresh_token_loader
54+
def token_not_fresh_callback(jwt_header, jwt_payload):
55+
return (
56+
jsonify(
57+
{
58+
"description": "The token is not fresh.",
59+
"error": "fresh_token_required",
60+
}
61+
),
62+
401,
63+
)
64+
65+
@jwt.additional_claims_loader
66+
def add_claims_to_jwt(identity):
67+
# Look in the database and see whether the user is an admin
68+
if identity == 1:
69+
return {"is_admin": True}
70+
return {"is_admin": False}
71+
72+
@jwt.expired_token_loader
73+
def expired_token_callback(jwt_header, jwt_payload):
74+
return (
75+
jsonify({"message": "The token has expired.", "error": "token_expired"}),
76+
401,
77+
)
78+
79+
@jwt.invalid_token_loader
80+
def invalid_token_callback(error):
81+
return (
82+
jsonify(
83+
{"message": "Signature verification failed.", "error": "invalid_token"}
84+
),
85+
401,
86+
)
87+
88+
@jwt.unauthorized_loader
89+
def missing_token_callback(error):
90+
return (
91+
jsonify(
92+
{
93+
"description": "Request does not contain an access token.",
94+
"error": "authorization_required",
95+
}
96+
),
97+
401,
98+
)
99+
100+
@app.before_first_request
101+
def create_tables():
102+
db.create_all()
103+
104+
api.register_blueprint(ItemBlueprint)
105+
api.register_blueprint(StoreBlueprint)
106+
api.register_blueprint(TagBlueprint)
107+
api.register_blueprint(UserBlueprint)
108+
109+
return app

0 commit comments

Comments
 (0)