cffc26d by Anon Ray at 2014-06-10 1
from flask import Module, jsonify, request, make_response
2
from flask import abort, g, json, current_app
75d3bd6 by Anon Ray at 2014-02-19 3
cffc26d by Anon Ray at 2014-06-10 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
8fe0bbc by Anon Ray at 2014-05-10 8
from swtstore.classes.utils.httputils import makeCORSHeaders
f867edb by Anon Ray at 2014-04-10 9
from swtstore.classes import oauth
75d3bd6 by Anon Ray at 2014-02-19 10
11
12
api = Module(__name__)
13
d120774 by Anon Ray at 2014-03-08 14
75d3bd6 by Anon Ray at 2014-02-19 15
# Get a specific sweet
8fe0bbc by Anon Ray at 2014-05-10 16
# Update a specific sweet
17
@api.route('/sweets/<int:id>', methods=['GET', 'PUT'])
75d3bd6 by Anon Ray at 2014-02-19 18
def getSweetById(id):
8fe0bbc by Anon Ray at 2014-05-10 19
20
    # Get a specific sweet
21
    if request.method == 'GET':
75d3bd6 by Anon Ray at 2014-02-19 22
        sweet = Sweet.query.get(id)
23
8fe0bbc by Anon Ray at 2014-05-10 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)
75d3bd6 by Anon Ray at 2014-02-19 37
8fe0bbc by Anon Ray at 2014-05-10 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())
75d3bd6 by Anon Ray at 2014-02-19 49
d120774 by Anon Ray at 2014-03-08 50
75d3bd6 by Anon Ray at 2014-02-19 51
# Post a sweet to the sweet store
52
@api.route('/sweets', methods=['OPTIONS', 'POST'])
f867edb by Anon Ray at 2014-04-10 53
@oauth.require_oauth('email', 'sweet')
54
def createSweet(oauth_request):
75d3bd6 by Anon Ray at 2014-02-19 55
56
    response = make_response()
57
f867edb by Anon Ray at 2014-04-10 58
    client = oauth_request.client
59
8fe0bbc by Anon Ray at 2014-05-10 60
    #TODO: make a decorator of CORS request
61
    response = makeCORSHeaders(response, client.host_url)
d120774 by Anon Ray at 2014-03-08 62
63
    if request.method == 'OPTIONS':
64
        response.status_code = 200
65
        return response
66
8fe0bbc by Anon Ray at 2014-05-10 67
    payload = request.json or request.data
68
    if not payload:
69
        current_app.logger.error('data not found in payload!')
cffc26d by Anon Ray at 2014-06-10 70
        g.error = 'data not found in payload!'
75d3bd6 by Anon Ray at 2014-02-19 71
        abort(400)
72
8fe0bbc by Anon Ray at 2014-05-10 73
    current_app.logger.debug('new sweet payload recvd.. %s', payload)
75d3bd6 by Anon Ray at 2014-02-19 74
8fe0bbc by Anon Ray at 2014-05-10 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
75d3bd6 by Anon Ray at 2014-02-19 78
8fe0bbc by Anon Ray at 2014-05-10 79
    try:
80
        swts = Sweet.createSweets(who, payload)
cffc26d by Anon Ray at 2014-06-10 81
    except (InvalidPayload, ContextDoNotExist) as e:
82
        current_app.logger.error('Error creating sweets. Error: %s', e)
8fe0bbc by Anon Ray at 2014-05-10 83
        abort(400)
75d3bd6 by Anon Ray at 2014-02-19 84
85
    response.status_code = 200
8fe0bbc by Anon Ray at 2014-05-10 86
    response.headers['Content-type'] = 'application/json'
87
    response.data = json.dumps([i.to_dict() for i in swts])
75d3bd6 by Anon Ray at 2014-02-19 88
    return response
89
90
91
# The Sweet query API: /sweets/q?who=<>&what=<>&where=<>
92
# args: who, what, where
f867edb by Anon Ray at 2014-04-10 93
@api.route('/sweets/q', methods=['GET', 'OPTIONS'])
8fe0bbc by Anon Ray at 2014-05-10 94
#@oauth.require_oauth('sweet')
95
def querySweets():
f867edb by Anon Ray at 2014-04-10 96
97
    response = make_response()
