complete swagger

This commit is contained in:
Evgenii Alekseev 2019-09-15 01:07:15 +03:00
parent c83f36f40b
commit 80ea82904b
14 changed files with 515 additions and 114 deletions

104
README.md
View File

@ -2,104 +2,30 @@
Service which allows to manage savage loot distribution easy. Service which allows to manage savage loot distribution easy.
## REST API ## Installation and usage
### Party API This service requires python >= 3.7. For other dependencies see `setup.py`.
* `GET /api/v1/party` In general installation process looks like:
Get party list. Parameters:
* `nick`: player full nickname to filter, string, optional.
* `POST /api/v1/party`
Add or remove party member. Parameters: ```bash
python setup.py build
* `action`: action to do, string, required. One of `add`, `remove`. python setup.py test # if you want to run tests
* `job`: player job name, string, required. ```
* `nick`: player nickname, string, required.
* `link`: link to ariyala set to parse BiS, string, optional.
### BiS API
* `GET /api/v1/party/bis` Service can be run from `src` directory by using command:
Get party/player BiS. Parameters: ```bash
python -m service.application.application
* `nick`: player full nickname to filter, string, optional. ```
* `POST /api/v1/party/bis`
Add or remove item to/from BiS. Parameters: To see all available options type `--help`.
* `action`: action to do, string, required. One of `add`, `remove`.
* `job`: player job name, string, required.
* `nick`: player nickname, string, required.
* `is_tome`: is item tome gear or not, bool, required.
* `piece`: item name, string, required.
* `PUT /api/v1/party/bis`
Create BiS from ariyala link. Parameters: ## Web service
* `job`: player job name, string, required.
* `nick`: player nickname, string, required.
* `link`: link to ariyala set to parse BiS, string, required.
### Loot API
* `GET /api/v1/party/loot` REST API documentation is available at `http://0.0.0.0:8000/api-docs`. HTML representation is available at `http://0.0.0.0:8000`.
Get party/player loot. Parameters:
* `nick`: player full nickname to filter, string, optional.
* `POST /api/v1/party/loot`
Add or remove item to/from loot list. Parameters:
* `action`: action to do, string, required. One of `add`, `remove`.
* `job`: player job name, string, required.
* `nick`: player nickname, string, required.
* `is_tome`: is item tome gear or not, bool, required.
* `piece`: item name, string, required.
* `PUT /api/v1/party/loot`
Suggest players to get loot. Parameters:
* `is_tome`: is item tome gear or not, bool, required.
* `piece`: item name, string, required.
### Users API
* `DELETE /api/v1/login/{username}`
Delete user with specified username. Parameters:
* `username`: username to remove, required.
* `POST /api/v1/login`
Login with credentials. Parameters:
* `username`: username to login, string, required.
* `password`: password to login, string, required.
* `PUT /api/v1/login`
Create new user. Parameters:
* `username`: username to login, string, required.
* `password`: password to login, string,
* `permission`: user permission, one of `get`, `post`, optional, default `get`.
* `POST /api/v1/logout`
Logout.
*Note*: host and port depend on configuration settings.
## Configuration ## Configuration

View File

