Skip to content

Commit a6bc509

Browse files
matthewhjleclanche
authored andcommitted
Enable multiple application support
* Provides backwards compatibility through conf adapters that determine which settings are supported and where the data can be loaded from. This implementation adds 3: * LegacyConfig - supports the existing settings format * AppConfig - supports a dictionary of application_ids and their settings * AppModelConfig - supports storing the settings in the database for multiple apps
1 parent dcbd63a commit a6bc509

20 files changed

+1128
-102
lines changed

push_notifications/api/rest_framework.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ def to_representation(self, value):
3333
# Serializers
3434
class DeviceSerializerMixin(ModelSerializer):
3535
class Meta:
36-
fields = ("id", "name", "registration_id", "device_id", "active", "date_created")
36+
fields = (
37+
"id", "name", "application_id", "registration_id", "device_id",
38+
"active", "date_created"
39+
)
3740
read_only_fields = ("date_created",)
3841

3942
# See https://github.com/tomchristie/django-rest-framework/issues/1101

push_notifications/apns.py

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from . import models
1414
from . import NotificationError
1515
from .apns_errors import reason_for_exception_class
16-
from .settings import PUSH_NOTIFICATIONS_SETTINGS as SETTINGS
16+
from .conf import get_manager
1717

1818

1919
class APNSError(NotificationError):
@@ -30,20 +30,21 @@ def __init__(self, status):
3030
self.status = status
3131

3232

33-
def _apns_create_socket(certfile=None):
34-
certfile = certfile or SETTINGS.get("APNS_CERTIFICATE")
33+
def _apns_create_socket(certfile=None, application_id=None):
34+
certfile = certfile or get_manager().get_apns_certificate(application_id)
3535
client = apns2_client.APNsClient(
3636
certfile,
37-
use_sandbox=SETTINGS.get("APNS_USE_SANDBOX"),
38-
use_alternative_port=SETTINGS.get("APNS_USE_ALTERNATIVE_PORT"))
37+
use_sandbox=get_manager().get_apns_use_sandbox(application_id),
38+
use_alternative_port=get_manager().get_apns_use_alternative_port(application_id)
39+
)
3940
client.connect()
4041
return client
4142

4243

4344
def _apns_prepare(
44-
token, alert, badge=None, sound=None, category=None, content_available=False,
45-
action_loc_key=None, loc_key=None, loc_args=[], extra={}, mutable_content=False,
46-
thread_id=None, url_args=None):
45+
token, alert, application_id=None, badge=None, sound=None, category=None,
46+
content_available=False, action_loc_key=None, loc_key=None, loc_args=[],
47+
extra={}, mutable_content=False, thread_id=None, url_args=None):
4748
if action_loc_key or loc_key or loc_args:
4849
apns2_alert = apns2_payload.PayloadAlert(
4950
body=alert if alert else {}, body_localized_key=loc_key,
@@ -59,8 +60,10 @@ def _apns_prepare(
5960
url_args, custom=extra, thread_id=thread_id)
6061

6162

62-
def _apns_send(registration_id, alert, batch=False, **kwargs):
63-
client = _apns_create_socket(kwargs.pop("certfile", None))
63+
def _apns_send(
64+
registration_id, alert, batch=False, application_id=None, certfile=None, **kwargs
65+
):
66+
client = _apns_create_socket(certfile=certfile, application_id=application_id)
6467

6568
notification_kwargs = {}
6669

@@ -80,14 +83,19 @@ def _apns_send(registration_id, alert, batch=False, **kwargs):
8083
data = [apns2_client.Notification(
8184
token=rid, payload=_apns_prepare(rid, alert, **kwargs)) for rid in registration_id]
8285
return client.send_notification_batch(
83-
data, SETTINGS.get("APNS_TOPIC"), **notification_kwargs)
86+
data, get_manager().get_apns_topic(application_id=application_id),
87+
**notification_kwargs
88+
)
8489

8590
data = _apns_prepare(registration_id, alert, **kwargs)
8691
client.send_notification(
87-
registration_id, data, SETTINGS.get("APNS_TOPIC"), **notification_kwargs)
92+
registration_id, data,
93+
get_manager().get_apns_topic(application_id=application_id),
94+
**notification_kwargs
95+
)
8896

8997

90-
def apns_send_message(registration_id, alert, **kwargs):
98+
def apns_send_message(registration_id, alert, application_id=None, certfile=None, **kwargs):
9199
"""
92100
Sends an APNS notification to a single registration_id.
93101
This will send the notification as form data.
@@ -100,7 +108,10 @@ def apns_send_message(registration_id, alert, **kwargs):
100108
"""
101109

102110
try:
103-
_apns_send(registration_id, alert, **kwargs)
111+
_apns_send(
112+
registration_id, alert, application_id=application_id,
113+
certfile=certfile, **kwargs
114+
)
104115
except apns2_errors.APNsException as apns2_exception:
105116
if isinstance(apns2_exception, apns2_errors.Unregistered):
106117
device = models.APNSDevice.objects.get(registration_id=registration_id)
@@ -109,7 +120,9 @@ def apns_send_message(registration_id, alert, **kwargs):
109120
raise APNSServerError(status=reason_for_exception_class(apns2_exception.__class__))
110121

111122

112-
def apns_send_bulk_message(registration_ids, alert, **kwargs):
123+
def apns_send_bulk_message(
124+
registration_ids, alert, application_id=None, certfile=None, **kwargs
125+
):
113126
"""
114127
Sends an APNS notification to one or more registration_ids.
115128
The registration_ids argument needs to be a list.
@@ -119,7 +132,10 @@ def apns_send_bulk_message(registration_ids, alert, **kwargs):
119132
to this for silent notifications.
120133
"""
121134

122-
results = _apns_send(registration_ids, alert, batch=True, **kwargs)
135+
results = _apns_send(
136+
registration_ids, alert, batch=True, application_id=application_id,
137+
certfile=certfile, **kwargs
138+
)
123139
inactive_tokens = [token for token, result in results.items() if result == "Unregistered"]
124140
models.APNSDevice.objects.filter(registration_id__in=inactive_tokens).update(active=False)
125141
return results

push_notifications/conf/__init__.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from django.utils.module_loading import import_string
2+
from .app import AppConfig # noqa: F401
3+
from .appmodel import AppModelConfig # noqa: F401
4+
from .legacy import LegacyConfig # noqa: F401
5+
from ..settings import PUSH_NOTIFICATIONS_SETTINGS as SETTINGS
6+
7+
8+
manager = None
9+
10+
11+
def get_manager(reload=False):
12+
global manager
13+
14+
if not manager or reload is True:
15+
manager = import_string(SETTINGS["CONFIG"])()
16+
17+
return manager
18+
19+
20+
# implementing get_manager as a function allows tests to reload settings
21+
get_manager()

0 commit comments

Comments
 (0)