示例#1
0
    def test_meta(self) -> None:

        # This is a valid package containing other packages... but no task will be found
        tasks = Meta.get_celery_tasks("restapi.utilities")
        assert isinstance(tasks, list)
        assert len(tasks) == 0

        tasks = Meta.get_celery_tasks("this-should-not-exist")
        assert isinstance(tasks, list)
        assert len(tasks) == 0

        mcls = Meta.get_classes_from_module(
            "this-should-not-exist")  # type: ignore
        assert isinstance(mcls, dict)
        assert len(mcls) == 0

        assert not Meta.get_module_from_string("this-should-not-exist")

        try:
            Meta.get_module_from_string(
                "this-should-not-exist",
                exit_on_fail=True,
            )
            pytest.fail("ModuleNotFoundError not raised")  # pragma: no cover
        except ModuleNotFoundError:
            pass

        # This method is not very robust... but... let's test the current implementation
        # It basicaly return the first args if it is an instance of some classes
        assert not Meta.get_self_reference_from_args()
        selfref = Meta.get_self_reference_from_args("test")
        assert selfref == "test"

        models = Meta.import_models("this-should",
                                    "not-exist",
                                    mandatory=False)
        assert isinstance(models, dict)
        assert len(models) == 0

        try:
            Meta.import_models("this-should", "not-exist", mandatory=True)
            pytest.fail("SystemExit not raised")  # pragma: no cover
        except SystemExit:
            pass

        # Check exit_on_fail default value
        models = Meta.import_models("this-should", "not-exist")
        assert isinstance(models, dict)
        assert len(models) == 0

        assert Meta.get_instance("invalid.path", "InvalidClass") is None
        assert Meta.get_instance("customization", "InvalidClass") is None
        assert Meta.get_instance("customization", "Customizer") is not None
示例#2
0
    def project_initialization(self, instances, app=None):
        """ Custom initialization of your project

        Please define your class Initializer in
        project/YOURPROJECT/backend/initialization/initialization.py
        """

        try:
            # NOTE: this might be a pattern
            # see in meta.py:get_customizer_class
            module_path = "{}.{}.{}".format(
                CUSTOM_PACKAGE,
                'initialization',
                'initialization',
            )
            module = Meta.get_module_from_string(module_path)
            meta = Meta()
            Initializer = meta.get_class_from_string(
                'Initializer', module, skip_error=True
            )
            if Initializer is None:
                log.debug("No custom init available")
            else:
                try:
                    Initializer(instances, app=app)
                except BaseException as e:
                    log.error("Errors during custom initialization: {}", e)
                else:
                    log.info("Vanilla project has been initialized")

        except BaseException:
            log.debug("No custom init available")
示例#3
0
    def extract_endpoints(self, base_dir: Path) -> List[Type[EndpointResource]]:

        endpoints_classes: List[Type[EndpointResource]] = []
        # get last item of the path
        # normpath is required to strip final / if any
        base_module = base_dir.name

        apis_dir = base_dir.joinpath("endpoints")
        apiclass_module = f"{base_module}.endpoints"
        for epfile in apis_dir.glob("*.py"):

            # get module name (es: endpoints.filename)

            module_name = f"{apiclass_module}.{epfile.stem}"
            # Convert module name into a module
            log.debug("Importing {}", module_name)
            module = Meta.get_module_from_string(
                module_name,
                exit_on_fail=True,
            )

            # Extract classes from the module
            # module can't be none because of exit_on_fail=True...
            # but my-py can't understand this
            classes = Meta.get_new_classes_from_module(module)  # type: ignore
            for class_name, epclss in classes.items():
                # Filtering out classes without expected data
                if (
                    not hasattr(epclss, "methods") or epclss.methods is None
                ):  # pragma: no cover
                    continue

                log.debug("Importing {} from {}", class_name, module_name)

                skip, dependency = self.skip_endpoint(epclss.depends_on)

                if skip:
                    log.debug(
                        "Skipping '{} {}' due to unmet dependency: {}",
                        module_name,
                        class_name,
                        dependency,
                    )
                    continue

                endpoints_classes.append(epclss)

        return endpoints_classes
示例#4
0
    def custom_post_handle_user_input(self, user_node, input_data):
        module_path = "{}.initialization.initialization".format(CUSTOM_PACKAGE)
        module = Meta.get_module_from_string(module_path)

        meta = Meta()
        Customizer = meta.get_class_from_string('Customizer',
                                                module,
                                                skip_error=True)
        if Customizer is None:
            log.debug("No user properties customizer available")
        else:
            try:
                Customizer().custom_post_handle_user_input(
                    self, user_node, input_data)
            except BaseException as e:
                log.error("Unable to customize user properties: {}", e)
