|
1 | 1 | """ |
2 | 2 | Serializers for the notifications API. |
3 | 3 | """ |
| 4 | + |
4 | 5 | from django.core.exceptions import ValidationError |
5 | 6 | from rest_framework import serializers |
6 | 7 |
|
|
9 | 10 | from openedx.core.djangoapps.notifications.models import ( |
10 | 11 | CourseNotificationPreference, |
11 | 12 | Notification, |
12 | | - get_notification_channels, get_additional_notification_channel_settings |
| 13 | + get_additional_notification_channel_settings, |
| 14 | + get_notification_channels |
13 | 15 | ) |
| 16 | + |
14 | 17 | from .base_notification import COURSE_NOTIFICATION_APPS, COURSE_NOTIFICATION_TYPES, EmailCadence |
| 18 | +from .email.utils import is_notification_type_channel_editable |
15 | 19 | from .utils import remove_preferences_with_no_access |
16 | 20 |
|
17 | 21 |
|
@@ -202,3 +206,113 @@ class Meta: |
202 | 206 | 'last_seen', |
203 | 207 | 'created', |
204 | 208 | ) |
| 209 | + |
| 210 | + |
| 211 | +def validate_email_cadence(email_cadence: str) -> str: |
| 212 | + """ |
| 213 | + Validate email cadence value. |
| 214 | + """ |
| 215 | + if EmailCadence.get_email_cadence_value(email_cadence) is None: |
| 216 | + raise ValidationError(f'{email_cadence} is not a valid email cadence.') |
| 217 | + return email_cadence |
| 218 | + |
| 219 | + |
| 220 | +def validate_notification_app(notification_app: str) -> str: |
| 221 | + """ |
| 222 | + Validate notification app value. |
| 223 | + """ |
| 224 | + if not COURSE_NOTIFICATION_APPS.get(notification_app): |
| 225 | + raise ValidationError(f'{notification_app} is not a valid notification app.') |
| 226 | + return notification_app |
| 227 | + |
| 228 | + |
| 229 | +def validate_notification_app_enabled(notification_app: str) -> str: |
| 230 | + """ |
| 231 | + Validate notification app is enabled. |
| 232 | + """ |
| 233 | + |
| 234 | + if COURSE_NOTIFICATION_APPS.get(notification_app) and COURSE_NOTIFICATION_APPS.get(notification_app)['enabled']: |
| 235 | + return notification_app |
| 236 | + raise ValidationError(f'{notification_app} is not a valid notification app.') |
| 237 | + |
| 238 | + |
| 239 | +def validate_notification_type(notification_type: str) -> str: |
| 240 | + """ |
| 241 | + Validate notification type value. |
| 242 | + """ |
| 243 | + if not COURSE_NOTIFICATION_TYPES.get(notification_type): |
| 244 | + raise ValidationError(f'{notification_type} is not a valid notification type.') |
| 245 | + return notification_type |
| 246 | + |
| 247 | + |
| 248 | +def validate_notification_channel(notification_channel: str) -> str: |
| 249 | + """ |
| 250 | + Validate notification channel value. |
| 251 | + """ |
| 252 | + valid_channels = set(get_notification_channels()) | set(get_additional_notification_channel_settings()) |
| 253 | + if notification_channel not in valid_channels: |
| 254 | + raise ValidationError(f'{notification_channel} is not a valid notification channel setting.') |
| 255 | + return notification_channel |
| 256 | + |
| 257 | + |
| 258 | +class UserNotificationPreferenceUpdateAllSerializer(serializers.Serializer): |
| 259 | + """ |
| 260 | + Serializer for user notification preferences update with custom field validators. |
| 261 | + """ |
| 262 | + notification_app = serializers.CharField( |
| 263 | + required=True, |
| 264 | + validators=[validate_notification_app, validate_notification_app_enabled] |
| 265 | + ) |
| 266 | + value = serializers.BooleanField(required=False) |
| 267 | + notification_type = serializers.CharField( |
| 268 | + required=True, |
| 269 | + ) |
| 270 | + notification_channel = serializers.CharField( |
| 271 | + required=False, |
| 272 | + validators=[validate_notification_channel] |
| 273 | + ) |
| 274 | + email_cadence = serializers.CharField( |
| 275 | + required=False, |
| 276 | + validators=[validate_email_cadence] |
| 277 | + ) |
| 278 | + |
| 279 | + def validate(self, attrs): |
| 280 | + """ |
| 281 | + Cross-field validation for notification preference update. |
| 282 | + """ |
| 283 | + notification_app = attrs.get('notification_app') |
| 284 | + notification_type = attrs.get('notification_type') |
| 285 | + notification_channel = attrs.get('notification_channel') |
| 286 | + email_cadence = attrs.get('email_cadence') |
| 287 | + |
| 288 | + # Validate email_cadence requirements |
| 289 | + if email_cadence and not notification_type: |
| 290 | + raise ValidationError({ |
| 291 | + 'notification_type': 'notification_type is required for email_cadence.' |
| 292 | + }) |
| 293 | + |
| 294 | + # Validate notification_channel requirements |
| 295 | + if not email_cadence and notification_type and not notification_channel: |
| 296 | + raise ValidationError({ |
| 297 | + 'notification_channel': 'notification_channel is required for notification_type.' |
| 298 | + }) |
| 299 | + |
| 300 | + # Validate notification type |
| 301 | + if all([not COURSE_NOTIFICATION_TYPES.get(notification_type), notification_type != "core"]): |
| 302 | + raise ValidationError(f'{notification_type} is not a valid notification type.') |
| 303 | + |
| 304 | + # Validate notification type and channel is editable |
| 305 | + if notification_channel and notification_type: |
| 306 | + if not is_notification_type_channel_editable( |
| 307 | + notification_app, |
| 308 | + notification_type, |
| 309 | + notification_channel |
| 310 | + ): |
| 311 | + raise ValidationError({ |
| 312 | + 'notification_channel': ( |
| 313 | + f'{notification_channel} is not editable for notification type ' |
| 314 | + f'{notification_type}.' |
| 315 | + ) |
| 316 | + }) |
| 317 | + |
| 318 | + return attrs |
0 commit comments