def test_hosts(self, factory, server, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = create_spec(spec_dict) validator = RequestValidator(spec) request = MockRequest(server, "get", "/status") result = validator.validate(request) assert not result.errors
def test_param_present(self, factory, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = create_spec(spec_dict) path = spec['/resource/{resId}'] assert len(path.parameters) == 1 param = path.parameters['resId'] assert param.required assert param.location == ParameterLocation.PATH
def test_get_object_failure(self, factory, response, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = create_spec(spec_dict) request = MockRequest("http://www.example.com", "get", "/object/{objectId}") validator = ResponseValidator(spec) response = MockResponse(data=json.dumps(response)) result = validator.validate(request, response) assert result.errors
def test_post_object_success(self, factory, response, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = create_spec(spec_dict) validator = RequestValidator(spec) request = MockRequest("http://www.example.com", "post", "/object", data=json.dumps(response)) result = validator.validate(request) assert not result.errors
def test_invalid_operation(self, factory, server, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = create_spec(spec_dict) validator = RequestValidator(spec) request = MockRequest(server, "get", "/nonexistent") result = validator.validate(request) assert len(result.errors) == 1 assert isinstance(result.errors[0], InvalidOperation) assert result.body is None assert result.parameters == {}
def test_invalid_operation(self, factory, server, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = create_spec(spec_dict) validator = RequestValidator(spec) request = MockRequest(server, "post", "/status") result = validator.validate(request) assert len(result.errors) == 1 assert isinstance(result.errors[0], OperationNotFound) assert result.body is None assert result.parameters == RequestParameters()
def test_param_present(self, factory, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = create_spec(spec_dict) path = spec / "paths#/resource/{resId}" parameters = path / "parameters" assert len(parameters) == 1 param = parameters[0] assert param["name"] == "resId" assert param["required"] assert param["in"] == "path"
def test_param_present(self, factory, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = create_spec(spec_dict) path = spec / 'paths#/resource/{resId}' parameters = path / 'parameters' assert len(parameters) == 1 param = parameters[0] assert param['name'] == 'resId' assert param['required'] assert param['in'] == 'path'
def test_no_param(self, factory): spec_dict = factory.spec_from_file("data/v3.0/links.yaml") spec = create_spec(spec_dict) resp = spec['/status']['get'].responses['default'] assert len(resp.links) == 1 link = resp.links['noParamLink'] assert link.operationId == 'noParOp' assert link.server is None assert link.request_body is None assert len(link.parameters) == 0
def test_no_param(self, factory): spec_dict = factory.spec_from_file("data/v3.0/links.yaml") spec = create_spec(spec_dict) resp = spec / "paths#/status#get#responses#default" links = resp / "links" assert len(links) == 1 link = links / "noParamLink" assert link["operationId"] == "noParOp" assert "server" not in link assert "requestBody" not in link assert "parameters" not in link
def test_no_param(self, factory): spec_dict = factory.spec_from_file("data/v3.0/links.yaml") spec = create_spec(spec_dict) resp = spec / 'paths#/status#get#responses#default' links = resp / 'links' assert len(links) == 1 link = links / 'noParamLink' assert link['operationId'] == 'noParOp' assert 'server' not in link assert 'requestBody' not in link assert 'parameters' not in link
def main(): global app specfile = "./apis/api.yaml" specurl = "file://" + abspath(specfile) specdict = read_yaml_file(specfile) openapi_spec = create_spec(specdict, spec_url=specurl) openapi_middleware = FalconOpenAPIMiddleware.from_spec(openapi_spec) app = API(middleware=[openapi_middleware]) auth_server = Auth() user_server = User() app.add_route('/user', user_server) app.add_route('/token', auth_server)
def test_param(self, factory): spec_dict = factory.spec_from_file("data/v3.0/links.yaml") spec = create_spec(spec_dict) resp = spec['/status/{resourceId}']['get'].responses['default'] assert len(resp.links) == 1 link = resp.links['paramLink'] assert link.operationId == 'paramOp' assert link.server is None assert link.request_body == 'test' assert len(link.parameters) == 1 param = link.parameters['opParam'] assert param == '$request.path.resourceId'
def test_param(self, factory): spec_dict = factory.spec_from_file("data/v3.0/links.yaml") spec = create_spec(spec_dict) resp = spec / "paths#/status/{resourceId}#get#responses#default" links = resp / "links" assert len(links) == 1 link = links / "paramLink" assert link["operationId"] == "paramOp" assert "server" not in link assert link["requestBody"] == "test" parameters = link["parameters"] assert len(parameters) == 1 param = parameters["opParam"] assert param == "$request.path.resourceId"
def test_request_override_param(self, spec_dict): # override path parameter on operation spec_dict["paths"]["/resource"]["get"]["parameters"] = [{ # full valid parameter object required "name": "resId", "in": "query", "required": False, "schema": { "type": "integer", }, }] validator = RequestValidator(create_spec(spec_dict)) request = MockRequest('http://example.com', 'get', '/resource') result = validator.validate(request) assert len(result.errors) == 0 assert result.body is None assert result.parameters == {}
def test_param(self, factory): spec_dict = factory.spec_from_file("data/v3.0/links.yaml") spec = create_spec(spec_dict) resp = spec / 'paths#/status/{resourceId}#get#responses#default' links = resp / 'links' assert len(links) == 1 link = links / 'paramLink' assert link['operationId'] == 'paramOp' assert 'server' not in link assert link['requestBody'] == 'test' parameters = link['parameters'] assert len(parameters) == 1 param = parameters['opParam'] assert param == '$request.path.resourceId'
def test_request_override_param_uniqueness(self, spec_dict): # add parameter on operation with same name as on path but # different location spec_dict["paths"]["/resource"]["get"]["parameters"] = [{ # full valid parameter object required "name": "resId", "in": "header", "required": False, "schema": { "type": "integer", }, }] validator = RequestValidator(create_spec(spec_dict)) request = MockRequest('http://example.com', 'get', '/resource') result = validator.validate(request) assert len(result.errors) == 1 assert type(result.errors[0]) == MissingRequiredParameter assert result.body is None assert result.parameters == {}
def flask_spec(self, factory): specfile = 'data/v3.0/flask_factory.yaml' return create_spec(factory.spec_from_file(specfile))
def spec(self, spec_dict): return create_spec(spec_dict)
def setup_openapi( app: web.Application, schema_path: Union[str, Path], *operations: OperationTableDef, route_prefix: str = None, is_validate_response: bool = False, has_openapi_schema_handler: bool = True, ) -> None: """Setup OpenAPI schema to use with aiohttp.web application. Unlike `aiohttp-apispec <https://aiohttp-apispec.readthedocs.io/>`_ and other tools, which provides OpenAPI/Swagger support for aiohttp.web applications, ``rororo`` changes the way of using OpenAPI schema with ``aiohttp.web`` apps. ``rororo`` relies on concrete OpenAPI schema file, path to which need to be registered on application startup (mostly inside of ``create_app`` factory or right after :class:`aiohttp.web.Application` instantiation). And as valid OpenAPI schema ensure unique ``operationId`` used accross the schema ``rororo`` uses them as a key while telling aiohttp.web to use given view handler for serving required operation. With that in mind registering (setting up) OpenAPI schema requires: 1. :class:`aiohttp.web.Application` instance 2. Path to file (json or yaml) with OpenAPI schema 3. OpenAPI operation handlers mapping (rororo's equialent of :class:`aiohttp.web.RouteTableDef`) In common cases setup looks like, .. code-block:: python from pathlib import Path from typing import List from aiohttp import web from .views import operations def create_app(argv: List[str] = None) -> web.Application: app = web.Application() setup_openapi( app, Path(__file__).parent / 'openapi.yaml', operations ) return app It is also possible to setup route prefix to use if server URL inside of your OpenAPI schema ends with path, like ``http://yourserver.url/api``. For that cases you need to pass ``'/api'`` as a ``route_prefix`` keyword argument. By default, ``rororo`` will not validate operation responses against OpenAPI schema due to performance reasons. To enable this feature, pass ``is_validate_response`` truthy flag. By default, ``rororo`` will share the OpenAPI schema which is registered for your aiohttp.web application. In case if you don't want to share this schema, pass ``has_openapi_schema_handler=False`` on setting up OpenAPI. """ def read_schema(path: Path) -> DictStrAny: content = path.read_text() if path.suffix == ".json": return json.loads(content) if path.suffix in {".yml", ".yaml"}: return yaml.safe_load(content) raise ConfigurationError( f"Unsupported OpenAPI schema file: {path}. At a moment rororo " "supports loading OpenAPI schemas from: .json, .yml, .yaml files") # Ensure OpenAPI schema is a readable file path = Path(schema_path) if isinstance(schema_path, str) else schema_path if not path.exists() or not path.is_file(): uid = os.getuid() raise ConfigurationError( f"Unable to find OpenAPI schema file at {path}. Please check that " "file exists at given path and readable by current user ID: " f"{uid}") # Store OpenAPI schema dict in the application dict app[OPENAPI_SCHEMA_APP_KEY] = oas = read_schema(path) # Create the spec and put it to the application dict as well try: app[OPENAPI_SPEC_APP_KEY] = create_spec(oas) except OpenAPIValidationError: raise ConfigurationError( f"Unable to load valid OpenAPI schema in {path}. In most cases " "it means that given file doesn't contain valid OpenAPI 3 schema. " "To get full details about errors run `openapi-spec-validator " f"{path}`") # Store whether rororo need to validate response or not. By default: not app[OPENAPI_IS_VALIDATE_RESPONSE_APP_KEY] = is_validate_response # Register all operation handlers to web application for item in operations: app.router.add_routes( convert_operations_to_routes(item, oas, prefix=route_prefix)) # Register the route to dump openapi schema used for the application if # required if has_openapi_schema_handler: app.router.add_get( add_prefix("/openapi.{schema_format}", route_prefix), views.openapi_schema, )
def spec(self, factory): specfile = 'contrib/falcon/data/v3.0/falcon_factory.yaml' return create_spec(factory.spec_from_file(specfile))
def spec(factory): spec_dict = factory.spec_from_file("data/v3.0/read_only_write_only.yaml") return create_spec(spec_dict)
def spec(factory): spec_dict = factory.spec_from_file("data/v3.0/security_override.yaml") return create_spec(spec_dict)
def django_spec(self, factory): specfile = 'data/v3.0/django_factory.yaml' return create_spec(factory.spec_from_file(specfile))
def spec(self, spec_dict, spec_uri): return create_spec(spec_dict, spec_uri)
def spec(self, factory): specfile = "contrib/flask/data/v3.0/flask_factory.yaml" return create_spec(factory.spec_from_file(specfile))
def spec(self, factory): specfile = 'contrib/requests/data/v3.0/requests_factory.yaml' return create_spec(factory.spec_from_file(specfile))
def test_raises_on_invalid(self, spec_dict): with pytest.raises(ValidationError): create_spec(spec_dict)