Skip to content

Commit 868d8f4

Browse files
authored
Add email/sms annual limit columns to services table (#2329)
* Add email/sms annual limit columns to services table * Add feature flag check - formatting * Add FF removal comments * Add tests * Update migration version * Update migration version * Bump utils
1 parent b75c267 commit 868d8f4

File tree

10 files changed

+184
-6
lines changed

10 files changed

+184
-6
lines changed

app/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,8 @@ class Config(object):
519519
"job": "{}-dvla-file-per-job".format(os.getenv("NOTIFY_ENVIRONMENT", "development")),
520520
"notification": "{}-dvla-letter-api-files".format(os.getenv("NOTIFY_ENVIRONMENT", "development")),
521521
}
522+
SERVICE_ANNUAL_EMAIL_LIMIT = env.int("SERVICE_ANNUAL_EMAIL_LIMIT", 10_000_000)
523+
SERVICE_ANNUAL_SMS_LIMIT = env.int("SERVICE_ANNUAL_SMS_LIMIT", 25_000)
522524

523525
FREE_SMS_TIER_FRAGMENT_COUNT = 250000
524526

@@ -566,6 +568,7 @@ class Config(object):
566568
FF_CELERY_CUSTOM_TASK_PARAMS = env.bool("FF_CELERY_CUSTOM_TASK_PARAMS", True)
567569
FF_CLOUDWATCH_METRICS_ENABLED = env.bool("FF_CLOUDWATCH_METRICS_ENABLED", False)
568570
FF_SALESFORCE_CONTACT = env.bool("FF_SALESFORCE_CONTACT", False)
571+
FF_ANNUAL_LIMIT = env.bool("FF_ANNUAL_LIMIT", False)
569572

570573
# SRE Tools auth keys
571574
SRE_USER_NAME = "SRE_CLIENT_USER"

app/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
DELIVERY_STATUS_CALLBACK_TYPE = "delivery_status"
6666
COMPLAINT_CALLBACK_TYPE = "complaint"
6767
SERVICE_CALLBACK_TYPES = [DELIVERY_STATUS_CALLBACK_TYPE, COMPLAINT_CALLBACK_TYPE]
68+
DEFAULT_SMS_ANNUAL_LIMIT = 25000
69+
DEFAULT_EMAIL_ANNUAL_LIMIT = 10000000
6870

6971
sms_sending_vehicles = db.Enum(*[vehicle.value for vehicle in SmsSendingVehicles], name="sms_sending_vehicles")
7072

@@ -565,6 +567,8 @@ class Service(BaseModel, Versioned):
565567
sensitive_service = db.Column(db.Boolean, nullable=True)
566568
organisation_id = db.Column(UUID(as_uuid=True), db.ForeignKey("organisation.id"), index=True, nullable=True)
567569
organisation = db.relationship("Organisation", backref="services")
570+
email_annual_limit = db.Column(db.BigInteger, nullable=False, default=DEFAULT_EMAIL_ANNUAL_LIMIT)
571+
sms_annual_limit = db.Column(db.BigInteger, nullable=False, default=DEFAULT_SMS_ANNUAL_LIMIT)
568572

