def create_and_validate_request(endpoint, method, payload='', params=[], headers=[]): """ Helper function to create OpenAPIRequest and validate it :param endpoint: API endpoint :param method: API request method :param payload: API request payload :param params: API request payload :param headers: API request headers :return: - request: OpenAPIRequest - request_result: result of request validation """ parameters = RequestParameters(query=ImmutableMultiDict(params), path=endpoint, header=headers) request = OpenAPIRequest( full_url_pattern=endpoint, method=method, parameters=parameters, body=payload, mimetype='application/json', ) validator = RequestValidator(spec) request_result = validator.validate(request) return request, request_result
def validate_data(path, request, response, validate_request, validate_response): if response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED: return spec = _load_spec(resolve(path).kwargs.get('version')) request = DjangoOpenAPIRequestFactory.create(request) response = DjangoOpenAPIResponseFactory.create(response) # request if validate_request: validator = RequestValidator( spec, custom_formatters=CUSTOM_FORMATTERS) result = validator.validate(request) try: result.raise_for_errors() except OpenAPIMediaTypeError: if response.status_code != status.HTTP_400_BAD_REQUEST: raise except OpenAPIParameterError: # TODO(stephenfin): In API v2.0, this should be an error. As things # stand, we silently ignore these issues. assert response.status_code == status.HTTP_200_OK # response if validate_response: validator = ResponseValidator( spec, custom_formatters=CUSTOM_FORMATTERS) result = validator.validate(request, response) result.raise_for_errors()
def test_request_validator_with_query(self, spec, request_factory): validator = RequestValidator(spec) request = request_factory('GET', '/browse/12', query_string='detail_level=2', subdomain='kb') openapi_request = FalconOpenAPIRequestFactory.create(request) result = validator.validate(openapi_request) assert not result.errors
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_request_validator_path_pattern(self, django_spec, request_factory): from django.urls import resolve validator = RequestValidator(django_spec) request = request_factory.get('/admin/auth/group/1/') request.resolver_match = resolve('/admin/auth/group/1/') openapi_request = DjangoOpenAPIRequest(request) result = validator.validate(openapi_request) assert not result.errors
def test_post_object_failure(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 result.errors
async def validate_request(request: web.Request, spec: OpenApiSpec): """ Validates aiohttp.web.Request against an opeapi specification Returns parameters dict, body object and list of errors (exceptions objects) """ req = await AiohttpOpenAPIRequest.create(request) validator = RequestValidator(spec) result = validator.validate(req) return result.parameters, result.body, result.errors
def test_request_validator_path_pattern(self, spec): validator = RequestValidator(spec) request = requests.Request( 'POST', 'http://localhost/browse/12/', params={'q': 'string'}, headers={'content-type': 'application/json'}, json={'param1': 1}, ) openapi_request = RequestsOpenAPIRequest(request) result = validator.validate(openapi_request) assert not result.errors
def test_request_validator_path_pattern(self, spec): validator = RequestValidator(spec) request = requests.Request( "POST", "http://localhost/browse/12/", params={"q": "string"}, headers={"content-type": "application/json"}, json={"param1": 1}, ) openapi_request = RequestsOpenAPIRequest(request) result = validator.validate(openapi_request) assert not result.errors
def test_invalid_path(self, factory, server, spec_path): spec_dict = factory.spec_from_file(spec_path) spec = Spec.create(spec_dict) validator = RequestValidator(spec) request = MockRequest(server, "get", "/nonexistent") result = validator.validate(request) assert len(result.errors) == 1 assert isinstance(result.errors[0], PathNotFound) assert result.body is None assert result.parameters == 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, "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 validate_body(spec, request, request_factory=None): if request_factory is not None: request = request_factory(request) validator = RequestValidator(spec) result = validator.validate(request) try: result.raise_for_errors() except OpenAPIParameterError: return result.body else: return result.body
def validate_body(spec, request, wrapper_class=None): if wrapper_class is not None: request = wrapper_class(request) validator = RequestValidator(spec) result = validator.validate(request) try: result.raise_for_errors() except OpenAPIParameterError: return result.body else: return result.body
def validate_request(response): """Validate an API request""" openapi_spec = get_openapi_spec() validator = RequestValidator(openapi_spec) request = wrap_request(response.request, openapi_spec) result = validator.validate(request) print(result.errors) result.raise_for_errors() validator = ResponseValidator(openapi_spec) response = wrap_response(response) result = validator.validate(request, response) print(result.errors) result.raise_for_errors()
def sent_request(client14, container): path = container['request_data']['path'] resp = client14.simulate_get('{}/spec/1.4'.format(path)) assert HTTP_OK == resp.status spec = create_spec(resp.json) req = FalconOpenAPIWrapper(app, **container['request_data']) validator = RequestValidator(spec) result = validator.validate(req) assert not result.errors validator = ResponseValidator(spec) result = validator.validate(req, req) assert not result.errors container['response'] = req.request
def reload(self) -> None: # Because importing yamole (and in turn, yaml) takes # significant time, and we only use python-yaml for our API # docs, importing it lazily here is a significant optimization # to `manage.py` startup. # # There is a bit of a race here...we may have two processes # accessing this module level object and both trying to # populate self.data at the same time. Hopefully this will # only cause some extra processing at startup and not data # corruption. from yamole import YamoleParser with open(self.path) as f: yaml_parser = YamoleParser(f) self.data = yaml_parser.data validator_spec = create_spec(self.data) self.core_data = RequestValidator(validator_spec) self.create_regex_dict() self.last_update = os.path.getmtime(self.path) # Form a set of all documented events self.documented_events.clear() schema = (self.data['paths']['/events']['get']['responses'] ['200']['content']['application/json']['schema']) for events in schema['properties']['events']['items']['oneOf']: op_str = ':' if 'op' in events['properties']: op_str = f':{events["properties"]["op"]["enum"][0]}' self.documented_events.add(events['properties']['type']['enum'][0] + op_str)
def __init__( self, api_spec_path: Optional[str] = None, server: Optional[str] = None, logger: logging.Logger = _default_logger, ): """ Initialize the API spec. :param api_spec_path: Directory API path and filename of the API spec YAML source file. """ self._validator = None # type: Optional[RequestValidator] self.logger = logger if api_spec_path is not None: try: api_spec_dict = read_yaml_file(api_spec_path) if server is not None: api_spec_dict["servers"] = [{"url": server}] api_spec = create_spec(api_spec_dict) self._validator = RequestValidator(api_spec) except OpenAPIValidationError as e: # pragma: nocover self.logger.error( f"API specification YAML source file not correctly formatted: {str(e)}" ) except Exception: self.logger.exception( "API specification YAML source file not correctly formatted." ) raise
def validate_parameters(spec, request, request_factory=None): if request_factory is not None: request = request_factory(request) validator = RequestValidator(spec) result = validator.validate(request) try: result.raise_for_errors() except ( OpenAPIRequestBodyError, OpenAPIMediaTypeError, OpenAPISchemaError, ): return result.parameters else: return result.parameters
def register() -> None: if hupper.is_active(): # pragma: no cover hupper.get_reloader().watch_files([filepath]) spec_dict = read_yaml_file(filepath) validate_spec(spec_dict) spec = create_spec(spec_dict) def spec_view(request: Request) -> FileResponse: return FileResponse(filepath, request=request, content_type="text/yaml") config.add_route(route_name, route) config.add_view(route_name=route_name, view=spec_view) custom_formatters = config.registry.settings.get( "pyramid_openapi3_formatters") config.registry.settings["pyramid_openapi3"] = { "filepath": filepath, "spec_route_name": route_name, "spec": spec, "request_validator": RequestValidator(spec, custom_formatters), "response_validator": ResponseValidator(spec, custom_formatters), }
def register() -> None: settings = config.registry.settings.get(apiname) if settings and settings.get("spec") is not None: raise ConfigurationError( "Spec has already been configured. You may only call " "pyramid_openapi3_spec or pyramid_openapi3_spec_directory once" ) if hupper.is_active(): # pragma: no cover hupper.get_reloader().watch_files([filepath]) spec_dict = read_yaml_file(filepath) validate_spec(spec_dict) spec = create_spec(spec_dict) def spec_view(request: Request) -> FileResponse: return FileResponse(filepath, request=request, content_type="text/yaml") config.add_route(route_name, route) config.add_view(route_name=route_name, permission=permission, view=spec_view) custom_formatters = config.registry.settings.get("pyramid_openapi3_formatters") config.registry.settings[apiname] = { "filepath": filepath, "spec_route_name": route_name, "spec": spec, "request_validator": RequestValidator( spec, custom_formatters=custom_formatters ), "response_validator": ResponseValidator( spec, custom_formatters=custom_formatters ), } APIS.append(apiname)
def check_reload(self) -> None: # Because importing yamole (and in turn, yaml) takes # significant time, and we only use python-yaml for our API # docs, importing it lazily here is a significant optimization # to `manage.py` startup. # # There is a bit of a race here...we may have two processes # accessing this module level object and both trying to # populate self.data at the same time. Hopefully this will # only cause some extra processing at startup and not data # corruption. from yamole import YamoleParser mtime = os.path.getmtime(self.openapi_path) # Using == rather than >= to cover the corner case of users placing an # earlier version than the current one if self.mtime == mtime: return with open(self.openapi_path) as f: yamole_parser = YamoleParser(f) self._openapi = yamole_parser.data spec = create_spec(self._openapi) self._request_validator = RequestValidator(spec) self.create_endpoints_dict() self.mtime = mtime
def check_reload(self) -> None: # Because importing yaml takes significant time, and we only # use python-yaml for our API docs, importing it lazily here # is a significant optimization to `manage.py` startup. # # There is a bit of a race here...we may have two processes # accessing this module level object and both trying to # populate self.data at the same time. Hopefully this will # only cause some extra processing at startup and not data # corruption. import yaml from jsonref import JsonRef with open(self.openapi_path) as f: mtime = os.fstat(f.fileno()).st_mtime # Using == rather than >= to cover the corner case of users placing an # earlier version than the current one if self.mtime == mtime: return openapi = yaml.load(f, Loader=yaml.CSafeLoader) spec = create_spec(openapi) self._request_validator = RequestValidator(spec) self._openapi = naively_merge_allOf_dict(JsonRef.replace_refs(openapi)) self.create_endpoints_dict() self.mtime = mtime
def sent_request(client14, container, spec_url='/spec/1.4'): version = container['request_data']['headers'].get( 'X-API-VERSION') or '1.4' resp = client14.simulate_get('/spec/{}'.format(version)) assert HTTP_OK == resp.status spec = create_spec(resp.json) req = FalconOpenAPIWrapper(app, **container['request_data']) validator = RequestValidator(spec) result = validator.validate(req) assert not result.errors validator = ResponseValidator(spec) result = validator.validate(req, req) assert not result.errors container['response'] = req.request
def spec_validate_parameters(spec, request, request_factory=None): if request_factory is not None: request = request_factory(request) validator = RequestValidator(spec) result = validate_parameters(validator, request) return result.parameters
def spec_validate_body(spec, request, request_factory=None): if request_factory is not None: request = request_factory(request) validator = RequestValidator(spec) result = validate_body(validator, request) return result.body
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 validate(*args, **kwargs): if ENABLE_OPENAPI_VALIDATION: spec = get_openapi_spec() openapi_request = FlaskOpenAPIRequest(request) validator = RequestValidator(spec) result = validator.validate(openapi_request) result.raise_for_errors() response = function(*args, **kwargs) if ENABLE_OPENAPI_VALIDATION: openapi_response = FlaskOpenAPIResponse(response) validator = ResponseValidator(spec) result = validator.validate(openapi_request, openapi_response) result.raise_for_errors() return response
def validate_request(response): """Validate an API request""" path = (Path(here) / '../jupyterlab_server/rest-api.yml').resolve() yaml = YAML(typ='safe') spec_dict = yaml.load(path.read_text(encoding='utf-8')) spec = create_spec(spec_dict) validator = RequestValidator(spec) request = wrap_request(response.request, spec) result = validator.validate(request) print(result.errors) result.raise_for_errors() validator = ResponseValidator(spec) response = wrap_response(response) result = validator.validate(request, response) print(result.errors) result.raise_for_errors()
def __init__(self, get_response): self.get_response = get_response if not hasattr(settings, "OPENAPI_SPEC"): raise ImproperlyConfigured("OPENAPI_SPEC not defined in settings") request_validator = RequestValidator(settings.OPENAPI_SPEC) response_validator = ResponseValidator(settings.OPENAPI_SPEC) self.validation_processor = OpenAPIProcessor(request_validator, response_validator)
def validate_data(path, request, response): if response.status_code == status.HTTP_405_METHOD_NOT_ALLOWED: return spec = _load_spec(resolve(path).kwargs.get('version')) request = DRFOpenAPIRequest(request) response = DRFOpenAPIResponse(response) # request validator = RequestValidator(spec, custom_formatters=CUSTOM_FORMATTERS) result = validator.validate(request) try: result.raise_for_errors() except OpenAPIMediaTypeError: assert response.status_code == status.HTTP_400_BAD_REQUEST except OpenAPIParameterError: # TODO(stephenfin): In API v2.0, this should be an error. As things # stand, we silently ignore these issues. assert response.status_code == status.HTTP_200_OK # response validator = ResponseValidator(spec, custom_formatters=CUSTOM_FORMATTERS) result = validator.validate(request, response) result.raise_for_errors()