FIX(notifications): action_code accepts dots, PushMessage.stats actually updates #4
3 changed files with 38 additions and 4 deletions
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 5.2.6 on 2026-09-01 12:23
|
||||||
|
|
||||||
|
import django.core.validators
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('push_notifications', '0006_rename_code_notificationlink_action_code_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='notificationlink',
|
||||||
|
name='action_code',
|
||||||
|
field=models.CharField(db_index=True, max_length=100, unique=True, validators=[django.core.validators.RegexValidator(message='Enter a valid action code: letters, numbers, underscores, hyphens, or dots.', regex='^[-a-zA-Z0-9_.]+\\Z')]),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='pushmessage',
|
||||||
|
name='action_code',
|
||||||
|
field=models.CharField(blank=True, db_index=True, max_length=100, null=True, validators=[django.core.validators.RegexValidator(message='Enter a valid action code: letters, numbers, underscores, hyphens, or dots.', regex='^[-a-zA-Z0-9_.]+\\Z')]),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from django.core.validators import RegexValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from oauth2_provider.settings import oauth2_settings
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
|
||||||
|
|
@ -6,6 +7,15 @@ from utils.models import BaseModel
|
||||||
from utils.clients import gotify
|
from utils.clients import gotify
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
# action_code values are dotted, e.g. "billboard.approved", "escrow.timeout"
|
||||||
|
# (see docs/frontend-notification-click-actions.md) — a plain SlugField
|
||||||
|
# rejects the dot, so every real call using the documented catalog gets a
|
||||||
|
# 400 from the serializer. This mirrors slug's allowed charset but adds ".".
|
||||||
|
validate_action_code = RegexValidator(
|
||||||
|
regex=r'^[-a-zA-Z0-9_.]+\Z',
|
||||||
|
message="Enter a valid action code: letters, numbers, underscores, hyphens, or dots.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PushUserQuerySet(models.QuerySet):
|
class PushUserQuerySet(models.QuerySet):
|
||||||
def submit(self, user):
|
def submit(self, user):
|
||||||
|
|
@ -51,7 +61,7 @@ class NotificationLink(BaseModel):
|
||||||
type) instead of a raw URL, so where a tap should navigate is a config
|
type) instead of a raw URL, so where a tap should navigate is a config
|
||||||
change here, not a code deploy across every producer service.
|
change here, not a code deploy across every producer service.
|
||||||
"""
|
"""
|
||||||
action_code = models.SlugField(max_length=100, unique=True, db_index=True)
|
action_code = models.CharField(max_length=100, unique=True, db_index=True, validators=[validate_action_code])
|
||||||
description = models.CharField(max_length=255, blank=True)
|
description = models.CharField(max_length=255, blank=True)
|
||||||
url_template = models.CharField(
|
url_template = models.CharField(
|
||||||
max_length=1000,
|
max_length=1000,
|
||||||
|
|
@ -93,7 +103,7 @@ class PushMessage(BaseModel):
|
||||||
# action_code, resolved against NotificationLink at send time (see
|
# action_code, resolved against NotificationLink at send time (see
|
||||||
# get_gotify_extras). click_object_id_value is substituted into that
|
# get_gotify_extras). click_object_id_value is substituted into that
|
||||||
# action_code's {object_id} placeholder.
|
# action_code's {object_id} placeholder.
|
||||||
action_code = models.SlugField(max_length=100, null=True, blank=True, db_index=True)
|
action_code = models.CharField(max_length=100, null=True, blank=True, db_index=True, validators=[validate_action_code])
|
||||||
click_object_id_value = models.CharField(max_length=255, null=True, blank=True)
|
click_object_id_value = models.CharField(max_length=255, null=True, blank=True)
|
||||||
stats = models.IntegerField(default=StateChoices.INIT, choices=StateChoices.choices)
|
stats = models.IntegerField(default=StateChoices.INIT, choices=StateChoices.choices)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ def send_push_notification(push_message_uuid):
|
||||||
message=push_message.message,
|
message=push_message.message,
|
||||||
priority=push_message.priority,
|
priority=push_message.priority,
|
||||||
extras=push_message.get_gotify_extras())
|
extras=push_message.get_gotify_extras())
|
||||||
push_message.state = push_message.StateChoices.DONE
|
push_message.stats = push_message.StateChoices.DONE
|
||||||
push_message.save()
|
push_message.save(update_fields=["stats"])
|
||||||
|
|
||||||
|
|
||||||
@celery_app.task
|
@celery_app.task
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue