Ejemplo n.º 1
0
    def route(self, method, route, handler, nodoc=False, resource=None):
        """
        Define a route for your REST resource.

        :param method: The HTTP method, e.g. 'GET', 'POST', 'PUT', 'PATCH'
        :type method: str
        :param route: The route, as a list of path params relative to the
            resource root. Elements of this list starting with ':' are assumed
            to be wildcards.
        :type route: tuple[str]
        :param handler: The method to be called if the route and method are
            matched by a request. Wildcards in the route will be expanded and
            passed as kwargs with the same name as the wildcard identifier.
        :type handler: function
        :param nodoc: If your route intentionally provides no documentation,
            set this to True to disable the warning on startup.

        :type nodoc: bool
        :param resource: The name of the resource at the root of this route.
            The resource instance (self) can also be passed. This allows the
            mount path to be looked up. This allows a resource to be mounted at a
            prefix.
        """
        self._ensureInit()
        # Insertion sort to maintain routes in required order.
        nLengthRoutes = self._routes[method.lower()][len(route)]
        for i in range(len(nLengthRoutes)):
            if self._shouldInsertRoute(route, nLengthRoutes[i][0]):
                nLengthRoutes.insert(i, (route, handler))
                break
        else:
            nLengthRoutes.append((route, handler))

        # Now handle the api doc if the handler has any attached
        if resource is None and hasattr(self, 'resourceName'):
            resource = self.resourceName
        elif resource is None:
            resource = self

        if hasattr(handler, 'description'):
            if handler.description is not None:
                docs.addRouteDocs(
                    resource=resource, route=route, method=method,
                    info=handler.description.asDict(), handler=handler)
        elif not nodoc:
            routePath = '/'.join([resource] + list(route))
            logprint.warning(
                'WARNING: No description docs present for route %s %s' % (
                    method, routePath))

        # Warn if there is no access decorator on the handler function
        if not hasattr(handler, 'accessLevel'):
            routePath = '/'.join([resource] + list(route))
            logprint.warning(
                'WARNING: No access level specified for route %s %s' % (
                    method, routePath))
Ejemplo n.º 2
0
 def reconnect(self):
     """
     Reconnect to the database and rebuild indices if necessary.  If a
     unique index on key does not exist, make one, first discarding any
     extant index on key and removing duplicate keys if necessary.
     """
     super(Setting, self).reconnect()
     try:
         indices = self.collection.index_information()
     except pymongo.errors.OperationFailure:
         indices = []
     hasUniqueKeyIndex = False
     presentKeyIndices = []
     for index in indices:
         if indices[index]['key'][0][0] == 'key':
             if indices[index].get('unique'):
                 hasUniqueKeyIndex = True
                 break
             presentKeyIndices.append(index)
     if not hasUniqueKeyIndex:
         for index in presentKeyIndices:
             self.collection.drop_index(index)
         duplicates = self.collection.aggregate([{
             '$group': {
                 '_id': '$key',
                 'key': {
                     '$first': '$key'
                 },
                 'ids': {
                     '$addToSet': '$_id'
                 },
                 'count': {
                     '$sum': 1
                 }
             }
         }, {
             '$match': {
                 'count': {
                     '$gt': 1
                 }
             }
         }])
         for duplicate in duplicates:
             logprint.warning('Removing duplicate setting with key %s.' %
                              (duplicate['key']))
             # Remove all of the duplicates.  Keep the item with the lowest
             # id in Mongo.
             for duplicateId in sorted(duplicate['ids'])[1:]:
                 self.collection.delete_one({'_id': duplicateId})
         self.collection.create_index('key', unique=True)
Ejemplo n.º 3
0
    def _ensureInit(self):
        """
        Calls ``Resource.__init__`` if the subclass constructor did not already
        do so.

        In the past, Resource subclasses were not expected to call their
        superclass constructor.
        """
        if not hasattr(self, '_routes'):
            Resource.__init__(self)
            logprint.warning(
                'WARNING: Resource subclass "%s" did not call '
                '"Resource__init__()" from its constructor.' %
                self.__class__.__name__)
