def validate_domain_struct(self): """ Validates that Eve configuration settings conform to the requirements. """ try: domain = self.config['DOMAIN'] except: raise ConfigException('DOMAIN dictionary missing or wrong.') if not isinstance(domain, dict): raise ConfigException('DOMAIN must be a dict.')
def validate_info(): v = Validator() schema = { 'title': { 'required': True, 'type': 'string' }, 'version': { 'required': True, 'type': 'string' }, 'description': { 'type': 'string' }, 'termsOfService': { 'type': 'string' }, 'contact': { 'type': 'dict', 'schema': { 'name': { 'type': 'string' }, 'url': { 'type': 'string', 'validator': _validate_url }, 'email': { 'type': 'string', 'regex': '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' } } }, 'license': { 'type': 'dict', 'schema': { 'name': { 'type': 'string', 'required': True }, 'url': { 'type': 'string', 'validator': _validate_url } } }, } if eve_swagger.INFO not in app.config: raise ConfigException('%s setting is required in Eve configuration.' % eve_swagger.INFO) if not v.validate(app.config[eve_swagger.INFO], schema): raise ConfigException('%s is misconfigured: %s' % (eve_swagger.INFO, v.errors))
def _validate_resource_settings(self, resource, settings): """ Validates one resource in configuration settings. :param resource: name of the resource which settings refer to. :param settings: settings of resource to be validated. .. versionchanged:: 0.4 validate that auth_field is not set to ID_FIELD. See #266. .. versionadded:: 0.2 """ self.validate_methods( self.supported_resource_methods, settings["resource_methods"], "[%s] resource " % resource, ) self.validate_methods( self.supported_item_methods, settings["item_methods"], "[%s] item " % resource, ) # while a resource schema is optional for read-only access, # it is mandatory for write-access to resource/items. if ( "POST" in settings["resource_methods"] or "PATCH" in settings["item_methods"] ): if len(settings["schema"]) == 0: raise ConfigException( "A resource schema must be provided " "when POST or PATCH methods are allowed " "for a resource [%s]." % resource ) self.validate_roles("allowed_roles", settings, resource) self.validate_roles("allowed_read_roles", settings, resource) self.validate_roles("allowed_write_roles", settings, resource) self.validate_roles("allowed_item_roles", settings, resource) self.validate_roles("allowed_item_read_roles", settings, resource) self.validate_roles("allowed_item_write_roles", settings, resource) if settings["auth_field"] == settings["id_field"]: raise ConfigException( '"%s": auth_field cannot be set to id_field ' "(%s)" % (resource, settings["id_field"]) ) self.validate_schema(resource, settings["schema"])
def __init__(self, ext): """ Constructor. :param ext: instance of :class:`EveMongoengine`. """ # get authentication info username = ext.app.config.get("MONGO_USERNAME", None) password = ext.app.config.get("MONGO_PASSWORD", None) auth = (username, password) if any(auth) and not all(auth): raise ConfigException("Must set both USERNAME and PASSWORD " "or neither") # try to connect to db self.conn = connect( ext.app.config["MONGO_DBNAME"], host=ext.app.config["MONGO_HOST"], port=ext.app.config["MONGO_PORT"], ) self.models = ext.models self.app = ext.app # create dummy driver instead of PyMongo, which causes errors # when instantiating after config was initialized self.driver = type("Driver", (), {})() self.driver.db = get_db() # authenticate if any(auth): self.driver.db.authenticate(username, password) # helper object for managing PATCHes, which are a bit dirty self.updater = MongoengineUpdater(self) # map resource -> Mongoengine class self.cls_map = ResourceClassMap(self)
def _set_resource_projection(self, ds, schema, settings): """ Set datasource projection for a resource .. versionchanged:: 0.6.3 Fix: If datasource source is specified no fields are included by default. Closes #842. .. versionadded:: 0.6.2 """ projection = ds.get('projection', {}) # check if any exclusion projection is defined exclusion = any(((k, v) for k, v in projection.items() if v == 0)) \ if projection else None # If no exclusion projection is defined, enhance the projection # with automatic fields. Using both inclusion and exclusion will # be rejected by Mongo if not exclusion and len(schema) and \ settings['allow_unknown'] is False: if not projection: projection.update(dict((field, 1) for (field) in schema)) # enable retrieval of actual schema fields only. Eventual db # fields not included in the schema won't be returned. # despite projection, automatic fields are always included. projection[settings['id_field']] = 1 projection[self.config['LAST_UPDATED']] = 1 projection[self.config['DATE_CREATED']] = 1 projection[self.config['ETAG']] = 1 if settings['versioning'] is True: projection[self.config['VERSION']] = 1 projection[settings['id_field'] + self.config['VERSION_ID_SUFFIX']] = 1 else: # all fields are returned. projection = None ds.setdefault('projection', projection) if settings['soft_delete'] is True and not exclusion and \ ds['projection'] is not None: ds['projection'][self.config['DELETED']] = 1 # list of all media fields for the resource if isinstance(schema, dict): settings['_media'] = [ field for field, definition in schema.items() if isinstance(definition, dict) and ( definition.get('type') == 'media' or (definition.get('type') == 'list' and definition.get('schema', {}).get('type') == 'media')) ] else: settings['_media'] = [] if settings['_media'] and not self.media: raise ConfigException('A media storage class of type ' ' eve.io.media.MediaStorage must be defined ' 'for "media" fields to be properly stored.')
def find_list_of_ids(self, resource, ids, client_projection=None): """ Retrieves a list of documents based on a list of primary keys. The primary key is the field defined in `ID_FIELD`. This is a separate function to allow us to use per-database optimizations for this type of query. :param resource: resource name. :param ids: a list of ids corresponding to the documents to retrieve :param client_projection: a specific projection to use :return: a list of documents matching the ids in `ids` from the collection specified in `resource` """ item_title = self.app.config["DOMAIN"][resource][ "item_title"].capitalize() if item_title in self.node_base_classes_names: node_class = self.get_node_class(item_title) results = self.find_nodes(node_class, req=ParsedRequest()) elif resource in self.node_types: node_class = self.get_node_class( class_name=item_title, base_classes=self.node_base_classes) results = self.find_nodes(node_class) else: raise ConfigException( "Resource {} wasn't found in neither node or relation types.". format(resource)) return Neo4jResult( [result for result in results if result["id"] in ids])
def find_one(self, resource, req, **lookup): """ Retrieves a single document/record. Consumed when a request hits an item endpoint (`/people/id/`). :param resource: resource being accessed. You should then use the ``datasource`` helper function to retrieve both the db collection/table and base query (filter), if any. :param req: an instance of ``eve.utils.ParsedRequest``. This contains all the constraints that must be fulfilled in order to satisfy the original request (where and sort parts, paging, etc). As we are going to only look for one document here, the only req attribute that you want to process here is``req.projection``. :param **lookup: the lookup fields. This will most likely be a record id or, if alternate lookup is supported by the API, the corresponding query. """ item_title = self.app.config["DOMAIN"][resource][ "item_title"].capitalize() if item_title in self.node_base_classes_names: node_class = self.get_node_class(item_title) results = self.find_nodes(node_class, req) elif resource in self.node_types: node_class = self.get_node_class( class_name=item_title, base_classes=self.node_base_classes) results = self.find_nodes(node_class, req) else: raise ConfigException( "Resource {} wasn't found in neither node or relation types.". format(resource)) return Neo4jResult([results[0]], parsed_request=req)
def find(self, resource, req, sub_resource_lookup): """ Retrieves a set of documents (rows), matching the current request. Consumed when a request hits a collection/document endpoint (`/people/`). :param resource: resource being accessed. You should then use the ``datasource`` helper function to retrieve both the db collection/table and base query (filter), if any. :param req: an instance of ``eve.utils.ParsedRequest``. This contains all the constraints that must be fulfilled in order to satisfy the original request (where and sort parts, paging, etc). Be warned that `where` and `sort` expressions will need proper parsing, according to the syntax that you want to support with your driver. For example ``eve.io.Mongo`` supports both Python and Mongo-like query syntaxes. :param sub_resource_lookup: sub-resource lookup from the endpoint url. """ item_title = self.app.config["DOMAIN"][resource][ "item_title"].capitalize() if item_title in self.node_base_classes_names: node_class = self.get_node_class(item_title) results = self.find_nodes(node_class, req) elif resource in self.node_types: node_class = self.get_node_class( class_name=item_title, base_classes=self.node_base_classes) results = self.find_nodes(node_class, req) else: raise ConfigException( "Resource {} wasn't found in neither node or relation types.". format(resource)) return Neo4jResult(results, parsed_request=req)
def _validate_resource_settings(self, resource, settings): """ Validates one resource in configuration settings. :param resource: name of the resource which settings refer to. :param settings: settings of resource to be validated. .. versionadded:: 0.2 """ self.validate_methods(self.supported_resource_methods, settings['resource_methods'], '[%s] resource ' % resource) self.validate_methods(self.supported_item_methods, settings['item_methods'], '[%s] item ' % resource) # while a resource schema is optional for read-only access, # it is mandatory for write-access to resource/items. if 'POST' in settings['resource_methods'] or \ 'PATCH' in settings['item_methods']: if len(settings['schema']) == 0: raise ConfigException('A resource schema must be provided ' 'when POST or PATCH methods are allowed' 'for a resource [%s].' % resource) self.validate_roles('allowed_roles', settings, resource) self.validate_roles('allowed_item_roles', settings, resource) self.validate_schema(resource, settings['schema'])
def __init__(self, ext): """ Constructor. :param ext: instance of :class:`EveMongoengine`. """ # get authentication info username = ext.app.config['MONGO_USERNAME'] password = ext.app.config['MONGO_PASSWORD'] auth = (username, password) if any(auth) and not all(auth): raise ConfigException('Must set both USERNAME and PASSWORD ' 'or neither') # try to connect to db self.conn = connect(ext.app.config['MONGO_DBNAME'], host=ext.app.config['MONGO_HOST'], port=ext.app.config['MONGO_PORT']) self.models = ext.models self.app = ext.app # create dummy driver instead of PyMongo, which causes errors # when instantiating after config was initialized self.driver = type('Driver', (), {})() self.driver.db = get_db() # authenticate if any(auth): self.driver.db.authenticate(username, password)
def _get_column(self, column_name): try: return self._mapper.columns[column_name] except KeyError: raise ConfigException("{model}.{column_name} does not exist." .format(model=self.model.__name__, column_name=column_name))
def validate_roles(self, directive, candidate, resource): """ The same as eve's, but letting roles to be sets, which is a more adequate type that easies some code. """ roles = candidate[directive] if not isinstance(roles, set) and not isinstance(roles, list): raise ConfigException("'%s' must be set" "[%s]." % (directive, resource))
def id_field(self, id_field): pk_columns = [c.name for c in self._mapper.primary_key] if not (len(pk_columns) == 1 and pk_columns[0] == id_field): column = self._get_column(id_field) if not column.unique: raise ConfigException( "{model}.{id_field} is not unique.".format( model=self.model.__name__, id_field=id_field)) self._id_field = id_field
def item_lookup_field(self, item_lookup_field): if item_lookup_field != self.id_field: column = self._get_column(item_lookup_field) if not column.unique: raise ConfigException( "{model}.{item_lookup_field} is not unique." .format(model=self.model.__name__, item_lookup_field=item_lookup_field)) self._item_lookup_field = item_lookup_field
def _deduce_id_field(self): pk_columns = [c.name for c in self.model.__mapper__.primary_key] if len(pk_columns) == 1: return pk_columns[0] else: raise ConfigException( "{model}'s primary key consists of zero or multiple columns, " "thus we cannot deduce which one to use. Please manually " "specify a unique column to use as `id_field`: " "`ResourceConfig({model}, id_field=...)`" .format(model=self.model.__name__))
def _set_resource_projection(self, ds, schema, settings): """ Set datasource projection for a resource .. versionadded:: 0.7 """ projection = ds.get('projection', {}) # check if any exclusion projection is defined exclusion = any(((k, v) for k, v in projection.items() if v == 0)) # If no exclusion projection is defined, enhance the projection # with automatic fields. Using both inclusion and exclusion will # be rejected by Mongo if not exclusion and len(schema) and \ settings['allow_unknown'] is False: # enable retrieval of actual schema fields only. Eventual db # fields not included in the schema won't be returned. # despite projection, automatic fields are always included. projection[settings['id_field']] = 1 projection[self.config['LAST_UPDATED']] = 1 projection[self.config['DATE_CREATED']] = 1 projection[self.config['ETAG']] = 1 if settings['versioning'] is True: projection[self.config['VERSION']] = 1 projection[settings['id_field'] + self.config['VERSION_ID_SUFFIX']] = 1 projection.update(dict((field, 1) for (field) in schema)) else: # all fields are returned. projection = None ds.setdefault('projection', projection) if settings['soft_delete'] is True and not exclusion and \ ds['projection'] is not None: ds['projection'][self.config['DELETED']] = 1 # 'defaults' helper set contains the names of fields with default # values in their schema definition. # TODO support default values for embedded documents. settings['defaults'] = build_defaults(schema) # list of all media fields for the resource settings['_media'] = [ field for field, definition in schema.items() if definition.get('type') == 'media' ] if settings['_media'] and not self.media: raise ConfigException('A media storage class of type ' ' eve.io.media.MediaStorage must be defined ' 'for "media" fields to be properly stored.')
def validate_schema(self, schema): # TODO are there other mandatory settings items? Validate them here offender = None if eve.DATE_CREATED in schema: offender = eve.DATE_CREATED if eve.LAST_UPDATED in schema: offender = eve.LAST_UPDATED if eve.ID_FIELD in schema: offender = eve.ID_FIELD if offender: raise ConfigException('"%s" field not allowed in schema (will be ' 'handled automatically).' % offender)
def _validate_resource_settings(self, resource, settings): """ Validates one resource in configuration settings. :param resource: name of the resource which settings refer to. :param settings: settings of resource to be validated. .. versionchanged:: 0.4 validate that auth_field is not set to ID_FIELD. See #266. .. versionadded:: 0.2 """ self.validate_methods(self.supported_resource_methods, settings['resource_methods'], '[%s] resource ' % resource) self.validate_methods(self.supported_item_methods, settings['item_methods'], '[%s] item ' % resource) # while a resource schema is optional for read-only access, # it is mandatory for write-access to resource/items. if 'POST' in settings['resource_methods'] or \ 'PATCH' in settings['item_methods']: if len(settings['schema']) == 0: raise ConfigException('A resource schema must be provided ' 'when POST or PATCH methods are allowed ' 'for a resource [%s].' % resource) self.validate_roles('allowed_roles', settings, resource) self.validate_roles('allowed_read_roles', settings, resource) self.validate_roles('allowed_write_roles', settings, resource) self.validate_roles('allowed_item_roles', settings, resource) self.validate_roles('allowed_item_read_roles', settings, resource) self.validate_roles('allowed_item_write_roles', settings, resource) if settings['auth_field'] == self.config['ID_FIELD']: raise ConfigException('"%s": auth_field cannot be set to ID_FIELD ' '(%s)' % (resource, self.config['ID_FIELD'])) self.validate_schema(resource, settings['schema'])
def validate_methods(self, allowed, proposed, item): """Compares allowed and proposed methods, raising a `ConfigException` when they don't match. :param allowed: a list of supported (allowed) methods. :param proposed: a list of proposed methods. :param item: name of the item to which the methods would be applied. Used when raising the exception. """ diff = set(proposed) - set(allowed) if diff: raise ConfigException("Unallowed %s method(s): %s. " "Supported: %s" % (item, ", ".join(diff), ", ".join(allowed)))
def _get_resource(self): try: return self._related_resource_configs[(self._model, self._name)] except LookupError: try: relationship = self._mapper.relationships[ self._field.target_collection] return self._related_resource_configs[relationship.argument()] except LookupError: model = self._mapper.class_ raise ConfigException( 'Cannot determine related resource for {model}.{field}. ' 'Please specify `related_resources` manually.'.format( model=model.__name__, field=self._name))
def validate_config(self): """ Makes sure that REST methods expressed in the configuration settings are supported. .. versionchanged:: 0.1.0 Support for PUT method. .. versionchanged:: 0.0.4 Support for 'allowed_roles' and 'allowed_item_roles' .. versionchanged:: 0.0.2 Support for DELETE resource method. """ supported_resource_methods = ['GET', 'POST', 'DELETE'] supported_item_methods = ['GET', 'PATCH', 'DELETE', 'PUT'] # make sure that global resource methods are supported. self.validate_methods(supported_resource_methods, self.config.get('RESOURCE_METHODS'), 'resource') # make sure that global item methods are supported. self.validate_methods(supported_item_methods, self.config.get('ITEM_METHODS'), 'item') # make sure that individual resource/item methods are supported. for resource, settings in self.config['DOMAIN'].items(): self.validate_methods(supported_resource_methods, settings['resource_methods'], '[%s] resource ' % resource) self.validate_methods(supported_item_methods, settings['item_methods'], '[%s] item ' % resource) # while a resource schema is optional for read-only access, # it is mandatory for write-access to resource/items. if 'POST' in settings['resource_methods'] or \ 'PATCH' in settings['item_methods']: if len(settings['schema']) == 0: raise ConfigException('A resource schema must be provided ' 'when POST or PATCH methods are ' 'allowed for a resource [%s].' % resource) self.validate_roles('allowed_roles', settings, resource) self.validate_roles('allowed_item_roles', settings, resource) self.validate_schema(resource, settings['schema'])
def validate_roles(self, directive, candidate, resource): """ Validates that user role directives are syntactically and formally adequate. :param directive: either 'allowed_[read_|write_]roles' or 'allow_item_[read_|write_]roles'. :param candidate: the candidate setting to be validated. :param resource: name of the resource to which the candidate settings refer to. .. versionadded:: 0.0.4 """ roles = candidate[directive] if not isinstance(roles, list): raise ConfigException("'%s' must be list" "[%s]." % (directive, resource))
def validate_roles(self, directive, candidate, resource): """ Validates that user role directives are syntactically and formally adeguate. :param directive: either 'allowed_roles' or 'allow_item_roles'. :param candidate: the candidate setting to be validated. :param resource: name of the resource to which the candidate settings refer to. .. versionadded:: 0.0.4 """ roles = candidate[directive] if roles is not None and (not isinstance(roles, list) or not len(roles)): raise ConfigException("'%s' must be a non-empty list, or None " "[%s]." % (directive, resource))
def _get_resource(self): try: return self._related_resource_configs[(self._model, self._name)] except LookupError: try: arg = self._relationship.argument if isinstance(arg, DeclarativeMeta): return self._related_resource_configs[arg] elif callable(arg): return self._related_resource_configs[arg()] else: return self._related_resource_configs[arg.class_] except LookupError: raise ConfigException( 'Cannot determine related resource for {model}.{field}. ' 'Please specify `related_resources` manually.'.format( model=self._model.__name__, field=self._name))
def check_auth(self, token, allowed_roles, resource, method): """ Check if API request is authorized. Examines token in header and checks Redis cache to see if token is valid. If so, request is allowed. :param token: OAuth 2.0 access token submitted. :param allowed_roles: Allowed user roles. :param resource: Resource being requested. :param method: HTTP method being executed (POST, GET, etc.) """ if not token: return False user_id = self.redis.get(token) if not user_id: return False # now switching to the user-reserved mongo instance. mongo_prefix = 'MONGO%s' % user_id # TODO remove defaulting to localhost so exception is raised # if db host is not available. Right now, unless redis hodls a # key for the user, all dbs are hosted on localhost. host = self.redis.get(user_id) or 'localhost' if not host: raise ConfigException('Cannot locate host for user database %s' % user_id) uri = 'mongodb://%s/%s' % (host, user_id) current_app.config['%s_URI' % mongo_prefix] = uri self.set_mongo_prefix(mongo_prefix) return True
def _set_resource_projection(self, ds, schema, settings): """ Set datasource projection for a resource .. versionchanged:: 0.6.3 Fix: If datasource source is specified no fields are included by default. Closes #842. .. versionadded:: 0.6.2 """ # get existing or empty projection setting projection = ds.get("projection", {}) # If exclusion projections are defined, they are use for # concealing fields (rather than actual mongo exlusions). # If inclusion projections are defined, exclusion projections are # just ignored. # Enhance the projection with automatic fields. if len(schema) and settings["allow_unknown"] is False: inclusion_projection = dict( [(k, v) for k, v in projection.items() if v == 1] ) exclusion_projection = dict( [(k, v) for k, v in projection.items() if v == 0] ) # if inclusion project is empty, add all fields not excluded if not inclusion_projection: projection.update( dict( (field, 1) for (field) in schema if field not in exclusion_projection ) ) # enable retrieval of actual schema fields only. Eventual db # fields not included in the schema won't be returned. # despite projection, automatic fields are always included. projection[settings["id_field"]] = 1 projection[self.config["LAST_UPDATED"]] = 1 projection[self.config["DATE_CREATED"]] = 1 projection[self.config["ETAG"]] = 1 if settings["versioning"] is True: projection[self.config["VERSION"]] = 1 projection[settings["id_field"] + self.config["VERSION_ID_SUFFIX"]] = 1 ds.setdefault("projection", projection) if settings["soft_delete"] is True and projection: projection[self.config["DELETED"]] = 1 # set projection and projection is always a dictionary ds["projection"] = projection # list of all media fields for the resource if isinstance(schema, dict): settings["_media"] = [ field for field, definition in schema.items() if isinstance(definition, dict) and ( definition.get("type") == "media" or ( definition.get("type") == "list" and definition.get("schema", {}).get("type") == "media" ) ) ] else: settings["_media"] = [] if settings["_media"] and not self.media: raise ConfigException( "A media storage class of type " " eve.io.media.MediaStorage must be defined " 'for "media" fields to be properly stored.' )
def _set_resource_defaults(self, resource, settings): """ Low-level method which sets default values for one resource. .. versionchanged:: 0.5 Don't set default projection if 'allow_unknown' is active (#497). 'internal_resource' .. versionchanged:: 0.3 Set projection to None when schema is not provided for the resource. Support for '_media' helper. .. versionchanged:: 0.2 'resource_title', 'default_sort', 'embedded_fields'. Support for endpoint-level authenticatoin classes. """ settings.setdefault('url', resource) settings.setdefault('resource_methods', self.config['RESOURCE_METHODS']) settings.setdefault('public_methods', self.config['PUBLIC_METHODS']) settings.setdefault('allowed_roles', self.config['ALLOWED_ROLES']) settings.setdefault('allowed_read_roles', self.config['ALLOWED_READ_ROLES']) settings.setdefault('allowed_write_roles', self.config['ALLOWED_WRITE_ROLES']) settings.setdefault('cache_control', self.config['CACHE_CONTROL']) settings.setdefault('cache_expires', self.config['CACHE_EXPIRES']) settings.setdefault('item_lookup_field', self.config['ITEM_LOOKUP_FIELD']) settings.setdefault('item_url', self.config['ITEM_URL']) settings.setdefault('resource_title', settings['url']) settings.setdefault('item_title', resource.rstrip('s').capitalize()) settings.setdefault('item_lookup', self.config['ITEM_LOOKUP']) settings.setdefault('public_item_methods', self.config['PUBLIC_ITEM_METHODS']) settings.setdefault('allowed_item_roles', self.config['ALLOWED_ITEM_ROLES']) settings.setdefault('allowed_item_read_roles', self.config['ALLOWED_ITEM_READ_ROLES']) settings.setdefault('allowed_item_write_roles', self.config['ALLOWED_ITEM_WRITE_ROLES']) settings.setdefault('allowed_filters', self.config['ALLOWED_FILTERS']) settings.setdefault('sorting', self.config['SORTING']) settings.setdefault('embedding', self.config['EMBEDDING']) settings.setdefault('embedded_fields', []) settings.setdefault('pagination', self.config['PAGINATION']) settings.setdefault('projection', self.config['PROJECTION']) settings.setdefault('versioning', self.config['VERSIONING']) settings.setdefault('internal_resource', self.config['INTERNAL_RESOURCE']) # TODO make sure that this we really need the test below if settings['item_lookup']: item_methods = self.config['ITEM_METHODS'] else: item_methods = eve.ITEM_METHODS settings.setdefault('item_methods', item_methods) settings.setdefault('auth_field', self.config['AUTH_FIELD']) settings.setdefault('allow_unknown', self.config['ALLOW_UNKNOWN']) settings.setdefault('extra_response_fields', self.config['EXTRA_RESPONSE_FIELDS']) settings.setdefault('mongo_write_concern', self.config['MONGO_WRITE_CONCERN']) settings.setdefault('hateoas', self.config['HATEOAS']) settings.setdefault('authentication', self.auth if self.auth else None) # empty schemas are allowed for read-only access to resources schema = settings.setdefault('schema', {}) self.set_schema_defaults(schema) datasource = {} settings.setdefault('datasource', datasource) settings['datasource'].setdefault('source', resource) settings['datasource'].setdefault('filter', None) settings['datasource'].setdefault('default_sort', None) if len(schema) and settings['allow_unknown'] is False: # enable retrieval of actual schema fields only. Eventual db # fields not included in the schema won't be returned. projection = {} # despite projection, automatic fields are always included. projection[self.config['ID_FIELD']] = 1 projection[self.config['LAST_UPDATED']] = 1 projection[self.config['DATE_CREATED']] = 1 projection[self.config['ETAG']] = 1 if settings['versioning'] is True: projection[self.config['VERSION']] = 1 projection[ self.config['ID_FIELD'] + self.config['VERSION_ID_SUFFIX']] = 1 projection.update(dict((field, 1) for (field) in schema)) else: # all fields are returned. projection = None settings['datasource'].setdefault('projection', projection) # 'defaults' helper set contains the names of fields with default # values in their schema definition. # TODO support default values for embedded documents. settings['defaults'] = build_defaults(schema) # list of all media fields for the resource settings['_media'] = [field for field, definition in schema.items() if definition.get('type') == 'media'] if settings['_media'] and not self.media: raise ConfigException('A media storage class of type ' ' eve.io.media.MediaStorage but be defined ' 'for "media" fields to be properly stored.')
def _set_resource_defaults(self, resource, settings): """ Low-level method which sets default values for one resource. .. versionchanged:: 0.6.2 Fix: startup crash when both SOFT_DELETE and ALLOW_UNKNOWN are True. (#722). .. versionchanged:: 0.6.1 Fix: inclusive projection defined for a datasource is ignored (#722). .. versionchanged:: 0.6 Support for 'mongo_indexes'. .. versionchanged:: 0.5 Don't set default projection if 'allow_unknown' is active (#497). 'internal_resource' .. versionchanged:: 0.3 Set projection to None when schema is not provided for the resource. Support for '_media' helper. .. versionchanged:: 0.2 'resource_title', 'default_sort', 'embedded_fields'. Support for endpoint-level authenticatoin classes. """ settings.setdefault('url', resource) settings.setdefault('resource_methods', self.config['RESOURCE_METHODS']) settings.setdefault('public_methods', self.config['PUBLIC_METHODS']) settings.setdefault('allowed_roles', self.config['ALLOWED_ROLES']) settings.setdefault('allowed_read_roles', self.config['ALLOWED_READ_ROLES']) settings.setdefault('allowed_write_roles', self.config['ALLOWED_WRITE_ROLES']) settings.setdefault('cache_control', self.config['CACHE_CONTROL']) settings.setdefault('cache_expires', self.config['CACHE_EXPIRES']) settings.setdefault('id_field', self.config['ID_FIELD']) settings.setdefault('item_lookup_field', self.config['ITEM_LOOKUP_FIELD']) settings.setdefault('item_url', self.config['ITEM_URL']) settings.setdefault('resource_title', settings['url']) settings.setdefault('item_title', resource.rstrip('s').capitalize()) settings.setdefault('item_lookup', self.config['ITEM_LOOKUP']) settings.setdefault('public_item_methods', self.config['PUBLIC_ITEM_METHODS']) settings.setdefault('allowed_item_roles', self.config['ALLOWED_ITEM_ROLES']) settings.setdefault('allowed_item_read_roles', self.config['ALLOWED_ITEM_READ_ROLES']) settings.setdefault('allowed_item_write_roles', self.config['ALLOWED_ITEM_WRITE_ROLES']) settings.setdefault('allowed_filters', self.config['ALLOWED_FILTERS']) settings.setdefault('sorting', self.config['SORTING']) settings.setdefault('embedding', self.config['EMBEDDING']) settings.setdefault('embedded_fields', []) settings.setdefault('pagination', self.config['PAGINATION']) settings.setdefault('projection', self.config['PROJECTION']) settings.setdefault('versioning', self.config['VERSIONING']) settings.setdefault('soft_delete', self.config['SOFT_DELETE']) settings.setdefault('bulk_enabled', self.config['BULK_ENABLED']) settings.setdefault('internal_resource', self.config['INTERNAL_RESOURCE']) settings.setdefault('etag_ignore_fields', None) # TODO make sure that this we really need the test below if settings['item_lookup']: item_methods = self.config['ITEM_METHODS'] else: item_methods = eve.ITEM_METHODS settings.setdefault('item_methods', item_methods) settings.setdefault('auth_field', self.config['AUTH_FIELD']) settings.setdefault('allow_unknown', self.config['ALLOW_UNKNOWN']) settings.setdefault('transparent_schema_rules', self.config['TRANSPARENT_SCHEMA_RULES']) settings.setdefault('extra_response_fields', self.config['EXTRA_RESPONSE_FIELDS']) settings.setdefault('mongo_write_concern', self.config['MONGO_WRITE_CONCERN']) settings.setdefault('mongo_indexes', {}) settings.setdefault('hateoas', self.config['HATEOAS']) settings.setdefault('authentication', self.auth if self.auth else None) # empty schemas are allowed for read-only access to resources schema = settings.setdefault('schema', {}) self.set_schema_defaults(schema, settings['id_field']) # 'defaults' helper set contains the names of fields with default # values in their schema definition. # TODO support default values for embedded documents. settings['defaults'] = build_defaults(schema) # list of all media fields for the resource settings['_media'] = [field for field, definition in schema.items() if definition.get('type') == 'media'] if settings['_media'] and not self.media: raise ConfigException('A media storage class of type ' ' eve.io.media.MediaStorage but be defined ' 'for "media" fields to be properly stored.') self._set_resource_datasource(resource, schema, settings)
def _set_resource_defaults(self, resource, settings): """ Low-level method which sets default values for one resource. .. versionchanged:: 0.6.2 Fix: startup crash when both SOFT_DELETE and ALLOW_UNKNOWN are True. (#722). .. versionchanged:: 0.6.1 Fix: inclusive projection defined for a datasource is ignored (#722). .. versionchanged:: 0.6 Support for 'mongo_indexes'. .. versionchanged:: 0.5 Don't set default projection if 'allow_unknown' is active (#497). 'internal_resource' .. versionchanged:: 0.3 Set projection to None when schema is not provided for the resource. Support for '_media' helper. .. versionchanged:: 0.2 'resource_title', 'default_sort', 'embedded_fields'. Support for endpoint-level authenticatoin classes. """ settings.setdefault('url', resource) settings.setdefault('resource_methods', self.config['RESOURCE_METHODS']) settings.setdefault('public_methods', self.config['PUBLIC_METHODS']) settings.setdefault('allowed_roles', self.config['ALLOWED_ROLES']) settings.setdefault('allowed_read_roles', self.config['ALLOWED_READ_ROLES']) settings.setdefault('allowed_write_roles', self.config['ALLOWED_WRITE_ROLES']) settings.setdefault('cache_control', self.config['CACHE_CONTROL']) settings.setdefault('cache_expires', self.config['CACHE_EXPIRES']) settings.setdefault('id_field', self.config['ID_FIELD']) settings.setdefault('item_lookup_field', self.config['ITEM_LOOKUP_FIELD']) settings.setdefault('item_url', self.config['ITEM_URL']) settings.setdefault('resource_title', settings['url']) settings.setdefault('item_title', resource.rstrip('s').capitalize()) settings.setdefault('item_lookup', self.config['ITEM_LOOKUP']) settings.setdefault('public_item_methods', self.config['PUBLIC_ITEM_METHODS']) settings.setdefault('allowed_item_roles', self.config['ALLOWED_ITEM_ROLES']) settings.setdefault('allowed_item_read_roles', self.config['ALLOWED_ITEM_READ_ROLES']) settings.setdefault('allowed_item_write_roles', self.config['ALLOWED_ITEM_WRITE_ROLES']) settings.setdefault('allowed_filters', self.config['ALLOWED_FILTERS']) settings.setdefault('sorting', self.config['SORTING']) settings.setdefault('embedding', self.config['EMBEDDING']) settings.setdefault('embedded_fields', []) settings.setdefault('pagination', self.config['PAGINATION']) settings.setdefault('projection', self.config['PROJECTION']) settings.setdefault('versioning', self.config['VERSIONING']) settings.setdefault('soft_delete', self.config['SOFT_DELETE']) settings.setdefault('bulk_enabled', self.config['BULK_ENABLED']) settings.setdefault('internal_resource', self.config['INTERNAL_RESOURCE']) settings.setdefault('etag_ignore_fields', None) # TODO make sure that this we really need the test below if settings['item_lookup']: item_methods = self.config['ITEM_METHODS'] else: item_methods = eve.ITEM_METHODS settings.setdefault('item_methods', item_methods) settings.setdefault('auth_field', self.config['AUTH_FIELD']) settings.setdefault('allow_unknown', self.config['ALLOW_UNKNOWN']) settings.setdefault('transparent_schema_rules', self.config['TRANSPARENT_SCHEMA_RULES']) settings.setdefault('extra_response_fields', self.config['EXTRA_RESPONSE_FIELDS']) settings.setdefault('mongo_write_concern', self.config['MONGO_WRITE_CONCERN']) settings.setdefault('mongo_indexes', {}) settings.setdefault('hateoas', self.config['HATEOAS']) settings.setdefault('authentication', self.auth if self.auth else None) # empty schemas are allowed for read-only access to resources schema = settings.setdefault('schema', {}) self.set_schema_defaults(schema, settings['id_field']) datasource = {} settings.setdefault('datasource', datasource) ds = settings['datasource'] ds.setdefault('source', resource) ds.setdefault('filter', None) ds.setdefault('default_sort', None) projection = ds.get('projection', {}) # check if any exclusion projection is defined exclusion = any(((k, v) for k, v in projection.items() if v == 0)) # If no exclusion projection is defined, enhance the projection # with automatic fields. Using both inclusion and exclusion will # be rejected by Mongo if not exclusion and len(schema) and \ settings['allow_unknown'] is False: if not projection: projection.update(dict((field, 1) for (field) in schema)) # enable retrieval of actual schema fields only. Eventual db # fields not included in the schema won't be returned. # despite projection, automatic fields are always included. projection[settings['id_field']] = 1 projection[self.config['LAST_UPDATED']] = 1 projection[self.config['DATE_CREATED']] = 1 projection[self.config['ETAG']] = 1 if settings['versioning'] is True: projection[self.config['VERSION']] = 1 projection[ settings['id_field'] + self.config['VERSION_ID_SUFFIX']] = 1 else: # all fields are returned. projection = None ds.setdefault('projection', projection) if settings['soft_delete'] is True and not exclusion and \ ds['projection'] is not None: ds['projection'][self.config['DELETED']] = 1 # 'defaults' helper set contains the names of fields with default # values in their schema definition. # TODO support default values for embedded documents. settings['defaults'] = build_defaults(schema) # list of all media fields for the resource settings['_media'] = [field for field, definition in schema.items() if definition.get('type') == 'media'] if settings['_media'] and not self.media: raise ConfigException('A media storage class of type ' ' eve.io.media.MediaStorage but be defined ' 'for "media" fields to be properly stored.')