Example #1
0
def handle_exception():
    """
    Context manager translating network related exceptions
    to custom :mod:`~galaxy.api.errors`.
    """
    try:
        yield
    except asyncio.TimeoutError:
        raise BackendTimeout()
    except aiohttp.ServerDisconnectedError:
        raise BackendNotAvailable()
    except aiohttp.ClientConnectionError:
        raise NetworkError()
    except aiohttp.ContentTypeError as error:
        raise UnknownBackendResponse(error.message)
    except aiohttp.ClientResponseError as error:
        if error.status == HTTPStatus.UNAUTHORIZED:
            raise AuthenticationRequired(error.message)
        if error.status == HTTPStatus.FORBIDDEN:
            raise AccessDenied(error.message)
        if error.status == HTTPStatus.SERVICE_UNAVAILABLE:
            raise BackendNotAvailable(error.message)
        if error.status == HTTPStatus.TOO_MANY_REQUESTS:
            raise TooManyRequests(error.message)
        if error.status >= 500:
            raise BackendError(error.message)
        if error.status >= 400:
            logger.warning("Got status %d while performing %s request for %s",
                           error.status, error.request_info.method,
                           str(error.request_info.url))
            raise UnknownError(error.message)
    except aiohttp.ClientError as e:
        logger.exception("Caught exception while performing request")
        raise UnknownError(repr(e))
    async def request(self, method, url, *args, **kwargs):
        try:
            response = await self._session.request(method, url, *args,
                                                   **kwargs)
        except asyncio.TimeoutError:
            raise BackendTimeout()
        except aiohttp.ServerDisconnectedError:
            raise BackendNotAvailable()
        except aiohttp.ClientConnectionError:
            raise NetworkError()
        except aiohttp.ContentTypeError:
            raise UnknownBackendResponse()
        except aiohttp.ClientError:
            logging.exception(
                "Caught exception while running {} request for {}".format(
                    method, url))
            raise UnknownError()
        if response.status == HTTPStatus.UNAUTHORIZED:
            raise AuthenticationRequired()
        if response.status == HTTPStatus.FORBIDDEN:
            raise AccessDenied()
        if response.status == HTTPStatus.SERVICE_UNAVAILABLE:
            raise BackendNotAvailable()
        if response.status == HTTPStatus.TOO_MANY_REQUESTS:
            raise TooManyRequests()
        if response.status >= 500:
            raise BackendError()
        if response.status >= 400:
            logging.warning(
                "Got status {} while running {} request for {}".format(
                    response.status, method, url))
            raise UnknownError()

        return response
Example #3
0
 async def get_user_presence(self, user_id: str, context: Any) -> UserPresence:
     user_info = context.get(user_id)
     if user_info is None:
         raise UnknownError(
             "User {} not in friend list (plugin only supports fetching presence for friends)".format(user_id)
         )
     return await presence_from_user_info(user_info, self._translations_cache)
Example #4
0
    async def _import_trophies(
        self,
        comm_id: CommunicationId,
        pending_tids: Set[TitleId],
        pending_tid_cids: _TID_CIDS_DICT,
        tid_trophies: _TID_TROPHIES_DICT,
        timestamp: UnixTimestamp
    ):
        def handle_error(error_):
            for tid_ in pending_tids:
                del pending_tid_cids[tid_]

        try:
            trophies: List[Achievement] = await self._psn_client.async_get_earned_trophies(comm_id)
            self._trophies_cache.update(comm_id, trophies, timestamp)
            while pending_tids:
                tid = pending_tids.pop()
                game_trophies = tid_trophies[tid]
                game_trophies.extend(trophies)
                pending_comm_ids = pending_tid_cids[tid]
                pending_comm_ids.remove(comm_id)
                if not pending_comm_ids:
                    # the game has already all comm ids processed
                    del pending_tid_cids[tid]
        except ApplicationError as error:
            handle_error(error)
        except Exception:
            logging.exception("Unhandled exception. Please report it to the plugin developers")
            handle_error(UnknownError())
