From 80ea82904b0038e8f16afdddb6921aa0c442a4cd Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Sun, 15 Sep 2019 01:07:15 +0300 Subject: [PATCH] complete swagger --- README.md | 104 ++++------------------- src/service/api/spec.py | 10 ++- src/service/api/views/api/bis.py | 93 +++++++++++++++++++- src/service/api/views/api/login.py | 79 ++++++++++++++++- src/service/api/views/api/logout.py | 27 +++++- src/service/api/views/api/loot.py | 91 +++++++++++++++++++- src/service/api/views/api/openapi.py | 88 ++++++++++++++++++- src/service/api/views/api/player.py | 4 +- src/service/api/views/common/bis_base.py | 5 +- src/service/models/bis.py | 31 +++++++ src/service/models/loot.py | 24 +++++- src/service/models/piece.py | 1 - src/service/models/player.py | 42 +++++++++ src/service/models/user.py | 30 ++++++- 14 files changed, 515 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index f2feea9..c0cebf4 100644 --- a/README.md +++ b/README.md @@ -2,104 +2,30 @@ 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` - - Get party list. Parameters: - - * `nick`: player full nickname to filter, string, optional. - -* `POST /api/v1/party` +In general installation process looks like: - Add or remove party member. Parameters: - - * `action`: action to do, string, required. One of `add`, `remove`. - * `job`: player job name, string, required. - * `nick`: player nickname, string, required. - * `link`: link to ariyala set to parse BiS, string, optional. - -### BiS API +```bash +python setup.py build +python setup.py test # if you want to run tests +``` -* `GET /api/v1/party/bis` +Service can be run from `src` directory by using command: - Get party/player BiS. Parameters: - - * `nick`: player full nickname to filter, string, optional. - -* `POST /api/v1/party/bis` +```bash +python -m service.application.application +``` - Add or remove item to/from BiS. 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/bis` +To see all available options type `--help`. - Create BiS from ariyala link. Parameters: - - * `job`: player job name, string, required. - * `nick`: player nickname, string, required. - * `link`: link to ariyala set to parse BiS, string, required. - -### Loot API +## Web service -* `GET /api/v1/party/loot` - - 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. +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`. +*Note*: host and port depend on configuration settings. ## Configuration diff --git a/src/service/api/spec.py b/src/service/api/spec.py index bb73dfd..2c05279 100644 --- a/src/service/api/spec.py +++ b/src/service/api/spec.py @@ -11,13 +11,15 @@ from apispec import APISpec from service.core.version import __version__ 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.job import Job +from service.models.loot import Loot 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.upgrade import Upgrade +from service.models.user import User def get_spec(app: Application) -> APISpec: @@ -45,13 +47,17 @@ def get_spec(app: Application) -> APISpec: # components spec.components.schema(Action.model_name(), Action.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(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(Player.model_name(), Player.model_spec()) spec.components.schema(PlayerEdit.model_name(), PlayerEdit.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(User.model_name(), User.model_spec()) # default responses spec.components.response('BadRequest', dict( diff --git a/src/service/api/views/api/bis.py b/src/service/api/views/api/bis.py index b7b6894..8a84a38 100644 --- a/src/service/api/views/api/bis.py +++ b/src/service/api/views/api/bis.py @@ -7,6 +7,7 @@ # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # from aiohttp.web import Response +from typing import Any, Dict, List, Optional, Type from service.models.job import Job 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.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: try: @@ -34,7 +119,7 @@ class BiSView(BiSBaseView): except Exception: 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): return wrap_invalid_param(required, data) @@ -65,10 +150,10 @@ class BiSView(BiSBaseView): try: 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: self.request.app.logger.exception('could not parse bis') return wrap_exception(e, data) - return wrap_json({'link': link}) \ No newline at end of file + return wrap_json(bis) \ No newline at end of file diff --git a/src/service/api/views/api/login.py b/src/service/api/views/api/login.py index 503e19b..4595c18 100644 --- a/src/service/api/views/api/login.py +++ b/src/service/api/views/api/login.py @@ -7,12 +7,89 @@ # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # 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.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: username = self.request.match_info['username'] diff --git a/src/service/api/views/api/logout.py b/src/service/api/views/api/logout.py index fcac24f..bb6e483 100644 --- a/src/service/api/views/api/logout.py +++ b/src/service/api/views/api/logout.py @@ -4,12 +4,37 @@ # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # 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.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: try: diff --git a/src/service/api/views/api/loot.py b/src/service/api/views/api/loot.py index 7e0d755..8054b56 100644 --- a/src/service/api/views/api/loot.py +++ b/src/service/api/views/api/loot.py @@ -7,6 +7,7 @@ # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # from aiohttp.web import Response +from typing import Any, Dict, List, Optional, Type from service.models.job import Job 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.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: try: @@ -34,7 +119,7 @@ class LootView(LootBaseView): except Exception: 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): return wrap_invalid_param(required, data) @@ -59,7 +144,7 @@ class LootView(LootBaseView): except Exception: data = dict(await self.request.post()) - required = ['is_tome', 'piece'] + required = ['is_tome', 'name'] if any(param not in data for param in required): return wrap_invalid_param(required, data) diff --git a/src/service/api/views/api/openapi.py b/src/service/api/views/api/openapi.py index 6215660..1ce8f60 100644 --- a/src/service/api/views/api/openapi.py +++ b/src/service/api/views/api/openapi.py @@ -16,9 +16,38 @@ from service.models.serializable import Serializable class OpenApi(Serializable): @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 {} + @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 def endpoint_get_description(cls: Type[OpenApi]) -> Optional[str]: return None @@ -61,8 +90,8 @@ class OpenApi(Serializable): return None @classmethod - def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> str: - return '' + 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]: @@ -87,7 +116,10 @@ class OpenApi(Serializable): 'requestBody': { 'content': { 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() } @@ -97,6 +129,54 @@ class OpenApi(Serializable): '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 def endpoint_spec(cls: Type[OpenApi], operations: List[str]) -> Dict[str, Any]: return { diff --git a/src/service/api/views/api/player.py b/src/service/api/views/api/player.py index ac3fe0a..eb12fe8 100644 --- a/src/service/api/views/api/player.py +++ b/src/service/api/views/api/player.py @@ -54,8 +54,8 @@ class PlayerView(PlayerBaseView, OpenApi): return 'Create new party player or remove existing' @classmethod - def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> str: - return 'PlayerEdit' + def endpoint_post_request_body(cls: Type[OpenApi], content_type: str) -> List[str]: + return ['PlayerEdit'] @classmethod def endpoint_post_responses(cls: Type[OpenApi]) -> Dict[str, Any]: diff --git a/src/service/api/views/common/bis_base.py b/src/service/api/views/common/bis_base.py index 7c86ab7..7dcbffa 100644 --- a/src/service/api/views/common/bis_base.py +++ b/src/service/api/views/common/bis_base.py @@ -10,6 +10,7 @@ from aiohttp.web import View from typing import List, Optional from service.core.ariyala_parser import AriyalaParser +from service.models.bis import BiS from service.models.piece import Piece from service.models.player import PlayerId @@ -35,13 +36,13 @@ class BiSBaseView(View): return await self.bis_remove(player_id, piece) 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']) items = parser.get(link, player_id.job.name) for piece in items: await self.request.app['party'].set_item_bis(player_id, piece) 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: await self.request.app['party'].remove_item_bis(player_id, piece) diff --git a/src/service/models/bis.py b/src/service/models/bis.py index 0d3746b..2bb293f 100644 --- a/src/service/models/bis.py +++ b/src/service/models/bis.py @@ -11,11 +11,42 @@ import itertools from dataclasses import dataclass from typing import Any, Dict, List, Optional, Type, Union +from .job import Job from .piece import Piece from .serializable import Serializable 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 class BiS(Serializable): weapon: Optional[Piece] = None diff --git a/src/service/models/loot.py b/src/service/models/loot.py index 3447ae1..0fa04c3 100644 --- a/src/service/models/loot.py +++ b/src/service/models/loot.py @@ -7,13 +7,31 @@ # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # from dataclasses import dataclass -from typing import Union +from typing import Any, Dict, List, Type, Union from .piece import Piece +from .serializable import Serializable from .upgrade import Upgrade @dataclass -class Loot: +class Loot(Serializable): player_id: int - piece: Union[Piece, Upgrade] \ No newline at end of file + 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'] \ No newline at end of file diff --git a/src/service/models/piece.py b/src/service/models/piece.py index 2bcded5..4a38497 100644 --- a/src/service/models/piece.py +++ b/src/service/models/piece.py @@ -86,7 +86,6 @@ class Piece(Serializable): }, 'name': { 'description': 'piece name', - 'required': True, 'type': 'string' } } diff --git a/src/service/models/player.py b/src/service/models/player.py index 9075964..f53fdd7 100644 --- a/src/service/models/player.py +++ b/src/service/models/player.py @@ -66,6 +66,48 @@ class PlayerIdWithCounters(PlayerId): loot_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 class Player(Serializable): diff --git a/src/service/models/user.py b/src/service/models/user.py index 72713e9..588ebf4 100644 --- a/src/service/models/user.py +++ b/src/service/models/user.py @@ -7,10 +7,36 @@ # License: 3-clause BSD, see https://opensource.org/licenses/BSD-3-Clause # from dataclasses import dataclass +from typing import Any, Dict, List, Type + +from .serializable import Serializable @dataclass -class User: +class User(Serializable): username: str password: str - permission: str \ No newline at end of file + 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'] \ No newline at end of file