def test_app_with_resolver(simple_api_spec_dir, spec): from connexion.resolver import Resolver resolver = Resolver() app = App(__name__, port=5001, specification_dir='..' / simple_api_spec_dir.relative_to(TEST_FOLDER), resolver=resolver) api = app.add_api(spec) assert api.resolver is resolver
def test_resolve_invalid_reference(api): with pytest.raises(InvalidSpecification) as exc_info: Operation(api=api, method='GET', path='endpoint', path_parameters=[], operation=OPERATION5, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions={}, definitions={}, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) exception = exc_info.value # type: InvalidSpecification assert exception.reason == "GET endpoint '$ref' needs to start with '#/'"
def test_parameter_reference(): operation = Operation(method='GET', path='endpoint', operation=OPERATION4, app_produces=['application/json'], app_security=[], security_definitions={}, definitions={}, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert operation.parameters == [{'in': 'path', 'type': 'integer'}]
def resolve_operation_id(self, operation): spec = operation.operation x_domain_module = spec.get('x-swagger-domain-module', '') x_subdomain_module = spec.get('x-swagger-subdomain-module', '') x_router_controller = spec.get('x-swagger-router-controller', '') modules = [module for module in (x_domain_module, x_subdomain_module, x_router_controller) if module] if modules: spec['x-swagger-router-controller'] = '.'.join(modules) return Resolver.resolve_operation_id(self, operation)
def test_standard_resolve_x_router_controller(): operation = OpenAPIOperation(api=None, method='GET', path='endpoint', path_parameters=[], operation={ 'x-openapi-router-controller': 'fakeapi.hello', 'operationId': 'post_greeting', }, app_security=[], components=COMPONENTS, resolver=Resolver()) assert operation.operation_id == 'fakeapi.hello.post_greeting'
def test_standard_resolve_x_router_controller(): operation = Operation(method='GET', path='endpoint', operation={ 'x-swagger-router-controller': 'fakeapi.hello', 'operationId': 'post_greeting', }, app_produces=['application/json'], app_security=[], security_definitions={}, definitions={}, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert operation.operation_id == 'fakeapi.hello.post_greeting'
def test_default(api): op_spec = make_operation(OPERATION4) op_spec['parameters'][1]['default'] = 1 Swagger2Operation( api=api, method='GET', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions={}, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver() ) op_spec = make_operation(OPERATION6, parameters=False) op_spec['parameters'][0]['default'] = { 'keep_stacks': 1, 'image_version': 'one', 'senza_yaml': 'senza.yaml', 'new_traffic': 100 } Swagger2Operation( api=api, method='POST', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions={}, definitions=DEFINITIONS, parameter_definitions={}, resolver=Resolver() )
def test_parameter_reference(api): op_spec = make_operation(OPERATION3, definitions=False) operation = Swagger2Operation(api=api, method='GET', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions={}, definitions={}, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert operation.parameters == [{'in': 'path', 'type': 'integer'}]
def test_get_path_parameter_types(api): op_spec = make_operation(OPERATION1, parameters=False) op_spec['parameters'] = [ {'in': 'path', 'type': 'int', 'name': 'int_path'}, {'in': 'path', 'type': 'string', 'name': 'string_path'}, {'in': 'path', 'type': 'string', 'format': 'path', 'name': 'path_path'} ] operation = Swagger2Operation( api=api, method='GET', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], definitions=DEFINITIONS, resolver=Resolver() ) assert {'int_path': 'int', 'string_path': 'string', 'path_path': 'path'} == operation.get_path_parameter_types()
def main(argv=sys.argv[1:]): parser = argparse.ArgumentParser(description='Workflow Execution Service') parser.add_argument("--backend", type=str, default="wes_service.cwl_runner") parser.add_argument("--port", type=int, default=8080) parser.add_argument("--opt", type=str, action="append") args = parser.parse_args(argv) app = connexion.App(__name__) backend = utils.get_function_from_name(args.backend + ".create_backend")(args.opt) def rs(x): return getattr(backend, x) res = resource_stream(__name__, 'swagger/proto/workflow_execution_service.swagger.json') app.add_api(json.load(res), resolver=Resolver(rs)) app.run(port=args.port)
def test_invalid_reference(): with pytest.raises(InvalidSpecification) as exc_info: # type: py.code.ExceptionInfo operation = Operation(method='GET', path='endpoint', operation=OPERATION3, app_produces=['application/json'], app_security=[], security_definitions={}, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) schema = operation.body_schema exception = exc_info.value assert str(exception) == "<InvalidSpecification: GET endpoint '$ref' needs to point to definitions or parameters>" assert repr(exception) == "<InvalidSpecification: GET endpoint '$ref' needs to point to definitions or parameters>"
def test_multi_body(): with pytest.raises(InvalidSpecification) as exc_info: # type: py.code.ExceptionInfo operation = Operation(method='GET', path='endpoint', operation=OPERATION2, app_produces=['application/json'], app_security=[], security_definitions={}, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) schema = operation.body_schema exception = exc_info.value assert str(exception) == "<InvalidSpecification: GET endpoint There can be one 'body' parameter at most>" assert repr(exception) == "<InvalidSpecification: GET endpoint There can be one 'body' parameter at most>"
def test_non_existent_reference(): with pytest.raises(InvalidSpecification) as exc_info: # type: py.code.ExceptionInfo operation = Operation(method='GET', path='endpoint', operation=OPERATION1, app_produces=['application/json'], app_security=[], security_definitions={}, definitions={}, parameter_definitions={}, resolver=Resolver()) schema = operation.body_schema exception = exc_info.value assert str(exception) == "<InvalidSpecification: GET endpoint Definition 'new_stack' not found>" assert repr(exception) == "<InvalidSpecification: GET endpoint Definition 'new_stack' not found>"
def add_api(self, swagger_file, base_path=None, arguments=None, auth_all_paths=None, swagger_ui=None, swagger_path=None, swagger_url=None, validate_responses=False, resolver=Resolver()): """ Adds an API to the application based on a swagger file :param swagger_file: swagger file with the specification :type swagger_file: pathlib.Path :param base_path: base path where to add this api :type base_path: str | None :param arguments: api version specific arguments to replace on the specification :type arguments: dict | None :param auth_all_paths: whether to authenticate not defined paths :type auth_all_paths: bool :param swagger_ui: whether to include swagger ui or not :type swagger_ui: bool :param swagger_path: path to swagger-ui directory :type swagger_path: string | None :param swagger_url: URL to access swagger-ui documentation :type swagger_url: string | None :param validate_responses: True enables validation. Validation errors generate HTTP 500 responses. :type validate_responses: bool :param resolver: Operation resolver. :type resolver: Resolver | types.FunctionType :rtype: Api """ resolver = Resolver(resolver) if hasattr(resolver, '__call__') else resolver swagger_ui = swagger_ui if swagger_ui is not None else self.swagger_ui swagger_path = swagger_path if swagger_path is not None else self.swagger_path swagger_url = swagger_url if swagger_url is not None else self.swagger_url auth_all_paths = auth_all_paths if auth_all_paths is not None else self.auth_all_paths logger.debug('Adding API: %s', swagger_file) # TODO test if base_url starts with an / (if not none) arguments = arguments or dict() arguments = dict(self.arguments, **arguments) # copy global arguments and update with api specfic yaml_path = self.specification_dir / swagger_file api = Api(swagger_yaml_path=yaml_path, base_url=base_path, arguments=arguments, swagger_ui=swagger_ui, swagger_path=swagger_path, swagger_url=swagger_url, resolver=resolver, validate_responses=validate_responses, auth_all_paths=auth_all_paths, debug=self.debug) self.app.register_blueprint(api.blueprint) return api
def test_no_token_info(): operation = Operation(method='GET', path='endpoint', operation=OPERATION1, app_produces=['application/json'], app_security=SECURITY_DEFINITIONS_WO_INFO, security_definitions=SECURITY_DEFINITIONS_WO_INFO, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert isinstance(operation.function, types.FunctionType) assert operation._Operation__security_decorator is security_passthrough assert operation.method == 'GET' assert operation.produces == ['application/json'] assert operation.security == [{'oauth': ['uid']}] assert operation.body_schema == DEFINITIONS['new_stack']
def run_api_server(args): if args.replay is not None: logging.info( f"Will attempt to resend all not delivered reports every {args.replay} seconds" ) replay_thread = get_replay_thread(args.replay) replay_thread.start() app = connexion.FlaskApp(__name__, host=args.host, port=args.port, specification_dir="openapi", server="tornado") backend = CWLApiBackend( simulated_reports_location=args.simulation ) # when simulated_reports_location points to the valid file the simulation mode will be enabled app.add_api(specification="swagger_configuration.yaml", resolver=Resolver(lambda x: getattr(backend, x))) app.run()
def test_multiple_security_schemes_and(api): """Tests an operation with multiple security schemes in AND fashion.""" def return_api_key_name(func, in_, name): return name verify_api_key = mock.MagicMock(side_effect=return_api_key_name) api.security_handler_factory.verify_api_key = verify_api_key verify_multiple = mock.MagicMock(return_value='verify_multiple_result') api.security_handler_factory.verify_multiple_schemes = verify_multiple op_spec = make_operation(OPERATION9) operation = Swagger2Operation( api=api, method='GET', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], app_security=SECURITY_DEFINITIONS_2_KEYS, security_definitions=SECURITY_DEFINITIONS_2_KEYS, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert isinstance(operation.function, types.FunctionType) assert verify_api_key.call_count == 2 verify_api_key.assert_any_call(math.ceil, 'header', 'X-Auth-1') verify_api_key.assert_any_call(math.ceil, 'header', 'X-Auth-2') # Assert verify_multiple_schemes is called with mapping from scheme name # to result of security_handler_factory.verify_api_key() verify_multiple.assert_called_with({ 'key1': 'X-Auth-1', 'key2': 'X-Auth-2' }) security_decorator = operation.security_decorator assert len(security_decorator.args[0]) == 1 assert security_decorator.args[0][0] == 'verify_multiple_result' assert security_decorator.args[1] is None assert operation.method == 'GET' assert operation.produces == ['application/json'] assert operation.consumes == ['application/json'] assert operation.security == [{'key1': [], 'key2': []}]
def test_invalid_reference(api): with pytest.raises(InvalidSpecification) as exc_info: # type: py.code.ExceptionInfo operation = Operation(api=api, method='GET', path='endpoint', path_parameters=[], operation=OPERATION3, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions={}, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) operation.body_schema exception = exc_info.value assert str(exception).startswith("<InvalidSpecification: GET endpoint $ref") assert repr(exception).startswith("<InvalidSpecification: GET endpoint $ref")
def test_multi_body(api): with pytest.raises(InvalidSpecification) as exc_info: # type: py.code.ExceptionInfo op_spec = make_operation(OPERATION2) operation = Swagger2Operation(api=api, method='GET', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions={}, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) operation.body_schema exception = exc_info.value assert str(exception) == "GET endpoint There can be one 'body' parameter at most" assert repr(exception) == """<InvalidSpecification: "GET endpoint There can be one 'body' parameter at most">"""
def test_operation(): operation = Operation(method='GET', path='endpoint', operation=OPERATION1, app_produces=['application/json'], app_security=[], security_definitions=SECURITY_DEFINITIONS, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert isinstance(operation.function, types.FunctionType) # security decorator should be a partial with verify_oauth as the function and token url and scopes as arguments. # See https://docs.python.org/2/library/functools.html#partial-objects assert operation._Operation__security_decorator.func is verify_oauth assert operation._Operation__security_decorator.args == ( 'https://ouath.example/token_info', set(['uid'])) assert operation.method == 'GET' assert operation.produces == ['application/json'] assert operation.security == [{'oauth': ['uid']}] assert operation.body_schema == DEFINITIONS['new_stack']
def test_operation(api, monkeypatch): dummy = object() verify_oauth = mock.MagicMock(return_value=dummy) monkeypatch.setattr('connexion.operations.secure.verify_oauth', verify_oauth) op_spec = make_operation(OPERATION1) operation = Swagger2Operation( api=api, method='GET', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions=SECURITY_DEFINITIONS_REMOTE, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert isinstance(operation.function, types.FunctionType) security_decorator = operation.security_decorator assert security_decorator.func is verify_security assert len(security_decorator.args[0]) == 1 assert security_decorator.args[0][0] is dummy assert security_decorator.args[1] == ['uid'] call_args = verify_oauth.call_args[0] assert call_args[0].func is get_tokeninfo_remote assert call_args[0].args == ('https://oauth.example/token_info', ) assert call_args[1] is validate_scope assert operation.method == 'GET' assert operation.produces == ['application/json'] assert operation.consumes == ['application/json'] assert operation.security == [{'oauth': ['uid']}] expected_body_schema = op_spec["parameters"][0]["schema"] expected_body_schema.update({'definitions': DEFINITIONS}) assert operation.body_schema == expected_body_schema
def test_operation(api, security_handler_factory): verify_oauth = mock.MagicMock(return_value='verify_oauth_result') security_handler_factory.verify_oauth = verify_oauth security_handler_factory.get_token_info_remote = mock.MagicMock( return_value='get_token_info_remote_result') op_spec = make_operation(OPERATION1) operation = Swagger2Operation( api=api, method='GET', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions=SECURITY_DEFINITIONS_REMOTE, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert isinstance(operation.function, types.FunctionType) security_decorator = operation.security_decorator assert len(security_decorator.args[0]) == 1 assert security_decorator.args[0][0] == 'verify_oauth_result' assert security_decorator.args[1] == ['uid'] verify_oauth.assert_called_with('get_token_info_remote_result', security_handler_factory.validate_scope) security_handler_factory.get_token_info_remote.assert_called_with( 'https://oauth.example/token_info') assert operation.method == 'GET' assert operation.produces == ['application/json'] assert operation.consumes == ['application/json'] assert operation.security == [{'oauth': ['uid']}] expected_body_schema = op_spec["parameters"][0]["schema"] expected_body_schema.update({'definitions': DEFINITIONS}) assert operation.body_schema == expected_body_schema
def test_operation_composed_definition(api): op_spec = make_operation(OPERATION8) operation = Swagger2Operation(api=api, method='GET', path='endpoint', path_parameters=[], operation=op_spec, app_produces=['application/json'], app_consumes=['application/json'], app_security=[], security_definitions=SECURITY_DEFINITIONS_REMOTE, definitions=DEFINITIONS, parameter_definitions=PARAMETER_DEFINITIONS, resolver=Resolver()) assert isinstance(operation.function, types.FunctionType) assert operation.method == 'GET' assert operation.produces == ['application/json'] assert operation.consumes == ['application/json'] assert operation.security == [{'oauth': ['uid']}] expected_body_schema = op_spec["parameters"][0]["schema"] expected_body_schema.update({'definitions': DEFINITIONS}) assert operation.body_schema == expected_body_schema
def add_api(self, specification, base_path=None, arguments=None, auth_all_paths=None, swagger_json=None, swagger_ui=None, swagger_path=None, swagger_url=None, validate_responses=False, strict_validation=False, resolver=Resolver(), resolver_error=None): """ Adds an API to the application based on a swagger file or API dict :param specification: swagger file with the specification | specification dict :type specification: pathlib.Path or dict :param base_path: base path where to add this api :type base_path: str | None :param arguments: api version specific arguments to replace on the specification :type arguments: dict | None :param auth_all_paths: whether to authenticate not defined paths :type auth_all_paths: bool :param swagger_json: whether to include swagger json or not :type swagger_json: bool :param swagger_ui: whether to include swagger ui or not :type swagger_ui: bool :param swagger_path: path to swagger-ui directory :type swagger_path: string | None :param swagger_url: URL to access swagger-ui documentation :type swagger_url: string | None :param validate_responses: True enables validation. Validation errors generate HTTP 500 responses. :type validate_responses: bool :param strict_validation: True enables validation on invalid request parameters :type strict_validation: bool :param resolver: Operation resolver. :type resolver: Resolver | types.FunctionType :param resolver_error: If specified, turns ResolverError into error responses with the given status code. :type resolver_error: int | None :rtype: Api """ # Turn the resolver_error code into a handler object self.resolver_error = resolver_error resolver_error_handler = None if self.resolver_error is not None: resolver_error_handler = self._resolver_error_handler resolver = Resolver(resolver) if hasattr(resolver, '__call__') else resolver swagger_json = swagger_json if swagger_json is not None else self.swagger_json swagger_ui = swagger_ui if swagger_ui is not None else self.swagger_ui swagger_path = swagger_path if swagger_path is not None else self.swagger_path swagger_url = swagger_url if swagger_url is not None else self.swagger_url auth_all_paths = auth_all_paths if auth_all_paths is not None else self.auth_all_paths # TODO test if base_url starts with an / (if not none) arguments = arguments or dict() arguments = dict(self.arguments, **arguments) # copy global arguments and update with api specfic if isinstance(specification, dict): specification = specification else: specification = self.specification_dir / specification api = Api(specification=specification, base_url=base_path, arguments=arguments, swagger_json=swagger_json, swagger_ui=swagger_ui, swagger_path=swagger_path, swagger_url=swagger_url, resolver=resolver, resolver_error_handler=resolver_error_handler, validate_responses=validate_responses, strict_validation=strict_validation, auth_all_paths=auth_all_paths, debug=self.debug, validator_map=self.validator_map) self.app.register_blueprint(api.blueprint) return api
def test_standard_get_function(): function = Resolver().resolve_function_from_operation_id( 'connexion.FlaskApp.common_error_handler') assert function == connexion.FlaskApp.common_error_handler
import connexion from api.resolver import tensor_bridge_api_resolver from connexion.resolver import Resolver from checks import Checks DEBUG = False app = connexion.App(__name__, specification_dir='swagger/', debug=DEBUG) app.add_api('tensor_bridge.json', resolver=Resolver(function_resolver=tensor_bridge_api_resolver)) Checks(app.app) if DEBUG: app.run(port=8999, debug=False) else: application = app.app
def __init__(self, api: str): Resolver.__init__(self, self.function_resolver) self.api = api
spec = SpecBuilder()\ .add_spec(spec_path.joinpath("cm4.yaml")) \ .add_spec(spec_path.joinpath("vm.yaml")) \ .add_spec(spec_path.joinpath("flavor.yaml"))\ .add_spec(spec_path.joinpath("image.yaml")) options = {'swagger_path': swagger_ui_3_path} app = connexion.FlaskApp(__name__, specification_dir=spec_dir, options=options) # Inject the `controllers` namespace into `operationId`s specified in the spec # files. This allows for the (potentially many) controllers to be organized # into thier own folder and to potentially have multiple versions of the # controller classes in different folders. custom_resolver = Resolver(function_resolver=lambda fname: get_function_from_name(f"controllers.{fname}")) app.add_api(spec, resolver=custom_resolver) CORS(app.app) @app.route("/") def home(): return render_template("index.html") # Expose js, css, img static folders as if they were # root folders, not under `static/` because it's easier # than getting the JS app to rewrite all of the URLs to # include `static/`.
def __init__(self, api: str, stratus: StratusCore ): Resolver.__init__( self, self.function_resolver ) self.api = api self.stratus = stratus
def __init__( self, specification, base_path=None, arguments=None, validate_responses=False, strict_validation=False, resolver=None, auth_all_paths=False, debug=False, resolver_error_handler=None, validator_map=None, pythonic_params=False, pass_context_arg_name=None, options=None, ): """ Copied connexion.apis.abstract.AbstractAPI.__init__ + added necessary imports. Done just to use custom Specification class """ import logging logger = logging.getLogger("connexion.apis.flask_api") from connexion.options import ConnexionOptions from connexion.resolver import Resolver self.debug = debug self.validator_map = validator_map self.resolver_error_handler = resolver_error_handler logger.debug( "Loading specification: %s", specification, extra={ "swagger_yaml": specification, "base_path": base_path, "arguments": arguments, "auth_all_paths": auth_all_paths, }, ) # Avoid validator having ability to modify specification self.specification = Specification.load(specification, arguments=arguments) logger.debug("Read specification", extra={"spec": self.specification}) self.options = ConnexionOptions(options, oas_version=self.specification.version) logger.debug( "Options Loaded", extra={ "swagger_ui": self.options.openapi_console_ui_available, "swagger_path": self.options.openapi_console_ui_from_dir, "swagger_url": self.options.openapi_console_ui_path, }, ) self._set_base_path(base_path) logger.debug("Security Definitions: %s", self.specification.security_definitions) self.resolver = resolver or Resolver() logger.debug("Validate Responses: %s", str(validate_responses)) self.validate_responses = validate_responses logger.debug("Strict Request Validation: %s", str(strict_validation)) self.strict_validation = strict_validation logger.debug("Pythonic params: %s", str(pythonic_params)) self.pythonic_params = pythonic_params logger.debug("pass_context_arg_name: %s", pass_context_arg_name) self.pass_context_arg_name = pass_context_arg_name if self.options.openapi_spec_available: self.add_openapi_json() self.add_openapi_yaml() if self.options.openapi_console_ui_available: self.add_swagger_ui() self.add_paths() if auth_all_paths: self.add_auth_on_not_found(self.specification.security, self.specification.security_definitions)