-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackend.py
More file actions
215 lines (183 loc) · 6.74 KB
/
backend.py
File metadata and controls
215 lines (183 loc) · 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import sys
import logging
from flask import Flask, jsonify, abort, make_response, request, url_for, g
from flask_cors import CORS
from flask_httpauth import HTTPBasicAuth
from flask_sqlalchemy import SQLAlchemy
from passlib.apps import custom_app_context as pwd_context
from flask_redis import FlaskRedis
from dns_resolver import get_redis_address
from kafka import KafkaProducer
# Setup logging facility
console = logging.StreamHandler(sys.stdout)
console.setFormatter(logging.Formatter('%(asctime)s [%(levelname)s] %(name)s %(message)s'))
log = logging.getLogger('')
log.addHandler(console)
log.setLevel(logging.INFO)
logger = logging.getLogger(__name__)
# Fetch the address of service redis from Consul
(redis_ip, redis_port) = get_redis_address()
redis_url = "redis://%s:%s/0" % (redis_ip, redis_port)
logger.info('Got the address of service redis as: %s' % redis_url)
app = Flask(__name__)
CORS(app)
auth = HTTPBasicAuth()
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['REDIS_URL'] = redis_url
redis_store = FlaskRedis(app)
API_USAGE_KEY = 'visit:api:total'
courses = [
{
'id': 1,
'title': u'Software Architecture',
'description': u'Software architecture refers to the high level structures of a software system, the discipline of creating such structures, and the documentation of these structures.',
'done': False
},
{
'id': 2,
'title': u'Software Management',
'description': u'Software project management is an art and science of planning and leading software projects.',
'done': False
}
]
producer = KafkaProducer(bootstrap_servers='kafka:29092')
topic = 'api-visits'
# Record API visit
@app.before_request
def before_request():
producer.send(topic, bytes(request.path, encoding='utf-8'))
# redis_store.incr(API_USAGE_KEY)
# Get API usage
@app.route('/ce/api/v1.0/usages', methods=['GET'])
def get_usage():
count = redis_store.get(API_USAGE_KEY)
# count = 12345
return jsonify({"count": str(count, encoding='utf-8')})
# GET one specific course
@app.route('/ce/api/v1.0/courses/<int:course_id>', methods=['GET'])
# @auth.login_required
def get_course(course_id):
course = list(filter(lambda t: t['id'] == course_id, courses))
if len(course) == 0:
abort(404)
return jsonify( course[0] )
# transfer error page into JSON format
# @app.errorhandler(404)
# def not_found(error):
# return make_response(jsonify({'error': 'Not found'}), 404)
# POST a new course
@app.route('/ce/api/v1.0/courses', methods=['POST'])
# @auth.login_required
def create_course():
if not request.json or not 'title' in request.json:
abort(400)
course = {
'id': courses[-1]['id'] + 1,
'title': request.json['title'],
'description': request.json.get('description', ""),
'done': False
}
courses.append(course)
return jsonify( course ), 201
# PUT an update
@app.route('/ce/api/v1.0/courses/<int:course_id>', methods=['PUT'])
# @auth.login_required
def update_course(course_id):
course = list(filter(lambda t: t['id'] == course_id, courses))
if len(course) == 0:
abort(404)
if not request.json:
abort(400)
# if 'title' in request.json and type(request.json['title']) != unicode:
# abort(400)
# if 'description' in request.json and type(request.json['description']) is not unicode:
# abort(400)
if 'done' in request.json and type(request.json['done']) is not bool:
abort(400)
course[0]['title'] = request.json.get('title', course[0]['title'])
course[0]['description'] = request.json.get('description', course[0]['description'])
course[0]['done'] = request.json.get('done', course[0]['done'])
return jsonify( course[0] )
# DELETE a course
@app.route('/ce/api/v1.0/courses/<int:course_id>', methods=['DELETE'])
# @auth.login_required
def delete_course(course_id):
course = list(filter(lambda t: t['id'] == course_id, courses))
if len(course) == 0:
abort(404)
courses.remove(course[0])
return jsonify({'result': True})
# optimize web service interface: change id into url
def make_client_course(course):
new_course = {}
for field in course:
if field == 'id':
new_course['url'] = url_for('get_course', course_id=course['id'],_external=True)
else:
new_course[field] = course[field]
return new_course
# @app.route('/ce/api/v1.0/courses',methods=['GET'])
# def get_courses():
# return jsonify({'courses': list(map(make_client_course, courses))})
# strengthen security: login session
# @auth.get_password
# def get_password(username):
# if username == 'miguel':
# return 'python'
# return None
@auth.error_handler
def unauthorized():
return make_response(jsonify({'error': 'Unauthorized access'}), 403)
# @app.route('/ce/api/v1.0/courses', methods=['GET'])
# # @auth.login_required
# def get_courses():
# return jsonify({'courses': list(map(make_client_course, courses))})
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.String(32), index = True)
password_hash = db.Column(db.String(128))
def hash_password(self, password):
self.password_hash = pwd_context.encrypt(password)
def verify_password(self, password):
return pwd_context.verify(password, self.password_hash)
# must be here to create db
db.create_all()
# POST a new user
@app.route('/api/users', methods=['POST'])
def new_user():
username = request.json.get('username')
password = request.json.get('password')
if username is None or password is None:
abort(400) # missing arguments
if User.query.filter_by(username = username).first() is not None:
abort(400) # existing user
user = User(username = username)
user.hash_password(password)
db.session.add(user)
db.session.commit()
return jsonify({ 'username': user.username }), 201, {'Location': url_for('get_user', id = user.id, _external = True)}
@app.route('/api/users/<int:id>')
def get_user(id):
user = User.query.get(id)
if not user:
abort(400)
return jsonify({'username': user.username})
@app.route('/ce/api/v1.0/courses')
# @auth.login_required
# def get_resource():
# return jsonify({'data': 'Hello, %s!' % g.user.username})
def get_courses():
return jsonify({'courses': courses})
@auth.verify_password
def verify_password(username, password):
user = User.query.filter_by(username = username).first()
if not user or not user.verify_password(password):
return False
g.user = user
return True
if __name__ == '__main__':
# Run the main function of this module
app.run(host='0.0.0.0', port=5000, debug=True)