def load_data_from_mongoexport(res_id, export_location, collection_name, remove_id=False): """ This file should come from mongoexport, with or without the --jsonArray flag. That is to say, it should either be a series of documents, each on its own line, or a single array of documents. All documents will be inserted into the given collection. """ export_location = _data_file_path(export_location) with open(export_location) as export: first_char = export.read(1) export.seek(0, SEEK_SET) if first_char == '[': # Data is already in an array documents = loads(export.read()) else: # Each line of data is an object documents = [] for line in export: documents.append(loads(line)) if remove_id: _remove_id(documents) with UseResId(res_id): get_db()[collection_name].insert(documents)
def db_collection_insert(res_id, collection_name): # TODO: Ensure request.json is not None. if 'document' in request.json: document = request.json['document'] else: error = '\'document\' argument not found in the insert request.' raise MWSServerError(400, error) # Check quota size = get_collection_size(res_id, collection_name) # Handle inserting both a list of docs or a single doc if isinstance(document, list): req_size = 0 for d in document: req_size += len(BSON.encode(d)) else: req_size = len(BSON.encode(document)) if size + req_size > current_app.config['QUOTA_COLLECTION_SIZE']: raise MWSServerError(403, 'Collection size exceeded') # Insert document with UseResId(res_id): try: get_db()[collection_name].insert(document) return empty_success() except InvalidDocument as e: raise MWSServerError(400, e.message)
def db_collection_insert(res_id, collection_name): # TODO: Ensure request.json is not None. try: request.json = loads(request.data) except (InvalidId, TypeError) as e: raise MWSServerError(400, str(e)) if 'document' in request.json: document = request.json['document'] else: error = '\'document\' argument not found in the insert request.' raise MWSServerError(400, error) # Check quota size = get_collection_size(res_id, collection_name) # Handle inserting both a list of docs or a single doc if isinstance(document, list): req_size = 0 for d in document: req_size += len(BSON.encode(d)) else: req_size = len(BSON.encode(document)) if size + req_size > current_app.config['QUOTA_COLLECTION_SIZE']: raise MWSServerError(403, 'Collection size exceeded') # Insert document with UseResId(res_id): try: get_db()[collection_name].insert(document) return empty_success() except (DuplicateKeyError, InvalidDocument, InvalidId, TypeError) as e: raise MWSServerError(400, str(e))
def db_collection_update(res_id, collection_name): query = update = None if request.json: query = request.json.get('query') update = request.json.get('update') upsert = request.json.get('upsert', False) multi = request.json.get('multi', False) if query is None or update is None: error = 'update requires spec and document arguments' raise MWSServerError(400, error) # Check quota size = get_collection_size(res_id, collection_name) with UseResId(res_id): # Computation of worst case size increase - update size * docs affected # It would be nice if we were able to make a more conservative estimate # of the space difference that an update will cause. (especially if it # results in smaller documents) db = get_db() affected = db[collection_name].find(query).count() req_size = len(BSON.encode(update)) * affected if size + req_size > current_app.config['QUOTA_COLLECTION_SIZE']: raise MWSServerError(403, 'Collection size exceeded') try: db[collection_name].update(query, update, upsert, multi=multi) return empty_success() except OperationFailure as e: raise MWSServerError(400, e.message)
def db_drop(res_id): DB = get_db() collections = get_collection_names(res_id) with UseResId(res_id): for c in collections: DB.drop_collection(c) return empty_success()
def db_collection_update(res_id, collection_name): query = update = None if request.json: query = request.json.get('query') update = request.json.get('update') upsert = request.json.get('upsert', False) multi = request.json.get('multi', False) if query is None or update is None: error = 'update requires spec and document arguments' raise MWSServerError(400, error) # Check quota size = get_collection_size(res_id, collection_name) with UseResId(res_id): # Computation of worst case size increase - update size * docs affected # It would be nice if we were able to make a more conservative estimate # of the space difference that an update will cause. (especially if it # results in smaller documents) db = get_db() affected = db[collection_name].find(query).count() req_size = len(BSON.encode(update)) * affected if size + req_size > current_app.config['QUOTA_COLLECTION_SIZE']: raise MWSServerError(403, 'Collection size exceeded') try: db[collection_name].update(query, update, upsert, multi=multi) return empty_success() except (DuplicateKeyError, InvalidDocument, InvalidId, TypeError, OperationFailure) as e: raise MWSServerError(400, str(e))
def test_updates_collection_list(self): with self.real_app.app_context(): db = get_db() res_id = 'myresid.' # Setup resource id record clients_collection = db[CLIENTS_COLLECTION] clients_collection.remove({'res_id': res_id}) clients_collection.insert({ 'res_id': res_id, 'collections': [] }) with UseResId(res_id) as db: self.assertItemsEqual(get_collection_names(res_id), []) db.foo.insert({'message': 'test'}) self.assertItemsEqual(get_collection_names(res_id), ['foo']) self.assertItemsEqual(list(db.foo.find({}, {'_id': 0})), [{'message': 'test'}]) db.bar.update({}, {'message': 'test'}, upsert=True) self.assertItemsEqual(get_collection_names(res_id), ['foo', 'bar']) self.assertItemsEqual(list(db.bar.find({}, {'_id': 0})), [{'message': 'test'}]) db.foo.drop() self.assertItemsEqual(get_collection_names(res_id), ['bar']) self.assertNotIn(res_id + 'foo', get_collection_names(res_id))
def cleanup_collections(res_id): db = get_db() for coll in db.collection_names(): _logger.info(coll) if coll.startswith(res_id): _logger.info('dropping') db.drop_collection(coll)
def test_updates_collection_list(self): with self.real_app.app_context(): db = get_db() res_id = 'myresid.' # Setup resource id record clients_collection = db[CLIENTS_COLLECTION] clients_collection.remove({'res_id': res_id}) clients_collection.insert({'res_id': res_id, 'collections': []}) with UseResId(res_id) as db: self.assertItemsEqual(get_collection_names(res_id), []) db.foo.insert({'message': 'test'}) self.assertItemsEqual(get_collection_names(res_id), ['foo']) self.assertItemsEqual(list(db.foo.find({}, {'_id': 0})), [{ 'message': 'test' }]) db.bar.update({}, {'message': 'test'}, upsert=True) self.assertItemsEqual(get_collection_names(res_id), ['foo', 'bar']) self.assertItemsEqual(list(db.bar.find({}, {'_id': 0})), [{ 'message': 'test' }]) db.foo.drop() self.assertItemsEqual(get_collection_names(res_id), ['bar']) self.assertNotIn(res_id + 'foo', get_collection_names(res_id))
def test_keep_mws_alive(self, datetime_mock): first = datetime.datetime(2012, 7, 4) second = first + datetime.timedelta(days=1) datetime_mock.now.return_value = first db = get_db() # get a session to keep alive rv = self.app.post('/mws/') res_id = loads(rv.data)['res_id'] with self.app.session_transaction() as sess: session_id = sess['session_id'] res = db.clients.find({ 'res_id': res_id, 'session_id': session_id }, {'timestamp': 1}) _id = res[0]['_id'] old_ts = res[0]['timestamp'] self.assertEqual(old_ts, first) datetime_mock.now.return_value = second url = '/mws/' + res_id + '/keep-alive' rv = self.app.post(url) self.assertEqual(rv.status_code, 204) newres = db.clients.find({'_id': _id}, {'timestamp': 1}) self.assertEqual(newres[0]['timestamp'], second)
def db_collection_find(res_id, collection_name): # TODO: Should we specify a content type? Then we have to use an options # header, and we should probably get the return type from the content-type # header. try: parse_get_json(request) except (InvalidId, TypeError) as e: raise MWSServerError(400, str(e)) query = request.json.get('query') projection = request.json.get('projection') skip = request.json.get('skip', 0) limit = request.json.get('limit', 0) sort = request.json.get('sort', {}) sort = sort.items() with UseResId(res_id): coll = get_db()[collection_name] try: cursor = coll.find(query, projection, skip, limit) except (InvalidId, TypeError) as e: raise MWSServerError(400, str(e)) if len(sort) > 0: cursor.sort(sort) documents = list(cursor) return to_json({'result': documents})
def test_mangles_collection_names_automatically(self): with self.real_app.app_context(): db = get_db() with UseResId('myresid.'): coll = db.foo self.assertEqual(coll.name, 'myresid.foo') coll = db.foo self.assertEqual(coll.name, 'foo')
def db_collection_aggregate(res_id, collection_name): parse_get_json(request) try: with UseResId(res_id): coll = get_db()[collection_name] result = coll.aggregate(request.json) return to_json(result) except OperationFailure as e: raise MWSServerError(400, e.message)
def keep_mws_alive(res_id): clients = get_db()[CLIENTS_COLLECTION] clients.update({ 'session_id': session.get('session_id'), 'res_id': res_id }, {'$set': { 'timestamp': datetime.now() }}) return empty_success()
def get_collection_size(res_id, collection_name): coll = get_internal_coll_name(res_id, collection_name) try: return get_db().command({'collstats': coll})['size'] except OperationFailure as e: if 'ns not found' in e.message: return 0 else: raise MWSServerError(500, e.message)
def _get_res_id(): if 'session_id' not in session: raise MWSServerError(400, "Invalid request (missing session)") session_id = session['session_id'] clients = get_db()[CLIENTS_COLLECTION] doc = clients.find_one({'session_id': session_id}, {'res_id': 1, '_id': 0}) if not doc: raise MWSServerError(500, "Resource id not associated with session") return doc['res_id']
def get_collection_size(res_id, collection_name): coll = get_internal_coll_name(res_id, collection_name) try: return get_db().command({'collstats': coll})['size'] except OperationFailure as e: if 'ns not found' in str(e): return 0 else: raise MWSServerError(500, str(e))
def setUp(self): super(DBCollectionTestCase, self).setUp() self.coll_name = "test_collection" self.internal_coll_name = get_internal_coll_name(self.res_id, self.coll_name) self.db = get_db() self.db_collection = self.db[self.internal_coll_name] self.make_request_url = "/mws/%s/db/%s/%%s" % (self.res_id, self.coll_name)
def setUp(self): super(DBCollectionTestCase, self).setUp() self.coll_name = 'test_collection' self.internal_coll_name = to_coll_name(self.res_id, self.coll_name) self.db = get_db() self.db_collection = self.db[self.internal_coll_name] self.make_request_url = '/mws/%s/db/%s/%%s' % \ (self.res_id, self.coll_name)
def setUp(self): super(ExpireSessionsTestCase, self).setUp() self.context = self.real_app.app_context() self.context.push() self.db = get_db() # All test data should be before this date # We can assume we don't have any real data before this date self.test_before_date = datetime(2012, 7, 6) self.db.clients.remove({'timestamp': {'$lt': self.test_before_date}})
def setUp(self): super(QuotaCollectionsTestCase, self).setUp() self.old_quota = self.real_app.config['QUOTA_NUM_COLLECTIONS'] self.res_id = 'myresid.' with self.real_app.app_context(): self.db = get_db() collections = get_collection_names(self.res_id) with UseResId(self.res_id): for c in collections: self.db.drop_collection(c)
def db_collection_remove(res_id, collection_name): constraint = request.json.get('constraint') if request.json else {} just_one = request.json and request.json.get('just_one', False) with UseResId(res_id): collection = get_db()[collection_name] if just_one: collection.find_and_modify(constraint, remove=True) else: collection.remove(constraint) return empty_success()
def db_collection_aggregate(res_id, collection_name): parse_get_json(request) try: with UseResId(res_id): coll = get_db()[collection_name] try: result = coll.aggregate(request.json) return to_json(result) except (InvalidId, TypeError, InvalidDocument) as e: raise MWSServerError(400, str(e)) except OperationFailure as e: raise MWSServerError(400, str(e))
def db_collection_count(res_id, collection_name): parse_get_json(request) query = request.json.get('query') skip = request.json.get('skip', 0) limit = request.json.get('limit', 0) use_skip_limit = bool(skip or limit) with UseResId(res_id): coll = get_db()[collection_name] cursor = coll.find(query, skip=skip, limit=limit) count = cursor.count(use_skip_limit) return to_json({'count': count})
def setUp(self): super(DBTestCase, self).setUp() # Todo: For stuff that isn't checking authentication, # we probably don't want to rely on/use the authentication code rv = self.app.post('/mws/') response_dict = loads(rv.data) self.assertIn('res_id', response_dict) self.res_id = response_dict['res_id'] self.assertIsNotNone(self.res_id) self.db = get_db() self.make_request_url = '/mws/%s/db/%%s' % (self.res_id)
def expire_sessions(app): with app.app_context(): db = get_db(MWSExceptions=False) delta = timedelta(seconds=EXPIRE_SESSION_DURATION) exp = datetime.now() - delta sessions = db.clients.find({'timestamp': {'$lt': exp}}) for sess in sessions: db.clients.remove(sess) # Todo: Only remove collections if no one else is using this res_id res_id = sess['res_id'] for c in sess['collections']: db.drop_collection(get_internal_coll_name(res_id, c)) app.logger.info('Timed out expired sessions dead before %s' % exp)
def db_collection_save(res_id, collection_name): # TODO: Ensure request.json is not None. if 'document' in request.json: document = request.json['document'] else: error = '\'document\' argument not found in the save request.' raise MWSServerError(400, error) # Check quota size = get_collection_size(res_id, collection_name) req_size = len(BSON.encode(document)) if size + req_size > current_app.config['QUOTA_COLLECTION_SIZE']: raise MWSServerError(403, 'Collection size exceeded') # Save document with UseResId(res_id): try: get_db()[collection_name].save(document) return empty_success() except (InvalidId, TypeError, InvalidDocument, DuplicateKeyError) as e: raise MWSServerError(400, str(e))
def load_data_from_json(res_id, file_name, remove_id=False): """ The top level of this file should be an object who's keys are collection names which map to an array of documents to be inserted into the collection """ with open(file_name) as json_file: collections = loads(json_file.read()) db = get_db() _logger.info(db.collection_names()) with UseResId(res_id): for collection, documents in collections.iteritems(): if remove_id: _remove_id(documents) db[collection].insert(documents)
def db_collection_save(res_id, collection_name): # TODO: Ensure request.json is not None. if 'document' in request.json: document = request.json['document'] else: error = '\'document\' argument not found in the save request.' raise MWSServerError(400, error) # Check quota size = get_collection_size(res_id, collection_name) req_size = len(BSON.encode(document)) if size + req_size > current_app.config['QUOTA_COLLECTION_SIZE']: raise MWSServerError(403, 'Collection size exceeded') # Save document with UseResId(res_id): try: get_db()[collection_name].save(document) return empty_success() except InvalidDocument as e: raise MWSServerError(400, e.message)
def db_collection_remove(res_id, collection_name): constraint = request.json.get('constraint') if request.json else {} just_one = request.json and request.json.get('just_one', False) with UseResId(res_id): collection = get_db()[collection_name] try: if just_one: collection.find_and_modify(constraint, remove=True) else: collection.remove(constraint) except (InvalidDocument, InvalidId, TypeError) as e: raise MWSServerError(400, str(e)) return empty_success()
def load_data_from_json(res_id, file_name, remove_id=False): """ The top level of this file should be an object who's keys are collection names which map to an array of documents to be inserted into the collection """ file_name = _data_file_path(file_name) with open(file_name) as json_file: collections = loads(json_file.read()) db = get_db() _logger.info(db.collection_names()) with UseResId(res_id): for collection, documents in collections.iteritems(): if remove_id: _remove_id(documents) db[collection].insert(documents)
def db_collection_count(res_id, collection_name): try: parse_get_json(request) except (InvalidId, TypeError) as e: raise MWSServerError(400, str(e)) query = request.json.get('query') skip = request.json.get('skip', 0) limit = request.json.get('limit', 0) use_skip_limit = bool(skip or limit) with UseResId(res_id): coll = get_db()[collection_name] try: cursor = coll.find(query, skip=skip, limit=limit) count = cursor.count(use_skip_limit) return to_json({'count': count}) except InvalidDocument as e: raise MWSServerError(400, str(e))
def db_collection_find(res_id, collection_name): # TODO: Should we specify a content type? Then we have to use an options # header, and we should probably get the return type from the content-type # header. parse_get_json(request) query = request.json.get('query') projection = request.json.get('projection') skip = request.json.get('skip', 0) limit = request.json.get('limit', 0) sort = request.json.get('sort', {}) sort = sort.items() with UseResId(res_id): coll = get_db()[collection_name] cursor = coll.find(query, projection, skip, limit) if len(sort) > 0: cursor.sort(sort) documents = list(cursor) return to_json({'result': documents})
def test_updates_collection_list(self): with self.real_app.app_context(): db = get_db() res_id = 'myresid.' # Setup resource id record clients_collection = db[CLIENTS_COLLECTION] clients_collection.remove({'res_id': res_id}) clients_collection.insert({'res_id': res_id, 'collections': []}) def get_collections(): # Can't use the util function because we would be using it # inside the with, so the collection name would be mangled return clients_collection.find({'res_id': res_id}, { '_id': 0, 'collections': 1 })[0]['collections'] with UseResId(res_id): self.assertItemsEqual(get_collections(), []) db.foo.insert({'message': 'test'}) self.assertItemsEqual(get_collections(), ['foo']) self.assertItemsEqual(list(db.foo.find({}, {'_id': 0})), [{ 'message': 'test' }]) db.bar.update({}, {'message': 'test'}) self.assertItemsEqual(get_collections(), ['foo']) db.bar.update({}, {'message': 'test'}, upsert=True) self.assertItemsEqual(get_collections(), ['foo', 'bar']) self.assertItemsEqual(list(db.bar.find({}, {'_id': 0})), [{ 'message': 'test' }]) db.foo.drop() self.assertItemsEqual(get_collections(), ['bar']) self.assertNotIn(res_id + 'foo', db.collection_names()) db.drop_collection('bar') self.assertItemsEqual(get_collections(), []) self.assertNotIn(res_id + 'bar', db.collection_names())
def test_updates_collection_list(self): with self.real_app.app_context(): db = get_db() res_id = 'myresid.' # Setup resource id record clients_collection = db[CLIENTS_COLLECTION] clients_collection.remove({'res_id': res_id}) clients_collection.insert({ 'res_id': res_id, 'collections': [] }) def get_collections(): # Can't use the util function because we would be using it # inside the with, so the collection name would be mangled return clients_collection.find( {'res_id': res_id}, {'_id': 0, 'collections': 1} )[0]['collections'] with UseResId(res_id): self.assertItemsEqual(get_collections(), []) db.foo.insert({'message': 'test'}) self.assertItemsEqual(get_collections(), ['foo']) self.assertItemsEqual(list(db.foo.find({}, {'_id': 0})), [{'message': 'test'}]) db.bar.update({}, {'message': 'test'}) self.assertItemsEqual(get_collections(), ['foo']) db.bar.update({}, {'message': 'test'}, upsert=True) self.assertItemsEqual(get_collections(), ['foo', 'bar']) self.assertItemsEqual(list(db.bar.find({}, {'_id': 0})), [{'message': 'test'}]) db.foo.drop() self.assertItemsEqual(get_collections(), ['bar']) self.assertNotIn(res_id + 'foo', db.collection_names()) db.drop_collection('bar') self.assertItemsEqual(get_collections(), []) self.assertNotIn(res_id + 'bar', db.collection_names())
def create_mws_resource(): session_id = session.get('session_id', str(uuid.uuid4())) session['session_id'] = session_id clients = get_db()[CLIENTS_COLLECTION] cursor = clients.find({'session_id': session_id}, {'res_id': 1, '_id': 0}) if cursor.count(): # TODO: handle multiple res_id per session res_id = cursor[0]['res_id'] is_new = False else: res_id = generate_res_id() clients.insert({ 'version': 1, 'res_id': res_id, 'collections': [], 'session_id': session_id, 'timestamp': datetime.now() }) is_new = True return to_json({'res_id': res_id, 'is_new': is_new})
def test_keep_mws_alive(self, datetime_mock): first = datetime.datetime(2012, 7, 4) second = first + datetime.timedelta(days=1) datetime_mock.now.return_value = first db = get_db() # get a session to keep alive rv = self.app.post("/mws/") res_id = loads(rv.data)["res_id"] with self.app.session_transaction() as sess: session_id = sess["session_id"] res = db.clients.find({"res_id": res_id, "session_id": session_id}, {"timestamp": 1}) _id = res[0]["_id"] old_ts = res[0]["timestamp"] self.assertEqual(old_ts, first) datetime_mock.now.return_value = second url = "/mws/" + res_id + "/keep-alive" rv = self.app.post(url) self.assertEqual(rv.status_code, 204) newres = db.clients.find({"_id": _id}, {"timestamp": 1}) self.assertEqual(newres[0]["timestamp"], second)
def test_keep_mws_alive(self, datetime_mock): first = datetime.datetime(2012, 7, 4) second = first + datetime.timedelta(days=1) datetime_mock.now.return_value = first db = get_db() # get a session to keep alive rv = self.app.post('/mws/') res_id = loads(rv.data)['res_id'] with self.app.session_transaction() as sess: session_id = sess['session_id'] res = db.clients.find({'res_id': res_id, 'session_id': session_id}, {'timestamp': 1}) _id = res[0]['_id'] old_ts = res[0]['timestamp'] self.assertEqual(old_ts, first) datetime_mock.now.return_value = second url = '/mws/' + res_id + '/keep-alive' rv = self.app.post(url) self.assertEqual(rv.status_code, 204) newres = db.clients.find({'_id': _id}, {'timestamp': 1}) self.assertEqual(newres[0]['timestamp'], second)
def __init__(self, res_id): self.res_id = res_id self.db = get_db()
def keep_mws_alive(res_id): clients = get_db()[CLIENTS_COLLECTION] clients.update({'session_id': session.get('session_id'), 'res_id': res_id}, {'$set': {'timestamp': datetime.now()}}) return empty_success()
def db_collection_drop(res_id, collection_name): with UseResId(res_id): get_db().drop_collection(collection_name) return empty_success()
def cleanup_collections(res_id): db = get_db() for coll in get_collection_names(res_id): with UseResId(res_id): _logger.info('dropping %s', coll) db.drop_collection(coll)
def user_has_access(res_id, session_id): query = {'res_id': res_id, 'session_id': session_id} coll = get_db()[CLIENTS_COLLECTION] return_value = coll.find_one(query) return False if return_value is None else True