@ -11,13 +11,15 @@ from apispec import APISpec
from service.core.version import __version__ from service.core.version import __version__
from service.models.action import Action from service.models.action import Action
from service.models.bis import BiS from service.models.bis import BiS, BiSLink
from service.models.error import Error from service.models.error import Error
from service.models.job import Job from service.models.job import Job
from service.models.loot import Loot
from service.models.piece import Piece from service.models.piece import Piece
from service.models.player import Player, PlayerId from service.models.player import Player, PlayerId, PlayerIdWithCounters
from service.models.player_edit import PlayerEdit from service.models.player_edit import PlayerEdit
from service.models.upgrade import Upgrade from service.models.upgrade import Upgrade
from service.models.user import User
def get_spec(app: Application) -> APISpec: def get_spec(app: Application) -> APISpec:
@ -45,13 +47,17 @@ def get_spec(app: Application) -> APISpec:
# components # components
spec.components.schema(Action.model_name(), Action.model_spec()) spec.components.schema(Action.model_name(), Action.model_spec())
spec.components.schema(BiS.model_name(), BiS.model_spec()) spec.components.schema(BiS.model_name(), BiS.model_spec())
spec.components.schema(BiSLink.model_name(), BiSLink.model_spec())
spec.components.schema(Error.model_name(), Error.model_spec()) spec.components.schema(Error.model_name(), Error.model_spec())
spec.components.schema(Job.model_name(), Job.model_spec()) spec.components.schema(Job.model_name(), Job.model_spec())
spec.components.schema(Loot.model_name(), Loot.model_spec())
spec.components.schema(Piece.model_name(), Piece.model_spec()) spec.components.schema(Piece.model_name(), Piece.model_spec())
spec.components.schema(Player.model_name(), Player.model_spec()) spec.components.schema(Player.model_name(), Player.model_spec())
spec.components.schema(PlayerEdit.model_name(), PlayerEdit.model_spec()) spec.components.schema(PlayerEdit.model_name(), PlayerEdit.model_spec())
spec.components.schema(PlayerId.model_name(), PlayerId.model_spec()) spec.components.schema(PlayerId.model_name(), PlayerId.model_spec())
spec.components.schema(PlayerIdWithCounters.model_name(), PlayerIdWithCounters.model_spec())
spec.components.schema(Upgrade.model_name(), Upgrade.model_spec()) spec.components.schema(Upgrade.model_name(), Upgrade.model_spec())
spec.components.schema(User.model_name(), User.model_spec())
# default responses # default responses
spec.components.response('BadRequest', dict( spec.components.response('BadRequest', dict(

View File

@ -7,6 +7,7 @@
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
# #
from aiohttp.web import Response from aiohttp.web import Response
from typing import Any, Dict, List, Optional, Type
from service.models.job import Job from service.models.job import Job
from service.models.piece import Piece from service.models.piece import Piece
@ -15,8 +16,92 @@ from service.models.player import PlayerId
from service.api.utils import wrap_exception, wrap_invalid_param, wrap_json from service.api.utils import wrap_exception, wrap_invalid_param, wrap_json
from service.api.views.common.bis_base import BiSBaseView from service.api.views.common.bis_base import BiSBaseView
from .openapi import OpenApi
class BiSView(BiSBaseView):
class BiSView(BiSBaseView, OpenApi):
@classmethod
def endpoint_get_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Get party players BiS items'
@classmethod
def endpoint_get_parameters(cls: Type[OpenApi]) -> List[Dict[str, Any]]:
return [
{
'name': 'nick',
'in': 'query',
'description': 'player nick name to filter',
'required': False,
'type': 'string'
}
]
@classmethod
def endpoint_get_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': { 'schema': {
'type': 'array',
'items': {
'allOf': [{'$ref': cls.model_ref('Piece')}]
}}}}}
}
@classmethod
def endpoint_get_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'get party BiS items'
@classmethod
def endpoint_get_tags(cls: Type[OpenApi]) -> List[str]:
return ['BiS']
@classmethod
def endpoint_post_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Add new item to player BiS or remove existing'
@classmethod
def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return ['Piece', 'PlayerEdit']
@classmethod
def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'schema': {'$ref': cls.model_ref('Loot')}}}}
}
@classmethod
def endpoint_post_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'edit BiS'
@classmethod
def endpoint_post_tags(cls: Type[OpenApi]) -> List[str]:
return ['BiS']
@classmethod
def endpoint_put_consumes(cls: Type[OpenApi]) -> List[str]:
return ['application/json']
@classmethod
def endpoint_put_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Generate new BiS set'
@classmethod
def endpoint_put_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return ['BiSLink']
@classmethod
def endpoint_put_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'schema': {'$ref': cls.model_ref('BiS')}}}}
}
@classmethod
def endpoint_put_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'update BiS'
@classmethod
def endpoint_put_tags(cls: Type[OpenApi]) -> List[str]:
return ['BiS']
async def get(self) -> Response: async def get(self) -> Response:
try: try:
@ -34,7 +119,7 @@ class BiSView(BiSBaseView):
except Exception: except Exception:
data = dict(await self.request.post()) data = dict(await self.request.post())
required = ['action', 'is_tome', 'job', 'nick', 'piece'] required = ['action', 'is_tome', 'job', 'name', 'nick']
if any(param not in data for param in required): if any(param not in data for param in required):
return wrap_invalid_param(required, data) return wrap_invalid_param(required, data)
@ -65,10 +150,10 @@ class BiSView(BiSBaseView):
try: try:
player_id = PlayerId(Job[data['job']], data['nick']) player_id = PlayerId(Job[data['job']], data['nick'])
link = await self.bis_put(player_id, data['link']) bis = await self.bis_put(player_id, data['link'])
except Exception as e: except Exception as e:
self.request.app.logger.exception('could not parse bis') self.request.app.logger.exception('could not parse bis')
return wrap_exception(e, data) return wrap_exception(e, data)
return wrap_json({'link': link}) return wrap_json(bis)

