Beispiel #1
0
def test_missing_files(capfd):
    # Default is logger=True => print warning
    yaml_file = load_yaml_file("blabla")
    assert yaml_file == {}

    # If logger=False => raise Exception
    try:
        yaml_file = load_yaml_file("blabla", logger=False)
    except AttributeError:
        pass
    else:
        pytest.fail("AttributeError not raised with missing yaml file")

    # Testing optional yaml file
    # Logger=False but skip_error=True -> do nothing
    yaml_file = load_yaml_file("xxyyzz", logger=False, skip_error=True)
    assert yaml_file == {}

    out, err = capfd.readouterr()
    out = out.split("\n")
    err = err.split("\n")

    prefix_msg = "WARNING Failed to read YAML file ["
    suffix_msg = "]: File does not exist"

    f1 = "%s%s%s" % (prefix_msg, "blabla", suffix_msg)
    f2 = "%s%s%s" % (prefix_msg, "xxyyzz", suffix_msg)
    assert f1 in err
    assert f2 not in err
Beispiel #2
0
    def get_models(self):
        """ Read models from base/custom yaml files """

        filename = SWAGGER_MODELS_FILE

        # BASE definitions
        path = helpers.script_abspath(__file__, SWAGGER_DIR)
        data = load_yaml_file(filename, path=path)

        # CUSTOM definitions
        path = helpers.current_dir(CUSTOM_PACKAGE, SWAGGER_DIR)
        override = load_yaml_file(filename, path=path, skip_error=True)

        return mix(data, override)
Beispiel #3
0
    def lookup(self, endpoint, apiclass_module, swagger_endpoint_dir, isbase):

        log.verbose("Found endpoint dir: '%s'" % endpoint)

        if os.path.exists(os.path.join(swagger_endpoint_dir, 'SKIP')):
            log.info("Skipping: %s", endpoint)
            return None

        # Find yaml files
        conf = None
        yaml_files = {}
        yaml_listing = os.path.join(swagger_endpoint_dir, "*.%s" % YAML_EXT)

        for file in glob.glob(yaml_listing):
            if file.endswith('specs.%s' % YAML_EXT):
                # load configuration and find file and class
                conf = load_yaml_file(file)
            else:
                # add file to be loaded from swagger extension
                p = re.compile(r'\/([^\.\/]+)\.' + YAML_EXT + '$')
                match = p.search(file)
                method = match.groups()[0]
                yaml_files[method] = file

        if len(yaml_files) < 1:
            raise Exception("%s: no methods defined in any YAML" % endpoint)
        if conf is None or 'class' not in conf:
            raise ValueError("No 'class' defined for '%s'" % endpoint)

        current = self.load_endpoint(endpoint, apiclass_module, conf, isbase)
        current.methods = yaml_files
        return current
Beispiel #4
0
    def check_configuration(self, config_file_name):

        self.services_configuration = load_yaml_file(
            file=config_file_name,
            path=os.path.join(helpers.script_abspath(__file__), '..', '..',
                              CORE_CONFIG_PATH),
            logger=True)

        for service in self.services_configuration:

            name, prefix = self.prefix_name(service)

            # Was this service enabled from the developer?
            enable_var = str(prefix + 'enable').upper()
            self.available_services[name] = self.get_bool_from_os(enable_var)

            if self.available_services[name]:

                # read variables
                variables = self.load_variables(service, enable_var, prefix)
                service['variables'] = variables

                # set auth service
                if name == self.authentication_name:
                    self.authentication_service = variables.get('service')

        # log.pp(self.services_configuration)

        if self.authentication_service is None:
            raise AttributeError("no service defined behind authentication")
        else:
            log.info("Authentication based on '%s' service" %
                     self.authentication_service)
Beispiel #5
0
def load_project_configuration(project_path):
    args = {
        'path': project_path,
        'skip_error': False,
        'logger': False,
        'file': PROJECT_CONF_FILENAME,
        'keep_order': True
    }
    return load_yaml_file(**args)