Ejemplo n.º 4
0
    def _validateParamInfo(self, dataType, paramType, name):
        """
        Helper to convert and validate the dataType and paramType.
        Prints warnings if invalid values were passed.
        """
        # Legacy data type conversions
        if dataType == 'int':
            dataType = 'integer'

        # Parameter Object spec:
        # If type is "file", then the swagger "consumes" field MUST be either
        # "multipart/form-data", "application/x-www-form-urlencoded" or both
        # and the parameter MUST be in "formData".
        if dataType == 'file':
            paramType = 'formData'

        # Get type and format from common name
        dataTypeFormat = None
        if dataType in self._dataTypeMap:
            dataType, dataTypeFormat = self._dataTypeMap[dataType]
        # If we are dealing with the body then the dataType might be defined
        # by a schema added using addModel(...), we don't know for sure as we
        # don't know the resource name here to look it up.
        elif paramType != 'body':
            logprint.warning(
                'WARNING: Invalid dataType "%s" specified for parameter names "%s"'
                % (dataType, name))

        # Parameter Object spec:
        # Since the parameter is not located at the request body, it is limited
        # to simple types (that is, not an object).
        if paramType != 'body' and dataType not in (
                'string', 'number', 'integer', 'long', 'boolean', 'array',
                'file', 'float', 'double', 'date', 'dateTime'):
            logprint.warning(
                'WARNING: Invalid dataType "%s" specified for parameter "%s"' %
                (dataType, name))

        if paramType == 'form':
            paramType = 'formData'

        return dataType, dataTypeFormat, paramType
Ejemplo n.º 5
0
def addModel(name, model, resources=None, silent=False):
    """
    Add a model to the Swagger documentation.

    :param resources: The type(s) of resource(s) to add the model to. New
        resource types may be implicitly defined, with the expectation that
        routes will be added for them at some point. If no resources are
        passed, the model will be exposed for every resource type
    :param resources: str or tuple/list[str]
    :param name: The name of the model.
    :type name: str
    :param model: The model to add.
    :type model: dict
    :param silent: Set this to True to suppress warnings.
    :type silent: bool

    .. warning:: This is a low-level API which does not validate the format of
       ``model``. See the `Swagger Model documentation`_ for a complete
       specification of the correct format for ``model``.

    .. versionchanged:: The syntax and behavior of this function was modified
        after v1.3.2. The previous implementation did not include a resources
        parameter.

    .. _Swagger Model documentation: https://github.com/OAI/
        OpenAPI-Specification/blob/0122c22e7fb93b571740dd3c6e141c65563a18be/
        versions/2.0.md#definitionsObject
    """
    if resources:
        if isinstance(resources, six.string_types):
            resources = (resources, )
        for resource in resources:
            models[resource][name] = model
    else:
        if not silent:
            logprint.warning(
                'WARNING: adding swagger models without specifying resources '
                'to bind to is discouraged (%s).' % name)
        models[None][name] = model
Ejemplo n.º 6
0
def mountServer(path,
                database=None,
                fuseOptions=None,
                quiet=False,
                plugins=None):
    """
    Perform the mount.

    :param path: the mount location.
    :param database: a database connection URI, if it contains '://'.
        Otherwise, the default database is used.
    :param fuseOptions: a comma-separated string of options to pass to the FUSE
        mount.  A key without a value is taken as True.  Boolean values are
        case insensitive.  For instance, 'foreground' or 'foreground=True' will
        keep this program running until the SIGTERM or unmounted.
    :param quiet: if True, suppress Girder logs.
    :param plugins: an optional list of plugins to enable.  If None, use the
        plugins that are configured.
    """
    if quiet:
        curConfig = config.getConfig()
        curConfig.setdefault('logging', {})['log_quiet'] = True
        curConfig.setdefault('logging', {})['log_level'] = 'FATAL'
        girderformindlogger._attachFileLogHandlers()
    if database and '://' in database:
        cherrypy.config['database']['uri'] = database
    if plugins is not None:
        plugins = plugins.split(',')
    webroot, appconf = configureServer(plugins=plugins)
    girderformindlogger._setupCache()

    opClass = ServerFuse(stat=os.stat(path))
    options = {
        # By default, we run in the background so the mount command returns
        # immediately.  If we run in the foreground, a SIGTERM will shut it
        # down
        'foreground': False,
        # Cache files if their size and timestamp haven't changed.
        # This lets the OS buffer files efficiently.
        'auto_cache': True,
        # We aren't specifying our own inos
        'use_ino': False,
        # read-only file system
        'ro': True,
    }
    if sys.platform != 'darwin':
        # Automatically unmount when we try to mount again
        options['auto_unmount'] = True
    if fuseOptions:
        for opt in fuseOptions.split(','):
            if '=' in opt:
                key, value = opt.split('=', 1)
                value = (False if value.lower() == 'false' else
                         True if value.lower() == 'true' else value)
            else:
                key, value = opt, True
            if key in ('use_ino', 'ro', 'rw') and options.get(key) != value:
                logprint.warning('Ignoring the %s=%r option' % (key, value))
                continue
            options[key] = value
    Setting().set(SettingKey.GIRDER_MOUNT_INFORMATION, {
        'path': path,
        'mounttime': time.time()
    })
    FUSELogError(opClass, path, **options)