View File

@ -7,12 +7,89 @@
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
# #
from aiohttp.web import Response from aiohttp.web import Response
from typing import Any, Dict, List, Optional, Type
from service.api.utils import wrap_exception, wrap_invalid_param, wrap_json from service.api.utils import wrap_exception, wrap_invalid_param, wrap_json
from service.api.views.common.login_base import LoginBaseView from service.api.views.common.login_base import LoginBaseView
from .openapi import OpenApi
class LoginView(LoginBaseView):
class LoginView(LoginBaseView, OpenApi):
@classmethod
def endpoint_delete_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Delete registered user'
@classmethod
def endpoint_delete_parameters(cls: Type[OpenApi]) -> List[Dict[str, Any]]:
return [
{
'name': 'username',
'in': 'path',
'description': 'username to remove',
'required': True,
'type': 'string'
}
]
@classmethod
def endpoint_delete_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'type': 'object'}}}
}
@classmethod
def endpoint_delete_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'delete user'
@classmethod
def endpoint_delete_tags(cls: Type[OpenApi]) -> List[str]:
return ['users']
@classmethod
def endpoint_post_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Login as user'
@classmethod
def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return ['User']
@classmethod
def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'type': 'object'}}}
}
@classmethod
def endpoint_post_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'login'
@classmethod
def endpoint_post_tags(cls: Type[OpenApi]) -> List[str]:
return ['users']
@classmethod
def endpoint_put_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Create new user'
@classmethod
def endpoint_put_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return ['User']
@classmethod
def endpoint_put_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'type': 'object'}}}
}
@classmethod
def endpoint_put_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'create user'
@classmethod
def endpoint_put_tags(cls: Type[OpenApi]) -> List[str]:
return ['users']
async def delete(self) -> Response: async def delete(self) -> Response:
username = self.request.match_info['username'] username = self.request.match_info['username']

View File

@ -4,12 +4,37 @@
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
# #
from aiohttp.web import Response from aiohttp.web import Response
from typing import Any, Dict, List, Optional, Type
from service.api.utils import wrap_exception, wrap_json from service.api.utils import wrap_exception, wrap_json
from service.api.views.common.login_base import LoginBaseView from service.api.views.common.login_base import LoginBaseView
from .openapi import OpenApi
class LogoutView(LoginBaseView):
class LogoutView(LoginBaseView, OpenApi):
@classmethod
def endpoint_post_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Logout'
@classmethod
def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return []
@classmethod
def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'type': 'object'}}}
}
@classmethod
def endpoint_post_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'logout'
@classmethod
def endpoint_post_tags(cls: Type[OpenApi]) -> List[str]:
return ['users']
async def post(self) -> Response: async def post(self) -> Response:
try: try:

View File

