api/wheel.py (60 lines of code) (raw):
# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.
from boto3.dynamodb.conditions import Key
from utils import get_utc_timestamp, get_uuid, Wheel, WheelParticipant, check_string, to_update_kwargs
import base
import choice_algorithm
@base.route('/wheel', methods=['PUT', 'POST'])
def create_wheel(event):
"""
Create a wheel. Requires a name
:param event: Lambda event containing the API Gateway request body including a name
{
"body":
{
"name": string wheel name,
}
}
:return: response dictionary containing new wheel object if successful
{
"body":
{
"id": string ID of the wheel (DDB Hash Key),
"name": string name of the wheel,
"participant_count": number of participants in the wheel,
"created_at": creation timestamp,
"updated_at": updated timestamp,
}
}
"""
create_timestamp = get_utc_timestamp()
body = event['body']
if body is None or not check_string(body.get('name', None)):
raise base.BadRequestError(
f"New wheels require a name that must be a string with a length of at least 1. Got: {body}"
)
wheel = {
'id': get_uuid(),
'name': body['name'],
'created_at': create_timestamp,
'updated_at': create_timestamp,
}
with choice_algorithm.wrap_wheel_creation(wheel):
Wheel.put_item(Item=wheel)
return wheel
@base.route('/wheel/{wheel_id}', methods=['DELETE'])
def delete_wheel(event):
"""
Deletes the wheel and all of its participants
:param event: Lambda event containing the API Gateway request path parameter wheel_id
{
"pathParameters":
{
"wheel_id": string ID of the wheel (DDB Hash Key)
}
}
:return: response dictionary
"""
wheel_id = event['pathParameters']['wheel_id']
# DynamoDB always succeeds for delete_item,
Wheel.delete_item(Key={'id': wheel_id})
# Clear out all participants of the wheel. Query will be empty if it was already deleted
with WheelParticipant.batch_writer() as batch:
query_params = {
'KeyConditionExpression': Key('wheel_id').eq(wheel_id),
'ProjectionExpression': 'id'
}
# We don't use the default generator here because we don't want the deletes to change the query results
for p in list(WheelParticipant.iter_query(**query_params)):
batch.delete_item(Key={'id': p['id'], 'wheel_id': wheel_id})
@base.route('/wheel/{wheel_id}', methods=['GET'])
def get_wheel(event):
"""
Returns the wheel object corresponding to the given wheel_id
:param event: Lambda event containing the API Gateway request path parameter wheel_id
{
"pathParameters":
{
"wheel_id": string ID of the wheel (DDB Hash Key)
}
}
:return: response dictionary containing the requested wheel object if successful
{
"body":
{
"id": string ID of the wheel (DDB Hash Key),
"name": string name of the wheel,
"participant_count": number of participants in the wheel,
"created_at": creation timestamp,
"updated_at": updated timestamp,
}
}
"""
return Wheel.get_existing_item(Key={'id': event['pathParameters']['wheel_id']})
@base.route('/wheel', methods=['GET'])
def list_wheels(event):
"""
Get all available wheels
:param event: Lambda event containing query string parameters that are passed to Boto's scan() API for the wheel
table
{
"queryStringParameters":
{
...
}
}
:return: List of wheels
{
"body":
"Count": number of wheels,
"Items":
[
wheel1,
wheel2,
wheeln,
],
"ScannedCount": number of items before queryStringParameters were applied,
}
}
"""
parameters = event.get('queryStringParameters', None) or {}
return Wheel.scan(**parameters)
@base.route('/wheel/{wheel_id}', methods=['PUT', 'POST'])
def update_wheel(event):
"""
Update the name of the wheel and/or refresh its participant count
:param event: Lambda event containing the API Gateway request path parameter wheel_id
{
"pathParameters":
{
"wheel_id": string ID of the wheel (DDB Hash Key)
},
"body":
{
"id": string ID of the wheel (DDB Hash Key),
"name": string name of the wheel,
}
}
:return: response dictionary containing the updated wheel object if successful
{
"body":
{
"id": string ID of the wheel (DDB Hash Key),
"name": string name of the wheel,
"participant_count": number of participants in the wheel,
"created_at": creation timestamp,
"updated_at": updated timestamp,
}
}
"""
wheel_id = event['pathParameters']['wheel_id']
key = {'id': wheel_id}
# Make sure wheel exists
wheel = Wheel.get_existing_item(Key=key)
name = event['body'].get('name', None)
if not check_string(name):
raise base.BadRequestError("Updating a wheel requires a new name of at least 1 character in length")
update = {'name': name, 'updated_at': get_utc_timestamp()}
Wheel.update_item(Key=key, **to_update_kwargs(update))
# Represent the change locally for successful responses
wheel.update(update)
return wheel
@base.route('/wheel/{wheel_id}/reset', methods=['PUT', 'POST'])
def reset_wheel(event):
"""
Resets the weights of all participants of the wheel
:param event: Lambda event containing the API Gateway request path parameter wheel_id
{
"pathParameters":
{
"wheel_id": string ID of the wheel (DDB Hash Key)
}
}
:return: response dictionary
"""
# Ensure that the wheel exists
wheel_id = event['pathParameters']['wheel_id']
wheel = Wheel.get_existing_item(Key={'id': wheel_id})
choice_algorithm.reset_wheel(wheel)
@base.route('/wheel/{wheel_id}/unrig', methods=['PUT', 'POST'])
def unrig_participant(event):
"""
Remove rigging for the specified wheel
:param event: Lambda event containing the API Gateway request path parameter wheel_id
{
"pathParameters":
{
"wheel_id": string ID of the wheel (DDB Hash Key)
}
}
:return: response dictionary
"""
# By default, rigging the wheel isn't hidden but they can be
wheel_id = event['pathParameters']['wheel_id']
Wheel.update_item(Key={'id': wheel_id}, UpdateExpression='remove rigging')