8fe0bbc by Anon Ray at 2014-05-10 98
    origin = request.headers.get('Origin', '*')
99
    response = makeCORSHeaders(response, origin)
f867edb by Anon Ray at 2014-04-10 100
101
    if request.method == 'OPTIONS':
cffc26d by Anon Ray at 2014-06-10 102
        response.status_code = 200
f867edb by Anon Ray at 2014-04-10 103
        return response
d120774 by Anon Ray at 2014-03-08 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'):
8fe0bbc by Anon Ray at 2014-05-10 115
        params['what'] = args.get('what')
d120774 by Anon Ray at 2014-03-08 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
8fe0bbc by Anon Ray at 2014-05-10 123
    current_app.logger.debug('recvd params: %s', params)
d120774 by Anon Ray at 2014-03-08 124
8fe0bbc by Anon Ray at 2014-05-10 125
    sweets = Sweet.queryByAll(params)
d120774 by Anon Ray at 2014-03-08 126
127
    if len(sweets) == 0:
8fe0bbc by Anon Ray at 2014-05-10 128
        current_app.logger.info('No sweets found to satisfy query..')
d120774 by Anon Ray at 2014-03-08 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
8fe0bbc by Anon Ray at 2014-05-10 142
    context = Context.getByName(name)
d120774 by Anon Ray at 2014-03-08 143
    if context is None:
144
        abort(404)
145
8fe0bbc by Anon Ray at 2014-05-10 146
    current_app.logger.debug('getContextByName : %s', context)
d120774 by Anon Ray at 2014-03-08 147
    return jsonify(context.to_dict())
148
cffc26d by Anon Ray at 2014-06-10 149
d120774 by Anon Ray at 2014-03-08 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
8fe0bbc by Anon Ray at 2014-05-10 158
    current_app.logger.debug('getContextById response: %s',
159
                             jsonify(context.to_dict()).data)
160
d120774 by Anon Ray at 2014-03-08 161
    return jsonify(context.to_dict())
162
75d3bd6 by Anon Ray at 2014-02-19 163
164
# Create a new Sweet Context
f867edb by Anon Ray at 2014-04-10 165
@oauth.require_oauth('email', 'context')
d120774 by Anon Ray at 2014-03-08 166
@api.route('/contexts', methods=['POST'])
75d3bd6 by Anon Ray at 2014-02-19 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
8fe0bbc by Anon Ray at 2014-05-10 180
    current_app.logger.debug('new context payload recvd.. %s', payload)
75d3bd6 by Anon Ray at 2014-02-19 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?
8fe0bbc by Anon Ray at 2014-05-10 191
        current_app.logger.info('Context Already Exists Error')
75d3bd6 by Anon Ray at 2014-02-19 192
        abort(400)
193
8fe0bbc by Anon Ray at 2014-05-10 194
    current_app.logger.debug('new context created: %s', new_context)
195
75d3bd6 by Anon Ray at 2014-02-19 196
    # all ok. save the new context
cffc26d by Anon Ray at 2014-06-10 197
    new_context.persist()
75d3bd6 by Anon Ray at 2014-02-19 198
199
    response.status_code = 200
200
    return response
201
f867edb by Anon Ray at 2014-04-10 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()
8fe0bbc by Anon Ray at 2014-05-10 208
    response = makeCORSHeaders(response, oauth_request.client.host_url)
f867edb by Anon Ray at 2014-04-10 209
    response.status_code = 200
210
211
    if request.method == 'OPTIONS':
212
        return response
213
8fe0bbc by Anon Ray at 2014-05-10 214
    response.headers['Content-type'] = 'application/json'
215
    # We have the user object along with the oauth request. Just return it back
f867edb by Anon Ray at 2014-04-10 216
    response.data = json.dumps(oauth_request.user.to_dict())
217
    return response