Example #5
0
def test_get_room_history_from_timestamp_failure(plugin, readline, write):
    request = {
        "jsonrpc": "2.0",
        "id": "3",
        "method": "import_room_history_from_timestamp",
        "params": {
            "room_id": "10",
            "from_timestamp": 1549454800
        }
    }

    readline.side_effect = [json.dumps(request), ""]
    plugin.get_room_history_from_timestamp.coro.side_effect = UnknownError()
    asyncio.run(plugin.run())
    plugin.get_room_history_from_timestamp.assert_called_with(
        room_id="10",
        from_timestamp=1549454800
    )
    response = json.loads(write.call_args[0][0])

    assert response == {
        "jsonrpc": "2.0",
        "id": "3",
        "error": {
            "code": 0,
            "message": "Unknown error"
        }
    }
Example #6
0
 async def import_user_presence(user_id, context_) -> None:
     try:
         self._user_presence_import_success(user_id, await self.get_user_presence(user_id, context_))
     except ApplicationError as error:
         self._user_presence_import_failure(user_id, error)
     except Exception:
         logging.exception("Unexpected exception raised in import_user_presence")
         self._user_presence_import_failure(user_id, UnknownError())
    async def get_user_presence(self, user_id: str,
                                context: Any) -> UserPresence:
        if user_id not in context:
            raise UnknownError(
                'plugin/get_user_presence: failed to get info for user %s' %
                user_id)

        return context[user_id]
Example #8
0
    async def install_game(self, game_id):
        if game_id in self._under_installation:
            return

        self._under_installation.add(game_id)
        try:
            game = self._humble_games.get(game_id)
            if game is None:
                logging.error(
                    f'Install game: game {game_id} not found. Humble games: {self._humble_games.keys()}'
                )
                return

            if isinstance(game, ChoiceGame):
                webbrowser.open(game.presentation_url)
                return

            if isinstance(game, Key):
                try:
                    await gui.show_key(game)
                except Exception as e:
                    logging.error(e, extra={'platform_info': platform.uname()})
                    webbrowser.open('https://www.humblebundle.com/home/keys')
                return

            try:
                hp = HP.WINDOWS if IS_WINDOWS else HP.MAC
                curr_os_download = game.downloads[hp]
            except KeyError:
                raise UnknownError(
                    f'{game.human_name} has only downloads for {list(game.downloads.keys())}'
                )

            if isinstance(game, Subproduct):
                chosen_download_struct = self._download_resolver(
                    curr_os_download)
                urls = await self._api.sign_url_subproduct(
                    chosen_download_struct, curr_os_download.machine_name)
                webbrowser.open(urls['signed_url'])

            if isinstance(game, TroveGame):
                try:
                    urls = await self._api.sign_url_trove(
                        curr_os_download, game.machine_name)
                except AuthenticationRequired:
                    logging.info(
                        'Looks like your Humble Monthly subscription has expired.'
                    )
                    webbrowser.open(
                        'https://www.humblebundle.com/subscription/home')
                else:
                    webbrowser.open(urls['signed_url'])

        except Exception as e:
            logging.error(e, extra={'game': game})
            raise
        finally:
            self._under_installation.remove(game_id)
Example #9
0
 async def import_os_compatibility(game_id, context_):
     try:
         os_compatibility = await self.get_os_compatibility(game_id, context_)
         self._os_compatibility_import_success(game_id, os_compatibility)
     except ApplicationError as error:
         self._os_compatibility_import_failure(game_id, error)
     except Exception:
         logging.exception("Unexpected exception raised in import_os_compatibility")
         self._os_compatibility_import_failure(game_id, UnknownError())
Example #10
0
 async def get_local_size(
         self, game_id, context: Dict[str,
                                      pathlib.PurePath]) -> Optional[int]:
     try:
         return parse_map_crc_for_total_size(context[game_id])
     except (KeyError, FileNotFoundError) as e:
         raise UnknownError(
             f"Manifest for game {game_id} is not found: {repr(e)} | context: {context}"
         )
Example #11
0
 async def import_game_achievements(game_id, context_):
     try:
         achievements = await self.get_unlocked_achievements(game_id, context_)
         self._game_achievements_import_success(game_id, achievements)
     except ApplicationError as error:
         self._game_achievements_import_failure(game_id, error)
     except Exception:
         logging.exception("Unexpected exception raised in import_game_achievements")
         self._game_achievements_import_failure(game_id, UnknownError())
