mirror of
https://github.com/GSA/notifications-api.git
synced 2025-12-20 07:21:13 -05:00
Adding @nested_transactional for transactions that require more than one
db update/insert. Using a savepoint for the multiple transactions allows us to rollback if there is an error when executing the second db transaction. However, this does add a bit of complexity. Developers need to manage the db session when calling multiple nested tranactions. Unit tests have been added to test this functionality and some end to end tests have been done to make sure all transactions are rollback if there is an exception while executing the transaction.
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
from flask import current_app
|
||||
|
||||
from app import db
|
||||
from app.dao.dao_utils import transactional
|
||||
from app.dao.dao_utils import nested_transactional, transactional
|
||||
from app.dao.date_util import get_current_financial_year_start_year
|
||||
from app.models import AnnualBilling
|
||||
|
||||
|
||||
@transactional
|
||||
@nested_transactional
|
||||
def dao_create_or_update_annual_billing_for_year(service_id, free_sms_fragment_limit, financial_year_start):
|
||||
result = dao_get_free_sms_fragment_limit_for_year(service_id, financial_year_start)
|
||||
|
||||
@@ -53,7 +53,7 @@ def dao_get_all_free_sms_fragment_limit(service_id):
|
||||
).order_by(AnnualBilling.financial_year_start).all()
|
||||
|
||||
|
||||
def set_default_free_allowance_for_service(service, year_start=None, commit=True):
|
||||
def set_default_free_allowance_for_service(service, year_start=None):
|
||||
default_free_sms_fragment_limits = {
|
||||
'central': {
|
||||
2020: 250_000,
|
||||
|
||||
@@ -18,6 +18,23 @@ def transactional(func):
|
||||
return commit_or_rollback
|
||||
|
||||
|
||||
def nested_transactional(func):
|
||||
# This creates a save point for the nested transaction.
|
||||
# You must manage the commit or rollback from outer most call of the nested of the transactions.
|
||||
@wraps(func)
|
||||
def commit_or_rollback(*args, **kwargs):
|
||||
try:
|
||||
db.session.begin_nested()
|
||||
res = func(*args, **kwargs)
|
||||
db.session.commit()
|
||||
return res
|
||||
except Exception:
|
||||
db.session.rollback()
|
||||
raise
|
||||
|
||||
return commit_or_rollback
|
||||
|
||||
|
||||
class VersionOptions():
|
||||
|
||||
def __init__(self, model_class, history_class=None, must_write_history=True):
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
from sqlalchemy.sql.expression import func
|
||||
|
||||
from app import db
|
||||
from app.dao.dao_utils import VersionOptions, transactional, version_class
|
||||
from app.dao.dao_utils import (
|
||||
VersionOptions,
|
||||
nested_transactional,
|
||||
transactional,
|
||||
version_class,
|
||||
)
|
||||
from app.models import Domain, Organisation, Service, User
|
||||
|
||||
|
||||
@@ -105,7 +110,7 @@ def _update_organisation_services(organisation, attribute, only_where_none=True)
|
||||
db.session.add(service)
|
||||
|
||||
|
||||
@transactional
|
||||
@nested_transactional
|
||||
@version_class(Service)
|
||||
def dao_add_service_to_organisation(service, organisation_id):
|
||||
organisation = Organisation.query.filter_by(
|
||||
|
||||
@@ -7,7 +7,12 @@ from sqlalchemy.orm import joinedload
|
||||
from sqlalchemy.sql.expression import and_, asc, case, func
|
||||
|
||||
from app import db
|
||||
from app.dao.dao_utils import VersionOptions, transactional, version_class
|
||||
from app.dao.dao_utils import (
|
||||
VersionOptions,
|
||||
nested_transactional,
|
||||
transactional,
|
||||
version_class,
|
||||
)
|
||||
from app.dao.date_util import get_current_financial_year
|
||||
from app.dao.email_branding_dao import dao_get_email_branding_by_name
|
||||
from app.dao.letter_branding_dao import dao_get_letter_branding_by_name
|
||||
@@ -284,7 +289,7 @@ def dao_fetch_service_by_id_and_user(service_id, user_id):
|
||||
).one()
|
||||
|
||||
|
||||
@transactional
|
||||
@nested_transactional
|
||||
@version_class(Service)
|
||||
def dao_create_service(
|
||||
service,
|
||||
|
||||
Reference in New Issue
Block a user