def configure(self, config: ConfigParams): """ Configures this HttpEndpoint using the given configuration parameters. ### Configuration parameters ### - connection(s) - the connection resolver's connections; - "connection.discovery_key" - the key to use for connection resolving in a dis - "connection.protocol" - the connection's protocol; - "connection.host" - the target host; - "connection.port" - the target port; - "connection.uri" - the target URI. - "credential.ssl_key_file" - SSL private key in PEM - "credential.ssl_crt_file" - SSL certificate in PEM - "credential.ssl_ca_file" - Certificate authority (root certificate) in PEM :param config: configuration parameters, containing a "connection(s)" section. """ config = config.set_defaults(GrpcEndpoint.__defaultConfig) self.__connection_resolver.configure(config) self.__maintenance_enabled = config.get_as_boolean_with_default('options.maintenance_enabled', self.__maintenance_enabled) self.__file_max_size = ConfigParams().get_as_long_with_default(key='options.file_max_size', default_value=self.__file_max_size)
def _configure_service(self): # Configure Facade service service = self.get_one_required( Descriptor('pip-services', 'endpoint', 'http', 'default', '*')) service.configure( ConfigParams.from_tuples( 'root_path', '', # '/api/1.0', 'connection.protocol', 'http', 'connection.host', 'localhost', 'connection.port', 3000))
def read_config(correlation_id: Optional[str], path: str, parameters: ConfigParams) -> ConfigParams: """ Reads configuration from a file, parameterize it with given values and returns a new ConfigParams object. :param correlation_id: (optional) transaction id to trace execution through call chain. :param path: a path to configuration file. :param parameters: values to parameters the configuration. :return: ConfigParams configuration. """ value = JsonConfigReader(path).read_object_(correlation_id, parameters) return ConfigParams.from_value(value)
def setup_class(cls): if cls.mongoUri is None and cls.mongoHost is None: return db_config = ConfigParams.from_tuples('connection.uri', cls.mongoUri, 'connection.host', cls.mongoHost, 'connection.port', cls.mongoPort, 'connection.database', cls.mongoDatabase) cls.connection = MongoDbConnection() cls.connection.configure(db_config) cls.persistence = DummyMongoDbPersistence() cls.persistence.set_references( References.from_tuples( Descriptor("pip-services", "connection", "mongodb", "default", "1.0"), cls.connection)) cls.fixture = DummyPersistenceFixture(cls.persistence) cls.connection.open(None) cls.persistence.open(None) cls.persistence.clear(None)
def test_configure_if_path_key_check_property(self): file_name = '../test_JsonFilePersister' self._persister.configure(ConfigParams.from_tuples('path', file_name)) assert file_name == self._persister.path
def test_configure_with_no_path_key(self): try: self._persister.configure(ConfigParams()) except Exception: assert Exception is not None assert isinstance(Exception, ConfigException)
class GrpcService(IOpenable, IConfigurable, IRegisterable, IUnreferenceable): """ Abstract service that receives remove calls via GRPC protocol. ### Configuration parameters ### - dependencies: - endpoint: override for GRPC Endpoint dependency - controller: override for Controller dependency - connection(s): - discovery_key: (optional) a key to retrieve the connection from :class:`IDiscovery <pip_services3_components.connect.IDiscovery.IDiscovery>` - protocol: connection protocol: http or https - host: host name or IP address - port: port number - uri: resource URI or connection string with all parameters in it - credential - the HTTPS credentials: - ssl_key_file: the SSL private key in PEM - ssl_crt_file: the SSL certificate in PEM - ssl_ca_file: the certificate authorities (root cerfiticates) in PEM .. code-block:: python class MyGrpcService(GrpcService): __controller: IMyController ... def __init__(self): suoer().__init__('... path to proto ...', '.. service name ...') self._dependency_resolver.put( "controller", Descriptor("mygroup","controller","*","*","1.0") ) def set_references(self, references): super().set_references(references) self._controller = this._dependency_resolver.get_required("controller") def register(self): def method(correlation_id, args, getted_method): correlationId = call.request.correlationId; id = call.request.id; self._controller.getMyData(correlationId, id, callback); self.register_commadable_method("get_mydata", None, method) ... service = MyGrpcService() service.configure(ConfigParams.from_tuples( "connection.protocol", "http", "connection.host", "localhost", "connection.port", 8080 )) service.set_references(References.from_tuples( Descriptor("mygroup","controller","default","default","1.0"), controller )) service.open("123") """ __default_config = ConfigParams.from_tuples("dependencies.endpoint", "*:endpoint:grpc:*:1.0") def __init__(self, service_name: str = None): """ Creates a new instance of the service. :param service_name: a service name. """ self.__config: ConfigParams = None self.__references: IReferences = None self.__local_endpoint: bool = None self.__implementation: Any = {} self.__interceptors: List[Any] = [] self.__opened: bool = None # The GRPC endpoint that exposes this service. self._endpoint: GrpcEndpoint = None # The dependency resolver. self._dependency_resolver = DependencyResolver( GrpcService.__default_config) # The logger. self._logger = CompositeLogger() # The performance counters. self._counters = CompositeCounters() self.__service_name = service_name self.__registrable = lambda implementation: self._register_service( implementation) def configure(self, config: ConfigParams): """ Configures component by passing configuration parameters. :param config: configuration parameters to be set. """ config = config.set_defaults(GrpcService.__default_config) self.__config = config self._dependency_resolver.configure(config) def set_references(self, references: IReferences): """ Sets references to this endpoint's logger, counters, and connection resolver. ### References ### - logger: **"\*:logger:\*:\*:1.0"** - counters: **"\*:counters:\*:\*:1.0"** - discovery: **"\*:discovery:\*:\*:1.0"** (for the connection resolver) :param references: an IReferences object, containing references to a logger, counters, and a connection resolver. """ self._logger.set_references(references) self._counters.set_references(references) self._dependency_resolver.set_references(references) # Get endpoint self._endpoint = self._dependency_resolver.get_one_optional('endpoint') # Or create a local one if self._endpoint is None: self._endpoint = self.__create_endpoint() self.__local_endpoint = True else: self.__local_endpoint = False # Add registration callback to the endpoint self._endpoint.register(self) def unset_references(self): """ Unsets (clears) previously set references to dependent components. """ # Remove registration callback from endpoint if self._endpoint is not None: self._endpoint.unregister(self.__registrable) self._endpoint = None def __create_endpoint(self) -> GrpcEndpoint: endpoint = GrpcEndpoint() if self.__config: endpoint.configure(self.__config) if self.__references: endpoint.set_references(self.__references) return endpoint def _instrument(self, correlation_id: Optional[str], name: str) -> CounterTiming: """ Adds instrumentation to log calls and measure call time. It returns a CounterTiming object that is used to end the time measurement. :param correlation_id: (optional) transaction id to trace execution through call chain. :param name: a method name. :return: CounterTiming object to end the time measurement. """ self._logger.trace(correlation_id, 'Executing {} method'.format(name)) self._counters.increment_one(name + '.exec_time') return self._counters.begin_timing(name + '.exec_time') def _instrument_error(self, correlation_id: Optional[str], name: str, err: Exception, reerror=False): """ Adds instrumentation to error handling. :param correlation_id: (optional) transaction id to trace execution through call chain. :param name: a method name. :param err: an occured error :param reerror: if true - throw error """ if err is not None: self._logger.error(correlation_id, err, 'Failed to execute {} method'.format(name)) self._counters.increment_one(name + '.exec_errors') if reerror: raise err def is_open(self) -> bool: """ Checks if the component is opened. :return: true if the component has been opened and false otherwise. """ return self.__opened def open(self, correlation_id: Optional[str]): """ Opens the component. :param correlation_id: (optional) transaction id to trace execution through call chain. """ # TODO maybe need add async if self.__opened: return None if self._endpoint is None: self._endpoint = self.__create_endpoint() self._endpoint.register(self) self.__local_endpoint = True if self.__local_endpoint: try: self._endpoint.open(correlation_id) self.__opened = True except Exception as ex: self.__opened = False raise ex else: self.__opened = True def close(self, correlation_id: Optional[str]): """ Closes component and frees used resources. :param correlation_id: (optional) transaction id to trace execution through call chain. """ if not self.__opened: return None if self._endpoint is None: raise InvalidStateException(correlation_id, 'NO_ENDPOINT', 'HTTP endpoint is missing') if self.__local_endpoint: self._endpoint.close(correlation_id) self.__opened = False def register_commadable_method( self, method: str, schema: Schema, action: Callable[[Optional[str], Optional[str], Parameters], None]): """ Registers a commandable method in this objects GRPC server (service) by the given name. :param method: the GRPC method name. :param schema: the schema to use for parameter validation. :param action: the action to perform at the given route. """ self._endpoint._register_commandable_method(method, schema, action) def _register_interceptor(self, action: Callable): """ Registers a middleware for methods in GRPC endpoint. :param action: an action function that is called when middleware is invoked. """ if self._endpoint is not None: self._endpoint._register_interceptor(action) def _register_service(self, implementation: 'GrpcService'): # self.register() if self._endpoint is not None: self._endpoint.register_service(implementation) @abstractmethod def register(self): """
# -*- coding: utf-8 -*- from pip_services3_commons.refer.Descriptor import Descriptor from pip_services3_commons.config.ConfigParams import ConfigParams from pip_services3_commons.refer.References import References from ..DummyController import DummyController from ..services.DummyGrpcService import DummyGrpcService from .DummyGrpcClient import DummyGrpcClient from .DummyClientFixture import DummyClientFixture grpc_config = ConfigParams.from_tuples( "connection.protocol", "http", "connection.host", "localhost", "connection.port", 3000 ) class TestDummyGrpcClient: service = None client = None fixture = None @classmethod def setup_class(cls): ctrl = DummyController() cls.service = DummyGrpcService() cls.service.configure(grpc_config) references = References.from_tuples(
class GrpcEndpoint(IOpenable, IConfigurable, IReferenceable): """ Used for creating GRPC endpoints. An endpoint is a URL, at which a given service can be accessed by a client. ### Configuration parameters ### Parameters to pass to the [[configure]] method for component configuration: - connection(s) - the connection resolver's connections: - "connection.discovery_key" - the key to use for connection resolving in a discovery service; - "connection.protocol" - the connection's protocol; - "connection.host" - the target host; - "connection.port" - the target port; - "connection.uri" - the target URI. - credential - the HTTPS credentials: - "credential.ssl_key_file" - the SSL private key in PEM - "credential.ssl_crt_file" - the SSL certificate in PEM - "credential.ssl_ca_file" - the certificate authorities (root cerfiticates) in PEM ### References ### A logger, counters, and a connection resolver can be referenced by passing the following references to the object's [[setReferences]] method: - logger: <code>"\*:logger:\*:\*:1.0"</code>; - counters: <code>"\*:counters:\*:\*:1.0"</code>; - discovery: <code>"\*:discovery:\*:\*:1.0"</code> (for the connection resolver). ### Examples ### TODO!!! """ __defaultConfig = ConfigParams.from_tuples( "connection.protocol", "http", "connection.host", "0.0.0.0", "connection.port", 3000, "credential.ssl_key_file", None, "credential.ssl_crt_file", None, "credential.ssl_ca_file", None, "options.maintenance_enabled", None, "options.request_max_size", 1024 * 1024, "options.file_max_size", 200 * 1024 * 1024, "options.connect_timeout", 60000, "options.debug", None ) __server = None __connection_resolver = HttpConnectionResolver() __logger = CompositeLogger() __counters = CompositeCounters() __maintenance_enabled = False __file_max_size = 200 * 1024 * 1024 __uri = None __registrations = [] __commandable_methods = None __commandable_schemas = None __commandable_service = None __interceptors = [] def configure(self, config): """ Configures this HttpEndpoint using the given configuration parameters. __Configuration parameters:__ - __connection(s)__ - the connection resolver's connections; - "connection.discovery_key" - the key to use for connection resolving in a dis - "connection.protocol" - the connection's protocol; - "connection.host" - the target host; - "connection.port" - the target port; - "connection.uri" - the target URI. - "credential.ssl_key_file" - SSL private key in PEM - "credential.ssl_crt_file" - SSL certificate in PEM - "credential.ssl_ca_file" - Certificate authority (root certificate) in PEM :param config: configuration parameters, containing a "connection(s)" section. """ config = config.set_defaults(GrpcEndpoint.__defaultConfig) self.__connection_resolver.configure(config) self.__maintenance_enabled = config.get_as_boolean_with_default('options.maintenance_enabled', self.__maintenance_enabled) self.__file_max_size = ConfigParams().get_as_long_with_default(key='options.file_max_size', default_value=self.__file_max_size) def set_references(self, references): """ Sets references to this endpoint's logger, counters, and connection resolver. __References:__ - logger: <code>"\*:logger:\*:\*:1.0"</code> - counters: <code>"\*:counters:\*:\*:1.0"</code> - discovery: <code>"\*:discovery:\*:\*:1.0"</code> (for the connection resolver) :param references: an IReferences object, containing references to a logger, counters, and a connection resolver. """ self.__logger.set_references(references) self.__counters.set_references(references) self.__connection_resolver.set_references(references) def is_open(self): """ :return: whether or not this endpoint is open with an actively listening GRPC server. """ return self.__server is not None def open(self, correlation_id, max_workers=15): """ Opens a connection using the parameters resolved by the referenced connection resolver and creates a GRPC server (service) using the set options and parameters. :param correlation_id: (optional) transaction id to trace execution through call chain. :param max_workers: max count of workers in thread pool. """ if self.is_open(): return connection = self.__connection_resolver.resolve(correlation_id) self.__uri = connection.get_uri() try: self.__connection_resolver.register(correlation_id) credentials = None if connection.get_protocol('http') == 'https': ssl_key_file = connection.get_as_nullable_string('ssl_key_file') ssl_crt_file = connection.get_as_nullable_string('ssl_crt_file') with open(ssl_key_file, 'rb') as file: private_key = file.read() with open(ssl_crt_file, 'rb') as file: certificate = file.read() credentials = grpc.ssl_server_credentials(((private_key, certificate),)) # Create instance of express application if len(self.__interceptors) > 0: self.__server = grpc.server(futures.ThreadPoolExecutor(max_workers=max_workers), interceptors=self.__interceptors) else: self.__server = grpc.server(futures.ThreadPoolExecutor(max_workers=max_workers)) if credentials: self.__server.add_secure_port(str(connection.get_host()) + ':' + str(connection.get_port()), credentials) else: self.__server.add_insecure_port(str(connection.get_host()) + ':' + str(connection.get_port())) # Start operations self.__server.start() self.__connection_resolver.register(correlation_id) self.__perform_registrations() self.__logger.debug(correlation_id, 'Opened GRPC service at {}'.format(self.__uri)) except Exception as ex: self.__server = None err = ConnectionException(correlation_id, 'CANNOT_CONNECT', 'Opening GRPC service failed').wrap( ex).with_details('url', self.__uri) raise err def close(self, correlation_id): """ Closes this endpoint and the GRPC server (service) that was opened earlier. :param correlation_id: (optional) transaction id to trace execution through call chain. """ if self.__server is not None: self.__uri = None self.__commandable_methods = None self.__commandable_schemas = None self.__commandable_service = None # Eat exceptions try: self.__server.stop(None) self.__logger.debug(correlation_id, 'Closed GRPC service at {}'.format(self.__uri)) self.__server = None GrpcEndpoint.__interceptors = [] GrpcEndpoint.__registrations = [] GrpcEndpoint.__connection_resolver = HttpConnectionResolver() except Exception as ex: self.__logger.warn(correlation_id, 'Failed while closing GRPC service: '.format(ex)) def register(self, registration): """ Registers a registerable object for dynamic endpoint discovery. :param registration: the registration to add. :return: """ if registration is not None: self.__registrations.append(registration) def unregister(self, registration): """ Unregisters a registerable object, so that it is no longer used in dynamic endpoint discovery. :param registration: the registration to remove. """ self.__registrations = list(filter(lambda r: r == registration, self.__registrations)) def __perform_registrations(self): for registration in self.__registrations: registration.register() self.__register_commandable_service() def __register_commandable_service(self): if self.__commandable_methods is None: return self.__commandable_service = _CommandableMediator() self.__commandable_service.invoke_func(self.__invoke_commandable_method) self.register_service(self.__commandable_service) def __invoke_commandable_method(self, request, context): method = request.method action = self.__commandable_methods[method] if self.__commandable_methods else None correlation_id = request.correlation_id response = commandable_pb2.InvokeReply() # Handle method not found if action is None: err = InvocationException(correlation_id, 'METHOD_NOT_FOUND', 'Method ' + method + ' was not found').with_details('method', method) resp_err = ErrorDescriptionFactory.create(err) response.error = commandable_pb2.ErrorDescription() response.error.category = resp_err.category response.error.code = resp_err.code response.error.correlation_id = resp_err.correlation_id response.error.status = resp_err.status response.error.message = resp_err.message response.error.cause = resp_err.cause response.error.stack_trace = resp_err.stack_trace response.error.details.addAll(resp_err.details) response.resultEmpty = True response.resultJson = '' return response try: # Convert arguments args_empty = request.args_empty args_json = request.args_json args = Parameters.from_json(args_json) if not args_empty and args_json else Parameters() try: result = action(correlation_id, args, method) response.result_empty = result is None if type(result) is DataPage: response.result_json = json.dumps(result.to_json()) else: response.result_json = json.dumps(result) if result is not None else '' # TODO: Validate schema schema = self.__commandable_schemas[method] if schema: pass except Exception as ex: # Process result and generate response resp_err = ErrorDescriptionFactory.create(ex) response.error.category = resp_err.category response.error.code = resp_err.code response.error.correlation_id = resp_err.correlation_id response.error.status = resp_err.status response.error.message = resp_err.message response.error.cause = resp_err.cause response.error.stack_trace = resp_err.stack_trace response.error.details.update(resp_err.details) response.result_empty = True response.result_json = '' except Exception as ex: # Handle unexpected exception err = InvocationException(correlation_id, 'METHOD_FAILED', 'Method ' + method + ' failed').wrap( ex).with_details('method', method) resp_err = ErrorDescriptionFactory.create(err) response.error.category = resp_err.category response.error.code = resp_err.code response.error.correlation_id = resp_err.correlation_id response.error.status = resp_err.status response.error.message = resp_err.message response.error.cause = resp_err.cause response.error.stack_trace = resp_err.stack_trace response.error.details.update(resp_err.details) response.result_empty = True response.result_json = '' return response def register_service(self, service): """ Registers a service with related implementation :param service: a GRPC service object. """ service.add_servicer_to_server(self.__server) def register_commandable_method(self, method, schema, action): """ Registers a commandable method in this objects GRPC server (service) by the given name., :param method: the GRPC method name. :param schema: the schema to use for parameter validation. :param action: the action to perform at the given route. """ self.__commandable_methods = self.__commandable_methods or {} self.__commandable_methods[method] = action self.__commandable_schemas = self.__commandable_schemas or {} self.__commandable_schemas[method] = schema def register_interceptor(self, interceptor): self.__interceptors.append(interceptor)
# -*- coding: utf-8 -*- import datetime import json import requests from pip_services3_commons.config.ConfigParams import ConfigParams from pip_services3_rpc.services.HeartbeatRestService import HeartbeatRestService rest_config = ConfigParams.from_tuples('connection.protocol', 'http', 'connection.host', 'localhost', 'connection.port', 3003) class TestHeartBeatrestService: service = None rest = None @classmethod def setup_class(cls): cls.service = HeartbeatRestService() cls.service.configure(rest_config) def setup_method(self, method): self.service.open(None) def teardown_method(self, method): self.service.close(None) def test_status(self):
class GrpcService(IOpenable, IConfigurable, IRegisterable, IUnreferenceable): """ Abstract service that receives remove calls via GRPC protocol. ### Configuration parameters ### - dependencies: - endpoint: override for GRPC Endpoint dependency - controller: override for Controller dependency - connection(s): - discovery_key: (optional) a key to retrieve the connection from [[https://rawgit.com/pip-services-node/pip-services3-components-node/master/doc/api/interfaces/connect.idiscovery.html IDiscovery]] - protocol: connection protocol: http or https - host: host name or IP address - port: port number - uri: resource URI or connection string with all parameters in it - credential - the HTTPS credentials: - ssl_key_file: the SSL private key in PEM - ssl_crt_file: the SSL certificate in PEM - ssl_ca_file: the certificate authorities (root cerfiticates) in PEM ### Example ### TODO!!! """ __default_config = ConfigParams.from_tuples("dependencies.endpoint", "*:endpoint:grpc:*:1.0") __service_name = None __config = None __references = None __local_endpoint = None __registrable = None __implementation = {} __interceptors = [] __opened = None # The GRPC endpoint that exposes this service. _endpoint = None # The dependency resolver. _dependency_resolver = DependencyResolver(__default_config) # The logger. _logger = CompositeLogger() # The performance counters. _counters = CompositeCounters() def __init__(self, service_name=None): self.__service_name = service_name self.__registrable = lambda: self._register_service() def configure(self, config): """ Configures component by passing configuration parameters. :param config: configuration parameters to be set. """ config = config.set_defaults(GrpcService.__default_config) self.__config = config self._dependency_resolver.configure(config) def set_references(self, references): """ Sets references to this endpoint's logger, counters, and connection resolver. __References:__ - logger: <code>"\*:logger:\*:\*:1.0"</code> - counters: <code>"\*:counters:\*:\*:1.0"</code> - discovery: <code>"\*:discovery:\*:\*:1.0"</code> (for the connection resolver) :param references: an IReferences object, containing references to a logger, counters, and a connection resolver. """ self._logger.set_references(references) self._counters.set_references(references) self._dependency_resolver.set_references(references) # Get endpoint self._endpoint = self._dependency_resolver.get_one_optional('endpoint') # Or create a local one if self._endpoint is None: self._endpoint = self.__create_endpoint() self.__local_endpoint = True else: self.__local_endpoint = False # Add registration callback to the endpoint self._endpoint.register(self) def unset_references(self): """ Unsets (clears) previously set references to dependent components. """ # Remove registration callback from endpoint if self._endpoint is not None: self._endpoint.unregister(self.__registrable) self._endpoint = None def __create_endpoint(self): endpoint = GrpcEndpoint() if self.__config: endpoint.configure(self.__config) if self.__references: endpoint.set_references(self.__references) return endpoint def _instrument(self, correlation_id, name): """ Adds instrumentation to log calls and measure call time. It returns a Timing object that is used to end the time measurement. :param correlation_id: (optional) transaction id to trace execution through call chain. :param name: a method name. :return: Timing object to end the time measurement. """ self._logger.trace(correlation_id, 'Executing {} method'.format(name)) self._counters.increment_one(name + '.exec_time') return self._counters.begin_timing(name + '.exec_time') def _instrument_error(self, correlation_id, name, err, reerror=False): if err is not None: self._logger.error(correlation_id, err, 'Failed to execute {} method'.format(name)) self._counters.increment_one(name + '.exec_errors') if reerror: raise reerror def is_open(self): """ Checks if the component is opened. :return: true if the component has been opened and false otherwise. """ return self.__opened def open(self, correlation_id): """ Opens the component. :param correlation_id: (optional) transaction id to trace execution through call chain. :param callback: callback function that receives error or null no errors occured. """ # TODO maybe need add async if self.__opened: return None if self._endpoint is None: self._endpoint = self.__create_endpoint() self._endpoint.register(self) self.__local_endpoint = True if self.__local_endpoint: try: self._endpoint.open(correlation_id) self.__opened = True except Exception as ex: self.__opened = False raise ex else: self.__opened = True def close(self, correlation_id): """ Closes component and frees used resources. :param correlation_id: (optional) transaction id to trace execution through call chain. :param callback: callback function that receives error or null no errors occured. """ if not self.__opened: return None if self._endpoint is None: raise InvalidStateException(correlation_id, 'NO_ENDPOINT', 'HTTP endpoint is missing') if self.__local_endpoint: self._endpoint.close(correlation_id) self.__opened = False def register_commadable_method(self, method, schema, action): self._endpoint.register_commandable_method(method, schema, action) def _register_interceptor(self, action): if self._endpoint is not None: self._endpoint.register_interceptor(action) def _register_service(self, implementation): # self.register() if self._endpoint is not None: self._endpoint.register_service(implementation) def register(self): """
class GrpcClient(IOpenable, IReferenceable, IConfigurable): """ Abstract client that calls remove endpoints using GRPC protocol. ### Configuration parameters ### - connection(s): - discovery_key: (optional) a key to retrieve the connection from [[link]] - protocol: connection protocol: http or https - host: host name or IP address - port: port number - uri: resource URI or connection string with all parameters in it - options: - retries: number of retries (default: 3) - connect_timeout: connection timeout in milliseconds (default: 10 sec) - timeout: invocation timeout in milliseconds (default: 10 sec) # TODO description """ _default_config = ConfigParams.from_tuples( "connection.protocol", "http", "connection.host", "0.0.0.0", "connection.port", 3000, "options.request_max_size", 1024 * 1024, "options.connect_timeout", 10000, "options.timeout", 10000, "options.retries", 3, "options.debug", True) __client = None __client_name = None # The GRPC client chanel __channel = None # The connection resolver. __connetction_resolver = HttpConnectionResolver() # The logger. __logger = CompositeLogger() # The performance counters. __counters = CompositeCounters() # The configuration options. __options = ConfigParams() # The connection timeout in milliseconds. __connection_timeout = 100000 # The invocation timeout in milliseconds. __timeout = 100000 # The remote service uri which is calculated on open. __uri = None def __init__(self, client_name): self.__client_name = client_name def configure(self, config): """ Configures component by passing configuration parameters. :param config: configuration parameters to be set. """ config = config.set_defaults(GrpcClient._default_config) self.__connetction_resolver.configure(config) self.__options = self.__options.override(config.get_section('options')) self.__connection_timeout = config.get_as_integer_with_default( 'options.connect_timeout', self.__connection_timeout) self.__timeout = config.get_as_integer_with_default( 'options.timeout', self.__timeout) def set_references(self, references): """ Sets references to dependent components. :param references: references to locate the component dependencies. """ self.__logger.set_references(references) self.__counters.set_references(references) self.__connetction_resolver.set_references(references) def _instrument(self, correlation_id, name): """ Adds instrumentation to log calls and measure call time. It returns a Timing object that is used to end the time measurement. :param correlation_id: (optional) transaction id to trace execution through call chain. :param name: a method name. :return: Timing object to end the time measurement. """ self.__logger.trace(correlation_id, 'Executing {} method'.format(name)) self.__counters.increment_one(name + '.call_count') return self.__counters.begin_timing(name + '.call_time') def _instrument_error(self, correlation_id, name, err, reerror=False): """ Adds instrumentation to error handling. :param correlation_id: (optional) transaction id to trace execution through call chain. :param name: a method name. :param err: an occured error :param reerror: if true - throw error """ if err is not None: self.__logger.error(correlation_id, err, 'Failed to call {} method'.format(name)) self.__counters.increment_one(name + '.call_errors') if reerror is not None and reerror is True: raise err def is_open(self): """ Checks if the component is opened. :return: Returns true if the component has been opened and false otherwise. """ return self.__channel is not None def open(self, correlation_id): """ Opens the component. :param correlation_id: (optional) transaction id to trace execution through call chain. :return: Future that receives error or null no errors occured. """ if self.is_open(): return None try: connection = self.__connetction_resolver.resolve(correlation_id) self.__uri = connection.get_uri() options = [('grpc.max_connection_idle_ms', self.__connection_timeout), ('grpc.client_idle_timeout_ms', self.__timeout)] if connection.get_protocol('http') == 'https': ssl_ca_file = connection.get_as_nullable_string('ssl_ca_file') with open(ssl_ca_file, 'rb') as file: trusted_root = file.read() credentials = grpc.ssl_channel_credentials(trusted_root) channel = grpc.secure_channel(str(connection.get_host()) + ':' + str(connection.get_port()), credentials=credentials, options=options) else: channel = grpc.insecure_channel(str(connection.get_host()) + ':' + str(connection.get_port()), options=options) self.__channel = channel except Exception as ex: raise ConnectionException( correlation_id, 'CANNOT_CONNECT', 'Opening GRPC client failed').wrap(ex).with_details( 'url', self.__uri) def close(self, correlation_id): """ Closes component and frees used resources. :param correlation_id: (optional) transaction id to trace execution through call chain. :return: Future that receives error or null no errors occured. """ if self.__channel is not None: # Eat exceptions try: self.__logger.debug( correlation_id, 'Closed GRPC service at {}'.format(self.__uri)) except Exception as ex: self.__logger.warn( correlation_id, 'Failed while closing GRPC service: {}'.format(ex)) # if self.__client is not None: # self.__client = None self.__channel.close() self.__channel = None self.__uri = None GrpcClient.__connetction_resolver = HttpConnectionResolver() def call(self, method, client, request): """ Calls a remote method via GRPC protocol. :param client: current client :param request: (optional) request object. :return: (optional) Future that receives result object or error. """ client = client(self.__channel) executor = futures.ThreadPoolExecutor(max_workers=1) response = executor.submit(client.__dict__[method], request).result() # TODO: check this realization return response def add_filter_params(self, params, filter): """ AddFilterParams method are adds filter parameters (with the same name as they defined) to invocation parameter map. :param params: invocation parameters. :param filter: (optional) filter parameters :return: invocation parameters with added filter parameters. """ params = StringValueMap() if params is None else params if filter is not None: for k in filter.keys(): params.put(k, filter[k]) return params def add_paging_params(self, params, paging): """ AddPagingParams method are adds paging parameters (skip, take, total) to invocation parameter map. :param params: invocation parameters. :param paging: (optional) paging parameters :return: invocation parameters with added paging parameters. """ params = StringValueMap() if params is None else params if paging is not None: params.put('total', paging.total) if paging.skip is not None: params.put('skip', paging.skip) if paging.take is not None: params.put('take', paging.take) return params
from ..services.DummyGrpcService import DummyGrpcService from .DummyGrpcClient import DummyGrpcClient from .DummyClientFixture import DummyClientFixture def get_fullpath(filepath): return os.path.join(os.path.dirname(__file__), filepath) port = 3000 grpc_config = ConfigParams.from_tuples( 'connection.protocol', 'https', 'connection.host', 'localhost', 'connection.port', port, 'credential.ssl_key_file', get_fullpath('../credentials/ssl_key_file'), 'credential.ssl_crt_file', get_fullpath('../credentials/ssl_crt_file'), 'credential.ssl_ca_file', get_fullpath('../credentials/ssl_ca_file') ) class TestCredentialsDummyGrpcClient: service = None client = None fixture = None @classmethod def setup_class(cls): ctrl = DummyController()