Esempio n. 1
0
def context(mocker, config, postgresql, mongo):
    mocker.patch.dict(os.environ, {
        'AUTHLIB_INSECURE_TRANSPORT': '1',
    })

    Context = config.get('components', 'core', 'context', cast=importstr)
    Context = type('ContextForTests', (ContextForTests, Context), {})
    context = Context()

    context.set('config', components.Config())
    store = context.set('store', Store())

    load_commands(config.get('commands', 'modules', cast=list))
    load(context, context.get('config'), config)
    check(context, context.get('config'))
    load(context, store, config)
    check(context, store)
    prepare(context, store.internal)
    migrate(context, store)
    prepare(context, store)
    migrate(context, store)

    context.bind('auth.server', AuthorizationServer, context)
    context.bind('auth.resource_protector', ResourceProtector, context,
                 BearerTokenValidator)

    yield context

    with context.enter():
        # FIXME: quick and dirty workaround on `context.wipe` missing a connection,
        # when exception is thrown in spinta's logic.
        context.set('transaction',
                    store.manifests['default'].backend.transaction(write=True))
        context.set('auth.token', AdminToken())

        # Remove all data after each test run.
        graph = collections.defaultdict(set)
        for model in store.manifests['default'].objects['model'].values():
            if model.name not in graph:
                graph[model.name] = set()
            for prop in model.properties.values():
                if prop.type.name == 'ref':
                    graph[prop.type.object].add(model.name)

        for models in toposort(graph):
            for name in models:
                context.wipe(name)

        # Datasets does not have foreign kei constraints, so there is no need to
        # topologically sort them. At least for now.
        for dataset in store.manifests['default'].objects['dataset'].values():
            for model in dataset.objects.values():
                context.wipe(model)

        context.wipe(store.internal.objects['model']['transaction'])
Esempio n. 2
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)})
Esempio n. 3
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)
Esempio n. 4
0
def main(ctx, option):
    c = Config()
    c.read(cli_args=option)

    load_commands(c.get('commands', 'modules', cast=list))

    Context = c.get('components', 'core', 'context', cast=importstr)
    context = Context()
    config = context.set('config', components.Config())
    store = context.set('store', Store())

    commands.load(context, config, c)
    commands.check(context, config)
    commands.load(context, store, c)
    commands.check(context, store)

    ctx.ensure_object(dict)
    ctx.obj['context'] = context
Esempio n. 5
0
def push(context: Context, store: Store, stream: types.GeneratorType):
    manifest = store.manifests['default']
    client_supplied_ids = ClientSuppliedIDs()
    for data in stream:
        data = dict(data)
        model_name = data.pop('type', None)
        assert model_name is not None, data
        model = get_model_by_name(manifest, model_name)
        client_id = client_supplied_ids.replace(model_name, data)
        check(context, model, model.backend, data)

        if 'id' in data:
            action = 'update'
        else:
            action = 'insert'
        pushed_data = push(context, model, model.backend, data, action=action)
        if pushed_data is not None:
            yield client_supplied_ids.update(client_id, {
                **data,
                'type': model_name,
                'id': pushed_data['id'],
            })
Esempio n. 6
0
def check(context: Context, store: Store):
    check(context, store.internal)
    for manifest in store.manifests.values():
        check(context, manifest)
Esempio n. 7
0
    level=logging.INFO,
    format='%(levelname)s: %(message)s',
)

c = Config()
c.read()

load_commands(c.get('commands', 'modules', cast=list))

Context = c.get('components', 'core', 'context', cast=importstr)
context = Context()
config = context.set('config', components.Config())
store = context.set('store', Store())

load(context, config, c)
check(context, config)
load(context, store, c)
check(context, store)

wait(context, store, c)

prepare(context, store.internal)
prepare(context, store)

context.set('auth.server', AuthorizationServer(context))
context.set('auth.resource_protector',
            ResourceProtector(context, BearerTokenValidator))

set_context(context)

app.debug = config.debug
Esempio n. 8
0
def check(context: Context, manifest: Manifest):
    for objects in manifest.objects.values():
        for obj in objects.values():
            check(context, obj)
Esempio n. 9
0
from spinta.commands import load, wait, prepare, check
from spinta.components import Context, Store
from spinta.utils.commands import load_commands
from spinta import components
from spinta.config import Config

logging.basicConfig(
    level=logging.INFO,
    format='%(levelname)s: %(message)s',
)

c = Config()
c.read()

load_commands(c.get('commands', 'modules', cast=list))

context = Context()
config = context.set('config', components.Config())
store = context.set('store', Store())

load(context, config, c)
load(context, store, c)
check(context, store)

wait(context, store, c)

prepare(context, store.internal)
prepare(context, store)

set_context(context)