Source code for pdsc.server

"""
Implmements an HTTP server that wraps client for remote calls to PDSC
"""
from __future__ import print_function
import json
import cherrypy
from cherrypy import expose, HTTPError

from .client import PdsClient
from .metadata import json_dumps

DEFAULT_SERVER_PORT = 7372
"""
The default PDSC server port (7372 is P-D-S-C on a numeric keypad)
"""

DEFAULT_SOCKET_TIMEOUT = 10000
"""
The default PDSC server socket timeout in seconds
"""

[docs]def content_type(t): """ Creates a decorator to set the response ``Content-Type`` header for CherryPy response handlers :param t: content type :return: content type decorator """ def decorator(f): f._cp_config = { 'response.headers.Content-Type': t, } return f return decorator
[docs]class PdsServer(object): """ Implements an HTTP server for PDSC using :py:mod:`cherrypy` .. Warning:: The PDSC server is not yet designed to be robust against malicious queries. While some care has been taken to properly parse arguments to avoid SQL injection attacks, for example, a thorough review of potential security vulnerabilities has not yet been performed. """ def __init__(self, database_directory=None, socket_host='0.0.0.0', port=DEFAULT_SERVER_PORT): """ :param database_directory: location of the PDSC databases; if ``None``, the ``PDSC_DATABASE_DIR`` environment variable is used to determine the database directory :param socket_host: specifies the network interface on which the server will listen :param port: specifies the port on which the server will listen """ self.client = PdsClient(database_directory) self.socket_host = socket_host self.port = port
[docs] def start(self): """ Starts the server; this function will block until the process is interrupted or killed """ cherrypy.config.update({ 'server.socket_port' : self.port, 'server.socket_host' : self.socket_host, 'server.socket_timeout': DEFAULT_SOCKET_TIMEOUT, }) cherrypy.quickstart(self)
[docs] @content_type('application/json') @expose def query(self, instrument, conditions=None): """ Serves an interface to :py:meth:`PdsClient.query <pdsc.client.PdsClient.query>` :param instrument: PDSC instrument name :param conditions: JSON-encoded conditions of the kind described for :py:meth:`PdsClient.query <pdsc.client.PdsClient.query>` :return: JSON-encoded list of :py:class:`~pdsc.metadata.PdsMetadata` objects corresponding to observations matching the specified query conditions """ instrument = str(instrument) if conditions is None: conditions = [] else: conditions = json.loads(conditions) metadata = self.client.query(instrument, conditions) return json_dumps(metadata)
[docs] @content_type('application/json') @expose def queryByObservationId(self, instrument, observation_ids): """ Serves an interface to :py:meth:`PdsClient.query_by_observation_id <pdsc.client.PdsClient.query_by_observation_id>` :param instrument: PDSC instrument name :param observation_ids: either a JSON-encoded list of observation ids, or a single observation id string :return: JSON-encoded list of :py:class:`~pdsc.metadata.PdsMetadata` objects corresponding to observations matching the specified ``observation_ids`` """ # TODO: Handle Bad arguments instrument = str(instrument) try: observation_ids = json.loads(observation_ids) except: observation_ids = str(observation_ids) if type(observation_ids) == list: observation_ids = list(map(str, observation_ids)) else: observation_ids = str(observation_ids) metadata = self.client.query_by_observation_id( instrument, observation_ids) return json_dumps(metadata)
[docs] @content_type('application/json') @expose def queryByLatLon(self, instrument, lat, lon, radius=0): """ Serves an interface to :py:meth:`PdsClient.find_observations_of_latlon <pdsc.client.PdsClient.find_observations_of_latlon>` :param instrument: PDSC instrument name :param lat: degrees latitude :param lon: degrees east longitude :param radius: query tolerance in meters :return: JSON-encoded list of observation ids corresponding to observations within ``radius`` of the given location """ instrument = str(instrument) lat = float(lat) lon = float(lon) radius = float(radius) observations = self.client.find_observations_of_latlon( instrument, lat, lon, radius ) return json.dumps(observations)
[docs] @content_type('application/json') @expose def queryByOverlap(self, instrument, observation_id, other_instrument): """ Serves an interface to :py:meth:`PdsClient.find_observations_of_latlon <pdsc.client.PdsClient.find_observations_of_latlon>` :param instrument: PDSC instrument name for query observation :param observation_id: query observation id :param other_instrument: PDSC instrument name for target instrument :return: JSON-encoded list of observation ids corresponding to observations overlapping the given observation """ instrument = str(instrument) observation_id = str(observation_id) other_instrument = str(other_instrument) observations = self.client.find_overlapping_observations( instrument, observation_id, other_instrument ) return json.dumps(observations)