1
from flask import Module, jsonify, request, make_response
2
from flask import abort, g, json, current_app
3
4
from swtstore.classes.models import Context, Sweet
5
from swtstore.classes.exceptions import AlreadyExistsError, InvalidPayload,\
6
    ContextDoNotExist
7
from swtstore.classes.utils import urlnorm  # normalize URLs
8
from swtstore.classes.utils.httputils import makeCORSHeaders
9
from swtstore.classes import oauth
10
11
12
api = Module(__name__)
13
14
15
# Get a specific sweet
16
# Update a specific sweet
17
@api.route('/sweets/<int:id>', methods=['GET', 'PUT'])
18
def getSweetById(id):
19
20
    # Get a specific sweet
21
    if request.method == 'GET':
22
        sweet = Sweet.query.get(id)
23
24
        if sweet is None:
25
            abort(404)
26
27
        current_app.logger.debug('getSweetById: %s', sweet)
28
        return jsonify(sweet.to_dict())
29
30
    # Update a specific sweet
31
    elif request.method == 'PUT':
32
        payload = request.json
33
        if payload is None:
34
            abort(400)
35
36
        current_app.logger.debug('Update Sweet: recvd payload: %s', payload)
37
38
        sweet = Sweet.query.get(id)
39
40
        if sweet is None:
41
            abort(404)
42
43
        current_app.logger.debug('Updating sweet %s with new how: %s ',
44
                                 sweet.id, payload)
45
46
        sweet.update(how=payload)
47
48
        return jsonify(sweet.to_dict())
49
50
51
# Post a sweet to the sweet store
52
@api.route('/sweets', methods=['OPTIONS', 'POST'])
53
@oauth.require_oauth('email', 'sweet')
54
def createSweet(oauth_request):
55
56
    response = make_response()
57
58
    client = oauth_request.client
59
60
    #TODO: make a decorator of CORS request
61
    response = makeCORSHeaders(response, client.host_url)
62
63
    if request.method == 'OPTIONS':
64
        response.status_code = 200
65
        return response
66
67
    payload = request.json or request.data
68
    if not payload:
69
        current_app.logger.error('data not found in payload!')
70
        g.error = 'data not found in payload!'
71
        abort(400)
72
73
    current_app.logger.debug('new sweet payload recvd.. %s', payload)
74
75
    # Get the authenticated user from the oauth request object.
76
    # Older swtr clients sending `who` in string will be ignored.
77
    who = oauth_request.user
78
79
    try:
80
        swts = Sweet.createSweets(who, payload)
81
    except (InvalidPayload, ContextDoNotExist) as e:
82
        current_app.logger.error('Error creating sweets. Error: %s', e)
83
        abort(400)
84
85
    response.status_code = 200
86
    response.headers['Content-type'] = 'application/json'
87
    response.data = json.dumps([i.to_dict() for i in swts])
88
    return response
89
90
91
# The Sweet query API: /sweets/q?who=<>&what=<>&where=<>
92
# args: who, what, where
93
@api.route('/sweets/q', methods=['GET', 'OPTIONS'])
94
#@oauth.require_oauth('sweet')
95
def querySweets():
96
97
    response = make_response()
98
    origin = request.headers.get('Origin', '*')
99
    response = makeCORSHeaders(response, origin)
100
101
    if request.method == 'OPTIONS':
102
        response.status_code = 200
103
        return response
104
105
    args = request.args
106
107
    # if no arguments are passed, its an invalid request
108
    if args is None:
109
        abort(400)
110
111
    params = {}
112
    if args.get('who'):
113
        params['who'] = args.get('who')
114
    if args.get('what'):
115
        params['what'] = args.get('what')
116
    if args.get('where'):
117
        params['where'] = urlnorm(args.get('where'))
118
119
    # if none of the above parameters are present, its an invalid request
120
    if len(params) == 0:
121
        abort(400)
122
123
    current_app.logger.debug('recvd params: %s', params)
124
125
    sweets = Sweet.queryByAll(params)
126
127
    if len(sweets) == 0:
128
        current_app.logger.info('No sweets found to satisfy query..')
129
        abort(404)
130
131
    swts = [i.to_dict() for i in sweets]
132
133
    response.data = json.dumps(swts)
134
    response.headers['Content-type'] = 'application/json'
135
    return response
136
137
138
# Get a specific context with its definition; based on name
139
@api.route('/contexts/<name>', methods=['GET'])
140
def getContextByName(name):
141
142
    context = Context.getByName(name)
143
    if context is None:
144
        abort(404)
145
146
    current_app.logger.debug('getContextByName : %s', context)
147
    return jsonify(context.to_dict())
148
149
150
# Get a specific context with its definition; based on id
151
@api.route('/contexts/<int:id>', methods=['GET'])
152
def getContextById(id):
153
154
    context = Context.query.get(id)
155
    if context is None:
156
        abort(404)
157
158
    current_app.logger.debug('getContextById response: %s',
159
                             jsonify(context.to_dict()).data)
160
161
    return jsonify(context.to_dict())
162
163
164
# Create a new Sweet Context
165
@oauth.require_oauth('email', 'context')
166
@api.route('/contexts', methods=['POST'])
167
def createContext():
168
169
    response = make_response()
170
171
    # try our best to get the data from request object
172
    if request.json:
173
        payload = request.json
174
    elif request.data:
175
        payload = json.loads(request.data)
176
    else:
177
        # if not found send back a 400
178
        abort(400)
179
180
    current_app.logger.debug('new context payload recvd.. %s', payload)
181
182
    # if data is invalid send back 400
183
    if 'name' not in payload and 'definition' not in payload:
184
        abort(400)
185
186
    try:
187
        new_context = Context(payload['name'], payload['definition'])
188
189
    except AlreadyExistsError:
190
        # context with same name exists; send back 400?
191
        current_app.logger.info('Context Already Exists Error')
192
        abort(400)
193
194
    current_app.logger.debug('new context created: %s', new_context)
195
196
    # all ok. save the new context
197
    new_context.persist()
198
199
    response.status_code = 200
200
    return response
201
202
203
# Send back logged in user data
204
@api.route('/users/me', methods=['GET', 'OPTIONS'])
205
@oauth.require_oauth('email')
206
def getCurrentUser(oauth_request):
207
    response = make_response()
208
    response = makeCORSHeaders(response, oauth_request.client.host_url)
209
    response.status_code = 200
210
211
    if request.method == 'OPTIONS':
212
        return response
213
214
    response.headers['Content-type'] = 'application/json'
215
    # We have the user object along with the oauth request. Just return it back
216
    response.data = json.dumps(oauth_request.user.to_dict())
217
    return response