class ResponseTest(TestCase): def setUp(self): self.endpoint = NautilusRetriever(["./tests/test_data/farsiLit"]) def test_with_get_capabilities(self): response = self.endpoint.getCapabilities(category="translation", output=MY_CAPYTAIN) ti = TextInventory(resource=response) self.assertEqual( len(ti["urn:cts:farsiLit:hafez.divan"].texts), 2, "Asserts that only two texts have been added to the TI" ) self.assertEqual( ti["urn:cts:farsiLit:hafez.divan.perseus-eng1"].lang, "eng", "Asserts that text have lang" ) response = self.endpoint.getCapabilities(category="edition", output=MY_CAPYTAIN) ti = TextInventory(resource=response) self.assertEqual( len(ti["urn:cts:farsiLit:hafez.divan"].texts), 1, "Asserts that only one text has been added to the TI" ) self.assertEqual( ti["urn:cts:farsiLit:hafez.divan.perseus-far1"].lang, "fa", "Asserts that text have lang" ) def test_with_get_capabilities_cts_response(self): response = self.endpoint.getCapabilities(category="translation", output=XML) self.assertIn( "<requestFilters>category=translation</requestFilters>", response, "Filters should be listed" ) ti = TextInventory(resource=response) self.assertEqual( len(ti["urn:cts:farsiLit:hafez.divan"].texts), 2, "Asserts that only two texts has been added to the TI" ) def test_get_passage_complete_urn(self): """ Test Get Passage """ response, metadata = self.endpoint.getPassage("urn:cts:farsiLit:hafez.divan.perseus-eng1:1.1.1.1", output=MY_CAPYTAIN) self.assertEqual( response.text(), "Ho ! Saki, pass around and offer the bowl (of love for God) : ### ", "It should be possible to retrieve text" ) def test_get_passage_partial_urn(self): """ Test Get Passage """ response, metadata = self.endpoint.getPassage("urn:cts:farsiLit:hafez.divan:1.1.1.1", output=MY_CAPYTAIN) self.assertEqual( response.text(), "الا یا ایها الساقی ادر کاسا و ناولها ### ", "It should be possible to retrieve text from edition without version" ) def test_get_passage_formatted(self): response = self.endpoint.getPassage("urn:cts:farsiLit:hafez.divan:1.1.1.1", output=XML) p = Passage(resource=xmlparser(response), urn="urn:cts:farsiLit:hafez.divan:1.1.1.1") """ self.assertEqual( p.text(), "الا یا ایها الساقی ادر کاسا و ناولها ###", "API Response should be parsable by MyCapytain Library" ) """ def test_get_passage_plus_formatted(self): response = self.endpoint.getPassagePlus("urn:cts:farsiLit:hafez.divan:1.1.1.2", output=XML) parsed_response = Passage(resource=xmlparser(response), urn="urn:cts:farsiLit:hafez.divan:1.1.1.2") self.assertEqual( parsed_response.text().strip(), "که عشق آسان نمود اول ولی افتاد مشکلها ***", "API Response should be parsable by MyCapytain Library" ) self.assertIn( "<prev><urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1.1.1</urn></prev>", response, "Previous URN should be found" ) self.assertIn( "<next><urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1.2.1</urn></next>", response, "Next URN should be found" ) def test_get_valid_reff(self): """ With reference """ response = self.endpoint.getValidReff("urn:cts:farsiLit:hafez.divan:1.1", output=XML) self.assertIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1.1</urn>", response, "First URN should be found" ) self.assertNotIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.2.1</urn>", response, "First URN should be found" ) def test_get_valid_reff_simple(self): """ Without reference """ response = self.endpoint.getValidReff("urn:cts:farsiLit:hafez.divan", output=XML) self.assertIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1</urn>", response, "First URN should be found" ) self.assertNotIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1</urn>", response, "First URN should be found" ) response = self.endpoint.getValidReff("urn:cts:farsiLit:hafez.divan", level=4, output=XML) self.assertIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1.1.2</urn>", response, "First URN should be found" ) self.assertNotIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1</urn>", response, "First URN should be found" ) response = self.endpoint.getValidReff( "urn:cts:farsiLit:hafez.divan.perseus-far1:1.1.1.1-1.1.2.1", level=4, output=XML ) self.assertIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1.1.1</urn>", response, "First URN should be found" ) self.assertIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1.1.2</urn>", response, "First URN should be found" ) self.assertIn( "<urn>urn:cts:farsiLit:hafez.divan.perseus-far1:1.1.2.1</urn>", response, "First URN should be found" ) """
class FlaskNautilus(object): """ Initiate the class :param prefix: Prefix on which to install the extension :param app: Application on which to register :param name: Name to use for the blueprint :param resources: List of directory to feed the inventory :type resources: list(str) :param logger: Logging handler. :type logger: logging :param parser_cache: Cache object :type parser_cache: BaseCache :param http_cache: HTTP Cache should be a FlaskCache object :param auto_parse: Parses on first execution the resources given to build inventory. Not recommended for production :cvar ROUTES: List of triple length tuples :cvar Access_Control_Allow_Methods: Dictionary with route name and allowed methods over CORS :cvar Access_Control_Allow_Origin: Dictionary with route name and allowed host over CORS or "*" :cvar LoggingHandler: Logging handler to be set for the blueprint :ivar logger: Logging handler :type logger: logging :ivar retriever: CapiTainS Retriever """ ROUTES = [ ('/', "r_dispatcher", ["GET"]) ] Access_Control_Allow_Methods = { "r_dispatcher": "OPTIONS, GET" } Access_Control_Allow_Origin = "*" LoggingHandler = logging.StreamHandler def __init__(self, prefix="", app=None, name=None, resources=None, parser_cache=None, compresser=True, http_cache=None, pagination=False, access_Control_Allow_Origin=None, access_Control_Allow_Methods=None, logger=None, auto_parse=True ): self.logger = None self.retriever = None self.setLogger(logger) if not resources: resources = list() # Set up endpoints with cache system if parser_cache: Text.CACHE_CLASS = parser_cache self.retriever = NautilusRetriever(resources, pagination=pagination, cache=parser_cache, logger=self.logger, auto_parse=auto_parse) else: self.retriever = NautilusRetriever(resources, pagination=pagination, auto_parse=auto_parse) self.retriever.resolver.TEXT_CLASS = Text self.app = app self.name = name self.prefix = prefix self.blueprint = None self.Access_Control_Allow_Methods = access_Control_Allow_Methods if not self.Access_Control_Allow_Methods: self.Access_Control_Allow_Methods = FlaskNautilus.Access_Control_Allow_Methods self.Access_Control_Allow_Origin = access_Control_Allow_Origin if not self.Access_Control_Allow_Origin: self.Access_Control_Allow_Origin = FlaskNautilus.Access_Control_Allow_Origin self.__http_cache = http_cache self.__compresser = False if self.name is None: self.name = __name__ if self.app: self.init_app(app=app, compresser=compresser) def setLogger(self, logger): """ Set up the Logger for the application :param logger: logging.Logger object :return: Logger instance """ self.logger = logger if not logger: self.logger = logging.getLogger("capitains_nautilus") else: self.logger = self.logger.getLogger("capitains_nautilus") formatter = logging.Formatter("[%(asctime)s] {%(pathname)s:%(lineno)d} %(levelname)s - %(message)s") stream = FlaskNautilus.LoggingHandler() stream.setLevel(logging.INFO) stream.setFormatter(formatter) self.logger.addHandler(stream) if self.retriever: self.retriever.logger = self.logger self.retriever.resolver.logger = self.logger return self.logger def init_app(self, app, compresser=False): """ Initiate the extension on the application :param app: Flask Application :return: Blueprint for Flask Nautilus registered in app :rtype: Blueprint """ if not self.app: self.app = app if not self.__http_cache: self.__http_cache = Cache(config={'CACHE_TYPE': 'simple'}) self.init_blueprint() if compresser: self.__compresser = Compress(app) return self.blueprint def init_blueprint(self): """ Properly generates the blueprint, registering routes and filters and connecting the app and the blueprint :return: Blueprint of the extension :rtype: Blueprint """ self.blueprint = Blueprint( self.name, self.name, url_prefix=self.prefix ) # Register routes for url, name, methods in FlaskNautilus.ROUTES: self.blueprint.add_url_rule( url, view_func=self.view(name), endpoint=name[2:], methods=methods ) self.app.register_blueprint(self.blueprint) return self.blueprint def view(self, function_name): """ Builds response according to a function name :param function_name: Route name / function name :return: Function """ if isinstance(self.Access_Control_Allow_Origin, dict): d = { "Access-Control-Allow-Origin": self.Access_Control_Allow_Origin[function_name], "Access-Control-Allow-Methods": self.Access_Control_Allow_Methods[function_name] } else: d = { "Access-Control-Allow-Origin": self.Access_Control_Allow_Origin, "Access-Control-Allow-Methods": self.Access_Control_Allow_Methods[function_name] } def r(*x, **y): val = list(getattr(self, function_name)(*x, **y)) val[2].update(d) return tuple(val) return r def r_dispatcher(self): """ Actual main route of CTS APIs. Transfer typical requests through the ?request=REQUESTNAME route :return: Response """ _request = request.args.get("request", None) if not _request: return "This request does not exist", 404, dict() # Should maybe return documentation on top of 404 ? elif _request.lower() == "getcapabilities": return self._r_GetCapabilities( urn=request.args.get("urn", None), inv=request.args.get("inv", None) ) elif _request.lower() == "getpassage": return self._r_GetPassage( urn=request.args.get("urn", None), inv=request.args.get("inv", None) ) elif _request.lower() == "getpassageplus": return self._r_GetPassagePlus( urn=request.args.get("urn", None), inv=request.args.get("inv", None) ) elif _request.lower() == "getlabel": return self._r_GetLabel( urn=request.args.get("urn", None), inv=request.args.get("inv", None) ) elif _request.lower() == "getfirsturn": return self._r_GetFirstUrn( urn=request.args.get("urn", None), inv=request.args.get("inv", None) ) elif _request.lower() == "getprevnexturn": return self._r_GetPrevNext( urn=request.args.get("urn", None), inv=request.args.get("inv", None) ) elif _request.lower() == "getvalidreff": return self._r_GetValidReff( urn=request.args.get("urn", None), inv=request.args.get("inv", None), level=request.args.get("level", 1, type=int) ) return "This request does not exist", 404, "" # Should maybe return documentation on top of 404 ? def _r_GetCapabilities(self, urn=None, inv=None): """ Provisional route for GetCapabilities request :param urn: URN to filter the resource :param inv: Inventory Identifier :return: GetCapabilities response """ return self.retriever.getCapabilities(inventory=inv, urn=urn).strip(), 200, {"content-type": "application/xml"} def _r_GetPassage(self, urn, inv): """ Provisional route for GetPassage request :param urn: URN to filter the resource :param inv: Inventory Identifier :return: GetPassage response """ return self.retriever.getPassage(inventory=inv, urn=urn).strip(), 200, {"content-type": "application/xml"} def _r_GetPassagePlus(self, urn, inv): """ Provisional route for GetPassagePlus request :param urn: URN to filter the resource :param inv: Inventory Identifier :return: GetPassagePlus response """ return self.retriever.getPassagePlus(inventory=inv, urn=urn).strip(), 200, {"content-type": "application/xml"} def _r_GetValidReff(self, urn, inv, level): """ Provisional route for GetValidReff request :param urn: URN to filter the resource :param inv: Inventory Identifier :return: GetValidReff response """ return self.retriever.getValidReff(inventory=inv, urn=urn, level=level).strip(), 200, {"content-type": "application/xml"} def _r_GetPrevNext(self, urn, inv): """ Provisional route for GetPrevNext request :param urn: URN to filter the resource :param inv: Inventory Identifier :return: GetPrevNext response """ return self.retriever.getPrevNextUrn(inventory=inv, urn=urn).strip(), 200, {"content-type": "application/xml"} def _r_GetFirstUrn(self, urn, inv): """ Provisional route for GetFirstUrn request :param urn: URN to filter the resource :param inv: Inventory Identifier :return: GetFirstUrn response """ return self.retriever.getFirstUrn(inventory=inv, urn=urn).strip(), 200, {"content-type": "application/xml"} def _r_GetLabel(self, urn, inv): """ Provisional route for GetLabel request :param urn: URN to filter the resource :param inv: Inventory Identifier :return: GetLabel response """ return self.retriever.getLabel(inventory=inv, urn=urn).strip(), 200, {"content-type": "application/xml"}