diff --git a/pype/modules/avalon_apps/avalon_app.py b/pype/modules/avalon_apps/avalon_app.py index c61704db14..38d6e4394c 100644 --- a/pype/modules/avalon_apps/avalon_app.py +++ b/pype/modules/avalon_apps/avalon_app.py @@ -3,11 +3,12 @@ import pype from pype import resources from .. import ( PypeModule, - ITrayModule + ITrayModule, + IWebServerRoutes ) -class AvalonModule(PypeModule, ITrayModule): +class AvalonModule(PypeModule, ITrayModule, IWebServerRoutes): name = "avalon" def initialize(self, modules_settings): @@ -73,6 +74,13 @@ class AvalonModule(PypeModule, ITrayModule): def connect_with_modules(self, _enabled_modules): return + def webserver_initialization(self, server_manager): + """Implementation of IWebServerRoutes interface.""" + + if self.tray_initialized: + from .rest_api import AvalonRestApiResource + self.rest_api_obj = AvalonRestApiResource(self, server_manager) + # Definition of Tray menu def tray_menu(self, tray_menu): from Qt import QtWidgets diff --git a/pype/modules/avalon_apps/rest_api.py b/pype/modules/avalon_apps/rest_api.py new file mode 100644 index 0000000000..e41801984b --- /dev/null +++ b/pype/modules/avalon_apps/rest_api.py @@ -0,0 +1,147 @@ +import os +import re +import json +import datetime + +import bson +from bson.objectid import ObjectId +import bson.json_util + +from aiohttp.web_request import Request +from aiohttp.web_response import Response + +from avalon.api import AvalonMongoDB +from pype.modules.webserver.base_routes import RestApiEndpoint + + +class _RestApiEndpoint(RestApiEndpoint): + def __init__(self, resource): + self.resource = resource + super(_RestApiEndpoint, self).__init__() + + @property + def dbcon(self): + return self.resource.dbcon + + +class AvalonProjectsEndpoint(_RestApiEndpoint): + async def get(self) -> Response: + output = [] + for project_name in self.dbcon.database.collection_names(): + project_doc = self.dbcon.database[project_name].find_one({ + "type": "project" + }) + output.append(project_doc) + return Response( + status=200, + body=self.resource.encode(output), + content_type="application/json" + ) + + +class AvalonProjectEndpoint(_RestApiEndpoint): + async def get(self, project_name) -> Response: + project_doc = self.dbcon.database[project_name].find_one({ + "type": "project" + }) + if project_doc: + return Response( + status=200, + body=self.resource.encode(project_doc), + content_type="application/json" + ) + return Response( + status=404, + reason="Project name {} not found".format(project_name) + ) + + +class AvalonAssetsEndpoint(_RestApiEndpoint): + async def get(self, project_name) -> Response: + asset_docs = list(self.dbcon.database[project_name].find({ + "type": "asset" + })) + return Response( + status=200, + body=self.resource.encode(asset_docs), + content_type="application/json" + ) + + +class AvalonAssetEndpoint(_RestApiEndpoint): + async def get(self, project_name, asset_name) -> Response: + asset_doc = self.dbcon.database[project_name].find_one({ + "type": "asset", + "name": asset_name + }) + if asset_doc: + return Response( + status=200, + body=self.resource.encode(asset_doc), + content_type="application/json" + ) + return Response( + status=404, + reason="Asset name {} not found in project {}".format( + asset_name, project_name + ) + ) + + +class AvalonRestApiResource: + def __init__(self, avalon_module, server_manager): + self.module = avalon_module + self.server_manager = server_manager + + self.dbcon = AvalonMongoDB() + self.dbcon.install() + + self.prefix = "/avalon" + + self.endpoint_defs = ( + ( + "GET", + "/projects", + AvalonProjectsEndpoint(self) + ), + ( + "GET", + "/projects/{project_name}", + AvalonProjectEndpoint(self) + ), + ( + "GET", + "/projects/{project_name}/assets", + AvalonAssetsEndpoint(self) + ), + ( + "GET", + "/projects/{project_name}/assets/{asset_name}", + AvalonAssetEndpoint(self) + ) + ) + + self.register() + + def register(self): + for methods, url, endpoint in self.endpoint_defs: + final_url = self.prefix + url + self.server_manager.add_route( + methods, final_url, endpoint.dispatch + ) + + @staticmethod + def json_dump_handler(value): + if isinstance(value, datetime.datetime): + return value.isoformat() + if isinstance(value, ObjectId): + return str(value) + raise TypeError(value) + + @classmethod + def encode(cls, data): + return json.dumps( + data, + indent=4, + default=cls.json_dump_handler + ).encode("utf-8")