Example #12
0
 async def import_game_time(game_id, context_):
     try:
         game_time = await self.get_game_time(game_id, context_)
         self._game_time_import_success(game_time)
     except ApplicationError as error:
         self._game_time_import_failure(game_id, error)
     except Exception:
         logging.exception("Unexpected exception raised in import_game_time")
         self._game_time_import_failure(game_id, UnknownError())
 async def get_subscription_games(
     self, subscription_name: str,
     context: Dict[str,
                   str]) -> AsyncGenerator[List[SubscriptionGame], None]:
     try:
         tier = context[subscription_name]
     except KeyError:
         raise UnknownError(
             f'Unknown subscription name {subscription_name}!')
     yield await self._backend_client.get_games_in_subscription(tier)
Example #14
0
 async def _import_element(self, id_, context_):
     try:
         element = await self._get(id_, context_)
         self._notification_success(id_, element)
     except ApplicationError as error:
         self._notification_failure(id_, error)
     except asyncio.CancelledError:
         pass
     except Exception:
         logger.exception("Unexpected exception raised in %s importer", self._name)
         self._notification_failure(id_, UnknownError())
Example #15
0
 async def _import_game_achievements(self, title_id: TitleId, comm_id: CommunicationId):
     try:
         trophies: List[Achievement] = await self._psn_client.async_get_earned_trophies(comm_id)
         timestamp = max(trophy.unlock_time for trophy in trophies)
         self._trophies_cache.update(comm_id, trophies, timestamp)
         self.game_achievements_import_success(title_id, trophies)
     except ApplicationError as error:
         self.game_achievements_import_failure(title_id, error)
     except Exception:
         logging.exception("Unhandled exception. Please report it to the plugin developers")
         self.game_achievements_import_failure(title_id, UnknownError())
Example #16
0
 def handle_status_code(status_code):
     if status_code == HTTPStatus.UNAUTHORIZED:
         raise AuthenticationRequired()
     if status_code == HTTPStatus.FORBIDDEN:
         raise AccessDenied()
     if status_code == HTTPStatus.SERVICE_UNAVAILABLE:
         raise BackendNotAvailable()
     if status_code >= 500:
         raise BackendError()
     if status_code >= 400:
         raise UnknownError()
Example #17
0
 async def _reedem_trove_download(self, download: TroveDownload, product_machine_name: str):
     """Unknown purpose - humble http client do this after post for signed_url
     Response should be text with {'success': True} if everything is OK
     """
     res = await self._request('post', self._TROVE_REDEEM_DOWNLOAD, params={
         'download': download.machine_name,
         'download_page': "false",  # TODO check what it does
         'product': product_machine_name
     })
     content = await res.read()
     if content != b"{'success': True}":
         logging.error(f'unexpected response while reedem trove download: {content}')
         raise UnknownError()
Example #18
0
    async def import_games_achievements(self, _game_ids):
        game_ids = set(_game_ids)
        error = UnknownError()
        try:
            achievement_sets = {
            }  # 'offerId' to 'achievementSet' names mapping
            for offer_id, achievement_set in (
                    await
                    self._backend_client.get_achievements_sets(self._persona_id
                                                               )).items():
                if not achievement_set:
                    self.game_achievements_import_success(offer_id, [])
                    game_ids.remove(offer_id)
                else:
                    achievement_sets[offer_id] = achievement_set

            if not achievement_sets:
                return

            for offer_id, achievements in (
                    await self._backend_client.get_achievements(
                        self._persona_id, achievement_sets)).items():
                try:
                    self.game_achievements_import_success(
                        offer_id, [
                            Achievement(achievement_id=key,
                                        achievement_name=value["name"],
                                        unlock_time=value["u"])
                            for key, value in achievements.items()
                            if value["complete"]
                        ])
                except KeyError:
                    self.game_achievements_import_failure(
                        offer_id, UnknownBackendResponse())
                except ApplicationError as error:
                    self.game_achievements_import_failure(offer_id, error)
                finally:
                    game_ids.remove(offer_id)
        except KeyError:
            error = UnknownBackendResponse()
        except ApplicationError as _error:
            error = _error
        except Exception:
            pass  # handled below
        finally:
            # any other exceptions or not answered game_ids are responded with an error
            [
                self.game_achievements_import_failure(game_id, error)
                for game_id in game_ids
            ]
