commit e5840d5cfed773de898d5f805d5c865681595521
parent 36b5de0df27aab5b779e9330bd460ec5b8606d5e
Author: Emily Doherty <git@emily.sh>
Date: Mon, 2 Oct 2023 01:07:21 -0700
Update updater to handle both retail and classic, as well as handle regional updates
Diffstat:
| M | wow-token-updater.py | | | 169 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
1 file changed, 136 insertions(+), 33 deletions(-)
diff --git a/wow-token-updater.py b/wow-token-updater.py
@@ -6,39 +6,68 @@ import os
import requests
-dynamo_client = boto3.client('dynamodb', region_name='us-east-1')
-timestream_client = boto3.client('timestream-write', region_name='us-east-1')
-current_table = os.environ['TABLE_NAME']
-recent_table = os.environ['RECENT_TABLE_NAME']
+local_region = os.environ['AWS_REGION']
+
+dynamo_client = boto3.client('dynamodb', region_name=local_region)
+timestream_client = boto3.client('timestream-write', region_name=local_region)
+tables = {
+ 'retail': {
+ 'recent': 'wow-token-price-recent',
+ 'current': 'wow-token-price',
+ 'timestream': 'wow-token-price-history'
+ },
+ 'classic': {
+ 'recent': 'wow-token-classic-price-recent',
+ 'current': 'wow-token-classic-price',
+ 'timestream': 'wow-token-classic-price-history'
+ }
+}
# a lambda handler to handle eventbridge triggered vents
def lambda_handler(event, context):
+ flavors = ['retail', 'classic']
regions = ['us', 'eu', 'kr', 'tw']
- live_prices = get_combined_live_price()
- print(live_prices)
- current_time_epoch = int(datetime.datetime.utcnow().timestamp())
+ timestamp = int(datetime.datetime.utcnow().timestamp())
+ for flavor in flavors:
+ flavor_handler(flavor, regions, timestamp)
+
+
+def flavor_handler(flavor: str, regions: list, timestamp: int) -> None:
+ live_prices = get_combined_live_price(flavor)
+ print(f"Current {flavor} prices:\t{live_prices}")
for region in regions:
print(f'Updating {region.upper()}...')
- update_token_price(region, current_time_epoch, live_prices[region])
+ update_token_price(flavor, region, timestamp, live_prices[region])
-def update_token_price(region: str, current_time_epoch: int, live_price: int):
- stored_price = get_stored_price(region)
+def update_token_price(flavor: str, region: str, current_time_epoch: int, live_price: int):
+ stored_price = get_stored_price(flavor, region)
+ regional_item = get_regional_update_item(flavor, region)
+ print(f'Current live price {live_price}')
print(f'Current stored price {stored_price}')
+ regional_price = int(regional_item['price']['S'])
+ print(f'Current regional price {regional_price}')
if stored_price != live_price:
- update_stored_token_price(region, live_price, current_time_epoch)
- update_recent_token_price(region, live_price, current_time_epoch)
- update_timestream_token_price(region, live_price, current_time_epoch)
+ # If the stored price is not the same as the live price, nor the regional price
+ # assume no other Lambda has updated it and update it
+ print(f"Stored price is differing from the live price, updating all databases")
+ update_stored_token_price(flavor, region, live_price, current_time_epoch)
+ update_recent_token_price(flavor, region, live_price, current_time_epoch)
+ update_regional_price(flavor, region, live_price, current_time_epoch)
+ update_timestream_token_price(flavor, region, live_price, current_time_epoch)
+ if (stored_price == live_price) and (regional_price != stored_price):
+ print(f"Stored price differs from regional price but not live price, updating regional databases")
+ update_regional_price(flavor, region, live_price, current_time_epoch)
+ update_timestream_token_price(flavor, region, live_price, current_time_epoch)
else:
- # price hasn't changed
- print(f'Price has not changed for {region.upper()}')
+ print(f"Price hasn't changed for {flavor} {region.upper()}")
# update the current price records in DynamoDB
-def update_stored_token_price(region: str, price: int, current_time_epoch: int) -> None:
+def update_stored_token_price(flavor: str, region: str, price: int, current_time_epoch: int) -> None:
dynamo_client.update_item(
- TableName=current_table,
+ TableName=tables[flavor]['current'],
Key={
'region': {
'S': region
@@ -55,13 +84,61 @@ def update_stored_token_price(region: str, price: int, current_time_epoch: int)
}
# ReturnValues="UPDATED_NEW"
)
- print(f'Updated {region.upper()} price to {price}')
+ print(f'Updated {flavor} {region.upper()} price to {price}')
+
+
+def update_regional_price(flavor: str, region: str, price: int, current_time_epoch: int) -> None:
+ if flavor == 'retail':
+ key = region
+ else:
+ key = f"{flavor}-{region}"
+ dynamo_client.update_item(
+ TableName='wow-token-regional',
+ Key={
+ 'region': {
+ 'S': key
+ }
+ },
+ UpdateExpression='SET price = :p, current_time = :t',
+ ExpressionAttributeValues={
+ ':p': {
+ 'S': str(price)
+ },
+ ':t': {
+ 'S': str(current_time_epoch)
+ }
+ }
+ # ReturnValues="UPDATED_NEW"
+ )
+ print(f'Updated regional {flavor} {region.upper()} price to {price}')
+
+
+def create_regional_item(flavor: str, region: str):
+ print(f"Creating default regional item in {flavor} {region}")
+ if flavor == 'retail':
+ key = region
+ else:
+ key = f"{flavor}-{region}"
+ dynamo_client.put_item(
+ TableName='wow-token-regional',
+ Item={
+ 'region': {
+ 'S': key
+ },
+ 'price': {
+ 'S': str(1)
+ },
+ 'timestamp': {
+ 'N': str(1)
+ }
+ }
+ )
# add a record to the recent token price table in DynamoDB
-def update_recent_token_price(region: str, price: int, current_time_epoch: int) -> None:
+def update_recent_token_price(flavor: str, region: str, price: int, current_time_epoch: int) -> None:
dynamo_client.put_item(
- TableName=recent_table,
+ TableName=tables[flavor]['recent'],
Item={
'region': {
'S': region
@@ -78,16 +155,16 @@ def update_recent_token_price(region: str, price: int, current_time_epoch: int)
}
# ReturnValues="UPDATED_NEW"
)
- print(f'Added {region.upper()} price {price} to {recent_table} table')
+ print(f'Added {region.upper()} price {price} to {tables[flavor]["recent"]} table')
-def update_timestream_token_price(region: str, price: int, current_time_epoch: int) -> None:
+def update_timestream_token_price(flavor: str, region: str, price: int, current_time_epoch: int) -> None:
record_inserted = False
while not record_inserted:
try:
print('Attempting to write to Timestream')
timestream_client.write_records(
- DatabaseName=os.environ['TIMESTREAM_DATABASE'],
+ DatabaseName=tables[flavor]['timestream'],
TableName=f'{region}-price-history',
Records=[
build_timestream_record(region, price, current_time_epoch),
@@ -97,7 +174,7 @@ def update_timestream_token_price(region: str, price: int, current_time_epoch: i
except Exception as e:
print(f'Error writing to Timestream: {e}')
time.sleep(2)
- print(f'Updated {region.upper()} price to {price} in Timestream')
+ print(f'Updated {flavor} {region.upper()} price to {price} in Timestream')
def build_timestream_record(region: str, price: int, current_time_epoch: int) -> dict:
@@ -118,9 +195,9 @@ def build_timestream_record(region: str, price: int, current_time_epoch: int) ->
# get the current stored token price from dynamodb
-def get_stored_price(region: str) -> int:
+def get_stored_price(flavor: str, region: str) -> int:
response = dynamo_client.get_item(
- TableName=current_table,
+ TableName=tables[flavor]['current'],
Key={
'region': {
'S': region
@@ -130,18 +207,44 @@ def get_stored_price(region: str) -> int:
return int(response['Item']['price']['S'])
-def get_combined_live_price() -> dict:
+def get_regional_update_item(flavor: str, region: str) -> dict:
+ if flavor == 'retail':
+ key = region
+ else:
+ key = f"{flavor}-{region}"
+ response = dynamo_client.get_item(
+ TableName='wow-token-regional',
+ Key={
+ 'region': {
+ 'S': key
+ }
+ }
+ )
+ if 'Item' in response:
+ return response['Item']
+ else:
+ create_regional_item(flavor, region)
+ time.sleep(5)
+ return get_regional_update_item(flavor, region)
+
+
+def get_combined_live_price(game_flavor: str) -> dict:
+ if game_flavor == 'retail':
+ namespace = 'dynamic'
+ else:
+ namespace = 'dynamic-classic'
+
return {
- 'us': get_token_price_from_blizzard('us'),
- 'eu': get_token_price_from_blizzard('eu'),
- 'kr': get_token_price_from_blizzard('kr'),
- 'tw': get_token_price_from_blizzard('tw')
+ 'us': get_token_price_from_blizzard('us', namespace),
+ 'eu': get_token_price_from_blizzard('eu', namespace),
+ 'kr': get_token_price_from_blizzard('kr', namespace),
+ 'tw': get_token_price_from_blizzard('tw', namespace)
}
-def get_token_price_from_blizzard(region: str):
+def get_token_price_from_blizzard(region: str, namespace: str):
api_endpoint = f'https://{region}.api.blizzard.com/data/wow/token/index'
- params = {'namespace': f'{os.environ.get("BLIZZARD_NAMESPACE")}-{region}'}
+ params = {'namespace': f'{namespace}-{region}'}
headers = {'Authorization': f'Bearer {get_oauth_token()}'}
response = requests.get(api_endpoint, params=params, headers=headers)
return int(response.json()['price'])