Completed
Push — develop ( dd1a3e...21427c )
by
unknown
17s queued 13s
created

new_revision()   B

Complexity

Conditions 5

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 19
dl 0
loc 35
rs 8.9833
c 0
b 0
f 0
cc 5
nop 4
1
# -*- coding: utf-8 -*-
2
from sqlalchemy.orm import Session
3
from sqlalchemy import inspect
4
from sqlalchemy.orm.unitofwork import UOWTransaction
5
from transaction import TransactionManager
6
from contextlib import contextmanager
7
8
from tracim_backend.exceptions import ContentRevisionDeleteError
9
from tracim_backend.exceptions import ContentRevisionUpdateError
10
from tracim_backend.exceptions import SameValueError
11
12
from tracim_backend.models.data import ContentRevisionRO
13
from tracim_backend.models.data import Content
14
from tracim_backend.models.meta import DeclarativeBase
15
16
17
def prevent_content_revision_delete(
18
        session: Session,
19
        flush_context: UOWTransaction,
20
        instances: [DeclarativeBase]
21
) -> None:
22
    for instance in session.deleted:
23
        if isinstance(instance, ContentRevisionRO) \
24
                and instance.revision_id is not None:
25
            raise ContentRevisionDeleteError(
26
                "ContentRevision is not deletable. " +
27
                "You must make a new revision with" +
28
                "is_deleted set to True. Look at " +
29
                "tracim.model.new_revision context " +
30
                "manager to make a new revision"
31
            )
32
33
34
class RevisionsIntegrity(object):
35
    """
36
    Simple static used class to manage a list with list of ContentRevisionRO
37
    who are allowed to be updated.
38
39
    When modify an already existing (understood have an identity in databse)
40
    ContentRevisionRO, if it's not in RevisionsIntegrity._updatable_revisions
41
    list, a ContentRevisionUpdateError thrown.
42
43
    This class is used by tracim.model.new_revision context manager.
44
    """
45
    _updatable_revisions = []
46
47
    @classmethod
48
    def add_to_updatable(cls, revision: 'ContentRevisionRO') -> None:
49
        if inspect(revision).has_identity:
50
            raise ContentRevisionUpdateError("ContentRevision is not updatable. %s already have identity." % revision)  # nopep8
51
52
        if revision not in cls._updatable_revisions:
53
            cls._updatable_revisions.append(revision)
54
55
    @classmethod
56
    def remove_from_updatable(cls, revision: 'ContentRevisionRO') -> None:
57
        if revision in cls._updatable_revisions:
58
            cls._updatable_revisions.remove(revision)
59
60
    @classmethod
61
    def is_updatable(cls, revision: 'ContentRevisionRO') -> bool:
62
        return revision in cls._updatable_revisions
63
64
65
@contextmanager
66
def new_revision(
67
        session: Session,
68
        tm: TransactionManager,
69
        content: Content,
70
        force_create_new_revision: bool=False,
71
) -> Content:
72
    """
73
    Prepare context to update a Content. It will add a new updatable revision
74
    to the content.
75
    :param session: Database _session
76
    :param tm: TransactionManager
77
    :param content: Content instance to update
78
    :param force_create_new_revision: Decide if new_rev should or should not
79
    be forced.
80
    :return:
81
    """
82
    with session.no_autoflush:
83
        try:
84
            if force_create_new_revision \
85
                    or inspect(content.revision).has_identity:
86
                content.new_revision()
87
            RevisionsIntegrity.add_to_updatable(content.revision)
88
            yield content
89
        except Exception as e:
90
            # INFO - GM - 14-11-2018 - rollback session and renew
91
            # transaction when error happened
92
            # This avoid bad _session data like new "temporary" revision
93
            # to be add when problem happen.
94
            session.rollback()
95
            tm.abort()
96
            tm.begin()
97
            raise e
98
        finally:
99
            RevisionsIntegrity.remove_from_updatable(content.revision)
100