Example #19
0
    async def do_request(self,
                         method,
                         url,
                         data=None,
                         json=True,
                         headers=None,
                         ignore_failure=False):
        loop = asyncio.get_event_loop()
        if not headers:
            headers = self._authentication_client.session.headers
        try:
            if data is None:
                data = {}
            params = {
                "method": method,
                "url": url,
                "data": data,
                "timeout": self._authentication_client.timeout,
                "headers": headers
            }
            try:
                response = await loop.run_in_executor(
                    None,
                    functools.partial(
                        self._authentication_client.session.request, **params))
            except requests.Timeout:
                raise BackendTimeout()

            if not ignore_failure:
                if response.status_code == HTTPStatus.UNAUTHORIZED:
                    raise AuthenticationRequired()
                if response.status_code == HTTPStatus.FORBIDDEN:
                    raise AccessDenied()
                if response.status_code == HTTPStatus.SERVICE_UNAVAILABLE:
                    raise BackendNotAvailable()
                if response.status_code >= 500:
                    raise BackendError()
                if response.status_code >= 400:
                    raise UnknownError()

            if json:
                return response.json()
            else:
                return response

        except Exception as e:
            log.exception(
                f"Request exception: {str(e)}; url: {url}, method: {method}, data: {data}, headers: {headers}"
            )
            raise
Example #20
0
 async def import_game_times(self, game_ids):
     remaining_game_ids = set(game_ids)
     try:
         game_times = await self._get_game_times_dict()
         for game_id in game_ids:
             game_time = game_times.get(game_id)
             if game_time is None:
                 self.game_time_import_failure(game_id, UnknownError())
             else:
                 self.game_time_import_success(game_time)
             remaining_game_ids.remove(game_id)
     except Exception as error:
         logging.exception("Fail to import game times")
         for game_id in remaining_game_ids:
             self.game_time_import_failure(game_id, error)
Example #21
0
    async def _do_request(self, method, url, *args, **kwargs):
        loop = asyncio.get_running_loop()
        r = await loop.run_in_executor(None, functools.partial(self.session.request, method, url, *args, **kwargs))
        log.info(f"{r.status_code}: response from endpoint {url}")

        if r.status_code in (HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN):
            raise AccessDenied()
        if r.status_code == HTTPStatus.SERVICE_UNAVAILABLE:
            raise BackendNotAvailable()
        if r.status_code >= 500:
            raise BackendError()
        if r.status_code >= 400:
            raise UnknownError()
        
        j = r.json()  # all ubi endpoints return jsons
        return j
Example #22
0
def test_get_friends_failure(plugin, read, write):
    request = {"jsonrpc": "2.0", "id": "3", "method": "import_friends"}

    read.side_effect = [json.dumps(request).encode() + b"\n", b""]
    plugin.get_friends.coro.side_effect = UnknownError()
    asyncio.run(plugin.run())
    plugin.get_friends.assert_called_with()
    response = json.loads(write.call_args[0][0])

    assert response == {
        "jsonrpc": "2.0",
        "id": "3",
        "error": {
            "code": 0,
            "message": "Unknown error",
        }
    }
Example #23
0
async def test_failure(plugin, read, write):
    request = {"jsonrpc": "2.0", "id": "3", "method": "import_owned_games"}

    read.side_effect = [
        async_return_value(create_message(request)),
        async_return_value(b"", 10)
    ]
    plugin.get_owned_games.side_effect = UnknownError()
    await plugin.run()
    plugin.get_owned_games.assert_called_with()
    assert get_messages(write) == [{
        "jsonrpc": "2.0",
        "id": "3",
        "error": {
            "code": 0,
            "message": "Unknown error"
        }
    }]
Example #24
0
    async def get_game_time(self, game_id: OfferId,
                            last_played_games: Any) -> GameTime:
        try:
            offer = self._offer_id_cache.get(game_id)
            if offer is None:
                logging.exception("Internal cache out of sync")
                raise UnknownError()

            master_title_id: MasterTitleId = offer["masterTitleId"]
            multiplayer_id: Optional[MultiplayerId] = self._get_multiplayer_id(
                offer)

            return await self._get_game_times_for_offer(
                game_id, master_title_id, multiplayer_id,
                last_played_games.get(master_title_id))

        except KeyError as e:
            logging.exception("Failed to import game times %s", repr(e))
            raise UnknownBackendResponse()
