Exemple #1
0
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({})
Exemple #2
0
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())
Exemple #3
0
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)
Exemple #4
0
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],
        }
Exemple #5
0
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)
Exemple #6
0
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)
Exemple #7
0
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)
Exemple #8
0
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())
Exemple #9
0
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)})
Exemple #10
0
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)
Exemple #11
0
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}.")
Exemple #12
0
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}
Exemple #13
0
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)