def test_request_with_no_json(petstore_spec): request = Mock(spec=IncomingRequest, path={'petId': '1234'}, json=Mock(side_effect=ValueError("No json here bub"))) op = petstore_spec.resources['pet'].operations['updatePet'] with pytest.raises(SwaggerMappingError) as excinfo: unmarshal_request(request, op) assert "Error reading request body JSON" in str(excinfo.value)
def test_correct_request_with_apiKey_security(getPetByIdPetstoreOperation): request = Mock( spec=IncomingRequest, path={'petId': '1234'}, headers={'api-key': 'key1'}, ) unmarshal_request(request, getPetByIdPetstoreOperation)
def test_correct_request_with_apiKey_security(petstore_spec): request = Mock( spec=IncomingRequest, path={'petId': '1234'}, headers={'api_key': 'key1'}, ) op = petstore_spec.resources['pet'].operations['getPetById'] unmarshal_request(request, op)
def test_wrong_request_with_apiKey_security(getPetByIdPetstoreOperation): request = Mock( spec=IncomingRequest, path={'petId': '1234'}, headers={}, ) with pytest.raises(SwaggerSecurityValidationError): unmarshal_request(request, getPetByIdPetstoreOperation)
def test_wrong_request_with_apiKey_security(petstore_spec): request = Mock( spec=IncomingRequest, path={'petId': '1234'}, headers={}, ) op = petstore_spec.resources['pet'].operations['getPetById'] with pytest.raises(SwaggerSecurityValidationError): unmarshal_request(request, op)
def process_resource(self, request, response, resource, params): # pylint: disable=unused-argument bravado_request = _BravadoRequest(request, params) get_op = functools.partial( self._spec.get_op_for_request, path_pattern=request.uri_template, ) operation = get_op(request.method) if not operation: # The URI exists but the method is wrong. # So we are going to reply with an error 405. # Error 405 requires we provide the list of allowed methods # for this URI. If None is found, then we produce a 404 error. allowed = [m for m in falcon.HTTP_METHODS if get_op(m)] if allowed: raise falcon.HTTPMethodNotAllowed(allowed) raise falcon.HTTPNotFound try: validated = unmarshal_request(bravado_request, operation) except ValidationError as e: raise _HTTPBadRequest(e) else: body = validated.pop('body', None) if body: validated.update(body) params.clear() params.update(validated)
def unmarshal_request(self) -> dict: b_req = BravadoRequest(self) operation = self.spec.get_op_for_request(request.method.lower(), b_req.endpoint()) if not operation: raise OpenAPIError('Failed to find proper operation method = {}, endpoint = {}'.format( request.method.lower(), b_req.endpoint() )) return unmarshal_request(b_req, operation)
def test_with_not_string_headers( minimal_swagger_dict, getPetById_spec, request_dict, swagger_type, swagger_format, header_name, header_value, ): url = '/pet/{petId}' parameter = { 'name': header_name, 'in': 'header', 'required': False, 'type': swagger_type, } if swagger_format: parameter['format'] = swagger_format minimal_swagger_dict['paths'][url]['get']['parameters'].append(parameter) minimal_swagger_spec = build_swagger_spec(minimal_swagger_dict) request_dict['url'] = url operation = Operation.from_spec( swagger_spec=minimal_swagger_spec, path_name='/pet/{petId}', http_method='get', op_spec=getPetById_spec, ) petId = 34 api_key = 'foo' request = construct_request( operation=operation, request_options={'headers': { header_name: header_value }}, petId=petId, api_key=api_key, ) # To unmarshall a request bravado-core needs the request to be wrapped # by an object with a specific list of attributes request_object = type( 'IncomingRequest', (IncomingRequest, ), { 'path': { 'petId': petId }, 'query': {}, 'form': {}, 'headers': request['headers'], 'files': mock.Mock(), }) assert request['headers'][header_name] == str(header_value) unmarshalled_request = unmarshal_request(request_object, operation) assert unmarshalled_request[header_name] == header_value
def test_request_with_path_parameter(getPetByIdPetstoreOperation): request = Mock( spec=IncomingRequest, path={'petId': '1234'}, headers={'api-key': 'key1'}, ) # /pet/{pet_id} fits the bill request_data = unmarshal_request(request, getPetByIdPetstoreOperation) assert request_data['petId'] == 1234 assert request_data['api-key'] == 'key1'
def validate_request_call(self, op, **kargs): params = unmarshal_request(self.request, op) response = self.app.request(op.path_name.format_map(params), body=json.dumps(self.request.json()).encode(), method=op.http_method.upper(), headers=self.headers, **kargs) schema = self.spec.deref(op.op_spec['responses'][str(response.status_code)]) casted_resp = self.cast_bravado_response(response) validate_response(schema, op, casted_resp) return response
def swaggerize_request(request, op, **kwargs): """ Delegate handling the Swagger concerns of the request to bravado-core. Post-invocation, the Swagger request parameters are available as a dict named `swagger_data` on the Pyramid request. :type request: :class:`pyramid.request.Request` :type op: :class:`bravado_core.operation.Operation` """ request_data = unmarshal_request(request, op) return request_data
def test_request_with_path_parameter(petstore_spec): request = Mock( spec=IncomingRequest, path={'petId': '1234'}, headers={'api_key': 'key1'}, ) # /pet/{pet_id} fits the bill op = petstore_spec.resources['pet'].operations['getPetById'] request_data = unmarshal_request(request, op) assert request_data['petId'] == 1234 assert request_data['api_key'] == 'key1'
def test_only_one_security_definition_in_use_at_time( security_spec, resource, operation, query, headers, expect_to_raise, ): request = Mock( spec=IncomingRequest, headers=headers, query=query, ) op = security_spec.resources[resource].operations[operation] try: with pytest.raises(SwaggerSecurityValidationError): unmarshal_request(request, op) except: if expect_to_raise: raise
def test_with_not_string_headers( minimal_swagger_dict, getPetById_spec, request_dict, swagger_type, swagger_format, header_name, header_value, ): url = '/pet/{petId}' parameter = { 'name': header_name, 'in': 'header', 'required': False, 'type': swagger_type, } if swagger_format: parameter['format'] = swagger_format minimal_swagger_dict['paths'][url]['get']['parameters'].append(parameter) minimal_swagger_spec = build_swagger_spec(minimal_swagger_dict) request_dict['url'] = url operation = Operation.from_spec( swagger_spec=minimal_swagger_spec, path_name='/pet/{petId}', http_method='get', op_spec=getPetById_spec, ) petId = 34 api_key = 'foo' request = construct_request( operation=operation, request_options={'headers': {header_name: header_value}}, petId=petId, api_key=api_key, ) # To unmarshall a request bravado-core needs the request to be wrapped # by an object with a specific list of attributes request_object = type('IncomingRequest', (IncomingRequest,), { 'path': {'petId': petId}, 'query': {}, 'form': {}, 'headers': request['headers'], 'files': mock.Mock(), }) expected_header_value = str(header_value) # we need to handle a backwards-incompatible change in bravado-core 5.0.5 if swagger_type == 'boolean': assert request['headers'][header_name] in (expected_header_value, expected_header_value.lower()) else: assert request['headers'][header_name] == expected_header_value unmarshalled_request = unmarshal_request(request_object, operation) assert unmarshalled_request[header_name] == header_value
def test_only_one_security_definition_in_use_at_time( security_spec, resource, operation, query, headers, expect_to_raise, ): request = Mock( spec=IncomingRequest, headers=headers, query=query, ) op = security_spec.resources[resource].operations[operation] raised_exception = None try: unmarshal_request(request, op) except SwaggerSecurityValidationError as e: raised_exception = e assert bool(raised_exception is not None) is expect_to_raise
def validate_request_call(self, op, **kargs): params = unmarshal_request(self.request, op) response = self.app.request(op.path_name.format_map(params), body=json.dumps( self.request.json()).encode(), method=op.http_method.upper(), headers=self.headers, **kargs) schema = self.spec.deref(op.op_spec["responses"][str( response.status_code)]) casted_resp = self.cast_bravado_response(response) validate_response(schema, op, casted_resp) return response
def test_request_with_no_json_and_optional_body_parameter(petstore_spec): request = Mock(spec=IncomingRequest, path={'petId': '1234'}, json=Mock(side_effect=ValueError("No json here bub"))) op = petstore_spec.resources['pet'].operations['updatePet'] op.op_spec['parameters'][0]['required'] = False op = Operation.from_spec( swagger_spec=petstore_spec, path_name=op.path_name, http_method=op.http_method, op_spec=op.op_spec, ) assert unmarshal_request(request, op) == {'body': None}
def swaggerize_request(request, op, **kwargs): """ Delegate handling the Swagger concerns of the request to bravado-core. Post-invocation, the Swagger request parameters are available as a dict named `swagger_data` on the Pyramid request. :type request: :class:`pyramid.request.Request` :type op: :class:`bravado_core.operation.Operation` :raises: RequestValidationError, RequestAuthenticationError """ try: request_data = unmarshal_request(request, op) except SwaggerSecurityValidationError as e: six.raise_from(RequestAuthenticationError(e), e) return request_data
def process_resource(self, request, response, resource, params): bravado_request = _BravadoRequest(request, params) operation = self._spec.get_op_for_request( request.method.upper(), request.uri_template) if not operation: raise falcon.HTTPNotFound try: validated = unmarshal_request(bravado_request, operation) except ValidationError as e: raise _HTTPBadRequest(e) else: body = validated.pop('body', None) if body: validated.update(body) params.clear() params.update(validated)
def validate_request(self, resource: str, operation: str, model: RequestValidationModel): """ Validate a 'request-like' object :param resource: API resource pointer :param operation: operation on resource :param model: model to validate :return: None if validation is successful, otherwise an exception will be raised """ spec = self.spec.resources[resource].operations[operation] if not spec: raise Base422Exception( 'No validation available', reason='A spec was requested but not resolved from API decs') try: request_data = unmarshal_request(model, spec) except (ValidationError, BaseException) as err: raise Base422Exception('Validation Failed', str(err)) return request_data
def test_request_with_no_parameters(petstore_spec): request = Mock(spec=IncomingRequest) # /user/logout conveniently has no params op = petstore_spec.resources['user'].operations['logoutUser'] request_data = unmarshal_request(request, op) assert 0 == len(request_data)
def _validate_request(swagger_op): unmarshal_request(BottleIncomingRequest(request), swagger_op)
def test_request_with_path_parameter(petstore_spec): request = Mock(spec=RequestLike, path={'petId': '1234'}) # /pet/{pet_id} fits the bill op = petstore_spec.resources['pet'].operations['getPetById'] request_data = unmarshal_request(request, op) assert request_data['petId'] == 1234
def test_request_with_no_parameters(petstore_spec): request = Mock(spec=RequestLike) # /user/logout conveniently has no params op = petstore_spec.resources['user'].operations['logoutUser'] request_data = unmarshal_request(request, op) assert 0 == len(request_data)
def handler_wrapper(**path_params): log.info("=> INCOMING REQUEST %s %s -> %s" % (endpoint.method, endpoint.path, handler_func.__name__)) # Get caller's klue-call-id or generate one call_id = request.headers.get('KlueCallID', None) if not call_id: call_id = str(uuid.uuid4()) stack.top.call_id = call_id # Append current server to call path, or start one call_path = request.headers.get('KlueCallPath', None) if call_path: call_path = "%s.%s" % (call_path, api_name) else: call_path = api_name stack.top.call_path = call_path if endpoint.param_in_body or endpoint.param_in_query: # Turn the flask request into something bravado-core can process... try: req = FlaskRequestProxy(request, endpoint.param_in_body) except BadRequest: ee = error_callback(ValidationError("Cannot parse json data: have you set 'Content-Type' to 'application/json'?")) return _responsify(api_spec, ee, 400) try: # Note: unmarshall validates parameters but does not fail # if extra unknown parameters are submitted parameters = unmarshal_request(req, endpoint.operation) # Example of parameters: {'body': RegisterCredentials()} except jsonschema.exceptions.ValidationError as e: ee = error_callback(ValidationError(str(e))) return _responsify(api_spec, ee, 400) # Call the endpoint, with proper parameters depending on whether # parameters are in body, query or url args = [] kwargs = {} if endpoint.param_in_path: kwargs = path_params if endpoint.param_in_body: # Remove the parameters already defined in path_params for k in path_params.keys(): del parameters[k] l = list(parameters.values()) assert len(l) == 1 args.append(l[0]) if endpoint.param_in_query: kwargs.update(parameters) result = handler_func(*args, **kwargs) if not result: e = error_callback(KlueException("Have nothing to send in response")) return _responsify(api_spec, e, 500) # Did we get the expected response? if endpoint.produces_html: if type(result) is not tuple: e = error_callback(KlueException("Method %s should return %s but returned %s" % (endpoint.handler_server, endpoint.produces, type(result)))) return _responsify(api_spec, e, 500) # Return an html page return result elif endpoint.produces_json: if not hasattr(result, '__module__') or not hasattr(result, '__class__'): e = error_callback(KlueException("Method %s did not return a class instance but a %s" % (endpoint.handler_server, type(result)))) return _responsify(api_spec, e, 500) # If it's already a flask Response, just pass it through. # Errors in particular may be either passed back as flask Responses, or # raised as exceptions to be caught and formatted by the error_callback result_type = result.__module__ + "." + result.__class__.__name__ if result_type == 'flask.wrappers.Response': return result # Otherwise, assume no error occured and make a flask Response out of # the result. # TODO: check that result is an instance of a model expected as response from this endpoint result_json = api_spec.model_to_json(result) # Send a Flask Response with code 200 and result_json r = jsonify(result_json) r.status_code = 200 return r
def _validate_request(swagger_op, ignore_security_definitions=False): if ignore_security_definitions: swagger_op = SecurityPatchedOperation(swagger_op) return unmarshal_request(BottleIncomingRequest(request), swagger_op)
def validate_request(request): path = rule_to_path(request.url_rule.rule) op = spec.get_op_for_request(request.method, path) request = FlaskRequestProxy(request) unmarshal_request(request, op)
def handler_wrapper(**path_params): if os.environ.get('PYM_DEBUG', None) == '1': log.debug("PYM_DEBUG: Request headers are: %s" % dict(request.headers)) # Get caller's pym-call-id or generate one call_id = request.headers.get('PymCallID', None) if not call_id: call_id = str(uuid.uuid4()) stack.top.call_id = call_id # Append current server to call path, or start one call_path = request.headers.get('PymCallPath', None) if call_path: call_path = "%s.%s" % (call_path, api_name) else: call_path = api_name stack.top.call_path = call_path if endpoint.param_in_body or endpoint.param_in_query or endpoint.param_in_formdata: # Turn the flask request into something bravado-core can process... has_data = endpoint.param_in_body or endpoint.param_in_formdata try: req = FlaskRequestProxy(request, has_data) except BadRequest: ee = error_callback(ValidationError("Cannot parse json data: have you set 'Content-Type' to 'application/json'?")) return _responsify(api_spec, ee, 400) try: # Note: unmarshall validates parameters but does not fail # if extra unknown parameters are submitted parameters = unmarshal_request(req, endpoint.operation) # Example of parameters: {'body': RegisterCredentials()} except jsonschema.exceptions.ValidationError as e: ee = error_callback(ValidationError(str(e))) return _responsify(api_spec, ee, 400) # Call the endpoint, with proper parameters depending on whether # parameters are in body, query or url args = [] kwargs = {} if endpoint.param_in_path: kwargs = path_params if endpoint.param_in_body: # Remove the parameters already defined in path_params for k in list(path_params.keys()): del parameters[k] lst = list(parameters.values()) assert len(lst) == 1 # Now convert the Bravado body object into a pymacaron model body = lst[0] cls = get_model(body.__class__.__name__) body = cls.from_bravado(body) args.append(body) if endpoint.param_in_query: kwargs.update(parameters) if endpoint.param_in_formdata: for k in list(path_params.keys()): del parameters[k] kwargs.update(parameters) if os.environ.get('PYM_DEBUG', None) == '1': log.debug("PYM_DEBUG: Request args are: [args: %s] [kwargs: %s]" % (args, kwargs)) result = handler_func(*args, **kwargs) if not result: e = error_callback(PyMacaronCoreException("Have nothing to send in response")) return _responsify(api_spec, e, 500) # Did we get the expected response? if endpoint.produces_html: if type(result) is not tuple: e = error_callback(PyMacaronCoreException("Method %s should return %s but returned %s" % (endpoint.handler_server, endpoint.produces, type(result)))) return _responsify(api_spec, e, 500) # Return an html page return result elif endpoint.produces_json: if not hasattr(result, '__module__') or not hasattr(result, '__class__'): e = error_callback(PyMacaronCoreException("Method %s did not return a class instance but a %s" % (endpoint.handler_server, type(result)))) return _responsify(api_spec, e, 500) # If it's already a flask Response, just pass it through. # Errors in particular may be either passed back as flask Responses, or # raised as exceptions to be caught and formatted by the error_callback result_type = result.__module__ + "." + result.__class__.__name__ if result_type == 'flask.wrappers.Response': return result # We may have got a pymacaron Error instance, in which case # it has a http_reply() method... if hasattr(result, 'http_reply'): # Let's transform this Error into a flask Response log.info("Looks like a pymacaron error instance - calling .http_reply()") return result.http_reply() # Otherwise, assume no error occured and make a flask Response out of # the result. # TODO: check that result is an instance of a model expected as response from this endpoint result_json = api_spec.model_to_json(result) # Send a Flask Response with code 200 and result_json r = jsonify(result_json) r.status_code = 200 return r