def test_load_modify_save(self, test_app_context, fixture_colony_a): ad = fixture_colony_a.to_dict() connection = get_redis_db_from_context() # It should be near the end of the list. index = connection.lrange(consts.KEY_COLONY_INDEX_BY_ID, -5, -1) assert fixture_colony_a.Hash in index # Reload it as a new object b = Colony.get_from_database(ad, db.get_redis_db_from_context()) assert b.Hash == fixture_colony_a.Hash for k, v in b.to_dict().items(): assert ad[k] == v b.LastAction = 2 b.save_to_database(connection) c = Colony.get_from_database(ad, db.get_redis_db_from_context()) assert c.LastAction == b.LastAction
def test_one_doesnt_exist(self, test_app_context, fixture_colony_a, fixture_colony_b): connection = get_redis_db_from_context() a = fixture_colony_a b = fixture_colony_b self.setup_data_in_db(a, connection) self.setup_data_in_db(b, connection) c = Colony.from_dict( { 'BaseName': 'TestColonyC', 'FactionName': 'TestFactionC', 'Planet': 'TestPlanetC', 'Hash': 'EFGH', 'OwnerType': 'Normal', 'OwnerID': 1 }, connection=db.get_redis_db_from_context()) colony_data = [a.to_dict(), b.to_dict(), c.to_dict()] result = list( Colony.get_many_from_database( colony_data, db.get_redis_db_from_context()).values()) assert len(result) == 3 assert result[0].BaseName == a.BaseName and result[0].FromDatabase assert result[1].BaseName == b.BaseName and result[1].FromDatabase assert result[2].BaseName == c.BaseName and not result[2].FromDatabase
def test_save_and_load_steam(self, test_app_context, fixture_colony_steam): connection = get_redis_db_from_context() self.setup_data_in_db(fixture_colony_steam, connection) # It should be near the end of the list. index = connection.lrange(consts.KEY_COLONY_INDEX_BY_ID, -5, -1) steam_index = connection.lrange( consts.KEY_COLONY_INDEX_BY_STEAM_ID.format( fixture_colony_steam.OwnerID), -5, -1) assert fixture_colony_steam.Hash in index assert fixture_colony_steam.Hash in steam_index # Reload it as a new object o_loaded = Colony.get_from_database(fixture_colony_steam.to_dict(), db.get_redis_db_from_context()) assert o_loaded.FromDatabase assert o_loaded.Hash == fixture_colony_steam.Hash od = fixture_colony_steam.to_dict() for k, v in o_loaded.to_dict().items(): assert od[k] == v
def test_class_init_from_dict_hash(self, test_app_context, fixture_colony_a): o = Colony.from_dict(fixture_colony_a.to_dict(), db.get_redis_db_from_context()) assert "87a41f64617b379cb80c7123351b35db356c97c8" == o.Hash
def test_get_colony_by_hash(self, test_app_context, fixture_colony_steam): connection = get_redis_db_from_context() self.setup_data_in_db(fixture_colony_steam, connection) result = Colony.get_from_database_by_hash( fixture_colony_steam.Hash, db.get_redis_db_from_context()) assert result.FromDatabase
def test_class_init_from_dict_to_dict(self, test_app_context, fixture_colony_a): a_dict = fixture_colony_a.to_dict() b = Colony.from_dict(a_dict, db.get_redis_db_from_context()).to_dict() for k, v in b.items(): assert a_dict[k] == v
def test_all_none(self, test_app_context): """ Test that none of these entries exist in the DB, Assert that all results have FromDatabase == False :param test_app_context: :return: """ a = Colony.from_dict( { 'BaseName': 'TestColonyA', 'FactionName': 'TestFactionA', 'Planet': 'TestPlanetA', 'Hash': '1234', 'OwnerType': 'Normal', 'OwnerID': 1 }, connection=db.get_redis_db_from_context()) b = Colony.from_dict( { 'BaseName': 'TestColonyB', 'FactionName': 'TestFactionB', 'Planet': 'TestPlanetB', 'Hash': '5678', 'OwnerType': 'Normal', 'OwnerID': 1 }, connection=db.get_redis_db_from_context()) c = Colony.from_dict( { 'BaseName': 'TestColonyC', 'FactionName': 'TestFactionC', 'Planet': 'TestPlanetC', 'Hash': 'EFGH', 'OwnerType': 'Normal', 'OwnerID': 1 }, connection=db.get_redis_db_from_context()) colony_data = [a.to_dict(), b.to_dict(), c.to_dict()] result = Colony.get_many_from_database(colony_data, db.get_redis_db_from_context()) s = list(zip(colony_data, result.values())) assert len(s) == 3 for a, b in s: assert not b.FromDatabase
def new_colony_from_request(incoming_data, connection): new_colony = { 'BaseName': escape(incoming_data['BaseName']), 'FactionName': escape(incoming_data['FactionName']), 'Planet': escape(incoming_data['Planet']), 'OwnerType': escape(incoming_data['OwnerType']), 'OwnerID': escape(incoming_data['OwnerID']), 'LastGameTick': escape(incoming_data['LastGameTick']) } return Colony.from_dict(new_colony, connection=connection)
def colony_get_data(colony_hash): print("Getting colony data") colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context()) if not colony: return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND) print("Sending colony data") return Response(json.dumps(colony.to_dict()), status=200, mimetype='application/json')
def fixture_colony_steam() -> Colony: s: Colony = Colony.from_dict( { 'BaseName': 'TestColonyS', 'FactionName': 'TestFactionS', 'Planet': 'TestPlanetS', 'DateCreated': 100, 'OwnerType': 'Steam', 'OwnerID': '76561198275909496' }, connection=db.get_redis_db_from_context()) return s
def fixture_colony_b() -> Colony: b: Colony = Colony.from_dict( { 'BaseName': 'TestColonyB', 'FactionName': 'TestFactionB', 'Planet': 'TestPlanetB', 'Hash': 'ABCD', 'OwnerType': 'Normal', 'OwnerID': 1 }, connection=db.get_redis_db_from_context()) return b
def fixture_colony_a() -> Colony: a: Colony = Colony.from_dict( { 'BaseName': 'TestColonyA', 'FactionName': 'TestFactionA', 'Planet': 'TestPlanetA', 'DateCreated': 1, 'OwnerType': 'Normal', 'OwnerID': 1 }, connection=db.get_redis_db_from_context()) return a
def subscription_update(colony_hash): db_connection = db.get_redis_db_from_context() colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context()) if not colony: current_app.logger.warning('{} colony not found in database'.format(colony.Hash)) return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND) sub_data = request.json # Validate token if 'Token' not in sub_data: current_app.logger.error('{} Subscription token was not in payload.'.format(colony.Hash)) return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID) # Fetch our token from DB token_in_db = db_connection.get(consts.KEY_PRIME_TOKEN_DATA.format(colony.Hash)) # Has it expired or ever existed? if token_in_db is None: current_app.logger.warning('{} Subscription token was not in database or has expired.'.format(colony.Hash)) return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID) # They should match if token_in_db != sub_data['Token']: current_app.logger.error( '{} Subscription tokens did not match {} != {}.'.format(colony.Hash, sub_data['Token'], token_in_db)) return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID) expiryTick = DaysPerQuadrum * TicksPerDay + colony.LastGameTick pipe = db_connection.pipeline() # Update subscription tick for Colony. pipe.set(consts.KEY_PRIME_SUBSCRIPTION_DATA.format(colony.Hash), int(expiryTick)) # Remove the token to prevent reuse. pipe.delete(consts.KEY_PRIME_TOKEN_DATA.format(colony_hash)) # Subscriptions expire after 42 days in real life. pipe.expireat(consts.KEY_PRIME_SUBSCRIPTION_DATA.format(colony.Hash), date_utils.add_days_to_current_time(30)) # Update Silver acquired. thing = Thing("Silver") subscriptionCost = int(db_connection.get(consts.KEY_CONFIGURATION_PRIME_COST)) pipe.hincrby(consts.KEY_THING_META.format(thing.Hash), 'Quantity', subscriptionCost) pipe.execute() current_app.logger.debug('{} Subscription successful.'.format(colony.Hash)) return Response("OK", status=consts.HTTP_OK)
def setup_data_in_db(colony: Colony, connection): do_exec = False if not isinstance(connection, redis.client.Pipeline): connection = connection.pipeline() do_exec = True connection.delete(consts.KEY_COLONY_METADATA.format(colony.Hash)) # Remove the key from the index if it already exists connection.lrem(consts.KEY_COLONY_INDEX_BY_ID, 0, colony.Hash) # Remove key from Steam User keys if colony.OwnerType == 'Steam': connection.lrem( consts.KEY_COLONY_INDEX_BY_STEAM_ID.format(colony.OwnerID), 0, colony.Hash) # Write the Colony to the database. colony.save_to_database(connection) if do_exec: connection.execute()
def colony_update_data(colony_hash): connection = db.get_redis_db_from_context() colony = Colony.get_from_database_by_hash(colony_hash, connection) print("Saving colony data") incoming_data = request.json created = False if not colony or colony.OwnerID != incoming_data['OwnerID']: # If the Owner ID's don't match. Create a new colony. # And assign it a new ID. Might have happened if save sharing. colony = new_colony_from_request(incoming_data, connection) created = True else: colony.BaseName = escape(incoming_data['BaseName']) colony.FactionName = escape(incoming_data['FactionName']) colony.Planet = escape(incoming_data['Planet']) colony.LastGameTick = escape(incoming_data['LastGameTick']) if incoming_data['HasSpawned']: colony.Ban() pipe = connection.pipeline() if colony.IsBanned(): return Response(consts.ERROR_BANNED, status=consts.HTTP_FORBIDDEN) # Make sure add the owners to the correct sets. if colony.OwnerType == 'Steam': key = consts.KEY_USER_INDEX_BY_STEAM_ID elif colony.OwnerType == 'Normal': key = consts.KEY_USER_INDEX_BY_NORMAL_ID else: return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID) pipe.sadd(key, colony.OwnerID) colony.save_to_database(pipe) pipe.execute() colony.ping() print("Sending colony hash") return Response(json.dumps({'Hash': colony.Hash}), status=201 if created else 200, mimetype='application/json')
def colony_set_supported_things(colony_hash: str): print("Received Supported things list from colony") colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context()) if not colony: return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND) try: gz_post_data = request.files['things'] # Decompress payload thing_file = gzip.GzipFile(fileobj=gz_post_data, mode='r') # Deserialize JSON back in to {'Locale': str, 'Things': List[Dict[str, str]]} payload = json.loads(thing_file.read().decode('UTF8')) # Set locale locale = payload['Locale'].lower() # This is a List[Dict[str, str]] supported_things_json = payload['Things'] print("Supported things parsed") except json.JSONDecodeError: print("Error in things list found") return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID) # Need to construct things then add Localized name to index pipe = db.get_redis_db_from_context().pipeline() pipe.sadd(consts.KEY_THING_LOCALE_KNOWN_LANGUAGES, locale) for thing_json in supported_things_json: thing = Thing.from_dict(thing_json) pipe.zincrby(KEY_THING_LOCALE_THING_NAMES.format(locale, thing.Hash), 1, thing.LocalizedName) pipe.execute() # This is immediately saved. colony.SupportedThings = supported_things_json print("Supported things saved") return Response('OK', status=200, mimetype='application/json')
def colony_set_mods(colony_hash: str): print("Received List of Mods from colony") colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context()) if not colony: return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND) try: gz_post_data = request.files['mods'] mod_file = gzip.GzipFile(fileobj=gz_post_data, mode='r') payload = json.loads(mod_file.read().decode('UTF8')) mod_list = payload['ModList'] except json.JSONDecodeError: print("Error in mod list found") return Response(consts.ERROR_INVALID, status=consts.HTTP_INVALID) # This is immediately saved. colony.ModList = mod_list print("List of Mods Saved") return Response('OK', status=200, mimetype='application/json')
def market_get_items(colony_hash): print("Sending Market items to Colony") connection = db.get_redis_db_from_context() colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context()) if not colony: return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND) things = Thing.get_many_from_database(colony.SupportedThings, connection) #thing_data = [thing.to_dict() for thing in things.values()] thing_data = [ thing.to_dict() for thing in things.values() if thing.Quantity > 0 ] print("Market items sent") content = gzip.compress(json.dumps(thing_data).encode('utf8'), 5) response = make_response(content, 200) response.headers['Content-length'] = len(content) response.headers['Content-Encoding'] = 'gzip' response.headers['Content-Type'] = "application/json" return response
def test_all_exist(self, test_app_context, fixture_colony_a, fixture_colony_b, fixture_colony_steam): connection = get_redis_db_from_context() a = fixture_colony_a b = fixture_colony_b c = fixture_colony_steam self.setup_data_in_db(a, connection) self.setup_data_in_db(b, connection) self.setup_data_in_db(c, connection) colony_data = [a.to_dict(), b.to_dict(), c.to_dict()] result = Colony.get_many_from_database(colony_data, db.get_redis_db_from_context()) s = zip(colony_data, result.values()) for a, b in s: assert a['BaseName'] == b.BaseName and b.FromDatabase
def subscription_check(colony_hash): print("Checking subscription status for colony") db_connection = db.get_redis_db_from_context() response = dict() colony = Colony.get_from_database_by_hash(colony_hash, db.get_redis_db_from_context()) if not colony: print("Colony does not exist in db") current_app.logger.error('{} colony not found in database'.format(colony.Hash)) return Response(consts.ERROR_NOT_FOUND, status=consts.HTTP_NOT_FOUND) response['SubscriptionCost'] = int(db_connection.get(consts.KEY_CONFIGURATION_PRIME_COST)) # 0 ticks left unless we get a score back from the sorted set. response['TickSubscriptionExpires'] = 0 ticks_remaining = db_connection.get(consts.KEY_PRIME_SUBSCRIPTION_DATA.format(colony.Hash)) if ticks_remaining is not None: response['TickSubscriptionExpires'] = int(ticks_remaining) else: response['TickSubscriptionExpires'] = 0 current_app.logger.debug('{} is generating a subscription token.'.format(colony.Hash)) # Generate a random token only valid for 30 seconds. token = make_token(colony.Hash) pipe = db_connection.pipeline() pipe.set(consts.KEY_PRIME_TOKEN_DATA.format(colony.Hash), token) pipe.expire(consts.KEY_PRIME_TOKEN_DATA.format(colony.Hash), 30) pipe.execute() response['Token'] = token current_app.logger.debug('{} new token is .'.format(colony.Hash, token)) print("Subscription status sent to colony") return Response(json.dumps(response), status=200, mimetype='application/json')