Beispiel #6
0
    def get_models(self):
        """ Read models from base/custom yaml files """

        filename = SWAGGER_MODELS_FILE

        # BASE definitions
        path = helpers.script_abspath(__file__, SWAGGER_DIR)
        data = load_yaml_file(filename, path=path)

        # CUSTOM definitions
        path = helpers.current_dir(CUSTOM_PACKAGE, SWAGGER_DIR)
        override = load_yaml_file(filename,
                                  path=path,
                                  skip_error=True,
                                  logger=False)
        # NOTE: with logger=False I skip the warning if this file doesn't exist

        return mix(data, override)
Beispiel #7
0
    def read_frameworks(self):

        file = os.path.join("config", "frameworks.yaml")
        self._frameworks = load_yaml_file(file)
Beispiel #8
0
    def read_my_swagger(self, file, method, endpoint):

        mapping = load_yaml_file(file)

        # content has to be a dictionary
        if not isinstance(mapping, dict):
            raise TypeError("Wrong method ")

        # read common
        commons = mapping.pop('common', {})

        # Check if there is at least one except for common
        if len(mapping) < 1:
            raise ValueError("No definition found inside: %s " % file)

        ################################
        # Using 'attrs': a way to save external attributes

        # Instance
        extra = ExtraAttributes()

        ################################
        # Specs should contain only labels written in spec before

        pattern = re.compile(r'\<([^\>]+)\>')

        for label, specs in mapping.items():

            if label not in endpoint.uris:
                raise KeyError(
                    "Invalid label '%s' found.\nAvailable labels: %s"
                    % (label, list(endpoint.uris.keys())))
            uri = endpoint.uris[label]

            ################################
            # add common elements to all specs
            for key, value in commons.items():
                if key not in specs:
                    specs[key] = value

            ################################
            # Separate external definitions

            # Find any custom part which is not swagger definition
            custom = specs.pop('custom', {})

            # Publish the specs on the final Swagger JSON
            # Default is to do it if not otherwise specified
            extra.publish = custom.get('publish', True)

            # Authentication
            if custom.get('authentication', False):

                # Add Bearer Token security to this method
                # This was already defined in swagger root
                specs['security'] = [{"Bearer": []}]

                # Automatically add the response for Unauthorized
                specs['responses'][hcodes.HTTP_BAD_UNAUTHORIZED] = {
                    'description': "Missing or invalid credentials or token"
                }

                # Recover required roles
                roles = custom.get('authorized', [])
                # roles = custom.get('authorized', ['normal_user'])

                # TODO: create a method inside 'auth' to check roles
                # for role in roles:
                #     pass

                # If everything is fine set the roles to be required by Flask
                extra.auth = roles
            else:
                extra.auth = None

            # Other things that could be saved into 'custom' subset?

            ###########################
            # Strip the uri of the parameter
            # and add it to 'parameters'
            newuri = uri[:]  # create a copy
            if 'parameters' not in specs:
                specs['parameters'] = []

            ###########################
            # Read Form Data Custom parameters
            cparam = specs.pop('custom_parameters', None)
            if cparam is not None:
                for fdp in cparam:

                    params = self._fdp.get(fdp)
                    if params is None:
                        log.critical_exit("No custom form data '%s'" % fdp)
                    else:
                        # Unable to extend with list by using extends() because
                        # it add references to the original object and do not
                        # create copies. Without copying, the same objects will
                        # be modified several times leading to errors
                        for p in params:
                            specs['parameters'].append(p.copy())

            ###########################
            # Read normal parameters
            for parameter in pattern.findall(uri):

                # create parameters
                x = parameter.split(':')
                xlen = len(x)
                paramtype = 'string'

                if xlen == 1:
                    paramname = x[0]
                elif xlen == 2:
                    paramtype = x[0]
                    paramname = x[1]

                # FIXME: complete for all types
                # http://swagger.io/specification/#data-types-12
                if paramtype == 'int':
                    paramtype = 'number'
                if paramtype == 'path':
                    paramtype = 'string'

                path_parameter = {
                    'name': paramname, 'type': paramtype,
                    'in': 'path', 'required': True
                }
                if paramname in endpoint.ids:
                    path_parameter['description'] = endpoint.ids[paramname]

                specs['parameters'].append(path_parameter)

                # replace in a new uri
                newuri = newuri.replace('<%s>' % parameter, '{%s}' % paramname)

            # cycle parameters and add them to the endpoint class
            query_params = []
            for param in specs['parameters']:

                if param["in"] != 'path':
                    if uri not in self._parameter_schemas:
                        self._parameter_schemas[uri] = {}

                    if method not in self._parameter_schemas[uri]:
                        self._parameter_schemas[uri][method] = []

                    self._parameter_schemas[uri][method].append(param.copy())

                extrainfo = param.pop('custom', {})

                if len(extrainfo) and endpoint.custom['schema']['expose']:

                    # TODO: read a 'custom.publish' in every yaml
                    # to decide if the /schema uri should be in swagger

                    if uri not in endpoint.custom['params']:
                        endpoint.custom['params'][uri] = {}
                    endpoint.custom['params'][uri][method] = extrainfo

                # enum [{key1: value1}, {key2: value2}] became enum [key1, ke2]
                enum = param.pop("enum", None)
                if enum is not None:
                    param["enum"] = []
                    for option in enum:
                        for k in option:
                            param["enum"].append(k)

                # handle parameters in URI for Flask
                if param['in'] == 'query':
                    query_params.append(param)

            if len(query_params) > 0:
                self.query_parameters(
                    endpoint.cls, method=method, uri=uri, params=query_params)

            # Swagger does not like empty arrays
            if len(specs['parameters']) < 1:
                specs.pop('parameters')

            ##################
            # Save definition for checking
            if uri not in self._original_paths:
                self._original_paths[uri] = {}
            self._original_paths[uri][method] = specs

            ##################
            # Skip what the developers does not want to be public in swagger
            # NOTE: do not skip if in testing mode
            if not extra.publish and not self._customizer._testing:
                continue

            # Handle global tags
            if 'tags' not in specs and len(endpoint.tags) > 0:
                specs['tags'] = []
            for tag in endpoint.tags:
                if tag not in specs['tags']:
                    specs['tags'].append(tag)

            ##################
            # NOTE: whatever is left inside 'specs' will be
            # passed later on to Swagger Validator...

            # Save definition for publishing
            if newuri not in self._paths:
                self._paths[newuri] = {}
            self._paths[newuri][method] = specs

            log.verbose("Built definition '%s:%s'" % (method.upper(), newuri))

        endpoint.custom['methods'][method] = extra
        return endpoint
