Compare commits

...

8 Commits
0.7 ... 0.12

Author SHA1 Message Date
Pawel Kierski
67e7a4c0b2 Don't create log file if not specified 2019-03-05 12:19:48 +01:00
Paweł Kierski
788d2550e6 SDK-2552 optional achievement id or name 2019-03-05 12:18:33 +01:00
Rafal Makagon
059a1ea343 update logging facility in plugin API 2019-03-05 09:36:44 +01:00
Pawel Kierski
300ade5d43 Fix handling unknown notification 2019-03-04 11:52:08 +01:00
Paweł Kierski
43556a0470 SDK-2586 Return "None" instead of "Unknown" state for local game for Origin 2019-03-01 14:10:48 +01:00
Romuald Bierbasz
e244d3bb44 SDK-2577: Add UnknownBackendResponse 2019-02-28 15:20:03 +01:00
Romuald Bierbasz
d6e6efc633 SDK-2571: Refactor logging 2019-02-28 10:31:12 +01:00
Paweł Kierski
a114c9721c Add Unknown for all enums 2019-02-22 11:26:17 +01:00
7 changed files with 86 additions and 20 deletions

View File

@@ -12,6 +12,7 @@ class Platform(Enum):
Battlenet = "battlenet"
class Feature(Enum):
Unknown = "Unknown"
ImportInstalledGames = "ImportInstalledGames"
ImportOwnedGames = "ImportOwnedGames"
LaunchGame = "LaunchGame"
@@ -24,16 +25,18 @@ class Feature(Enum):
VerifyGame = "VerifyGame"
class LicenseType(Enum):
SinglePurchase = "SinglePurchase"
FreeToPlay = "FreeToPlay"
OtherUserLicense = "OtherUserLicense"
Unknown = "Unknown"
Unknown = "Unknown"
SinglePurchase = "SinglePurchase"
FreeToPlay = "FreeToPlay"
OtherUserLicense = "OtherUserLicense"
class LocalGameState(Enum):
None_ = "None"
Installed = "Installed"
Running = "Running"
class PresenceState(Enum):
Unknown = "Unknown"
Online = "online"
Offline = "offline"
Away = "away"

View File

@@ -20,6 +20,10 @@ class BackendError(ApplicationError):
def __init__(self, data=None):
super().__init__(4, "Backend error", data)
class UnknownBackendResponse(ApplicationError):
def __init__(self, data=None):
super().__init__(4, "Backend responded in uknown way", data)
class InvalidCredentials(ApplicationError):
def __init__(self, data=None):
super().__init__(100, "Invalid credentials", data)

View File

@@ -104,6 +104,7 @@ class Server():
method = self._notifications.get(request.method)
if not method:
logging.error("Received uknown notification: %s", request.method)
return
callback, internal = method
if internal:
@@ -143,8 +144,8 @@ class Server():
self._send_error(request.id, MethodNotFound())
except JsonRpcError as error:
self._send_error(request.id, error)
except Exception as error: #pylint: disable=broad-except
logging.error("Unexpected exception raised in plugin handler: %s", repr(error))
except Exception: #pylint: disable=broad-except
logging.exception("Unexpected exception raised in plugin handler")
asyncio.create_task(handle())

View File