569573
email_branding = db.relationship(
570574
"EmailBranding",
@@ -608,6 +612,8 @@ def from_json(cls, data):
608612
fields.pop("letter_contact_block", None)
609613
fields.pop("email_branding", None)
610614
fields["sms_daily_limit"] = fields.get("sms_daily_limit", 100)
615+
fields["email_annual_limit"] = fields.get("email_annual_limit", DEFAULT_EMAIL_ANNUAL_LIMIT)
616+
fields["sms_annual_limit"] = fields.get("sms_annual_limit", DEFAULT_SMS_ANNUAL_LIMIT)
611617

612618
return cls(**fields)
613619

app/schemas.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ class ServiceSchema(BaseSchema, UUIDsAsStringsMixin):
268268
go_live_at = field_for(models.Service, "go_live_at", format="%Y-%m-%d %H:%M:%S.%f")
269269
organisation_notes = field_for(models.Service, "organisation_notes")
270270
sensitive_service = field_for(models.Service, "sensitive_service")
271+
email_annual_limit = field_for(models.Service, "email_annual_limit")
272+
sms_annual_limit = field_for(models.Service, "sms_annual_limit")
271273

272274
def get_letter_logo_filename(self, service):
273275
return service.letter_branding and service.letter_branding.filename

app/service/rest.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ def create_service():
267267
return jsonify(data=service_schema.dump(valid_service)), 201
268268

269269

270+
# TODO: FF_ANNUAL_LIMIT removal: Temporarily ignore complexity
271+
# flake8: noqa: C901
270272
@service_blueprint.route("/<uuid:service_id>", methods=["POST"])
271273
def update_service(service_id):
272274
req_json = request.get_json()
@@ -276,6 +278,12 @@ def update_service(service_id):
276278
service_name_changed = fetched_service.name != req_json.get("name", fetched_service.name)
277279
message_limit_changed = fetched_service.message_limit != req_json.get("message_limit", fetched_service.message_limit)
278280
sms_limit_changed = fetched_service.sms_daily_limit != req_json.get("sms_daily_limit", fetched_service.sms_daily_limit)
281+
email_annual_limit_changed = fetched_service.email_annual_limit != req_json.get(
282+
"email_annual_limit", fetched_service.email_annual_limit
283+
)
284+
sms_annual_limit_changed = fetched_service.sms_annual_limit != req_json.get(
285+
"sms_annual_limit", fetched_service.sms_annual_limit
286+
)
279287
current_data = dict(service_schema.dump(fetched_service).items())
280288

281289
current_data.update(request.get_json())
@@ -305,6 +313,15 @@ def update_service(service_id):
305313
if not fetched_service.restricted:
306314
_warn_service_users_about_sms_limit_changed(service_id, current_data)
307315

316+
# TODO: FF_ANNUAL_LIMIT removal
317+
if current_app.config["FF_ANNUAL_LIMIT"]:
318+
if sms_annual_limit_changed:
319+
# TODO: Delete cache for sms annual limit (if used)
320+
_warn_service_users_about_annual_sms_limit_changed(service_id, current_data)
321+
if email_annual_limit_changed:
322+
# TODO: Delete cache for email annual limit (if used)
323+
_warn_service_users_about_annual_email_limit_changed(service_id, current_data)
324+
308325
if service_going_live:
309326
_warn_services_users_about_going_live(service_id, current_data)
310327

@@ -329,6 +346,14 @@ def update_service(service_id):
329346
return jsonify(data=service_schema.dump(fetched_service)), 200
330347

331348

349+
def _warn_service_users_about_annual_email_limit_changed(service_id, data):
350+
pass
351+
352+
353+
def _warn_service_users_about_annual_sms_limit_changed(service_id, data):
354+
pass
355+
356+
332357
def _warn_service_users_about_message_limit_changed(service_id, data):
333358
send_notification_to_service_users(
334359
service_id=service_id,
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
Revision ID: 0463_add_annual_limit_column
3+
Revises: 0462_add_pinpoint_fields
4+
Create Date: 2024-06-27 13:32:00
5+
"""
6+
import os
7+
8+
import sqlalchemy as sa
9+
from alembic import op
10+
11+
from app import config
12+
13+
revision = "0463_add_annual_limit_column"
14+
down_revision = "0462_add_pinpoint_fields"
15+
16+
cfg = config.configs[os.environ["NOTIFY_ENVIRONMENT"]] # type: ignore
17+
default_email_annual_limit = str(cfg.SERVICE_ANNUAL_EMAIL_LIMIT)
18+
default_sms_annual_limit = str(cfg.SERVICE_ANNUAL_SMS_LIMIT)
19+
20+
21+
def upgrade():
22+
# Add the new column to the templates table
23+
op.add_column(
24+
"services", sa.Column("email_annual_limit", sa.BigInteger(), nullable=True, server_default=default_email_annual_limit)
25+
)
26+
op.add_column(
27+
"services", sa.Column("sms_annual_limit", sa.BigInteger(), nullable=True, server_default=default_sms_annual_limit)
28+
)
29+
# Add the new column to the templates_history table
30+
op.add_column(
31+
"services_history",
32+
sa.Column("email_annual_limit", sa.BigInteger(), nullable=True, server_default=default_email_annual_limit),
33+
)
34+
op.add_column(
35+
"services_history", sa.Column("sms_annual_limit", sa.BigInteger(), nullable=True, server_default=default_sms_annual_limit)
36+
)
37+
38+
# Set the default values for existing services
39+
op.execute(
40+
f"""
41+
UPDATE services
42+
SET email_annual_limit = {default_email_annual_limit},
43+
sms_annual_limit = {default_sms_annual_limit}
44+
"""
45+
)
46+
# Since sms / email annual limit have been statically set to 25k and 10mil respectively
47+
# we can update all service history rows safely
48+
op.execute(
49+
f"""
50+
UPDATE services_history
51+
SET email_annual_limit = {default_email_annual_limit},
52+
sms_annual_limit = {default_sms_annual_limit}
53+
"""
54+
)
55+
56+
op.alter_column("services", "email_annual_limit", nullable=False)
57+
op.alter_column("services", "sms_annual_limit", nullable=False)
58+
op.alter_column("services_history", "email_annual_limit", nullable=False)
59+
op.alter_column("services_history", "sms_annual_limit", nullable=False)
60+
61+
62+
def downgrade():
63+
# Remove the column from the services table
64+
op.drop_column("services", "email_annual_limit")
65+
op.drop_column("services", "sms_annual_limit")
66+
67+
# Remove the column from the services_history table
68+
op.drop_column("services_history", "email_annual_limit")
69+
op.drop_column("services_history", "sms_annual_limit")

0 commit comments

Comments
 (0)