Example #25
0
 async def prepare_subscription_games_context(
         self, subscription_names: List[str]) -> Any:
     self._check_authenticated()
     subscription_name_to_tier = {
         'EA Play': 'standard',
         'EA Play Pro': 'premium'
     }
     subscriptions = {}
     for sub_name in subscription_names:
         try:
             tier = subscription_name_to_tier[sub_name]
         except KeyError:
             logging.error(
                 "Assertion: 'Galaxy passed unknown subscription name %s. This should not happen!",
                 sub_name)
             raise UnknownError(f'Unknown subscription name {sub_name}!')
         subscriptions[
             sub_name] = await self._backend_client.get_games_in_subscription(
                 tier)
     return subscriptions
Example #26
0
    async def _request(self, method, *args, **kwargs):
        try:
            response = await self._session.request(method, *args, **kwargs)
        except asyncio.TimeoutError:
            raise BackendTimeout()
        except aiohttp.ClientConnectionError:
            raise NetworkError()
        logging.debug(f"Request response status: {response.status}")
        if response.status == HTTPStatus.UNAUTHORIZED:
            raise AuthenticationRequired()
        if response.status == HTTPStatus.FORBIDDEN:
            raise AccessDenied()
        if response.status == HTTPStatus.SERVICE_UNAVAILABLE:
            raise BackendNotAvailable()
        if response.status >= 500:
            raise BackendError()
        if response.status >= 400:
            raise UnknownError()

        return response
Example #27
0
def test_get_rooms_failure(plugin, readline, write):
    request = {
        "jsonrpc": "2.0",
        "id": "9",
        "method": "import_rooms"
    }

    readline.side_effect = [json.dumps(request), ""]
    plugin.get_rooms.coro.side_effect = UnknownError()
    asyncio.run(plugin.run())
    plugin.get_rooms.assert_called_with()
    response = json.loads(write.call_args[0][0])

    assert response == {
        "jsonrpc": "2.0",
        "id": "9",
        "error": {
            "code": 0,
            "message": "Unknown error"
        }
    }
    async def import_game_times(self, game_ids: List[str]):
        """
        Override this method to return game times for
        games owned by the currently authenticated user.
        Call game_time_import_success/game_time_import_failure for each game_id on the list.
        This method is called by GOG Galaxy Client.

        :param game_ids: ids of the games for which the game time is imported
        """
        try:
            game_times = await self.get_game_times()
            game_ids_set = set(game_ids)
            for game_time in game_times:
                if game_time.game_id not in game_ids_set:
                    continue
                self.game_time_import_success(game_time)
                game_ids_set.discard(game_time.game_id)
            for game_id in game_ids_set:
                self.game_time_import_failure(game_id, UnknownError())
        except Exception as error:
            for game_id in game_ids:
                self.game_time_import_failure(game_id, error)
Example #29
0
        async def import_game_time(offer_id: OfferId):
            try:
                offer = self._offer_id_cache.get(offer_id)
                if offer is None:
                    raise Exception("Internal cache out of sync")
                master_title_id: MasterTitleId = offer["masterTitleId"]
                multiplayer_id: Optional[
                    MultiplayerId] = self._get_multiplayer_id(offer)

                self.game_time_import_success(
                    await self._get_game_times_for_offer(
                        offer_id, master_title_id, multiplayer_id,
                        self._last_played_games.get(master_title_id)))
            except KeyError:
                self.game_time_import_failure(offer_id,
                                              UnknownBackendResponse())
            except ApplicationError as error:
                self.game_time_import_failure(offer_id, error)
            except Exception:
                logging.exception(
                    "Unhandled exception. Please report it to the plugin developers"
                )
                self.game_time_import_failure(offer_id, UnknownError())
async def test_start_game_times_import(plugin, write, mocker):
    game_time_import_success = mocker.patch.object(plugin, "game_time_import_success")
    game_time_import_failure = mocker.patch.object(plugin, "game_time_import_failure")
    game_times_import_finished = mocker.patch.object(plugin, "game_times_import_finished")

    game_ids = ["1", "5"]
    game_time = GameTime("1", 10, 1549550502)
    plugin.get_game_times.coro.return_value = [
        game_time
    ]
    await plugin.start_game_times_import(game_ids)

    with pytest.raises(ImportInProgress):
        await plugin.start_game_times_import(["4", "8"])

    # wait until all tasks are finished
    for _ in range(4):
        await asyncio.sleep(0)

    plugin.get_game_times.coro.assert_called_once_with()
    game_time_import_success.assert_called_once_with(game_time)
    game_time_import_failure.assert_called_once_with("5", UnknownError())
    game_times_import_finished.assert_called_once_with()