def wipe(context: Context, model: Model, backend: Mongo): authorize(context, 'wipe', model) transaction = context.get('transaction') # Delete all data for a given model model_collection = backend.db[model.get_type_value()] return model_collection.delete_many({})
def wipe(context: Context, model: Model, backend: PostgreSQL): authorize(context, 'wipe', model) connection = context.get('transaction').connection table = _get_table(backend, model) connection.execute(table.changes.delete()) connection.execute(table.main.delete())
def getall( context: Context, model: Model, backend: PostgreSQL, *, show: typing.List[str] = None, sort: typing.List[typing.Dict[str, str]] = None, offset=None, limit=None, count: bool = False, ): authorize(context, 'getall', model) connection = context.get('transaction').connection table = _get_table(backend, model).main jm = JoinManager(backend, model, table) if count: query = sa.select([sa.func.count()]).select_from(table) result = connection.execute(query) yield {'count': result.scalar()} else: query = sa.select(_getall_show(table, jm, show)) query = _getall_order_by(query, table, jm, sort) query = _getall_offset(query, offset) query = _getall_limit(query, limit) result = connection.execute(query) for row in result: yield _get_data_from_row(model, table, row, show=show)
def changes(context: Context, model: Model, backend: PostgreSQL, *, id=None, offset=None, limit=None): authorize(context, 'changes', model) connection = context.get('transaction').connection table = _get_table(backend, model).changes query = sa.select([table]).order_by(table.c.change_id) query = _changes_id(table, query, id) query = _changes_offset(table, query, offset) query = _changes_limit(query, limit) result = connection.execute(query) for row in result: yield { 'change_id': row[table.c.change_id], 'transaction_id': row[table.c.transaction_id], 'id': row[table.c.id], 'datetime': row[table.c.datetime], 'action': row[table.c.action], 'change': row[table.c.change], }
def getall(context: Context, model: Model, backend: PostgreSQL, **kwargs): authorize(context, 'getall', model) connection = context.get('transaction').connection table = backend.tables[model.manifest.name][model.name].main result = connection.execute(sa.select([table])) for row in result: yield prepare(context, 'getall', model, backend, row)
def getall(context: Context, model: Model, backend: Mongo, **kwargs): authorize(context, 'getall', model) transaction = context.get('transaction') # Yield all available entries. model_collection = backend.db[model.get_type_value()] for row in model_collection.find({}): yield prepare(context, 'getall', model, backend, row)
def get(context: Context, model: Model, backend: Mongo, id: str): authorize(context, 'getone', model) transaction = context.get('transaction') model_collection = backend.db[model.get_type_value()] row = model_collection.find_one({"_id": ObjectId(id)}) return prepare(context, 'getone', model, backend, row)
def wipe(context: Context, model: Model, backend: PostgreSQL): authorize(context, 'wipe', model) connection = context.get('transaction').connection changes = backend.tables[model.manifest.name][model.name].changes connection.execute(changes.delete()) main = backend.tables[model.manifest.name][model.name].main connection.execute(main.delete())
def push(context: Context, model: Model, backend: PostgreSQL, data: dict, *, action: str): authorize(context, action, model, data=data) # load and check if data is a valid for it's model data = load(context, model, data) check(context, model, data) data = prepare(context, model, data) transaction = context.get('transaction') connection = transaction.connection table = backend.tables[model.manifest.name][model.name] data = { k: v for k, v in data.items() if k in table.main.columns } if action == INSERT_ACTION: result = connection.execute( table.main.insert().values(data), ) row_id = result.inserted_primary_key[0] elif action == UPDATE_ACTION: data['id'] = int(data['id']) result = connection.execute( table.main.update(). where(table.main.c.id == data['id']). values(data) ) if result.rowcount == 1: row_id = data['id'] elif result.rowcount == 0: raise Exception("Update failed, {self.obj} with {data['id']} not found.") else: raise Exception("Update failed, {self.obj} with {data['id']} has found and update {result.rowcount} rows.") elif action == DELETE_ACTION: raise NotImplementedError else: raise Exception(f"Unknown action {action!r}.") # Track changes. connection.execute( table.changes.insert().values( transaction_id=transaction.id, id=row_id, datetime=utcnow(), action=action, change={k: v for k, v in data.items() if k not in {'id'}}, ), ) return prepare(context, action, model, backend, {'id': str(row_id)})
def push(context: Context, model: Model, backend: Mongo, data: dict, *, action: str): authorize(context, action, model, data=data) # load and check if data is a valid for it's model data = load(context, model, data) check(context, model, data) data = prepare(context, model, data) # Push data to Mongo backend, this can be an insert, update or delete. If # `id` is not given, it is an insert if `id` is given, it is an update. # # Deletes are not yet implemented, but for deletes `data` must equal to # `{'id': 1, _delete: True}`. # # Also this must return inserted/updated/deleted id. # # Also this command must write information to changelog after change is # done. transaction = context.get('transaction') model_collection = backend.db[model.get_type_value()] # Make a copy of data, because `pymongo` changes the reference `data` # object on `insert_one()` call. # # We want to have our data intact from whatever specific mongo metadata # MongoDB may add to our object. raw_data = copy.deepcopy(data) # FIXME: before creating revision check if there's not collision clash revision_id = get_new_id('revision id') raw_data['revision'] = revision_id if 'id' in data: result = model_collection.update_one({'_id': ObjectId(raw_data['id'])}, {'$set': raw_data}) assert result.matched_count == 1 and result.modified_count == 1 data_id = data['id'] else: data_id = model_collection.insert_one(raw_data).inserted_id # parse `ObjectId` to string and add it to our object raw_data['id'] = str(data_id) return prepare(context, action, model, backend, raw_data)
def get(context: Context, model: Model, backend: PostgreSQL, id: str): authorize(context, 'getone', model) connection = context.get('transaction').connection table = _get_table(backend, model).main query = (sa.select([table]).where(table.c.id == id)) result = connection.execute(query) result = list(itertools.islice(result, 2)) if len(result) == 1: row = result[0] return _get_data_from_row(model, table, row) elif len(result) == 0: return None else: context.error(f"Multiple rows were found, id={id}.")
def push(context: Context, model: Model, backend: PostgreSQL, data: dict, *, action): authorize(context, action, model, data=data) transaction = context.get('transaction') connection = transaction.connection table = _get_table(backend, model) data = _serialize(data) key = get_ref_id(data.pop('id')) values = { 'data': data, 'transaction_id': transaction.id, } row = backend.get( connection, [table.main.c.data, table.main.c.transaction_id], table.main.c.id == key, default=None, ) action = None # Insert. if row is None: action = INSERT_ACTION result = connection.execute(table.main.insert().values({ 'id': key, 'created': utcnow(), **values, })) changes = data # Update. else: changes = _get_patch_changes(row[table.main.c.data], data) if changes: action = UPDATE_ACTION result = connection.execute( table.main.update().where(table.main.c.id == key).where( table.main.c.transaction_id == row[ table.main.c.transaction_id]).values({ **values, 'updated': utcnow(), })) # TODO: Retries are needed if result.rowcount is 0, if such # situation happens, that means a concurrent transaction # changed the data and we need to reread it. # # And assumption is made here, than in the same # transaction there are no concurrent updates, if this # assumption is false, then we need to check against # change_id instead of transaction_id. else: # Nothing to update. return None # Track changes. connection.execute( table.changes.insert().values( transaction_id=transaction.id, id=key, datetime=utcnow(), action=action, change=changes, ), ) # Sanity check, is primary key was really what we tell it to be? assert action != INSERT_ACTION or result.inserted_primary_key[ 0] == key, f'{result.inserted_primary_key[0]} == {key}' # Sanity check, do we really updated just one row? assert action != UPDATE_ACTION or result.rowcount == 1, result.rowcount return {'id': key}
def get(context: Context, model: Model, backend: PostgreSQL, id: str): authorize(context, 'getone', model) connection = context.get('transaction').connection table = backend.tables[model.manifest.name][model.name].main result = backend.get(connection, table, table.c.id == int(id)) return prepare(context, 'getone', model, backend, result)