@ -7,6 +7,7 @@
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
# #
from aiohttp.web import Response from aiohttp.web import Response
from typing import Any, Dict, List, Optional, Type
from service.models.job import Job from service.models.job import Job
from service.models.piece import Piece from service.models.piece import Piece
@ -15,8 +16,92 @@ from service.models.player import PlayerId
from service.api.utils import wrap_exception, wrap_invalid_param, wrap_json from service.api.utils import wrap_exception, wrap_invalid_param, wrap_json
from service.api.views.common.loot_base import LootBaseView from service.api.views.common.loot_base import LootBaseView
from .openapi import OpenApi
class LootView(LootBaseView):
class LootView(LootBaseView, OpenApi):
@classmethod
def endpoint_get_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Get party players loot'
@classmethod
def endpoint_get_parameters(cls: Type[OpenApi]) -> List[Dict[str, Any]]:
return [
{
'name': 'nick',
'in': 'query',
'description': 'player nick name to filter',
'required': False,
'type': 'string'
}
]
@classmethod
def endpoint_get_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'schema': {
'type': 'array',
'items': {
'allOf': [{'$ref': cls.model_ref('Piece')}]
}}}}}
}
@classmethod
def endpoint_get_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'get party loot'
@classmethod
def endpoint_get_tags(cls: Type[OpenApi]) -> List[str]:
return ['loot']
@classmethod
def endpoint_post_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Add new loot item or remove existing'
@classmethod
def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return ['Piece', 'PlayerEdit']
@classmethod
def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'schema': {'$ref': cls.model_ref('Loot')}}}}
}
@classmethod
def endpoint_post_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'edit loot'
@classmethod
def endpoint_post_tags(cls: Type[OpenApi]) -> List[str]:
return ['loot']
@classmethod
def endpoint_put_description(cls: Type[OpenApi]) -> Optional[str]:
return 'Suggest loot to party member'
@classmethod
def endpoint_put_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return ['Piece']
@classmethod
def endpoint_put_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {
'200': {'content': {'application/json': {'schema': {
'type': 'array',
'items': {
'allOf': [{'$ref': cls.model_ref('PlayerIdWithCounters')}]
}}}}}
}
@classmethod
def endpoint_put_summary(cls: Type[OpenApi]) -> Optional[str]:
return 'suggest loot'
@classmethod
def endpoint_put_tags(cls: Type[OpenApi]) -> List[str]:
return ['loot']
async def get(self) -> Response: async def get(self) -> Response:
try: try:
@ -34,7 +119,7 @@ class LootView(LootBaseView):
except Exception: except Exception:
data = dict(await self.request.post()) data = dict(await self.request.post())
required = ['action', 'is_tome', 'job', 'nick', 'piece'] required = ['action', 'is_tome', 'job', 'name', 'nick']
if any(param not in data for param in required): if any(param not in data for param in required):
return wrap_invalid_param(required, data) return wrap_invalid_param(required, data)
@ -59,7 +144,7 @@ class LootView(LootBaseView):
except Exception: except Exception:
data = dict(await self.request.post()) data = dict(await self.request.post())
required = ['is_tome', 'piece'] required = ['is_tome', 'name']
if any(param not in data for param in required): if any(param not in data for param in required):
return wrap_invalid_param(required, data) return wrap_invalid_param(required, data)

View File

