From d63943a1adccccda1ddf43e609701a0615577670 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Mon, 11 Jan 2021 17:53:36 -0800 Subject: [PATCH] Add find_puzzle_for_puzzle_id This exists so that the puzzle-creation code can ensure that it's not creating a puzzle with the same puzzle_id as an existing puzzle. Previously, Turbot was just relying on Slack to guarantee uniqueness, (which worked when channel names were just made from hunt_id and puzzle_id). But more recently, the channel names have round names in them as well. This set up a possible collision where two puzzle with the same puzzle_id could be created but with different channel names, (if the two puzzles were placed in different rounds). To be able to fix this bug we need this query to be able to find a puzzle by means of using the puzzle_id. This does require a new index on the database, so that's here too, (and we're nuking our database to be able to roll this out). --- turbot/interaction.py | 13 ++++++++++++- turbot/puzzle.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/turbot/interaction.py b/turbot/interaction.py index 546e59a..a3fe496 100644 --- a/turbot/interaction.py +++ b/turbot/interaction.py @@ -572,7 +572,8 @@ def new_hunt_submission(turb, payload, metadata): {'AttributeName': 'SK', 'AttributeType': 'S'}, {'AttributeName': 'channel_id', 'AttributeType': 'S'}, {'AttributeName': 'is_hunt', 'AttributeType': 'S'}, - {'AttributeName': 'url', 'AttributeType': 'S'} + {'AttributeName': 'url', 'AttributeType': 'S'}, + {'AttributeName': 'puzzle_id', 'AttributeType': 'S'} ], ProvisionedThroughput={ 'ReadCapacityUnits': 5, @@ -616,6 +617,16 @@ def new_hunt_submission(turb, payload, metadata): 'Projection': { 'ProjectionType': 'ALL' } + }, + { + 'IndexName': 'puzzle_id_index', + 'KeySchema': [ + {'AttributeName': 'hunt_id', 'KeyType': 'HASH'}, + {'AttributeName': 'puzzle_id', 'KeyType': 'RANGE'}, + ], + 'Projection': { + 'ProjectionType': 'ALL' + } } ] ) diff --git a/turbot/puzzle.py b/turbot/puzzle.py index 3a0e312..1d1fda8 100644 --- a/turbot/puzzle.py +++ b/turbot/puzzle.py @@ -32,6 +32,34 @@ def find_puzzle_for_sort_key(turb, hunt_id, sort_key): else: return None +def find_puzzle_for_puzzle_id(turb, hunt_id, puzzle_id): + """Given a hunt_id and puzzle_id, return that puzzle + + Returns None if no puzzle with the given hunt_id and puzzle_id + exists in the database, otherwise a dictionary with all fields + from the puzzle's row in the database. + + Note: The sort_key is a modified version of the puzzle_id, (used + to make metapuzzles appear in the ordering before non-metapuzzles). + If you've been handed a sort_key, then looking up a puzzle by + sort_key is the right thing to do. But if you instead have just + a puzzle_id, see find_puzzle_for_puzzle_id rather than trying + to convert the puzzle_id into a sort_key to use this function. + """ + + response = turb.table.query( + IndexName='puzzle_id_index', + KeyConditionExpression=( + Key('hunt_id').eq(hunt_id) & + Key('puzzle_id').eq(puzzle_id) + ) + ) + + if response['Count'] == 0: + return None + + return response['Items'][0] + def find_puzzle_for_url(turb, hunt_id, url): """Given a hunt_id and URL, return the puzzle with that URL -- 2.43.0