@@ -1,9 +1,11 @@
import asyncio
import json
import logging
import logging.handlers
import dataclasses
from enum import Enum
from collections import OrderedDict
import sys
from galaxy.api.jsonrpc import Server, NotificationClient
from galaxy.api.consts import Feature
@@ -21,6 +23,7 @@ class JSONEncoder(json.JSONEncoder):
class Plugin():
def __init__(self, platform, reader, writer, handshake_token):
logging.info("Creating plugin for platform %s", platform.value)
self._platform = platform
self._feature_methods = OrderedDict()
@@ -167,7 +170,10 @@ class Plugin():
async def pass_control():
while self._active:
logging.debug("Passing control to plugin")
self.tick()
try:
self.tick()
except Exception:
logging.exception("Unexpected exception raised in plugin tick")
await asyncio.sleep(1)
await asyncio.gather(pass_control(), self._server.run())
@@ -309,22 +315,54 @@ class Plugin():
async def get_game_times(self):
raise NotImplementedError()
def _prepare_logging(logger_file):
root = logging.getLogger()
root.setLevel(logging.DEBUG)
if logger_file:
handler = logging.handlers.RotatingFileHandler(
logger_file,
mode="a",
maxBytes=10000000,
backupCount=10,
encoding="utf-8"
)
else:
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)
def create_and_run_plugin(plugin_class, argv):
if not issubclass(plugin_class, Plugin):
raise TypeError("plugin_class must be subclass of Plugin")
logger_file = argv[3] if len(argv) >= 4 else None
_prepare_logging(logger_file)
if len(argv) < 3:
raise ValueError("Not enough parameters, required: token, port")
logging.critical("Not enough parameters, required: token, port")
sys.exit(1)
token = argv[1]
try:
port = int(argv[2])
except ValueError as e:
raise ValueError("Failed to parse port value, {}".format(e))
except ValueError:
logging.critical("Failed to parse port value: %s", argv[2])
sys.exit(2)
if not (1 <= port <= 65535):
raise ValueError("Port value out of range (1, 65535)")
logging.critical("Port value out of range (1, 65535)")
sys.exit(3)
if not issubclass(plugin_class, Plugin):
logging.critical("plugin_class must be subclass of Plugin")
sys.exit(4)
async def coroutine():
reader, writer = await asyncio.open_connection("127.0.0.1", port)
plugin = plugin_class(reader, writer, token)
await plugin.run()
asyncio.run(coroutine())
try:
asyncio.run(coroutine())
except Exception:
logging.exception("Error while running plugin")
sys.exit(5)

View File

@@ -28,8 +28,13 @@ class Game():
@dataclass
class Achievement():
achievement_id: str
unlock_time: int
achievement_id: Optional[str] = None
achievement_name: Optional[str] = None
def __post_init__(self):
assert self.achievement_id or self.achievement_name, \
"One of achievement_id or achievement_name is required"
@dataclass
class LocalGame():

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="galaxy.plugin.api",
version="0.7",
version="0.12",
description="Galaxy python plugin API",
author='Galaxy team',
author_email='galaxy@gog.com',

View File

@@ -1,9 +1,18 @@
import asyncio
import json
from pytest import raises
from galaxy.api.types import Achievement
from galaxy.api.errors import UnknownError
def test_initialization_no_unlock_time():
with raises(Exception):
Achievement(achievement_id="lvl30", achievement_name="Got level 30")
def test_initialization_no_id_nor_name():
with raises(AssertionError):
Achievement(unlock_time=1234567890)
def test_success(plugin, readline, write):
request = {
"jsonrpc": "2.0",
@@ -15,8 +24,9 @@ def test_success(plugin, readline, write):
}
readline.side_effect = [json.dumps(request), ""]
plugin.get_unlocked_achievements.return_value = [
Achievement("lvl10", 1548421241),
Achievement("lvl20", 1548422395)
Achievement(achievement_id="lvl10", unlock_time=1548421241),
Achievement(achievement_name="Got level 20", unlock_time=1548422395),
Achievement(achievement_id="lvl30", achievement_name="Got level 30", unlock_time=1548495633)
]
asyncio.run(plugin.run())
plugin.get_unlocked_achievements.assert_called_with(game_id="14")
@@ -32,8 +42,13 @@ def test_success(plugin, readline, write):
"unlock_time": 1548421241
},
{
"achievement_id": "lvl20",
"achievement_name": "Got level 20",
"unlock_time": 1548422395
},
{
"achievement_id": "lvl30",
"achievement_name": "Got level 30",
"unlock_time": 1548495633
}
]
}
@@ -65,7 +80,7 @@ def test_failure(plugin, readline, write):
}
def test_unlock_achievement(plugin, write):
achievement = Achievement("lvl20", 1548422395)
achievement = Achievement(achievement_id="lvl20", unlock_time=1548422395)
async def couritine():
plugin.unlock_achievement("14", achievement)