Skip to content

Commit

Permalink
Fixed validation of execute methods
Browse files Browse the repository at this point in the history
Added a method for creating duplicate API requests
  • Loading branch information
tankalxat34 committed Jul 3, 2024
1 parent 50d1852 commit 3ef5305
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 44 deletions.
44 changes: 22 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Unofficial Python library that facilitates working with **<a href="https://devel

By downloading this library you fully agree with all official documents **Lesta Games** and **Wargaming.net** about **Lesta Games** and **Wargaming.net** products. *The author of the library ([Alexander Podstrechny](https://github.com/tankalxat34)) is not responsible for your actions performed with the help of this program code.*

> [!NOTE]
> If you like this project please add it to favorite :star: \
> Thanks for your feedback!
## Installing the library

Run the command below at the command line
Expand Down Expand Up @@ -65,17 +69,7 @@ In the terminal you will see:
},
"data": {
"563982544": {
"client_language": "",
"last_battle_time": 1569011404,
"account_id": 563982544,
"created_at": 1564320823,
"updated_at": 1715246332,
"private": null,
"global_rating": 1828,
"clan_id": null,
"statistics": {
// ...
},
// ...
"nickname": "tankalxat34",
"logout_at": 1597741881
}
Expand Down Expand Up @@ -111,17 +105,7 @@ In the terminal you will see:
},
"data": {
"563982544": {
"client_language": "",
"last_battle_time": 1569011404,
"account_id": 563982544,
"created_at": 1564320823,
"updated_at": 1715246332,
"private": null,
"global_rating": 1828,
"clan_id": null,
"statistics": {
// ...
},
// ...
"nickname": "tankalxat34",
"logout_at": 1597741881
}
Expand Down Expand Up @@ -152,6 +136,22 @@ https://api.worldoftanks.eu/wot/auth/logout/?application_id=YOUR_APPLICATION_ID
https://api.worldoftanks.eu/wot/auth/prolongate/?application_id=YOUR_APPLICATION_ID
```

### 5. Saving frequently called methods

If you use any method very often, you can save its function to a variable. Then you can call the saved method as many times as you want without purposefully passing parameters inside each call.

```python
from WgLestaAPI.Application import App
from WgLestaAPI.Constants import REGION, GAMENAMES

wgApp = App("YOUR_APPLICATION_ID", REGION.EU)

# method saving
getMyAccount = wgApp.createMethod("account.info", GAMENAMES.SHORTNAMES.WOT, account_id=563982544)

for i in range(3):
print(i, getMyAccount())
```

## Library functionality

Expand Down
47 changes: 45 additions & 2 deletions WgLestaAPI/Application.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Implementing common methods for running the WgLestaAPI library
"""
from typing import Any
from typing import Any, Callable
import urllib3
import aiohttp
import json
Expand All @@ -26,6 +26,7 @@ class App:
authUrl(**kwargs): Generates the authentication URL.
execute(api_method, game_shortname, type_request="GET", **kwargs): Executes a synchronous API request.
asyncExecute(api_method, game_shortname, type_request="GET", **kwargs): Executes an asynchronous API request.
createMethod(api_method, game_shortname, execution="sync", type_request="GET", **kwargs): Creates a constant method for API request
"""

def __init__(self, application_id: str, region: Constants.REGION) -> None:
Expand Down Expand Up @@ -106,6 +107,7 @@ def prolongate(self, **kwargs: dict[str, Any]) -> str:
api_url = self._getApiUrl(api_method="auth.prolongate", game_shortname=Constants.GAMENAMES.SHORTNAMES.TANKI if self.region in Constants.REGION.CIS else Constants.GAMENAMES.SHORTNAMES.WOT, **kwargs)
return api_url

@Utils.validateQuery
def execute(
self,
api_method: str,
Expand All @@ -131,7 +133,8 @@ def execute(
return json.loads(res.data)
except Exception:
return res


@Utils.validateQuery
async def asyncExecute(
self,
api_method: str,
Expand All @@ -158,3 +161,43 @@ async def asyncExecute(
return await response.json()
except Exception:
return response

def createMethod(
self,
api_method: str,
game_shortname: Constants.GAMENAMES.SHORTNAMES,
execution: Constants.METHODEXECUTION = Constants.METHODEXECUTION.SYNC,
type_request: Constants.TYPEREQUESTS = "GET",
**kwargs: dict[str, Any]
) -> Callable[..., dict | urllib3.BaseHTTPResponse | aiohttp.ClientResponse]:
"""Creates a constant method for API request
If you use any method very often, you can save its function to a variable. Then you can call the saved method as many times as you want without purposefully passing parameters inside each call.
Example:
```python
getMyAccount = wgApp.createMethod("account.info", GAMENAMES.SHORTNAMES.WOT, account_id=563982544)
for i in range(3):
print(getMyAccount())
```
Args:
api_method (str): The API method to be called.
game_shortname (Constants.GAMENAMES.SHORTNAMES): The short name of the game.
execution (Constants.METHODEXECUTION, optional): Type of method execution (`sync` or `async`). Defaults to Constants.METHODEXECUTION.SYNC (`sync`).
type_request (Constants.TYPEREQUESTS, optional): The type of HTTP request (default is "GET").
**kwargs: Additional query parameters.
Raises:
ValueError: if you choose invalid execution type for method
Returns:
A function with specified parameters that can be called as many times as desired
"""
if execution == Constants.METHODEXECUTION.SYNC:
return lambda self=self: self.execute(api_method, game_shortname, type_request, **kwargs)
elif execution == Constants.METHODEXECUTION.ASYNC:
return lambda self=self: self.asyncExecute(api_method, game_shortname, type_request, **kwargs)
else:
raise ValueError(f"Invalid value for type of method execution: '{execution}'")
37 changes: 26 additions & 11 deletions WgLestaAPI/Constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,19 @@ class SHORTNAMES(object):
"""
# CIS
TANKI = CIS_PREFIX + "wot" # Мир танков
"""Мир танков ("Mir tankov", only RU region)
"""Мир танков
(only `SU` region)
"""
KORABLI = CIS_PREFIX + "wows" # Мир кораблей
"""Мир кораблей ("Mir korabley", only RU region)
"""Мир кораблей
(only `SU` region)
"""
TANKSBLITZ = CIS_PREFIX + "wotb" # Tanks Blitz
"""Tanks Blitz (only RU region)
"""Tanks Blitz
(only `RU` region)
"""

# All short names
Expand Down Expand Up @@ -92,23 +98,32 @@ class LONGNAMES(object):
"""
# CIS
TANKI = "tanki" # Мир танков
"""Мир танков ("Mir tankov", only RU region)
"""Мир танков
(only `SU` region)
"""
KORABLI = "korabli" # Мир кораблей
"""Мир кораблей ("Mir korabley", only RU region)
"""Мир кораблей
(only `SU` region)
"""
TANKSBLITZ = "tanksblitz" # Tanks Blitz
"""Tanks Blitz (only RU region)
"""Tanks Blitz
(only `RU` region)
"""

# All long names
ALL = (WOT, WOTB, WOTC, WOWS, WOWP, WG, TANKI, KORABLI, TANKSBLITZ)


URL_PATTERNS = {
"docs": "https://developers.{api_holder}/reference/all/{game_shortname}/{method_block}/{method_name}/",
"auth": "https://{api_server}/{api_name}/auth/login/"
}
class METHODEXECUTION(object):
ASYNC = "async"
"""Asynchronous API request
"""
SYNC = "sync"
"""Synchronous API request
"""


SELECTOR = {
# CIS
Expand Down
11 changes: 7 additions & 4 deletions WgLestaAPI/Exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@

class RegionDoesNotExisting(Exception):
def __init__(self, value, game_shortname: str) -> None:
super().__init__(f"This region \"{value}\" does not existing in API services for the game longname \"{game_shortname}\". Available regions is: {', '.join(c.SELECTOR[game_shortname]["region"])}")

super().__init__(f"This region \"{value}\" does not existing in API services for the game \"{c.GAMENAMES.LONGNAMES.__dict__[game_shortname.replace(c.CIS_PREFIX, "").upper()]}\". Available regions for this game is: {', '.join(c.SELECTOR[game_shortname]["region"])}")

class ShortnameIsNotDefined(Exception):
def __init__(self, value) -> None:
super().__init__(f"This game shorname \"{value}\" is not defined")
super().__init__(f"This game shortname \"{value}\" is not defined")

class LongnameIsNotDefined(Exception):
def __init__(self, value) -> None:
super().__init__(f"This game longname \"{value}\" is not defined")

class GameDoesNotAppearThisRegion(Exception):
def __init__(self, value) -> None:
super().__init__(f"This game does not appear in this region: \"{value}\"")

class IncorrectMethodDeclaration(Exception):
def __init__(self, value) -> None:
super().__init__(f"Invalid declaration of this method: \"{value}\". You are using ({len(value.split('.'))}) parts of method instead of (2)")
super().__init__(f"Invalid declaration of this method: \"{value}\". You are using ({len(value.split('.'))}) parts of method instead of (2). Also you need use the dot-notation (for example `account.info`)")
31 changes: 26 additions & 5 deletions WgLestaAPI/Utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,31 @@
from . import Utils
from typing import Callable

from . import Constants
from . import Exceptions


def validateQuery(f: Callable):
def wrapper(*args, **kwargs):
appInstance = args[0]
region = appInstance.region
game_shortname = kwargs.get("game_shortname") if kwargs.get("game_shortname") else args[2]
api_method: str = kwargs.get("api_method") if kwargs.get("api_method") else args[1]

if game_shortname not in Constants.GAMENAMES.SHORTNAMES.ALL:
raise Exceptions.ShortnameIsNotDefined(game_shortname)

game_section = Constants.SELECTOR[game_shortname]
if region not in game_section["region"]:
raise Exceptions.RegionDoesNotExisting(region, game_shortname)

if "." not in api_method or \
len(api_method.split(".")) != 2:
raise Exceptions.IncorrectMethodDeclaration(api_method)

return f(*args, **kwargs)
return wrapper


def compileQuery(d: dict) -> str:
"""Comvert `dict` to url query string such as `?key1=value1&key2=value2`
Expand All @@ -17,14 +41,11 @@ def compileQuery(d: dict) -> str:
return res[:-1]


def constructUrl(application_id, region, api_method, game_shortname, **kwargs) -> str:
def constructUrl(application_id: str, region: str, api_method: str, game_shortname: str, **kwargs) -> str:
game_section = Constants.SELECTOR[game_shortname]
method_block, method_name = api_method.split(".")
query_url: str = compileQuery({"application_id": application_id, **kwargs})

if region not in game_section["region"]:
raise Exceptions.RegionDoesNotExisting(region, game_shortname)

return "https://{api}.{game_longname}.{region}/{game_shortname}/".format(
api = game_section["api_prefix"],
region = region,
Expand Down

0 comments on commit 3ef5305

Please sign in to comment.