def _verify_parameters( path: str, parameters: Sequence[OpenAPIParameter], ): """Verifies matching of parameters to the placeholders used in an URL-Template This works both ways, ensuring that no parameter is supplied which is then not used and that each template-variable in the URL-template has a corresponding parameter supplied, either globally or locally. Args: path: The URL-Template, for eample: '/user/{username}' parameters: A list of parameters. A parameter can either be a string referencing a globally defined parameter by name, or a dict containing a full parameter. Examples: In case of success, this function will return nothing. >>> _verify_parameters('/foo/{bar}', [{'name': 'bar', 'in': 'path'}]) Yet, when problems are found, ValueErrors are raised. >>> _verify_parameters('/foo', [{'name': 'foo', 'in': 'path'}]) Traceback (most recent call last): ... ValueError: Param 'foo', which is specified as 'path', not used in path. Found params: [] >>> _verify_parameters('/foo/{bar}', []) Traceback (most recent call last): ... ValueError: Param 'bar', which is used in the HTTP path, was not specified in [] Returns: Nothing. Raises: ValueError in case of a mismatch. """ param_names = _names_of(parameters) path_params = path_parameters(path) for path_param in path_params: if path_param not in param_names: raise ValueError( f"Param {path_param!r}, which is used in the HTTP path, was not specified in " f"{parameters!r}") for param in parameters: if isinstance( param, dict ) and param['in'] == 'path' and param['name'] not in path_params: raise ValueError( f"Param {repr(param['name'])}, which is specified as 'path', not used in path. " f"Found params: {path_params}") find_all_parameters(parameters, errors='raise')
def _param_key(_path, _parameters): # We key on _all_ required parameters, regardless their type. _param_names = set() for _param in _parameters: if isinstance(_param, dict) and _param.get('required', True): _param_names.add(_param['name']) for _param_name in path_parameters(_path): _param_names.add(_param_name) return tuple(sorted(_param_names))
def _param_key(_path, _parameters): # We key on _all_ required parameters, regardless their type. _param_names = set() for _param in _parameters: if ("schema" not in _param and isinstance(_param, dict) and _param.get("required", True)): _param_names.add(_param["name"]) for _param_name in path_parameters(_path): _param_names.add(_param_name) return tuple(sorted(_param_names))
def _verify_parameters( path: str, path_schema: Optional[Type[Schema]], ): """Verifies matching of parameters to the placeholders used in an URL-Template This works both ways, ensuring that no parameter is supplied which is then not used and that each template-variable in the URL-template has a corresponding parameter supplied, either globally or locally. Args: path: The URL-Template, for eample: '/user/{username}' path_schema: A marshmallow schema which is used for path parameter validation. Examples: In case of success, this function will return nothing. >>> from cmk.fields import String >>> class Params(Schema): ... bar = String() >>> _verify_parameters('/foo/{bar}', Params) >>> _verify_parameters('/foo', None) Yet, when problems are found, ValueErrors are raised. >>> _verify_parameters('/foo', Params) Traceback (most recent call last): ... ValueError: Params {'bar'} not used in path /foo. Found params: set() >>> _verify_parameters('/foo/{bar}', None) Traceback (most recent call last): ... ValueError: Params {'bar'} of path /foo/{bar} were not given in schema parameters set() Returns: Nothing. Raises: ValueError in case of a mismatch. """ if path_schema is None: schema_params = set() else: schema = path_schema() schema_params = set(schema.declared_fields.keys()) path_params = set(path_parameters(path)) missing_in_schema = path_params - schema_params missing_in_path = schema_params - path_params if missing_in_schema: raise ValueError( f"Params {missing_in_schema!r} of path {path} were not given in schema parameters " f"{schema_params!r}") if missing_in_path: raise ValueError( f"Params {missing_in_path!r} not used in path {path}. " f"Found params: {path_params!r}")
def _make_url( path: str, param_spec: Sequence[PrimitiveParameter], param_val: Dict[str, str], ) -> str: """Make a concrete URL according to parameter specs and value-mappings. Examples: For use in path >>> _make_url('/foo/{host}', [{'name': 'host', 'in': 'path'}], {'host': 'example.com'}) '/foo/example.com' Or in query-string: >>> _make_url('/foo', [{'name': 'host', 'in': 'query'}], {'host': 'example.com'}) '/foo?host=example.com' >>> _make_url('/foo', [{'name': 'host', 'in': 'query', 'required': False}], ... {'host': 'example.com'}) '/foo?host=example.com' >>> _make_url('/foo', [{'name': 'host', 'in': 'query', 'required': False}], {}) '/foo' Some edge-cases which are caught. >>> _make_url('/foo', [{'name': 'host', 'in': 'path'}], {}) Traceback (most recent call last): ... ValueError: No parameter mapping for required parameter 'host'. >>> _make_url('/foo', [{'name': 'host', 'in': 'path'}], {'host': 'example.com'}) Traceback (most recent call last): ... ValueError: Parameter 'host' (required path-parameter), not found in path '/foo' >>> import pytest >>> # This exceptions gets thrown by another function, so we don't care about the wording. >>> with pytest.raises(ValueError): ... _make_url('/foo/{host}', [], {'host': 'example.com'}) Args: path: The path. May have "{variable_name}" template parts in the path-name or not. param_spec: A list of parameters. param_val: A mapping of parameter-names to their desired values. Used to fill out the templates. Returns: The formatted path, query-string appended if applicable. """ path_params: Dict[str, PrimitiveParameter] = {} qs = [] for p in param_spec: param_name = p['name'] if param_name not in param_val: if p.get('required', True): raise ValueError(f"No parameter mapping for required parameter {param_name!r}.") # We skip optional parameters, when we don't have values for them. continue param_value = param_val[param_name] if p['in'] == 'query': qs.append(f"{param_name}={param_value}") elif p['in'] == 'path': if param_name not in path_parameters(path): raise ValueError(f"Parameter {param_name!r} (required path-parameter), " f"not found in path {path!r}") path_params[param_name] = {'example': param_value} query_string = '&'.join(qs) rv = fill_out_path_template(path, path_params) if query_string: rv += f"?{query_string}" return rv