# 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/{wheel_id}/participant', methods=['PUT', 'POST']) def create_participant(event): """ Create a participant :param event: Lambda event containing the API Gateway request body including a name and a url and the path parameter wheel_id { "pathParameters": { "wheel_id": string ID of the wheel (DDB Hash Key) }, "body": { "name": participant name string, "url: Valid URL for the participant, } } :return: response dictionary containing new participant object if successful { "body": { "id": string ID of the participant (DDB Hash Key), "wheel_id": string ID of the wheel (DDB Hash Key), "name": string name of the wheel, "url: URL for the participant, "created_at": creation timestamp, "updated_at": updated timestamp, } } """ wheel_id = event['pathParameters']['wheel_id'] body = event['body'] if not check_string(body.get('name', None)) or not check_string(body.get('url', None)): raise base.BadRequestError("Participants require a name and url which must be at least 1 character in length") wheel = Wheel.get_existing_item(Key={'id': wheel_id}) create_timestamp = get_utc_timestamp() participant = { 'wheel_id': wheel_id, 'id': get_uuid(), 'name': body['name'], 'url': body['url'], 'created_at': create_timestamp, 'updated_at': create_timestamp, } with choice_algorithm.wrap_participant_creation(wheel, participant): WheelParticipant.put_item(Item=participant) return participant @base.route('/wheel/{wheel_id}/participant/{participant_id}', methods=['DELETE']) def delete_participant(event): """ Deletes the participant from the wheel and redistributes wheel weights :param event: Lambda event containing the API Gateway request path parameters wheel_id and participant_id { "pathParameters": { "wheel_id": string ID of the wheel (DDB Hash Key) "participant_id": string ID of the participant (DDB Hash Key) }, } :return: response dictionary """ wheel_id = event['pathParameters']['wheel_id'] participant_id = event['pathParameters']['participant_id'] # Make sure the wheel exists wheel = Wheel.get_existing_item(Key={'id': wheel_id}) # REST-ful Deletes are idempotent and should not error if it's already been deleted response = WheelParticipant.delete_item(Key={'wheel_id': wheel_id, 'id': participant_id}, ReturnValues='ALL_OLD') if 'Attributes' in response: choice_algorithm.on_participant_deletion(wheel, response['Attributes']) @base.route('/wheel/{wheel_id}/participant', methods=['GET']) def list_participants(event): """ Gets the participants for the specified 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 a list of participants { "body": [ participant1, participant2, ... participantn, ] } """ wheel_id = event['pathParameters']['wheel_id'] # Make sure the wheel exists Wheel.get_existing_item(Key={'id': wheel_id}) return list(WheelParticipant.iter_query(KeyConditionExpression=Key('wheel_id').eq(wheel_id))) @base.route('/wheel/{wheel_id}/participant/{participant_id}', methods=['PUT', 'POST']) def update_participant(event): """ Update a participant's name and/or url :param event: Lambda event containing the API Gateway request body including updated name or url and the path parameters wheel_id and participant_id { "pathParameters": { "wheel_id": string ID of the wheel (DDB Hash Key) "participant_id": string ID of the participant (DDB Hash Key) }, "body": { "id": string ID of the participant (DDB Hash Key), "name": string name of the wheel (optional), "url: Valid URL for the participant (optional), } } :return: response dictionary containing the updated participant object if successful { "body": { "id": string ID of the participant (DDB Hash Key), "wheel_id": string ID of the wheel (DDB Hash Key), "name": string name of the wheel, "url: URL for the participant, "created_at": creation timestamp, "updated_at": updated timestamp, } } """ wheel_id = event['pathParameters']['wheel_id'] participant_id = event['pathParameters']['participant_id'] # Check that the participant exists participant = WheelParticipant.get_existing_item(Key={'id': participant_id, 'wheel_id': wheel_id}) body = event['body'] params = {'updated_at': get_utc_timestamp()} if not check_string(body.get('name', 'Not Specified')) or not check_string(body.get('url', 'Not Specified')): raise base.BadRequestError("Participants names and urls must be at least 1 character in length") if 'name' in body: params['name'] = body['name'] if 'url' in body: params['url'] = body['url'] WheelParticipant.update_item(Key={'id': participant_id, 'wheel_id': wheel_id}, **to_update_kwargs(params)) participant.update(params) return participant @base.route('/wheel/{wheel_id}/participant/{participant_id}/select', methods=['PUT', 'POST']) def select_participant(event): """ Indicates selection of a participant by the wheel. This will cause updates to the weights for all participants or removal of rigging if the wheel is rigged. :param event: Lambda event containing the API Gateway request path parameters wheel_id and participant_id { "pathParameters": { "wheel_id": string ID of the wheel to rig (DDB Hash Key) "participant_id": string ID of the participant to rig (DDB Hash Key) }, } :return: response dictionary """ wheel_id = event['pathParameters']['wheel_id'] participant_id = event['pathParameters']['participant_id'] wheel = Wheel.get_existing_item(Key={'id': wheel_id}) participant = WheelParticipant.get_existing_item(Key={'id': participant_id, 'wheel_id': wheel_id}) choice_algorithm.select_participant(wheel, participant) # Undo any rigging that has been set up Wheel.update_item(Key={'id': wheel['id']}, UpdateExpression='remove rigging') @base.route('/wheel/{wheel_id}/participant/{participant_id}/rig', methods=['PUT', 'POST']) def rig_participant(event): """ Rig the specified wheel for the specified participant. Default behavior is comical rigging (hidden == False) but hidden can be specified to indicate deceptive rigging (hidden == True) :param event: Lambda event containing the API Gateway request path parameters wheel_id and participant_id { "pathParameters": { "wheel_id": string ID of the wheel to rig (DDB Hash Key) "participant_id": string ID of the participant to rig (DDB Hash Key) }, "body": { "hidden": boolean indicates deceptive rigging if True, comical if False } } :return: response dictionary """ # By default, rigging the wheel isn't hidden but they can be wheel_id = event['pathParameters']['wheel_id'] participant_id = event['pathParameters']['participant_id'] hidden = bool(event['body'].get('hidden', False)) update = {'rigging': {'participant_id': participant_id, 'hidden': hidden}} Wheel.update_item(Key={'id': wheel_id}, **to_update_kwargs(update)) @base.route('/wheel/{wheel_id}/participant/suggest', methods=['GET']) def suggest_participant(event): """ Returns a suggested participant to be selected by the next wheel spin :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 a selected participant_id { "body": { "participant_id": string ID of the suggested participant (DDB Hash Key), "rigged": True (if rigged, otherwise this key is not present) } } """ wheel_id = event['pathParameters']['wheel_id'] wheel = Wheel.get_existing_item(Key={'id': wheel_id}) if 'rigging' in wheel: participant_id = wheel['rigging']['participant_id'] # Use rigging only if the rigged participant is still available if 'Item' in WheelParticipant.get_item(Key={'wheel_id': wheel_id, 'id': participant_id}): return_value = {'participant_id': participant_id} # Only return rigged: True if we're not using hidden rigging if not wheel['rigging'].get('hidden', False): return_value['rigged'] = True return return_value return {'participant_id': choice_algorithm.suggest_participant(wheel)}