mirror of
				https://github.com/arcan1s/ffxivbis.git
				synced 2025-10-31 05:33:41 +00:00 
			
		
		
		
	complete swagger
This commit is contained in:
		
							
								
								
									
										104
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								README.md
									
									
									
									
									
								
							| @ -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: | ```bash | ||||||
|  | python setup.py build | ||||||
|  | python setup.py test  # if you want to run tests | ||||||
|  | ``` | ||||||
|  |  | ||||||
|     * `nick`: player full nickname to filter, string, optional. | Service can be run from `src` directory by using command: | ||||||
|  |  | ||||||
| * `POST /api/v1/party` | ```bash | ||||||
|  | python -m service.application.application | ||||||
|  | ``` | ||||||
|  |  | ||||||
|     Add or remove party member. Parameters: | To see all available options type `--help`. | ||||||
|  |  | ||||||
|     * `action`: action to do, string, required. One of `add`, `remove`. | ## Web service | ||||||
|     * `job`: player job name, string, required. |  | ||||||
|     * `nick`: player nickname, string, required. |  | ||||||
|     * `link`: link to ariyala set to parse BiS, string, optional. |  | ||||||
|  |  | ||||||
| ### BiS API | 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 /api/v1/party/bis` |  | ||||||
|  |  | ||||||
|     Get party/player BiS. Parameters: |  | ||||||
|      |  | ||||||
|     * `nick`: player full nickname to filter, string, optional. |  | ||||||
|      |  | ||||||
| * `POST /api/v1/party/bis` |  | ||||||
|  |  | ||||||
|     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` |  | ||||||
|  |  | ||||||
|     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 |  | ||||||
|  |  | ||||||
| * `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. |  | ||||||
|  |  | ||||||
|  | *Note*: host and port depend on configuration settings.  | ||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
|  |  | ||||||
|  | |||||||
| @ -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( | ||||||
|  | |||||||
| @ -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) | ||||||
| @ -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'] | ||||||
|  | |||||||
| @ -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: | ||||||
|  | |||||||
| @ -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) | ||||||
|  |  | ||||||
|  | |||||||
| @ -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 { | ||||||
|  | |||||||
| @ -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]: | ||||||
|  | |||||||
| @ -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) | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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'] | ||||||
| @ -86,7 +86,6 @@ class Piece(Serializable): | |||||||
|             }, |             }, | ||||||
|             'name': { |             'name': { | ||||||
|                 'description': 'piece name', |                 'description': 'piece name', | ||||||
|                 'required': True, |  | ||||||
|                 'type': 'string' |                 'type': 'string' | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -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): | ||||||
|  | |||||||
| @ -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'] | ||||||
		Reference in New Issue
	
	Block a user