def test_with_timeouts(mock_marshal_param, minimal_swagger_spec, getPetById_spec, request_dict, timeout_kv): request_dict["url"] = "/pet/{petId}" op = CallableOperation(Operation.from_spec(minimal_swagger_spec, "/pet/{petId}", "get", getPetById_spec)) k, v = timeout_kv request = construct_request(op, request_options={k: v}, petId=34) assert request[k] == v assert mock_marshal_param.call_count == 1
def set_operation_params(self, optional=False): """ This is the main entry point. We return a set with the required and optional parameters for the provided operation / specification. :param optional: Should we set the values for the optional parameters? """ self._fix_common_spec_issues() # Make a copy of the operation operation = Operation.from_spec(self.operation.swagger_spec, self.operation.path_name, self.operation.http_method, self.operation.op_spec) for parameter_name, parameter in operation.params.iteritems(): # We make sure that all parameters have a fill attribute parameter.fill = None if self._should_skip_setting_param_value(parameter, optional): continue self._set_param_value(parameter) return operation
def set_operation_params(self, optional=False): """ This is the main entry point. We return a set with the required and optional parameters for the provided operation / specification. :param optional: Should we set the values for the optional parameters? """ self._fix_common_spec_issues() # Make a copy of the operation operation = Operation.from_spec(self.operation.swagger_spec, self.operation.path_name, self.operation.http_method, self.operation.op_spec) for parameter_name, parameter in operation.params.iteritems(): # We make sure that all parameters have a fill attribute parameter.fill = None if self._should_skip_setting_param_value(parameter, optional): continue self._set_param_value(parameter) return operation
def test_with_mocks(mock_marshal_param): get_op = minimal_swagger_spec.spec_dict['paths']['/pet/{petId}']['get'] del get_op['parameters'][0] op = Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', {}) op.construct_params(request_dict, op_kwargs={}) assert 0 == mock_marshal_param.call_count
def build_resources(swagger_spec): """Transforms the REST resources in the json-like swagger_spec into rich :Resource: objects that have associated :Operation:s. :type swagger_spec: :class:`bravado_core.spec.Spec` :returns: dict where (key,value) = (resource name, Resource) """ # Map operations to resources using operation tags if available. # - If an operation has multiple tags, it will be associated with multiple # resources! # - If an operation has no tags, its resource name will be derived from its # path # key = tag_name value = { operation_id : Operation } tag_to_operations = defaultdict(dict) paths = swagger_spec.spec_dict['paths'] for path_name, path_spec in iteritems(paths): for http_method, operation_spec in iteritems(path_spec): if http_method == 'parameters': continue operation = Operation.from_spec(swagger_spec, path_name, http_method, operation_spec) tags = operation_spec.get('tags', []) if not tags: tags.append(convert_path_to_resource(path_name)) for tag in tags: tag_to_operations[tag][operation.operation_id] = operation resources = {} for tag, operations in iteritems(tag_to_operations): resources[tag] = Resource(tag, operations) return resources
def build_resources(swagger_spec): """Transforms the REST resources in the json-like swagger_spec into rich :Resource: objects that have associated :Operation:s. :type swagger_spec: :class:`bravado_core.spec.Spec` :returns: dict where (key,value) = (resource name, Resource) """ # Map operations to resources using operation tags if available. # - If an operation has multiple tags, it will be associated with multiple # resources! # - If an operation has no tags, its resource name will be derived from its # path # key = tag_name value = { operation_id : Operation } tag_to_operations = defaultdict(dict) paths = swagger_spec.spec_dict['paths'] for path_name, path_spec in iteritems(paths): for http_method, operation_spec in iteritems(path_spec): if http_method == 'parameters': continue operation = Operation.from_spec( swagger_spec, path_name, http_method, operation_spec) tags = operation_spec.get('tags', []) if not tags: tags.append(convert_path_to_resource(path_name)) for tag in tags: tag_to_operations[tag][operation.operation_id] = operation resources = {} for tag, operations in iteritems(tag_to_operations): resources[tag] = Resource(tag, operations) return resources
def test_simple(mock_marshal_param, minimal_swagger_spec, getPetById_spec, request_dict): request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) construct_params(op, request_dict, op_kwargs={'petId': 34}) assert 1 == mock_marshal_param.call_count
def test_simple(mock_marshal_param, minimal_swagger_spec, getPetById_spec, request_dict): request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) construct_params(op, request_dict, op_kwargs={'petId': 34, 'api_key': 'foo'}) assert 2 == mock_marshal_param.call_count
def test_no_params(mock_marshal_param, minimal_swagger_spec, request_dict): get_op = minimal_swagger_spec.spec_dict['paths']['/pet/{petId}']['get'] del get_op['parameters'][0] op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', {})) construct_params(op, request_dict, op_kwargs={}) assert 0 == mock_marshal_param.call_count assert 0 == len(request_dict)
def test_with_mocks(mock_marshal_param): del getPetById_spec['parameters'][0]['required'] getPetById_spec['parameters'][0]['default'] = 99 request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) op.construct_params(request_dict, op_kwargs={}) assert 1 == mock_marshal_param.call_count
def test_no_params(mock_marshal_param, minimal_swagger_spec, request_dict): get_op = minimal_swagger_spec.spec_dict['paths']['/pet/{petId}']['get'] del get_op['parameters'][0] op = CallableOperation( Operation.from_spec(minimal_swagger_spec, '/pet/{petId}', 'get', {})) construct_params(op, request_dict, op_kwargs={}) assert 0 == mock_marshal_param.call_count assert 0 == len(request_dict)
def test_required_parameter_missing( minimal_swagger_spec, getPetById_spec, request_dict): request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) with pytest.raises(SwaggerMappingError) as excinfo: construct_params(op, request_dict, op_kwargs={}) assert 'required parameter' in str(excinfo.value)
def test_required_parameter_missing( minimal_swagger_spec, getPetById_spec, request_dict): request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) with pytest.raises(SwaggerMappingError) as excinfo: construct_params(op, request_dict, op_kwargs={}) assert 'required parameter' in str(excinfo.value)
def test_with_mocks(mock_marshal_param): del getPetById_spec['parameters'][0]['required'] getPetById_spec['parameters'][0]['default'] = 99 request_dict['url'] = '/pet/{petId}' op = OperationDecorator(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) op.construct_params(request_dict, op_kwargs={}) assert 1 == mock_marshal_param.call_count
def test_with_mocks(mock_marshal_param): get_op = minimal_swagger_spec.spec_dict['paths']['/pet/{petId}']['get'] del get_op['parameters'][0] op = OperationDecorator(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', {})) op.construct_params(request_dict, op_kwargs={}) assert 0 == mock_marshal_param.call_count assert 0 == len(request_dict)
def test_with_timeouts(_1, minimal_swagger_spec, getPetById_spec, request_dict, timeout_kv): request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) key, value = timeout_kv request = op.construct_request(petId=34, _request_options={key: value}) assert request[key] == value
def test_with_timeouts(mock_marshal_param, minimal_swagger_spec, getPetById_spec, request_dict, timeout_kv): request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) k, v = timeout_kv request = construct_request(op, request_options={k: v}, petId=34, api_key='foo') assert request[k] == v assert mock_marshal_param.call_count == 2
def test_validate_header_parameter_from_request_options( mock_marshal_param, minimal_swagger_spec, getPetById_spec, request_dict): request_dict['url'] = '/pet/{petId}' request_dict['headers']['api_key'] = 'api_key' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'delete', getPetById_spec)) construct_params(op, request_dict, op_kwargs={'petId': 1}) assert 2 == mock_marshal_param.call_count
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_non_required_parameter_with_default_used( mock_marshal_param, minimal_swagger_spec, getPetById_spec, request_dict): del getPetById_spec['parameters'][0]['required'] getPetById_spec['parameters'][0]['default'] = 99 request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) construct_params(op, request_dict, op_kwargs={'api_key': 'foo'}) assert 2 == mock_marshal_param.call_count
def test_with_timeouts( mock_marshal_param, minimal_swagger_spec, getPetById_spec, request_dict, timeout_kv, ): request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) k, v = timeout_kv request = construct_request(op, request_options={k: v}, petId=34, api_key='foo') assert request[k] == v assert mock_marshal_param.call_count == 2
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_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 build_resources(swagger_spec): # type: (Spec) -> AliasKeyDict """Transforms the REST resources in the json-like swagger_spec into rich :Resource: objects that have associated :Operation:s. :type swagger_spec: :class:`bravado_core.spec.Spec` :returns: dict where (key,value) = (resource name, Resource) """ # Map operations to resources using operation tags if available. # - If an operation has multiple tags, it will be associated with multiple # resources! # - If an operation has no tags, its resource name will be derived from its # path # key = tag_name value = { operation_id : Operation } tag_to_ops = defaultdict( dict ) # type: typing.DefaultDict[typing.Text, typing.Dict[typing.Text, Operation]] deref = swagger_spec.deref spec_dict = deref(swagger_spec._internal_spec_dict) paths_spec = deref(spec_dict.get('paths', {})) for path_name, path_spec in iteritems(paths_spec): path_spec = deref(path_spec) for http_method, op_spec in iteritems(path_spec): op_spec = deref(op_spec) # vendor extensions and parameters that are shared across all # operations for a given path are also defined at this level - we # just need to skip over them. if http_method.startswith('x-') or http_method == 'parameters': continue op = Operation.from_spec( swagger_spec, path_name, http_method, op_spec, ) tags = deref(op_spec.get('tags', [])) if not tags: tags.append(convert_path_to_resource(path_name)) for tag in tags: tag_to_ops[deref(tag)][op.operation_id] = op resources = AliasKeyDict() for tag, ops in iteritems(tag_to_ops): sanitized_tag = sanitize_name(tag) resources[sanitized_tag] = Resource(sanitized_tag, ops) resources.add_alias(tag, sanitized_tag) return resources
def test_use_msgpack( minimal_swagger_spec, getPetById_spec, ): op = CallableOperation( Operation.from_spec(minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) request = construct_request( op, request_options={ 'use_msgpack': True, }, petId=1, ) assert request['headers']['Accept'] == 'application/msgpack'
def test_use_msgpack( minimal_swagger_spec, getPetById_spec, ): op = CallableOperation( Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec ) ) request = construct_request( op, request_options={ 'use_msgpack': True, }, petId=1, ) assert request['headers']['Accept'] == 'application/msgpack'
def build_resources(swagger_spec): """Transforms the REST resources in the json-like swagger_spec into rich :Resource: objects that have associated :Operation:s. :type swagger_spec: :class:`bravado_core.spec.Spec` :returns: dict where (key,value) = (resource name, Resource) """ # Map operations to resources using operation tags if available. # - If an operation has multiple tags, it will be associated with multiple # resources! # - If an operation has no tags, its resource name will be derived from its # path # key = tag_name value = { operation_id : Operation } tag_to_ops = defaultdict(dict) deref = swagger_spec.deref spec_dict = deref(swagger_spec._internal_spec_dict) paths_spec = deref(spec_dict.get('paths', {})) for path_name, path_spec in iteritems(paths_spec): path_spec = deref(path_spec) for http_method, op_spec in iteritems(path_spec): op_spec = deref(op_spec) # vendor extensions and parameters that are shared across all # operations for a given path are also defined at this level - we # just need to skip over them. if http_method.startswith('x-') or http_method == 'parameters': continue op = Operation.from_spec(swagger_spec, path_name, http_method, op_spec) tags = deref(op_spec.get('tags', [])) if not tags: tags.append(convert_path_to_resource(path_name)) for tag in tags: tag_to_ops[deref(tag)][op.operation_id] = op resources = {} for tag, ops in iteritems(tag_to_ops): resources[tag] = Resource(tag, ops) return resources
def build_resources(swagger_spec): """Transforms the REST resources in the json-like swagger_spec into rich :Resource: objects that have associated :Operation:s. :type swagger_spec: :class:`bravado_core.spec.Spec` :returns: dict where (key,value) = (resource name, Resource) """ # Map operations to resources using operation tags if available. # - If an operation has multiple tags, it will be associated with multiple # resources! # - If an operation has no tags, its resource name will be derived from its # path # key = tag_name value = { operation_id : Operation } tag_to_ops = defaultdict(dict) deref = swagger_spec.deref spec_dict = deref(swagger_spec.spec_dict) paths_spec = deref(spec_dict.get('paths', {})) for path_name, path_spec in iteritems(paths_spec): path_spec = deref(path_spec) for http_method, op_spec in iteritems(path_spec): op_spec = deref(op_spec) # parameters that are shared across all operations for # a given path are also defined at this level - we # just need to skip over them. if http_method == 'parameters': continue op = Operation.from_spec(swagger_spec, path_name, http_method, op_spec) tags = deref(op_spec.get('tags', [])) if not tags: tags.append(convert_path_to_resource(path_name)) for tag in tags: tag_to_ops[deref(tag)][op.operation_id] = op resources = {} for tag, ops in iteritems(tag_to_ops): resources[tag] = Resource(tag, ops) return resources
def test_use_msgpack( minimal_swagger_spec, getPetById_spec, ): op = CallableOperation( Operation.from_spec(minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) request_options = { 'use_msgpack': True, 'headers': { 'Some-Header': 'header-value' } } # type: Dict[str, Any] request = construct_request( op, request_options=request_options, petId=1, ) assert request['headers']['Accept'] == 'application/msgpack' assert request['headers']['Some-Header'] == 'header-value', \ "Requested header should be present" assert 'Accept' not in request_options['headers'], \ "Original request options should not be modified"
def test_with_mocks(mock_marshal_param): request_dict['url'] = '/pet/{petId}' op = OperationDecorator(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) op.construct_params(request_dict, op_kwargs={'petId': 34}) assert 1 == mock_marshal_param.call_count
def test_extra_parameter_error(minimal_swagger_spec, request_dict): op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', {})) with pytest.raises(SwaggerMappingError) as excinfo: construct_params(op, request_dict, op_kwargs={'extra_param': 'bar'}) assert 'does not have parameter' in str(excinfo.value)
def call_on_each_endpoint(self, callback): """Find all server endpoints defined in the swagger spec and calls 'callback' for each, with an instance of EndpointData as argument. """ if 'paths' not in self.swagger_dict: return for path, d in self.swagger_dict['paths'].items(): for method, op_spec in d.items(): data = EndpointData(path, method) # Which server method handles this endpoint? if 'x-bind-server' not in op_spec: if 'x-no-bind-server' in op_spec: # That route should not be auto-generated log.info("Skipping generation of %s %s" % (method, path)) continue else: raise Exception("Swagger api defines no x-bind-server for %s %s" % (method, path)) data.handler_server = op_spec['x-bind-server'] # Make sure that endpoint only produces 'application/json' if 'produces' not in op_spec: raise Exception("Swagger api has no 'produces' section for %s %s" % (method, path)) if len(op_spec['produces']) != 1: raise Exception("Expecting only one type under 'produces' for %s %s" % (method, path)) if op_spec['produces'][0] == 'application/json': data.produces_json = True elif op_spec['produces'][0] == 'text/html': data.produces_html = True else: raise Exception("Only 'application/json' or 'text/html' are supported. See %s %s" % (method, path)) # Which client method handles this endpoint? if 'x-bind-client' in op_spec: data.handler_client = op_spec['x-bind-client'] # Should we decorate the server handler? if 'x-decorate-server' in op_spec: data.decorate_server = op_spec['x-decorate-server'] # Should we manipulate the requests parameters? if 'x-decorate-request' in op_spec: data.decorate_request = op_spec['x-decorate-request'] # Generate a bravado-core operation object data.operation = Operation.from_spec(self.spec, path, method, op_spec) # Figure out how parameters are passed: one json in body? one or # more values in query? if 'parameters' in op_spec: params = op_spec['parameters'] for p in params: if p['in'] == 'body': data.param_in_body = True if p['in'] == 'query': data.param_in_query = True if p['in'] == 'path': data.param_in_path = True if data.param_in_path: # Substitute {...} with <...> in path, to make a Flask friendly path data.path = data.path.replace('{', '<').replace('}', '>') if data.param_in_body and data.param_in_query: raise Exception("Cannot support params in both body and param (%s %s)" % (method, path)) else: data.no_params = True callback(data)
def test_success(minimal_swagger_spec, getPetById_spec, request_dict): request_dict['url'] = '/pet/{petId}' op = CallableOperation( Operation.from_spec(minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) assert op.__doc__.startswith('[GET] Find pet by ID')
def test_with_mocks(mock_marshal_param): request_dict['url'] = '/pet/{petId}' op = Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec) op.construct_params(request_dict, op_kwargs={'petId': 34}) assert 1 == mock_marshal_param.call_count
def test_extra_parameter_error(minimal_swagger_spec, request_dict): op = CallableOperation( Operation.from_spec(minimal_swagger_spec, '/pet/{petId}', 'get', {})) with pytest.raises(SwaggerMappingError) as excinfo: construct_params(op, request_dict, op_kwargs={'extra_param': 'bar'}) assert 'does not have parameter' in str(excinfo.value)
def test_success(minimal_swagger_spec, getPetById_spec, request_dict): request_dict['url'] = '/pet/{petId}' op = CallableOperation(Operation.from_spec( minimal_swagger_spec, '/pet/{petId}', 'get', getPetById_spec)) assert op.__doc__.startswith('[GET] Find pet by ID')