Skip to content

Commit 1269f28

Browse files
committed
Merge branch 'master' of github.com:PacktPublishing/Hands-on-Microservices-with-Python
2 parents 33b5b1d + 9c43f02 commit 1269f28

Some content is hidden

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

52 files changed

+1628
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
venv/*

Dockerfile

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM python:3-alpine
2+
3+
MAINTAINER Peter Fisher
4+
5+
COPY ./app /app
6+
7+
WORKDIR /app
8+
9+
RUN apk add --update \
10+
py-mysqldb \
11+
gcc \
12+
libc-dev \
13+
mariadb-dev \
14+
&& pip install -r requirements.txt \
15+
&& rm -rf /var/cache/apk/*
16+
17+
CMD ["python", "app.py"]
18+

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018 Packt
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Hands on Microservices with Python
2+
Published by Packt
3+
4+
Author: [Peter Fisher](http://howtocodewell.net)
5+
6+
## Install
7+
Please follow [installation instructions](doc/INSTALL.md)

app/__init__.py

Whitespace-only changes.

app/app.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
from flask import Flask, g
2+
3+
from flask_login import LoginManager, user_loaded_from_header
4+
from flask_bootstrap import Bootstrap
5+
from frontend import frontend_blueprint
6+
from user_api import user_api_blueprint
7+
from product_api import product_api_blueprint
8+
from order_api import order_api_blueprint
9+
from flask.sessions import SecureCookieSessionInterface
10+
import models
11+
import base64
12+
13+
app = Flask(__name__)
14+
15+
login_manager = LoginManager(app)
16+
login_manager.init_app(app)
17+
login_manager.login_view = 'frontend.login'
18+
19+
bootstrap = Bootstrap(app)
20+
21+
app.config.update(dict(
22+
SECRET_KEY="powerful secretkey",
23+
WTF_CSRF_SECRET_KEY="a csrf secret key",
24+
SQLALCHEMY_DATABASE_URI='mysql+mysqlconnector://root:test@db/order_sys',
25+
SQLALCHEMY_TRACK_MODIFICATIONS='false',
26+
API_URI='http://192.168.99.100'
27+
))
28+
29+
models.init_app(app)
30+
models.create_tables(app)
31+
32+
app.register_blueprint(frontend_blueprint)
33+
app.register_blueprint(user_api_blueprint)
34+
app.register_blueprint(product_api_blueprint)
35+
app.register_blueprint(order_api_blueprint)
36+
37+
38+
@login_manager.user_loader
39+
def load_user(user_id):
40+
return models.User.query.filter_by(id=user_id).first()
41+
42+
43+
@login_manager.request_loader
44+
def load_user_from_request(request):
45+
46+
# try to login using Basic Auth
47+
api_key = request.headers.get('Authorization')
48+
if api_key:
49+
api_key = api_key.replace('Basic ', '', 1)
50+
user = models.User.query.filter_by(api_key=api_key).first()
51+
if user:
52+
return user
53+
54+
return None
55+
56+
57+
class CustomSessionInterface(SecureCookieSessionInterface):
58+
"""Prevent creating session from API requests."""
59+
def save_session(self, *args, **kwargs):
60+
if g.get('login_via_header'):
61+
return
62+
return super(CustomSessionInterface, self).save_session(*args, **kwargs)
63+
64+
65+
app.session_interface = CustomSessionInterface()
66+
67+
68+
@user_loaded_from_header.connect
69+
def user_loaded_from_header(self, user=None):
70+
g.login_via_header = True
71+
72+
73+
if __name__ == '__main__':
74+
app.run(debug=True, host='0.0.0.0')

app/database/db.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
from flask_sqlalchemy import SQLAlchemy
3+
4+

app/database/products.json

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
[{
2+
"name" : "Bananas",
3+
"slug" : "bananas",
4+
"price": "£200",
5+
"in_stock" : 1,
6+
"image": "489px-Banana.png"
7+
},
8+
{
9+
"name" : "Apple",
10+
"slug" : "apple",
11+
"link": "",
12+
"price": "£200",
13+
"in_stock" : 1,
14+
"image": "apple.png"
15+
},
16+
{
17+
"name" : "Rubber Duck",
18+
"slug" : "rubber-ducks",
19+
"link": "",
20+
"price": "£200",
21+
"in_stock" : 1,
22+
"image": "rubber_duck.png"
23+
},
24+
{
25+
"name" : "Fidget Spinner Red",
26+
"slug" : "fidget-spinner-red",
27+
"link": "",
28+
"price": "£200",
29+
"in_stock" : 1,
30+
"image": "fidget-spinner-2399715_960_720.png"
31+
},
32+
{
33+
"name" : "Fidget Spinner Blue",
34+
"slug" : "fidget-spinner-blue",
35+
"link": "",
36+
"price": "£200",
37+
"in_stock" : 1,
38+
"image": "Fidget_spinner_in_blue.png"
39+
},
40+
{
41+
"name" : "Fidget Spinner Yellow",
42+
"slug" : "fidget-spinner-yellow",
43+
"link": "",
44+
"price": "£200",
45+
"in_stock" : 1,
46+
"image": "fidget-spinner-yellow.png"
47+
},
48+
{
49+
"name" : "Red Book",
50+
"slug" : "book-red",
51+
"link": "",
52+
"price": "£200",
53+
"in_stock" : 1,
54+
"image": "book-red.png"
55+
},
56+
{
57+
"name" : "Tomato",
58+
"slug" : "tomato",
59+
"link": "",
60+
"price": "£200",
61+
"in_stock" : 1,
62+
"image": "tomato.png"
63+
},
64+
{
65+
"name" : "Coffee",
66+
"slug" : "coffee",
67+
"link": "",
68+
"price": "£200",
69+
"in_stock" : 1,
70+
"image": "coffee.png"
71+
}
72+
]

app/database/users.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"peter_fisher": {
3+
"first_name" : "peter",
4+
"last_name" : "Fisher",
5+
"is_admin": 1
6+
},
7+
"john_davies": {
8+
"first_name" : "John",
9+
"last_name" : "Daives",
10+
"is_admin" : 0
11+
},
12+
"clare_white": {
13+
"first_name" : "Clare",
14+
"last_name" : "White",
15+
"is_admin" : 0
16+
}
17+
}

app/frontend/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from flask import Blueprint
2+
3+
frontend_blueprint = Blueprint('frontend', __name__, template_folder='templates')
4+
5+
6+
from . import routes

app/frontend/api.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from flask import abort
2+
import requests
3+
4+
5+
def get_request(url):
6+
try:
7+
r = requests.get(url)
8+
r.raise_for_status()
9+
return r.json()
10+
except requests.exceptions.HTTPError as err_http:
11+
print ("HTTP error: ", err_http)
12+
abort(404)
13+
except requests.exceptions.ConnectionError as err_connection:
14+
print ("Cannot connect: ", err_connection)
15+
abort(404)
16+
except requests.exceptions.Timeout as err_timeout:
17+
print ("Timeout: ", err_timeout)
18+
abort(404)
19+
except requests.exceptions.RequestException as err_request:
20+
print ("Error with request: ", err_request)
21+
abort(404)

app/frontend/forms.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from flask_wtf import FlaskForm
2+
from wtforms import StringField, PasswordField, SubmitField, HiddenField, IntegerField, SelectField
3+
from wtforms.validators import DataRequired, Email
4+
5+
6+
class LoginForm(FlaskForm):
7+
username = StringField('Username', validators=[DataRequired()])
8+
password = PasswordField('Password', validators=[DataRequired()])
9+
submit = SubmitField('Login')
10+
11+
12+
class RegisterForm(FlaskForm):
13+
username = StringField('Username', validators=[DataRequired()])
14+
first_name = StringField('First name', validators=[DataRequired()])
15+
last_name = StringField('last name', validators=[DataRequired()])
16+
email = StringField('Email address', validators=[DataRequired(), Email()])
17+
password = PasswordField('Password', validators=[DataRequired()])
18+
submit = SubmitField('Register')
19+
20+
21+
class OrderItemForm(FlaskForm):
22+
product_id = HiddenField(validators=[DataRequired()])
23+
quantity = IntegerField(validators=[DataRequired()])
24+
order_id = HiddenField()
25+
submit = SubmitField('Update')
26+
27+
28+
class ItemForm(FlaskForm):
29+
product_id = HiddenField(validators=[DataRequired()])
30+
quantity = HiddenField(validators=[DataRequired()], default=1)

0 commit comments

Comments
 (0)