def test_versioning_insert_child(db, version_pids, build_pid):
    """Test PIDNodeVersioning.insert_child(...)."""
    new_pids = create_pids(3)
    parent_pid = build_pid(version_pids[0]['parent'])
    h1 = PIDNodeVersioning(parent_pid)
    # insert as first child
    h1.insert_child(new_pids[0], 0)
    version_pids[0]['children'].insert(0, new_pids[0])
    assert h1.children.ordered('asc').all() == \
        filter_pids(version_pids[0]['children'], PIDStatus.REGISTERED)

    # insert as last child. This should insert just before the draft
    version_pids[0]['children'].insert(h1.index(h1.draft_child), new_pids[1])
    h1.insert_child(new_pids[1], -1)
    # Check that the parent redirects to the added PID
    assert (version_pids[0]['parent'].get_redirect() == new_pids[1])
    # Register the draft so that it appears in the children
    h1.draft_child.register()
    h1.update_redirect()
    assert h1.children.ordered('asc').all() == \
        filter_pids(version_pids[0]['children'], PIDStatus.REGISTERED)

    # insert again but without a draft child. It should be inserted at the end.
    version_pids[0]['children'].append(new_pids[2])
    h1.insert_child(new_pids[2], -1)
    assert h1.children.ordered('asc').all() == \
        filter_pids(version_pids[0]['children'], PIDStatus.REGISTERED)

    reserved_pid = create_pids(1, status=PIDStatus.RESERVED)[0]

    # Check the exception raised when trying to insert a RESERVED PID
    with pytest.raises(PIDRelationConsistencyError):
        h1.insert_child(reserved_pid)
예제 #2
0
def alembic_upgrade_database_data(alembic, verbose):
    """Migrate the database data from v2.0.0 to 2.1.0."""
    ### Add versioning PIDs ###
    # Reserve the record PID and versioning PID for unpublished deposits

    # Hack: disable record indexing during record migration
    from invenio_indexer.api import RecordIndexer
    old_index_fn = RecordIndexer.index
    RecordIndexer.index = lambda s, record: None

    if verbose:
        click.secho('migrating deposits and records...')
    with db.session.begin_nested():
        # Migrate published records
        records_pids = PersistentIdentifier.query.filter(
            PersistentIdentifier.pid_type == RecordUUIDProvider.pid_type,
            PersistentIdentifier.status == PIDStatus.REGISTERED,
        ).all()
        for rec_pid in records_pids:
            if verbose:
                click.secho('    record {}'.format(rec_pid.pid_value))
            try:
                record = Record.get_record(rec_pid.object_uuid)
            except NoResultFound:
                # The record is deleted but not the PID. Fix it.
                rec_pid.status = PIDStatus.DELETED
                continue
            # Create parent version PID
            parent_pid = RecordUUIDProvider.create().pid
            version_master = PIDNodeVersioning(parent=parent_pid)
            version_master.insert_draft_child(child=rec_pid)
            version_master.update_redirect()
            migrate_record_metadata(
                Record.get_record(rec_pid.object_uuid),
                parent_pid
            )

        # Migrate deposits
        deposit_pids = PersistentIdentifier.query.filter(
            PersistentIdentifier.pid_type == DepositUUIDProvider.pid_type,
            PersistentIdentifier.status == PIDStatus.REGISTERED,
        ).all()
        for dep_pid in deposit_pids:
            if verbose:
                click.secho('    deposit {}'.format(dep_pid.pid_value))
            try:
                deposit = Deposit.get_record(dep_pid.object_uuid)

                if deposit['publication_state'] != \
                        PublicationStates.published.name:
                    # The record is not published yet. Reserve the PID.
                    rec_pid = RecordUUIDProvider.create(
                        object_type='rec',
                        pid_value=dep_pid.pid_value,
                    ).pid
                    # Create parent version PID
                    parent_pid = RecordUUIDProvider.create().pid
                    assert parent_pid
                    version_master = PIDNodeVersioning(parent=parent_pid)
                    version_master.insert_draft_child(child=rec_pid)
                else:
                    # Retrieve previously created version PID
                    rec_pid = RecordUUIDProvider.get(dep_pid.pid_value).pid
                    version_master = PIDNodeVersioning(child=rec_pid)
                    parent_pid = version_master.parent
                    if not parent_pid:
                        click.secho('    record {} was deleted, but the deposit has not been removed'.format(rec_pid.pid_value), fg='red')

                if parent_pid:
                    migrate_record_metadata(
                        Deposit.get_record(dep_pid.object_uuid),
                        parent_pid
                    )
            except NoResultFound:
                # The deposit is deleted but not the PID. Fix it.
                dep_pid.status = PIDStatus.DELETED


    if verbose:
        click.secho('done migrating deposits.')
    RecordIndexer.index = old_index_fn
def test_update_redirect(db, version_pids, build_pid):
    """Test PIDNodeVersioning.update_redirect()."""
    # Test update_redirect on a PID without any child
    parent_pids = create_pids(1, prefix='parent', status=PIDStatus.RESERVED)
    draft_pids = create_pids(2, prefix='draft', status=PIDStatus.RESERVED)
    parent = PIDNodeVersioning(build_pid(parent_pids[0]))
    parent.update_redirect()
    assert parent_pids[0].status == PIDStatus.RESERVED

    # Test that update_redirect remains reserved once it has a draft child
    parent.insert_draft_child(draft_pids[0])
    assert parent_pids[0].status == PIDStatus.RESERVED

    h1 = PIDNodeVersioning(build_pid(version_pids[0]['parent']))

    def test_redirect(expected_length, expected_redirect):
        filtered = filter_pids(version_pids[0]['children'],
                               status=PIDStatus.REGISTERED)
        assert len(filtered) == expected_length
        assert h1.children.ordered('asc').all() == filtered
        assert h1._resolved_pid.get_redirect() == expected_redirect

    # Test update_redirect when it already points to the last version
    last = h1.last_child
    draft = h1.draft_child
    h1.update_redirect()
    test_redirect(3, last)

    # Test update_redirect after publishing the draft
    h1.draft_child.register()
    h1.update_redirect()
    test_redirect(4, draft)

    # Test update_redirect after deleting the last version
    h1.last_child.delete()
    h1.update_redirect()
    test_redirect(3, last)

    # Test that if every version is deleted the HEAD pid is also deleted
    for pid in filter_pids(version_pids[0]['children'],
                           status=PIDStatus.REGISTERED):
        pid.delete()
    h1.update_redirect()
    test_redirect(0, last)

    # Test that an exception is raised if unsupported PIDStatus are used.
    version_pids[0]['children'][0].status = PIDStatus.NEW
    with pytest.raises(PIDRelationConsistencyError):
        h1.update_redirect()