Skip to content

Commit

Permalink
Merge branch 'master' of github.com:pennlabs/penn-mobile into fix/lau…
Browse files Browse the repository at this point in the history
…ndry-api-rewrite
  • Loading branch information
dr-Jess committed Nov 22, 2024
2 parents 2b5e113 + b0105a1 commit a8b137b
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 27 deletions.
2 changes: 2 additions & 0 deletions backend/user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ def create_or_update_user_profile(sender, instance, created, **kwargs):
object exists for that User, it will create one
"""
Profile.objects.get_or_create(user=instance)
if courses_service := NotificationService.objects.filter(name="COURSES").first():
courses_service.enabled_users.add(instance)
66 changes: 41 additions & 25 deletions backend/user/notifications.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import collections
import os
import sys
from abc import ABC, abstractmethod

Expand Down Expand Up @@ -28,8 +27,8 @@


class NotificationWrapper(ABC):
def send_notification(self, tokens, title, body):
self.send_payload(tokens, self.create_payload(title, body))
def send_notification(self, tokens, title, body, urgent):
self.send_payload(tokens, self.create_payload(title, body, urgent))

def send_shadow_notification(self, tokens, body):
self.send_payload(tokens, self.create_shadow_payload(body))
Expand All @@ -43,7 +42,7 @@ def send_payload(self, tokens, payload):
self.send_one_notification(tokens[0], payload)

@abstractmethod
def create_payload(self, title, body):
def create_payload(self, title, body, urgent):
raise NotImplementedError # pragma: no cover

@abstractmethod
Expand All @@ -62,16 +61,14 @@ def send_one_notification(self, token, payload):
class AndroidNotificationWrapper(NotificationWrapper):
def __init__(self):
try:
server_key = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"penn-mobile-android-firebase-adminsdk-u9rki-c83fb20713.json",
)
cred = credentials.Certificate(server_key)
auth_key_path = "/app/secrets/notifications/android/fcm.json"
cred = credentials.Certificate(auth_key_path)
firebase_admin.initialize_app(cred)
except Exception as e:
print(f"Notifications Error: Failed to initialize Firebase client: {e}")

def create_payload(self, title, body):
def create_payload(self, title, body, urgent):
# TODO: do something with urgent
return {"notification": messaging.Notification(title=title, body=body)}

def create_shadow_payload(self, body):
Expand All @@ -88,36 +85,55 @@ def send_one_notification(self, token, payload):


class IOSNotificationWrapper(NotificationWrapper):
class CustomPayload(Payload):
# Custom payload to support interruption_level
def __init__(self, urgent, **kwargs):
super().__init__(**kwargs)
self.urgent = urgent

def dict(self):
result = super().dict()
if self.urgent:
result["aps"]["interruption-level"] = "time-sensitive"
return result

@staticmethod
def get_client(is_dev):
auth_key_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
f"apns-{'dev' if is_dev else 'prod'}.pem",
# TODO: We are getting a new client for each request, might be worth
# looking into how to keep the client alive.
auth_key_path = (
f"/app/secrets/notifications/ios{'/dev/apns-dev' if is_dev else '/prod/apns-prod'}.pem"
)
return APNsClient(credentials=auth_key_path, use_sandbox=is_dev)

def __init__(self, is_dev=False):
try:
self.client = self.get_client(is_dev)
self.is_dev = is_dev
self.topic = "org.pennlabs.PennMobile" + (".dev" if is_dev else "")
except Exception as e:
print(f"Notifications Error: Failed to initialize APNs client: {e}")

def create_payload(self, title, body):
def create_payload(self, title, body, urgent):
# TODO: we might want to add category here, but there is no use on iOS side for now
return Payload(
alert={"title": title, "body": body}, sound="default", badge=0, mutable_content=True
return IOSNotificationWrapper.CustomPayload(
alert={"title": title, "body": body},
sound="default",
badge=0,
mutable_content=True,
urgent=urgent,
)

def create_shadow_payload(self, body):
return Payload(content_available=True, custom=body, mutable_content=True)

def send_many_notifications(self, tokens, payload):
notifications = [Notification(token, payload) for token in tokens]
self.client.send_notification_batch(notifications=notifications, topic=self.topic)
self.get_client(self.is_dev).send_notification_batch(
notifications=notifications, topic=self.topic
)

def send_one_notification(self, token, payload):
self.client.send_notification(token, payload, self.topic)
self.get_client(self.is_dev).send_notification(token, payload, self.topic)


IOSNotificationSender = IOSNotificationWrapper()
Expand All @@ -126,8 +142,8 @@ def send_one_notification(self, token, payload):


@shared_task(name="notifications.ios_send_notification")
def ios_send_notification(tokens, title, body):
IOSNotificationSender.send_notification(tokens, title, body)
def ios_send_notification(tokens, title, body, urgent):
IOSNotificationSender.send_notification(tokens, title, body, urgent)


@shared_task(name="notifications.ios_send_shadow_notification")
Expand All @@ -136,8 +152,8 @@ def ios_send_shadow_notification(tokens, body):


@shared_task(name="notifications.android_send_notification")
def android_send_notification(tokens, title, body):
AndroidNotificationSender.send_notification(tokens, title, body)
def android_send_notification(tokens, title, body, urgent):
AndroidNotificationSender.send_notification(tokens, title, body, urgent)


@shared_task(name="notifications.android_send_shadow_notification")
Expand All @@ -146,8 +162,8 @@ def android_send_shadow_notification(tokens, body):


@shared_task(name="notifications.ios_send_dev_notification")
def ios_send_dev_notification(tokens, title, body):
IOSNotificationDevSender.send_notification(tokens, title, body)
def ios_send_dev_notification(tokens, title, body, urgent):
IOSNotificationDevSender.send_notification(tokens, title, body, urgent)


@shared_task(name="notifications.ios_send_dev_shadow_notification")
Expand Down
11 changes: 9 additions & 2 deletions backend/user/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ def get_object(self):


class NotificationTokenView(APIView, ABC):
permission_classes = [IsAuthenticated]
def get_permissions(self):
if self.request.method == "DELETE":
return []
return [IsAuthenticated()]

queryset = None

def get_defaults(self):
Expand Down Expand Up @@ -135,6 +139,7 @@ def post(self, request):
title = request.data.get("title")
body = request.data.get("body")
delay = max(request.data.get("delay", 0), 0)
urgent = request.data.get("urgent", False)

if None in [service, title, body]:
return Response({"detail": "Missing required parameters."}, status=400)
Expand All @@ -156,7 +161,9 @@ def post(self, request):
(android_tokens, android_send_notification),
]:
if tokens_list := list(tokens.values_list("token", flat=True)):
send.apply_async(args=(tokens_list, title, body), countdown=delay)
_ = delay
send(tokens_list, title, body, urgent)
# send.apply_async(args=(tokens_list, title, body, urgent), countdown=delay)

users_with_service_usernames = users_with_service.values_list("username", flat=True)
users_not_reached_usernames = list(set(usernames) - set(users_with_service_usernames))
Expand Down
Empty file removed backend/wrapped/tests.py
Empty file.
24 changes: 24 additions & 0 deletions k8s/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ export class MyChart extends PennLabsChart {
name: "penn-mobile",
subPath: "ios-key",
mountPath: "/app/ios_key.p8",
},
{
name: 'penn-mobile-apns-prod',
mountPath: '/app/secrets/notifications/ios/prod',
},
{
name: 'penn-mobile-apns-dev',
mountPath: '/app/secrets/notifications/ios/dev',
},
{
name: 'penn-mobile-fcm',
mountPath: '/app/secrets/notifications/android',
}
],
env: [
Expand All @@ -50,6 +62,18 @@ export class MyChart extends PennLabsChart {
name: "penn-mobile",
subPath: "ios-key",
mountPath: "/app/ios_key.p8",
},
{
name: 'penn-mobile-apns-prod',
mountPath: '/app/secrets/notifications/ios/prod',
},
{
name: 'penn-mobile-apns-dev',
mountPath: '/app/secrets/notifications/ios/dev',
},
{
name: 'penn-mobile-fcm',
mountPath: '/app/secrets/notifications/android',
}
]
},
Expand Down

0 comments on commit a8b137b

Please sign in to comment.