@ -16,9 +16,38 @@ from service.models.serializable import Serializable
class OpenApi(Serializable): class OpenApi(Serializable):
@classmethod @classmethod
def endpoint_delete_spec(cls: Type[OpenApi]) -> Dict[str, Any]: def endpoint_delete_description(cls: Type[OpenApi]) -> Optional[str]:
return None
@classmethod
def endpoint_delete_parameters(cls: Type[OpenApi]) -> List[Dict[str, Any]]:
return []
@classmethod
def endpoint_delete_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {} return {}
@classmethod
def endpoint_delete_summary(cls: Type[OpenApi]) -> Optional[str]:
return None
@classmethod
def endpoint_delete_tags(cls: Type[OpenApi]) -> List[str]:
return []
@classmethod
def endpoint_delete_spec(cls: Type[OpenApi]) -> Dict[str, Any]:
description = cls.endpoint_delete_description()
if description is None:
return {}
return {
'description': description,
'parameters': cls.endpoint_delete_parameters(),
'responses': cls.endpoint_with_default_responses(cls.endpoint_delete_responses()),
'summary': cls.endpoint_delete_summary(),
'tags': cls.endpoint_delete_tags()
}
@classmethod @classmethod
def endpoint_get_description(cls: Type[OpenApi]) -> Optional[str]: def endpoint_get_description(cls: Type[OpenApi]) -> Optional[str]:
return None return None
@ -61,8 +90,8 @@ class OpenApi(Serializable):
return None return None
@classmethod @classmethod
def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> str: def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return '' return []
@classmethod @classmethod
def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]: def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
@ -87,7 +116,10 @@ class OpenApi(Serializable):
'requestBody': { 'requestBody': {
'content': { 'content': {
content_type: { content_type: {
'schema': {'$ref': cls.model_ref(cls.endpoint_post_request_body(content_type))} 'schema': {'allOf': [
{'$ref': cls.model_ref(ref)}
for ref in cls.endpoint_post_request_body(content_type)
]}
} }
for content_type in cls.endpoint_post_consumes() for content_type in cls.endpoint_post_consumes()
} }
@ -97,6 +129,54 @@ class OpenApi(Serializable):
'tags': cls.endpoint_post_tags() 'tags': cls.endpoint_post_tags()
} }
@classmethod
def endpoint_put_consumes(cls: Type[OpenApi]) -> List[str]:
return ['application/json']
@classmethod
def endpoint_put_description(cls: Type[OpenApi]) -> Optional[str]:
return None
@classmethod
def endpoint_put_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return []
@classmethod
def endpoint_put_responses(cls: Type[OpenApi]) -> Dict[str, Any]:
return {}
@classmethod
def endpoint_put_summary(cls: Type[OpenApi]) -> Optional[str]:
return None
@classmethod
def endpoint_put_tags(cls: Type[OpenApi]) -> List[str]:
return []
@classmethod
def endpoint_put_spec(cls: Type[OpenApi]) -> Dict[str, Any]:
description = cls.endpoint_put_description()
if description is None:
return {}
return {
'consumes': cls.endpoint_put_consumes(),
'description': description,
'requestBody': {
'content': {
content_type: {
'schema': {'allOf': [
{'$ref': cls.model_ref(ref)}
for ref in cls.endpoint_put_request_body(content_type)
]}
}
for content_type in cls.endpoint_put_consumes()
}
},
'responses': cls.endpoint_with_default_responses(cls.endpoint_put_responses()),
'summary': cls.endpoint_put_summary(),
'tags': cls.endpoint_put_tags()
}
@classmethod @classmethod
def endpoint_spec(cls: Type[OpenApi], operations: List[str]) -> Dict[str, Any]: def endpoint_spec(cls: Type[OpenApi], operations: List[str]) -> Dict[str, Any]:
return { return {

View File

@ -54,8 +54,8 @@ class PlayerView(PlayerBaseView, OpenApi):
return 'Create new party player or remove existing' return 'Create new party player or remove existing'
@classmethod @classmethod
def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> str: def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> List[str]:
return 'PlayerEdit' return ['PlayerEdit']
@classmethod @classmethod
def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]: def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]:

View File

@ -10,6 +10,7 @@ from aiohttp.web import View
from typing import List, Optional from typing import List, Optional
from service.core.ariyala_parser import AriyalaParser from service.core.ariyala_parser import AriyalaParser
from service.models.bis import BiS
from service.models.piece import Piece from service.models.piece import Piece
from service.models.player import PlayerId from service.models.player import PlayerId
@ -35,13 +36,13 @@ class BiSBaseView(View):
return await self.bis_remove(player_id, piece) return await self.bis_remove(player_id, piece)
return None return None
async def bis_put(self, player_id: PlayerId, link: str) -> str: async def bis_put(self, player_id: PlayerId, link: str) -> BiS:
parser = AriyalaParser(self.request.app['config']) parser = AriyalaParser(self.request.app['config'])
items = parser.get(link, player_id.job.name) items = parser.get(link, player_id.job.name)
for piece in items: for piece in items:
await self.request.app['party'].set_item_bis(player_id, piece) await self.request.app['party'].set_item_bis(player_id, piece)
await self.request.app['party'].set_bis_link(player_id, link) await self.request.app['party'].set_bis_link(player_id, link)
return link return self.request.app['party'].players[player_id].bis
async def bis_remove(self, player_id: PlayerId, piece: Piece) -> Piece: async def bis_remove(self, player_id: PlayerId, piece: Piece) -> Piece:
await self.request.app['party'].remove_item_bis(player_id, piece) await self.request.app['party'].remove_item_bis(player_id, piece)

View File