示例#5
0
    def load_class_from_module(self, classname, service=None):

        if service is None:
            flaskext = ''
        else:
            flaskext = '.' + service.get('extension')

        # Try inside our extensions
        module = Meta.get_module_from_string(
            modulestring=BACKEND_PACKAGE + '.flask_ext' + flaskext,
            exit_on_fail=True
        )
        if module is None:
            log.exit("Missing {} for {}", flaskext, service)

        return getattr(module, classname)
示例#6
0
    def extract_endpoints(self, base_dir):

        endpoints_classes = []
        # get last item of the path
        # normpath is required to strip final / if any
        base_module = os.path.basename(os.path.normpath(base_dir))

        apis_dir = os.path.join(base_dir, "endpoints")
        apiclass_module = f"{base_module}.endpoints"
        for epfiles in glob.glob(f"{apis_dir}/*.py"):

            # get module name (es: endpoints.filename)
            module_file = os.path.basename(os.path.splitext(epfiles)[0])
            module_name = f"{apiclass_module}.{module_file}"
            # Convert module name into a module
            log.debug("Importing {}", module_name)
            module = Meta.get_module_from_string(
                module_name,
                exit_on_fail=True,
            )

            # Extract classes from the module
            # module can't be none because of exit_on_fail=True...
            # but my-py can't understand this
            classes = Meta.get_new_classes_from_module(module)  # type: ignore
            for class_name, epclss in classes.items():
                # Filtering out classes without expected data
                if not hasattr(epclss, "methods") or epclss.methods is None:
                    continue

                log.debug("Importing {} from {}.{}", class_name, apis_dir,
                          module_file)

                skip, dependency = self.skip_endpoint(epclss.depends_on)

                if skip:
                    log.debug(
                        "Skipping '{} {}' due to unmet dependency: {}",
                        module_name,
                        class_name,
                        dependency,
                    )
                    continue

                endpoints_classes.append(epclss)

        return endpoints_classes
示例#7
0
    def load_commands(self):
        Meta.get_module_from_string("restapi.services.bot")
        if EXTENDED_PACKAGE != EXTENDED_PROJECT_DISABLED:
            Meta.get_module_from_string(f"{EXTENDED_PACKAGE}.bot")

        Meta.get_module_from_string(f"{CUSTOM_PACKAGE}.bot")

        # Handle the rest as normal messages
        # NOTE: this has to be the last handler to be attached
        self.updater.dispatcher.add_handler(
            MessageHandler(Filters.text, self.invalid_message))
示例#8
0
    def custom_user_properties(self, userdata):
        module_path = "{}.initialization.initialization".format(CUSTOM_PACKAGE)
        module = Meta.get_module_from_string(module_path)

        meta = Meta()
        Customizer = meta.get_class_from_string('Customizer',
                                                module,
                                                skip_error=True)
        if Customizer is None:
            log.debug("No user properties customizer available")
        else:
            try:
                userdata = Customizer().custom_user_properties(userdata)
            except BaseException as e:
                log.error("Unable to customize user properties: {}", e)

        if "email" in userdata:
            userdata["email"] = userdata["email"].lower()

        return userdata
示例#9
0
    def do_schema(self):
        """ Schemas exposing, if requested """

        name = '{}.rest.schema'.format(BACKEND_PACKAGE)
        module = Meta.get_module_from_string(name,
                                             exit_if_not_found=True,
                                             exit_on_fail=True)
        schema_class = getattr(module, 'RecoverSchema')

        self._schema_endpoint = EndpointElements(
            cls=schema_class,
            exists=True,
            custom={
                'methods': {
                    'get': ExtraAttributes(auth=None),
                    # WHY DOES POST REQUEST AUTHENTICATION
                    # 'post': ExtraAttributes(auth=None)
                }
            },
            methods={},
        )
示例#10
0
 def get_module(connector: str, module: str) -> Optional[ModuleType]:
     return Meta.get_module_from_string(
         ".".join((module, CONNECTORS_FOLDER, connector))
     )