Ejemplo n.º 7
0
def getDbConnection(uri=None,
                    replicaSet=None,
                    autoRetry=True,
                    quiet=False,
                    **kwargs):
    """
    Get a MongoClient object that is connected to the configured database.
    We lazy-instantiate a module-level singleton, the MongoClient objects
    manage their own connection pools internally. Any extra kwargs you pass to
    this method will be passed through to the MongoClient.

    :param uri: if specified, connect to this mongo db rather than the one in
                the config.
    :param replicaSet: if uri is specified, use this replica set.
    :param autoRetry: if this connection should automatically retry operations
        in the event of an AutoReconnect exception. If you're testing the
        connection, set this to False. If disabled, this also will not cache
        the mongo client, so make sure to only disable if you're testing a
        connection.
    :type autoRetry: bool
    :param quiet: if true, don't logprint warnings and success.
    :type quiet: bool
    """
    global _dbClients

    origKey = (uri, replicaSet)
    if origKey in _dbClients:
        return _dbClients[origKey]

    dbConf = getDbConfig()

    if uri is None or uri == '':
        uri = dbConf.get('uri')
        replicaSet = dbConf.get('replica_set')

    clientOptions = {
        # This is the maximum time between when we fetch data from a cursor.
        # If it times out, the cursor is lost and we can't reconnect.  If it
        # isn't set, we have issues with replica sets when the primary goes
        # down.  This value can be overridden in the mongodb uri connection
        # string with the socketTimeoutMS.
        'socketTimeoutMS': 60000,
        'connectTimeoutMS': 20000,
        'serverSelectionTimeoutMS': 20000,
        'readPreference': 'secondaryPreferred',
        'replicaSet': replicaSet,
        'w': 'majority'
    }

    # All other options in the [database] section will be passed directly as
    # options to the mongo client
    for opt, val in six.viewitems(dict(dbConf)):
        if opt not in {'uri', 'replica_set'}:
            clientOptions[opt] = val

    # Finally, kwargs take precedence
    clientOptions.update(kwargs)
    # if the connection URI overrides any option, honor it above our own
    # settings.
    uriParams = urllib.parse.parse_qs(urllib.parse.urlparse(uri).query)
    for key in uriParams:
        if key in clientOptions:
            del clientOptions[key]

    if uri is None:
        dbUriRedacted = 'mongodb://*****:*****@')
        if len(parts) == 2:
            dbUriRedacted = 'mongodb://' + parts[1]
        else:
            dbUriRedacted = uri

        client = pymongo.MongoClient(uri, **clientOptions)

    if not quiet:
        desc = ''
        if replicaSet:
            desc += ', replica set: %s' % replicaSet
        logprint.info('Connecting to MongoDB: %s%s' % (dbUriRedacted, desc))

    # Make sure we can connect to the mongo server at startup
    client.server_info()

    if autoRetry:
        client = MongoProxy(client, logger=logger)
        _dbClients[origKey] = _dbClients[(uri, replicaSet)] = client

    return client