@ -11,11 +11,42 @@ import itertools
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Type, Union from typing import Any, Dict, List, Optional, Type, Union
from .job import Job
from .piece import Piece from .piece import Piece
from .serializable import Serializable from .serializable import Serializable
from .upgrade import Upgrade from .upgrade import Upgrade
@dataclass
class BiSLink(Serializable):
nick: str
job: Job
link: str
@classmethod
def model_properties(cls: Type[Serializable]) -> Dict[str, Any]:
return {
'job': {
'description': 'player job name',
'$ref': cls.model_ref('Job')
},
'link': {
'description': 'link to BiS set',
'example': 'https://ffxiv.ariyala.com/19V5R',
'type': 'string'
},
'nick': {
'description': 'player nick name',
'example': 'Siuan Sanche',
'type': 'string'
}
}
@classmethod
def model_required(cls: Type[Serializable]) -> List[str]:
return ['job', 'link', 'nick']
@dataclass @dataclass
class BiS(Serializable): class BiS(Serializable):
weapon: Optional[Piece] = None weapon: Optional[Piece] = None

View File

@ -7,13 +7,31 @@
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
# #
from dataclasses import dataclass from dataclasses import dataclass
from typing import Union from typing import Any, Dict, List, Type, Union
from .piece import Piece from .piece import Piece
from .serializable import Serializable
from .upgrade import Upgrade from .upgrade import Upgrade
@dataclass @dataclass
class Loot: class Loot(Serializable):
player_id: int player_id: int
piece: Union[Piece, Upgrade] piece: Union[Piece, Upgrade]
@classmethod
def model_properties(cls: Type[Serializable]) -> Dict[str, Any]:
return {
'piece': {
'description': 'player piece',
'$ref': cls.model_ref('Piece')
},
'player_id': {
'description': 'player identifier',
'$ref': cls.model_ref('PlayerId')
}
}
@classmethod
def model_required(cls: Type[Serializable]) -> List[str]:
return ['piece', 'player_id']

View File

@ -86,7 +86,6 @@ class Piece(Serializable):
}, },
'name': { 'name': {
'description': 'piece name', 'description': 'piece name',
'required': True,
'type': 'string' 'type': 'string'
} }
} }

View File

@ -66,6 +66,48 @@ class PlayerIdWithCounters(PlayerId):
loot_count_total: int loot_count_total: int
bis_count_total: int bis_count_total: int
@classmethod
def model_properties(cls: Type[Serializable]) -> Dict[str, Any]:
return {
'bis_count_total': {
'description': 'total savage pieces in BiS',
'type': 'integer'
},
'is_required': {
'description': 'is item required by BiS or not',
'type': 'boolean'
},
'job': {
'description': 'player job name',
'$ref': cls.model_ref('Job')
},
'loot_count': {
'description': 'count of this item which was already looted',
'type': 'integer'
},
'loot_count_bis': {
'description': 'count of BiS items which were already looted',
'type': 'integer'
},
'loot_count_total': {
'description': 'total count of items which were looted',
'type': 'integer'
},
'nick': {
'description': 'player nick name',
'type': 'string'
},
'priority': {
'description': 'player loot priority',
'type': 'integer'
}
}
@classmethod
def model_required(cls: Type[Serializable]) -> List[str]:
return ['bis_count_total', 'is_required', 'job', 'loot_count',
'loot_count_bis', 'loot_count_total', 'nick', 'priority']
@dataclass @dataclass
class Player(Serializable): class Player(Serializable):

View File

@ -7,10 +7,36 @@
# License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause
# #
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Dict, List, Type
from .serializable import Serializable
@dataclass @dataclass
class User: class User(Serializable):
username: str username: str
password: str password: str
permission: str permission: str
@classmethod
def model_properties(cls: Type[Serializable]) -> Dict[str, Any]:
return {
'password': {
'description': 'user password',
'type': 'string'
},
'permission': {
'default': 'get',
'description': 'user action permissions',
'type': 'string',
'enum': ['admin', 'get', 'post']
},
'username': {
'description': 'user name',
'type': 'string'
}
}
@classmethod
def model_required(cls: Type[Serializable]) -> List[str]:
return ['password', 'username']