Beispiel #9
0
def read(base_path, project_path=None, is_template=False, do_exit=True):
    """
    Read default configuration
    """

    project_configuration_files = [
        # DEFAULT
        {
            # 'path': SCRIPT_PATH,
            'path': base_path,
            'skip_error': False,
            'logger': False,
            'file': PROJECTS_DEFAULTS_FILE
        }
    ]

    if project_path is not None:
        project_configuration_files.append(
            # CUSTOM FROM THE USER
            {
                'path': project_path,
                'skip_error': False,
                'logger': False,
                'file': PROJECT_CONF_FILENAME
            }
        )

    confs = {}

    for args in project_configuration_files:
        try:
            args['keep_order'] = True
            f = args['file']
            confs[f] = load_yaml_file(**args)
            log.checked("Found '%s' rapydo configuration" % f)
        except AttributeError as e:
            if do_exit:
                log.exit(e)
            else:
                raise AttributeError(e)

    # Recover the two options
    base_configuration = confs.get(PROJECTS_DEFAULTS_FILE)
    if project_path is None:
        return base_configuration
    custom_configuration = confs.get(PROJECT_CONF_FILENAME, {})

    # Verify custom project configuration
    prj = custom_configuration.get('project')
    if prj is None:
        raise AttributeError("Missing project configuration")
    elif not is_template:

        # Check if these three variables were changed from the initial template
        checks = {
            'title': 'My project',
            'description': 'Title of my project',
            'name': 'rapydo'
        }
        for key, value in checks.items():
            if prj.get(key, '') == value:

                # get file name with the load file utility
                args = {}
                kwargs = project_configuration_files.pop()
                filepath = load_yaml_file(
                    *args, return_path=True, **kwargs)

                log.critical_exit(
                    "\n\nYour project is not yet configured:\n" +
                    "Please edit key '%s' in file %s" % (key, filepath)
                )

    # Mix default and custom configuration
    return mix(base_configuration, custom_configuration)