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
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)
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
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)
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)
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)
def read_frameworks(self): file = os.path.join("config", "frameworks.yaml") self._frameworks = load_yaml_file(file)
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
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)