-
Notifications
You must be signed in to change notification settings - Fork 3
/
app.py
422 lines (355 loc) · 13.5 KB
/
app.py
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
#!/usr/bin/python
"""
This file acts as the Entry Point to the Rest Services that will be
deployed on the Predix.io page for Demo Purpose.
@Authour : Harsha Narayana
"""
# Import Basic Library requirements.
from flask import Flask, render_template, request, \
jsonify, make_response, Response
from config import get_rest_information, get_redis_config, \
get_postgresql_config, get_amqp_config
from handle_redis import RedisHandler
from handle_postgres import PostgreSQL
from flask.ext.httpauth import HTTPBasicAuth
import md5
from functools import wraps
from nameko.standalone.rpc import ServiceRpcProxy
# Some Global Variable to handle REDIS, POSTGRES and RABBITMQ.
REDIS = None
POSTGRES = None
COUNTER = 1
AUTHENTICATION = {
"username": "5cc0ad025dd42c5b70e193e77fcc5e96",
"password": "5f4dcc3b5aa765d61d8327deb882cf99"
}
# This line initializes a Flask Application for the Current __name__.
app = Flask(__name__)
app.config['SECRET_KEY'] = "Luke I am Your Father !"
auth = HTTPBasicAuth()
"""
The Routing Functions are listed in the Below Code Section. Please be
careful while making changes to the Routing. They can break our app
if not done in the right way.
"""
# Basic Error Handler Mode. This will come in handly in case your application
# runs into an error. This can return a JSON response string.
@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Not found'}), 404)
# Write a Custom authentication mode function that gets invoked each time you
# try to validate an incoming request with the help of HTTPAuth.
def authenticate(f):
@wraps(f)
def wrapper(*args, **kwargs):
authentication = request.authorization
if authentication is None:
return Response('Failed Login! Missing Credentials',
401,
{'WWW-Authenticate': 'Basic realm="Auth!"'})
if authentication.get("username") is None or \
authentication.get("password") is None:
return Response('Failed Login! Missing Credentials',
401,
{'WWW-Authenticate': 'Basic realm="Auth!"'})
if not validate_credentials(
authentication.get("username"),
authentication.get("password")):
return Response('Failed Login! Invalid Credentials.',
401,
{'WWW-Authenticate': 'Invalid Credentials'})
return f(*args, **kwargs)
return wrapper
# This is the base URL Routing Function. Let's put a Sample Demo HTML page in
# here.
@app.route('/')
def home():
"""
This Page Renders a basic HTML page with a Message displayed in h3.
"""
return render_template("index.html")
# A Sample Routing path to show how the Routing for Different URL works.
@app.route("/sample/")
def sample():
"""
This Page Renders the Same Message as Basic HTML Home page. Along with
that, it adds an extra line to differentiate the content.
"""
return render_template("sample.html")
# Following Section Shows an HTML form for the User and Prompts him to enter
# an input message which gets pushed to a Secondary Page for Rendering.
@app.route("/signup/")
def signup():
"""
This Page Prompts the User with an HTML page that expects him to enter
Name and Email information and Submit it.
"""
return render_template("signup.html")
# The Following Section of the Code acts as a Post Submit Action Handler that
# processes the incoming POST request from the Browser to Flask and processes
# it accordingly.
@app.route("/submit/", methods=["POST"])
def submit():
"""
The Following page handles the Incoming Form that is submitted by
the User and Sends a response back in HTML format.
This behavior can be reproduced on any rest services.
"""
username = request.form['username']
email = request.form['useremail']
return render_template("submit.html", name=username, email=email)
# The following Function is used to Test the REDIS connection using the
# Rest Mode. This show the usage of Redis as an in-memory cache before
# it can be persisted onto any of the Databases.
@app.route("/redis-signup/")
def redis_signup():
return render_template("signup.html", redis=1)
# The following section of the code acts as a POST RESTful service API that
# adds the data to Redis Key-Value store and Persists to DB if specified in
# the payload. otherwise, it simply ignores the data and sends a JSON
# response back in succsss mode.
@app.route("/redis-rest-submit", methods=["POST"])
@authenticate
def create_redis_item():
"""
This function takes an incoming payload from a RESTful POST request
and processes it to look for the following keys.
1. redis
2. persist
Based on the value of the keys mentioned above it performs one of the
tasks below as applicable
"""
global REDIS
global POSTGRES
global COUNTER
has_redis = False
persist = False
message = dict()
if not request.json or 'payload' not in request.json:
message = {
"state": "failure",
"error": "payload key not found. Invalid Payload Data"
}
return jsonify({'message': message}), 400
if 'username' not in request.json.get('payload') or \
'email' not in request.json.get('payload'):
message = {
"state": "failure",
"error": "username & email are mandatory. Invalid Payload Data."
}
return jsonify({'message': message}), 400
payload = request.json.get('payload')
if payload.get('username') is None or payload.get('email') is None:
message = {
"state": "failure",
"error": "Username & email can't be empty. Invalid payload data."
}
return jsonify({'message': message}), 400
if 'redis' in request.json:
if request.json.get('redis'):
has_redis = True
if 'persist' in request.json:
if request.json.get('persist'):
persist = True
message = {
"state": "successful",
"info": "Hello, young padawan " + payload.get("username") + "," +
" the FORCE is strong with you. \n" +
"You have a long way to go before you become a Jedi. " +
"I will drop you a tutorial at " + payload.get("email")
}
if has_redis:
message['info'] = ""
REDIS.add_to_redis(payload.get("username"), payload.get("email"))
REDIS.add_to_redis("inserts", COUNTER)
COUNTER += 1
message['info'] = "Hello, master " + payload.get("username") + \
", the ONE RING has been waiting for you all this time. \n" + \
"Looks like we finally found each other. " + \
"To claim the ONE RING, please check your email " + \
payload.get("email")
message['redis_status'] = "stored"
if persist:
query = "INSERT INTO DEMO (USERNAME, EMAIL) VALUES(%s, %s)"
POSTGRES.run_query_store(
query_string=query,
binding=(
payload.get("username"),
payload.get("email")
)
)
message['persist'] = "successful"
return jsonify({'message': message}), 200
# The Following Section of the Code acts as a Post Submit Action Handler that
# processes the incoming POST request from the Browser to Flask and processes
# it accordingly.
@app.route("/redis-submit/", methods=["POST"])
def redis_submit():
"""
The Following page handles the Incoming Form that is submitted by
the User and Sends a response back in HTML format.
This behavior can be reproduced on any rest services.
"""
global REDIS
global COUNTER
global POSTGRES
username = request.form['username']
email = request.form['useremail']
persist = request.form.get('persist')
if persist is not None:
persist = 1
else:
persist = 0
if REDIS is None or REDIS.check_error():
if REDIS is not None:
message = REDIS.check_error()
else:
message = "No REDIS Object Handler Found."
return render_template(
"error.html",
name=username,
email=email,
message=message)
REDIS.reset_error()
REDIS.add_to_redis(username, email)
REDIS.add_to_redis("inserts", COUNTER)
COUNTER += 1
if persist:
query = "INSERT INTO DEMO (USERNAME, EMAIL) VALUES(%s, %s)"
POSTGRES.run_query_store(query_string=query, binding=(username, email))
if REDIS.check_error():
return render_template(
"error.html",
name=username,
email=email,
message=REDIS.check_error())
else:
return render_template(
"submit.html",
name=username,
email=email,
redis=1)
# The following Code is for Displaying the Entities in the Redis Cache.
@app.route("/get-redis")
def get_redis():
"""
This Function obtains the data in Redis KEY Value pair and returns them
in an array so that it can be rendered into a neat table in the HTML.
"""
global REDIS
redis_data = REDIS.get_all_keys()
return render_template("redis.html", redis_data=redis_data)
# This route is provided for you to check if the REDIS connection is setup
# or not before going ahead.
@app.route("/redis-status")
def redis_status():
global REDIS
return render_template(
"redis_status.html",
json_data=REDIS.get_redis_info())
# This Route acts as a way to check the Status for PostgreSQL.
@app.route("/postgres-status")
def postgres_status():
global POSTGRES
(status, message) = POSTGRES.check_status()
if status:
status = 1
else:
status = 0
if len(message) < 1:
message = "PostgreSQL Connection Successfully established."
return render_template(
"postgres_status.html",
status=status,
message=message)
# This function Routes the request made by the End user into a function that
# handles the process of getting the data from PostgreSQL machine and
# displaying it to the end user in HTML table format.
@app.route("/get-postgres")
def get_postgres():
global POSTGRES
postgres_data = POSTGRES.run_query("SELECT * FROM DEMO")
postgres_info = list()
for row in postgres_data:
item = dict()
item['id'] = row[0]
item['key'] = row[1]
item['value'] = row[2]
postgres_info.append(item)
return render_template("postgres.html", postgres_data=postgres_info)
"""
Authentication Based RESTful Services Example.
"""
# Write an authentication mechanism that can be used to validate incoming
# requests from the user. This is a basic authentication with static data.
def validate_credentials(username, password):
"""
Performs a basic MD5 based Authentication for both user and pass.
"""
global AUTHENTICATION
md5user = md5.new(username)
md5pass = md5.new(password)
return AUTHENTICATION.get("username") == \
md5user.hexdigest() and \
AUTHENTICATION.get("password") \
== md5pass.hexdigest()
# Basic Function that Takes a Set of Authentication values in BASIC Auth mode
# to validate the incoming RESTful requests.
@app.route("/authenticate/")
@authenticate
def authenticate_rest():
"""
This is a sample function that lets you use the basic auth mode for
performing user authentication as part of the RESTful requests.
"""
message = {
"state": "success",
"message": "Oh Captain, My Captain."
}
return jsonify({'message': message}), 200
# Micro-Services Example.
# Micro Service Entry Point for the Function from UI.
@app.route("/fibonacci")
def tasks():
return render_template("fibonaci.html")
# Post Event Handler for Python Function that invokes the Micro-service
@app.route("/fibonacci-submit", methods=['POST'])
def fibonacci_post():
number = int(request.form['number'])
with rpc_proxy() as task_proxy:
task_id = task_proxy.start_task("fibonacci", number)
return render_template("tasks.html", task_id=task_id)
# GET Even Handler that returns the Fibonacci Squence Value.
@app.route("/fibonacci-result/<string:task_id>")
def fibonacci_result(task_id):
with rpc_proxy() as task_proxy:
result = task_proxy.get_result(task_id)
return render_template("tasks_result.html", result=result)
# the ServiceRpcProxy instance isn't thread safe so we constuct one for
# each request; a more intelligent solution would be a thread-local or
# pool of shared proxies
def rpc_proxy():
URL = get_amqp_config()
config = {'AMQP_URI': URL}
return ServiceRpcProxy('tasks', config)
# This section of the Code Starts-up your Flask Application.
if __name__ == "__main__":
(flask_hostname, flask_port, flask_debug) = get_rest_information()
(redis_hostname, redis_port, redis_password) = get_redis_config()
(postgres_database, postgres_user, postgres_password,
postgres_host, postgres_port) = get_postgresql_config()
if REDIS is None:
REDIS = RedisHandler(
host=redis_hostname,
port=redis_port,
password=redis_password)
if POSTGRES is None:
POSTGRES = PostgreSQL(database=postgres_database,
user=postgres_user,
password=postgres_password,
host=postgres_host,
port=postgres_port)
app.run(
host=flask_hostname,
port=flask_port,
debug=flask_debug)