示例#11
0
    def find_endpoints(self):

        ##################
        # Walk folders looking for endpoints

        endpoints_folders = []
        # base swagger dir (rapydo/http-ap)
        endpoints_folders.append({'path': ABS_RESTAPI_PATH, 'iscore': True})

        # swagger dir from extended project, if any
        if self._extended_project is not None:

            endpoints_folders.append({
                'path':
                os.path.join(os.curdir, self._extended_project),
                'iscore':
                False
            })

        # custom swagger dir
        endpoints_folders.append({
            'path':
            os.path.join(os.curdir, CUSTOM_PACKAGE),
            'iscore':
            False
        })

        # already_loaded = {}
        for folder in endpoints_folders:

            base_dir = folder.get('path')
            iscore = folder.get('iscore')
            # get last item of the path
            # normapath is required to strip final / is any
            base_module = os.path.basename(os.path.normpath(base_dir))

            if iscore:
                apis_dir = os.path.join(base_dir, 'resources')
                apiclass_module = '{}.resources'.format(base_module)
            else:
                apis_dir = os.path.join(base_dir, 'apis')
                apiclass_module = '{}.apis'.format(base_module)

            # Looking for all file in apis folder
            for epfiles in os.listdir(apis_dir):

                # get module name (es: apis.filename)
                module_file = os.path.splitext(epfiles)[0]
                module_name = "{}.{}".format(apiclass_module, module_file)
                # Convert module name into a module
                log.debug("Importing {}", module_name)
                try:
                    module = Meta.get_module_from_string(
                        module_name, exit_on_fail=True, exit_if_not_found=True)
                except BaseException as e:
                    log.exit("Cannot import {}\nError: {}", module_name, e)

                # Extract classes from the module
                # classes = meta.get_classes_from_module(module)
                classes = meta.get_new_classes_from_module(module)
                for class_name in classes:
                    ep_class = classes.get(class_name)
                    # Filtering out classes without required data
                    if not hasattr(ep_class, "methods"):
                        continue
                    if ep_class.methods is None:
                        continue

                    # if class_name in already_loaded:
                    #     log.warning(
                    #         "Skipping import of {} from {}.{}, already loded from {}",
                    #         class_name,
                    #         apis_dir,
                    #         module_file,
                    #         already_loaded[class_name],
                    #     )
                    #     continue
                    # already_loaded[class_name] = "{}.{}".format(apis_dir, module_file)
                    log.debug("Importing {} from {}.{}", class_name, apis_dir,
                              module_file)
                    if not self._testing:
                        skip = False
                        for var in ep_class.depends_on:
                            pieces = var.strip().split(' ')
                            pieces_num = len(pieces)
                            if pieces_num == 1:
                                dependency = pieces.pop()
                                negate = False
                            elif pieces_num == 2:
                                negate, dependency = pieces
                                negate = negate.lower() == 'not'
                            else:
                                log.exit('Wrong parameter: {}', var)

                            check = detector.get_bool_from_os(dependency)
                            if negate:
                                check = not check

                            # Skip if not meeting the requirements of the dependency
                            if not check:
                                skip = True
                                break

                        if skip:
                            log.debug(
                                "Skipping '{} {}' due to unmet dependency: {}",
                                module_name, class_name, dependency)
                            continue

                    # Building endpoint
                    endpoint = EndpointElements(custom={})

                    endpoint.cls = ep_class
                    endpoint.exists = True
                    endpoint.iscore = iscore

                    # Global tags to be applied to all methods
                    endpoint.tags = ep_class.labels

                    # base URI
                    base = ep_class.baseuri
                    if base not in BASE_URLS:
                        log.warning("Invalid base {}", base)
                        base = API_URL
                    base = base.strip('/')
                    endpoint.base_uri = base

                    endpoint.uris = {}  # attrs python lib bug?
                    endpoint.custom['schema'] = {
                        'expose': ep_class.expose_schema,
                        'publish': {},
                    }

                    endpoint.methods = {}

                    mapping_lists = []
                    for m in ep_class.methods:
                        method_name = "_{}".format(m)
                        if not hasattr(ep_class, method_name):

                            method_name = m
                            if not hasattr(ep_class, method_name):
                                log.warning("{} configuration not found in {}",
                                            m, class_name)
                                continue
                            # Enable this warning to start conversions GET -> _GET
                            # Find other warning like this by searching:
                            # **FASTAPI**
                            # else:
                            #     log.warning(
                            #         "Obsolete dict {} in {}", m, class_name
                            #     )

                        conf = getattr(ep_class, method_name)
                        kk = conf.keys()
                        mapping_lists.extend(kk)
                        endpoint.methods[m.lower()] = copy.deepcopy(conf)

                    if endpoint.custom['schema']['expose']:
                        for uri in mapping_lists:
                            total_uri = '/{}{}'.format(endpoint.base_uri, uri)
                            schema_uri = '{}/schemas{}'.format(API_URL, uri)

                            p = hex(id(endpoint.cls))
                            self._schema_endpoint.uris[uri + p] = schema_uri

                            self._schemas_map[schema_uri] = total_uri

                    self._endpoints.append(endpoint)