def lookup( self, module_name: str, rel: LinkRelation, parameter_values: Dict[str, str], ) -> EndpointEntry: """Look up an endpoint definition Args: module_name: The name of the module where the endpoint has been defined in. rel: The rel of the endpoint. parameter_values: A simple mapping of parameter-names to values. Returns: An endpoint-struct. """ # Wen don't need to validate the matching of parameters after this as this expects all # parameters to be available to be supplied, else we never get the "endpoint_key". endpoint_key = (module_name, rel) parameter_key: ParameterKey = tuple(sorted(parameter_values.keys())) try: endpoint_entry = self._endpoints[endpoint_key] except KeyError: raise KeyError(f"Key {endpoint_key!r} not in {self._endpoints!r}") if parameter_key not in endpoint_entry: raise ValueError( f"Endpoint {endpoint_key} with parameters {parameter_key} not found. " f"The following parameter combinations are possible: " f"{list(endpoint_entry.keys())}") examples: Dict[str, OpenAPIParameter] = { key: { 'example': value } for key, value in parameter_values.items() } # Needs to fill out path templates! entry = endpoint_entry[parameter_key] entry['href'] = fill_out_path_template(entry['href'], examples) return entry
def fill_out_parameters(ctx: Dict[str, Any], val) -> str: """Fill out path parameters, either using the global parameter or the endpoint defined ones. This assumes the parameters to be defined as such: ctx['parameters']: Dict[str, Dict[str, str]] Args: ctx: A Jinja2 context val: The path template. Examples: This does a lot of Jinja2 setup. We may want to move this to a "proper" test-file. >>> parameters = {} >>> env = jinja2.Environment() >>> env.filters['fill_out_parameters'] = fill_out_parameters >>> host = {'name': 'host', 'in': 'path', 'example': 'example.com'} >>> service = {'name': 'service', 'in': 'path', 'example': 'CPU'} >>> tmpl_source = '{{ "/{host}/{service}" | fill_out_parameters }}' >>> tmpl = env.from_string(tmpl_source) >>> tmpl.render(path_params=[]) Traceback (most recent call last): ... ValueError: Parameter 'host' needed, but not supplied in {} >>> tmpl.render(path_params=[host, service]) '/example.com/CPU' Returns: A filled out string. """ return fill_out_path_template(val, to_param_dict(ctx['path_params']))
def _make_url( path: str, param_spec: Sequence[OpenAPIParameter], 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, OpenAPIParameter] = {} 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