"""Service connector. The service connectors facilitates how Grass proxies requests. It defines the destination, the protocol used and how the initial request needs to be transformed to work with the underlying services. For now, our most common use case is simple: all our services are REST services, and we only use domain name resolution. In the future, we might add service discovery (so we will have to reverse the service name to the machine ip instead or using DNS). Note: Since Grass is Restful, all protocol proxies should have a `get`, `post`, `put`, `patch`, `delete`, `head`, and `options` methods. """ from urllib.parse import urljoin from grass.protocols import utils class Service: """Service definition.""" def __init__(self, protocol): """Create the service. Args: protocol (module): protocol for the service. All protocols available live within `grass/protocols`. """ assert protocol, 'The protocol is required.' utils.validate_protocol(protocol) self.protocol = protocol def resolve(self, method, url): """Resolve the url for the service. Resolves a method and a url into information that is easy to consume by the protocol used by the specific service. For instance: for a service based on REST, this method will be used to recreate which url needs to be fetched by the protocol. But for some others, a service could have a different mechanism: RabbitMQ for instance works with messages instead of urls. Args: method (str): the http verb (get, post, head) in uppercase. url (str): the url fetched. """ raise NotImplementedError() class OrchardWebService(Service): """Orchard Web Service Definition.""" def __init__( self, domain, url_protocol='https', port=None, root='/', protocol=None ): """Create an Orchard Web Service. Args: domain (str): the bare domain name (no trailing slash). root (str): Useful if the service is mapped several times and the request needs to be point to a different root on the service. url_protocol (str): url protocol (defaults to http). port (int): the service's port number. protocol (module): protocol (one of the module in /protocols/.) """ super().__init__(protocol) self.domain = domain self.root = root self.url_protocol = url_protocol self.port = port def resolve(self, method, url): """Resolve the url for the service. See: Service.resolve for more details. """ base_domain = f'{self.domain}:{self.port}' if self.port else self.domain base = f'{self.url_protocol}://{base_domain}{self.root}' if not url: return base elif url.startswith('/'): return urljoin(base, url[1:]) return urljoin(base, url)