def test_filter_api_methods(server, api_methods_fixture, feature_manager): post = UrlMethod(properties=MethodProperties.methods["post_method"][0], slice=None, method_name="post_method", handler=None) methods = { "POST": post, "GET": UrlMethod( properties=MethodProperties.methods["get_method"][0], slice=None, method_name="get_method", handler=None ), } openapi = OpenApiConverter(server._transport.get_global_url_map(server.get_slices().values()), feature_manager) api_methods = openapi._filter_api_methods(methods) assert len(api_methods) == 1
def get_op_mapping(self) -> Dict[str, Dict[str, UrlMethod]]: """ Build a mapping between urls, ops and methods """ url_map: Dict[str, Dict[str, UrlMethod]] = defaultdict(dict) # Loop over all methods in this class that have a handler annotation. The handler annotation refers to a method # definition. This method definition defines how the handler is invoked. for method, handler_list in self._get_endpoint_metadata().items(): for method_handlers in handler_list: # Go over all method annotation on the method associated with the handler for properties in common.MethodProperties.methods[method]: url = properties.get_listen_url() # Associate the method with the handler if: # - the handler does not specific a method version # - the handler specifies a method version and the method version matches the method properties if method_handlers[1].__api_version__ is None or ( method_handlers[1].__api_version__ is not None and properties.api_version == method_handlers[1].__api_version__): # there can only be one if url in url_map and properties.operation in url_map[ url]: raise Exception( f"A handler is already registered for {properties.operation} {url}. " ) url_map[url][properties.operation] = UrlMethod( properties, self, method_handlers[1], method_handlers[0]) return url_map
def test_get_operation_no_docstring(api_methods_fixture): """ Test whether an OpenAPI operation is constructed correctly for a GET method which doesn't have a docstring. """ get = UrlMethod( properties=MethodProperties.methods["dummy_get_with_parameters_no_docstring"][0], slice=None, method_name="dummy_get_with_parameters_no_docstring", handler=None, ) operation_handler = OperationHandler(OpenApiTypeConverter(), ArgOptionHandler(OpenApiTypeConverter())) operation = operation_handler.handle_method(get, "/operation/{id}") # Asserts on request body assert operation.requestBody is None # Asserts on parameters assert operation.summary is None assert operation.description is None assert sorted(["header-val", "non_header", "param", "id"]) == sorted([parameter.name for parameter in operation.parameters]) param_map = {param.name: param.description for param in operation.parameters} assert len(param_map) == 4 assert param_map["header-val"] is None assert param_map["non_header"] is None assert param_map["param"] is None assert param_map["id"] is None # Asserts on response assert len(operation.responses) == 1 assert ["header-val"] == list(operation.responses["200"].headers.keys()) assert operation.responses["200"].description == ""
def test_post_operation_no_docstring(api_methods_fixture): """ Test whether an OpenAPI operation is constructed correctly for a POST method which doesn't have a docstring. """ post = UrlMethod( properties=MethodProperties.methods["dummy_post_with_parameters_no_docstring"][0], slice=None, method_name="dummy_post_with_parameters_no_docstring", handler=None, ) operation_handler = OperationHandler(OpenApiTypeConverter(), ArgOptionHandler(OpenApiTypeConverter())) operation = operation_handler.handle_method(post, "/operation/{id}") # Asserts on request body expected_params = ["param", "non_header"] actual_params = list(operation.requestBody.content["application/json"].schema_.properties.keys()) assert sorted(expected_params) == sorted(actual_params) assert operation.requestBody.description == "* **non_header:**\n* **param:**\n" # Asserts on parameters assert operation.summary is None assert operation.description is None assert sorted(["header-val", "id"]) == sorted([parameter.name for parameter in operation.parameters]) param_map = {param.name: param.description for param in operation.parameters} assert len(param_map) == 2 assert param_map["header-val"] is None assert param_map["id"] is None # Asserts on response assert len(operation.responses) == 1 assert ["header-val"] == list(operation.responses["200"].headers.keys()) assert operation.responses["200"].description == ""
def test_post_operation(api_methods_fixture): """ Test whether an OpenAPI operation is constructed correctly for a POST method which is fully annotated and documented. """ short_description = "This is a brief description." long_description = "This is a more in depth description of the method." header_description = "A header value." non_header_description = "Non header value via arg_options." param_description = "A parameter." id_description = "The id of the resource." return_value_description = "A return value." raises_os_error_description = "Something went wrong" raises_not_found_description = "Resource was not found." raises_dummy_exception_description = "A dummy exception" method_name = "dummy_post_with_parameters" post = UrlMethod( properties=MethodProperties.methods["dummy_post_with_parameters"][0], slice=None, method_name=method_name, handler=None, ) operation_handler = OperationHandler( OpenApiTypeConverter(), ArgOptionHandler(OpenApiTypeConverter())) operation = operation_handler.handle_method(post, "/operation/{id}") # Asserts on request body expected_params = ["param", "non_header"] actual_params = list(operation.requestBody.content["application/json"]. schema_.properties.keys()) assert sorted(expected_params) == sorted(actual_params) assert ( f"* **non_header:** {non_header_description}\n* **param:** {param_description}\n" == operation.requestBody.description) # Asserts on parameters assert operation.summary == short_description assert operation.description == long_description assert operation.operationId == method_name assert sorted(["header-val", "id"]) == sorted( [parameter.name for parameter in operation.parameters]) param_map = { param.name: param.description for param in operation.parameters } assert len(param_map) == 2 assert param_map["header-val"] == header_description assert param_map["id"] == id_description # Asserts on response assert len(operation.responses) == 4 assert operation.responses["200"].description == return_value_description assert operation.responses[ "404"].description == raises_not_found_description assert operation.responses[ "405"].description == raises_dummy_exception_description assert operation.responses[ "500"].description == raises_os_error_description
def test_get_operation_partial_documentation(api_methods_fixture): """ Test whether an OpenAPI operation is constructed correctly for a GET method which has missing entries in its docstring. """ short_description = "This is a brief description." tid_description = "The inmanta environment id." param_description = "A parameter." id_description = "The id of the resource." get = UrlMethod( properties=MethodProperties. methods["dummy_get_with_parameters_partial_documentation"][0], slice=None, method_name="dummy_get_with_parameters_partial_documentation", handler=None, ) operation_handler = OperationHandler( OpenApiTypeConverter(), ArgOptionHandler(OpenApiTypeConverter())) operation = operation_handler.handle_method( get, "/operation/{id_doc}/{id_no_doc}") # Asserts on request body assert operation.requestBody is None # Asserts on parameters assert operation.summary == short_description assert operation.description is None expected_parameters = [ "header-doc", "header-no-doc", "param_doc", "param_no_doc", "id_doc", "id_no_doc" ] actual_parameters = [parameter.name for parameter in operation.parameters] assert sorted(expected_parameters) == sorted(actual_parameters) param_map = { param.name: param.description for param in operation.parameters } assert len(param_map) == 6 assert param_map["header-doc"] == tid_description assert param_map["header-no-doc"] is None assert param_map["param_doc"] == param_description assert param_map["param_no_doc"] is None assert param_map["id_doc"] == id_description assert param_map["id_no_doc"] is None # Asserts on response assert len(operation.responses) == 1 assert sorted(["header-doc", "header-no-doc"]) == sorted( list(operation.responses["200"].headers.keys())) assert operation.responses["200"].description == ""
def test_get_function_parameters(api_methods_fixture): url_method = UrlMethod( properties=MethodProperties.methods["dummy_get_with_parameters"][0], slice=None, method_name="dummy_get_with_parameters", handler=None, ) function_parameter_handler = FunctionParameterHandler( OpenApiTypeConverter(), ArgOptionHandler(OpenApiTypeConverter()), "/basepath", url_method.properties ) function_parameters = function_parameter_handler.all_params_dct assert len(function_parameters) == 4 assert function_parameters["param"] == inspect.Parameter("param", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=int)
def handle_method(self, url_method: UrlMethod, path: str) -> Operation: function_parameter_handler = FunctionParameterHandler( self.type_converter, self.arg_option_handler, path, url_method.properties ) parameters = function_parameter_handler.get_parameters() responses = self._build_responses(url_method.properties) if url_method.get_operation() in ["POST", "PUT", "PATCH"]: extra_params = {"requestBody": function_parameter_handler.convert_request_body()} else: extra_params = {} tags = self._get_tags_of_operation(url_method) return Operation( responses=responses, operationId=url_method.method_name, parameters=(parameters if len(parameters) else None), summary=url_method.short_method_description, description=url_method.long_method_description, tags=tags, **extra_params, )