Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-django-push-notifications for openSUSE:Factory checked in at 2023-11-30 22:00:42
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-push-notifications (Old)
and /work/SRC/openSUSE:Factory/.python-django-push-notifications.new.25432 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-push-notifications"
Thu Nov 30 22:00:42 2023 rev:5 rq:1129794 version:3.0.2
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-django-push-notifications/python-django-push-notifications.changes 2023-01-06 17:06:38.652548151 +0100
+++ /work/SRC/openSUSE:Factory/.python-django-push-notifications.new.25432/python-django-push-notifications.changes 2023-11-30 22:01:31.313503262 +0100
@@ -1,0 +2,12 @@
+Wed Nov 29 12:14:57 UTC 2023 - Dirk Müller
+
+- update to 3.0.2:
+ * LegacyConfig.get_apns_use_alternative_port always return None
+ * Add django 4.1, remove master branch
+ * Update topic send_message docs in README
+ * Fix: HexadecimalField accepts non-hex values
+ * Expanded documentation for Web Push
+ * Allow APNS tokens of variable length.
+ * Add WebPush support for Safari
+
+-------------------------------------------------------------------
Old:
----
django-push-notifications-3.0.0.tar.gz
New:
----
django-push-notifications-3.0.2.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-django-push-notifications.spec ++++++
--- /var/tmp/diff_new_pack.V1bYh5/_old 2023-11-30 22:01:31.933526103 +0100
+++ /var/tmp/diff_new_pack.V1bYh5/_new 2023-11-30 22:01:31.933526103 +0100
@@ -16,9 +16,9 @@
#
-%{?!python_module:%define python_module() python-%{**} python3-%{**}}
+%{?sle15_python_module_pythons}
Name: python-django-push-notifications
-Version: 3.0.0
+Version: 3.0.2
Release: 0
Summary: Django package to send push notifications to mobile devices
License: MIT
++++++ django-push-notifications-3.0.0.tar.gz -> django-push-notifications-3.0.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/.github/workflows/release.yml new/django-push-notifications-3.0.2/.github/workflows/release.yml
--- old/django-push-notifications-3.0.0/.github/workflows/release.yml 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/.github/workflows/release.yml 2023-10-29 17:59:32.000000000 +0100
@@ -8,7 +8,7 @@
jobs:
build:
if: github.repository == 'jazzband/django-push-notifications'
- runs-on: ubuntu-latest
+ runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/.github/workflows/test.yml new/django-push-notifications-3.0.2/.github/workflows/test.yml
--- old/django-push-notifications-3.0.0/.github/workflows/test.yml 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/.github/workflows/test.yml 2023-10-29 17:59:32.000000000 +0100
@@ -5,7 +5,7 @@
jobs:
build:
name: build (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }})
- runs-on: ubuntu-latest
+ runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
@@ -37,6 +37,7 @@
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade tox tox-gh-actions
+ python -m pip install setuptools-scm==6.4.2
- name: Tox tests
run: |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/.pre-commit-config.yaml new/django-push-notifications-3.0.2/.pre-commit-config.yaml
--- old/django-push-notifications-3.0.0/.pre-commit-config.yaml 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/.pre-commit-config.yaml 2023-10-29 17:59:32.000000000 +0100
@@ -2,13 +2,13 @@
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.1.0
+ rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/asottile/pyupgrade
- rev: v2.31.0
+ rev: v3.15.0
hooks:
- id: pyupgrade
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/README.rst new/django-push-notifications-3.0.2/README.rst
--- old/django-push-notifications-3.0.0/README.rst 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/README.rst 2023-10-29 17:59:32.000000000 +0100
@@ -64,7 +64,7 @@
"WNS_PACKAGE_SECURITY_ID": "[your package security id, e.g: 'ms-app://e-3-4-6234...']",
"WNS_SECRET_KEY": "[your app secret key, e.g.: 'KDiejnLKDUWodsjmewuSZkk']",
"WP_PRIVATE_KEY": "/path/to/your/private.pem",
- "WP_CLAIMS": {'sub': "mailto: development@example.com"}
+ "WP_CLAIMS": {'sub': "mailto:development@example.com"}
}
.. note::
@@ -122,238 +122,11 @@
**WP settings**
-- Install:
-
-.. code-block:: python
-
- pip install pywebpush
- pip install py-vapid (Only for generating key)
-
-- Getting keys:
-
- - Create file (claim.json) like this:
-
-.. code-block:: bash
-
- {
- "sub": "mailto: development@example.com",
- "aud": "https://android.googleapis.com"
- }
-
- - Generate public and private keys:
-
-.. code-block:: bash
-
- vapid --sign claim.json
-
- No private_key.pem file found.
- Do you want me to create one for you? (Y/n)Y
- Do you want me to create one for you? (Y/n)Y
- Generating private_key.pem
- Generating public_key.pem
- Include the following headers in your request:
-
- Crypto-Key: p256ecdsa=BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70
-
- Authorization: WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2FuZHJvaWQuZ29vZ2xlYXBpcy5jb20iLCJleHAiOiIxNTA4NDkwODM2Iiwic3ViIjoibWFpbHRvOiBkZXZlbG9wbWVudEBleGFtcGxlLmNvbSJ9.r5CYMs86X3JZ4AEs76pXY5PxsnEhIFJ-0ckbibmFHZuyzfIpf1ZGIJbSI7knA4ufu7Hm8RFfEg5wWN1Yf-dR2A
-
- - Generate client public key (applicationServerKey)
-
-.. code-block:: bash
-
- vapid --applicationServerKey
-
- Application Server Key = BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70
-
-
-- Configure settings:
-
- ``WP_PRIVATE_KEY``: Absolute path to your private certificate file: os.path.join(BASE_DIR, "private_key.pem")
-- ``WP_CLAIMS``: Dictionary with the same sub info like claims file: {'sub': "mailto: development@example.com"}
+- ``WP_CLAIMS``: Dictionary with default value for the sub, (subject), sent to the webpush service, This would be used by the service if they needed to reach out to you (the sender). Could be a url or mailto e.g. {'sub': "mailto:development@example.com"}.
- ``WP_ERROR_TIMEOUT``: The timeout on WebPush POSTs. (Optional)
-- ``WP_POST_URL``: A dictionary (key per browser supported) with the full url that webpush notifications will be POSTed to. (Optional)
-
-
-- Configure client (javascript):
-
-.. code-block:: javascript
-
- // Utils functions:
-
- function urlBase64ToUint8Array (base64String) {
- var padding = '='.repeat((4 - base64String.length % 4) % 4)
- var base64 = (base64String + padding)
- .replace(/\-/g, '+')
- .replace(/_/g, '/')
-
- var rawData = window.atob(base64)
- var outputArray = new Uint8Array(rawData.length)
-
- for (var i = 0; i < rawData.length; ++i) {
- outputArray[i] = rawData.charCodeAt(i)
- }
- return outputArray;
- }
-
- function loadVersionBrowser () {
- if ("userAgentData" in navigator) {
- // navigator.userAgentData is not available in
- // Firefox and Safari
- const uaData = navigator.userAgentData;
- // Outputs of navigator.userAgentData.brands[n].brand are e.g.
- // Chrome: 'Google Chrome'
- // Edge: 'Microsoft Edge'
- // Opera: 'Opera'
- let browsername;
- let browserversion;
- let chromeVersion = null;
- for (var i = 0; i < uaData.brands.length; i++) {
- let brand = uaData.brands[i].brand;
- browserversion = uaData.brands[i].version;
- if (brand.match(/opera|chrome|edge|safari|firefox|msie|trident/i) !== null) {
- // If we have a chrome match, save the match, but try to find another match
- // E.g. Edge can also produce a false Chrome match.
- if (brand.match(/chrome/i) !== null) {
- chromeVersion = browserversion;
- }
- // If this is not a chrome match return immediately
- else {
- browsername = brand.substr(brand.indexOf(' ')+1);
- return {
- name: browsername,
- version: browserversion
- }
- }
- }
- }
- // No non-Chrome match was found. If we have a chrome match, return it.
- if (chromeVersion !== null) {
- return {
- name: "chrome",
- version: chromeVersion
- }
- }
- }
- // If no userAgentData is not present, or if no match via userAgentData was found,
- // try to extract the browser name and version from userAgent
- const userAgent = navigator.userAgent;
- var ua = userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
- if (/trident/i.test(M[1])) {
- tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
- return {name: 'IE', version: (tem[1] || '')};
- }
- if (M[1] === 'Chrome') {
- tem = ua.match(/\bOPR\/(\d+)/);
- if (tem != null) {
- return {name: 'Opera', version: tem[1]};
- }
- }
- M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
- if ((tem = ua.match(/version\/(\d+)/i)) != null) {
- M.splice(1, 1, tem[1]);
- }
- return {
- name: M[0],
- version: M[1]
- };
- };
- var applicationServerKey = "BEFuGfKKEFp-kEBMxAIw7ng8HeH_QwnH5_h55ijKD4FRvgdJU1GVlDo8K5U5ak4cMZdQTUJlkA34llWF0xHya70";
- ....
-
- // In your ready listener
- if ('serviceWorker' in navigator) {
- // The service worker has to store in the root of the app
- // http://stackoverflow.com/questions/29874068/navigator-serviceworker-is-never...
- var browser = loadVersionBrowser();
- navigator.serviceWorker.register('navigatorPush.service.js?version=1.0.0').then(function (reg) {
- reg.pushManager.subscribe({
- userVisibleOnly: true,
- applicationServerKey: urlBase64ToUint8Array(applicationServerKey)
- }).then(function (sub) {
- var endpointParts = sub.endpoint.split('/');
- var registration_id = endpointParts[endpointParts.length - 1];
- var data = {
- 'browser': browser.name.toUpperCase(),
- 'p256dh': btoa(String.fromCharCode.apply(null, new Uint8Array(sub.getKey('p256dh')))),
- 'auth': btoa(String.fromCharCode.apply(null, new Uint8Array(sub.getKey('auth')))),
- 'name': 'XXXXX',
- 'registration_id': registration_id
- };
- requestPOSTToServer(data);
- })
- }).catch(function (err) {
- console.log(':^(', err);
- });
-
-
-
-
- // Example navigatorPush.service.js file
-
- var getTitle = function (title) {
- if (title === "") {
- title = "TITLE DEFAULT";
- }
- return title;
- };
- var getNotificationOptions = function (message, message_tag) {
- var options = {
- body: message,
- icon: '/img/icon_120.png',
- tag: message_tag,
- vibrate: [200, 100, 200, 100, 200, 100, 200]
- };
- return options;
- };
-
- self.addEventListener('install', function (event) {
- self.skipWaiting();
- });
-
- self.addEventListener('push', function(event) {
- try {
- // Push is a JSON
- var response_json = event.data.json();
- var title = response_json.title;
- var message = response_json.message;
- var message_tag = response_json.tag;
- } catch (err) {
- // Push is a simple text
- var title = "";
- var message = event.data.text();
- var message_tag = "";
- }
- self.registration.showNotification(getTitle(title), getNotificationOptions(message, message_tag));
- // Optional: Comunicating with our js application. Send a signal
- self.clients.matchAll({includeUncontrolled: true, type: 'window'}).then(function (clients) {
- clients.forEach(function (client) {
- client.postMessage({
- "data": message_tag,
- "data_title": title,
- "data_body": message});
- });
- });
- });
-
- // Optional: Added to that the browser opens when you click on the notification push web.
- self.addEventListener('notificationclick', function(event) {
- // Android doesn't close the notification when you click it
- // See http://crbug.com/463146
- event.notification.close();
- // Check if there's already a tab open with this URL.
- // If yes: focus on the tab.
- // If no: open a tab with the URL.
- event.waitUntil(clients.matchAll({type: 'window', includeUncontrolled: true}).then(function(windowClients) {
- for (var i = 0; i < windowClients.length; i++) {
- var client = windowClients[i];
- if ('focus' in client) {
- return client.focus();
- }
- }
- })
- );
- });
+For more information about how to configure WebPush, see `docs/WebPush https://github.com/jazzband/django-push-notifications/blob/master/docs/WebPu...`_.
Sending messages
@@ -389,6 +162,37 @@
once constructed the payload exceeds the maximum size, an ``APNSDataOverflow`` exception will be raised before anything is sent.
Reference: `Apple Payload Documentation https://developer.apple.com/library/content/documentation/NetworkingInternet...`_
+Web Push accepts only one variable (``message``), which is passed directly to pywebpush. This message can be a simple string, which will be used as your notification's body, or it can be contain `any data supported by pywebpushhttps://github.com/web-push-libs/pywebpush`.
+
+Simple example:
+
+.. code-block:: python
+
+ from push_notifications.models import WebPushDevice
+
+ device = WebPushDevice.objects.get(registration_id=wp_reg_id)
+
+ device.send_message("You've got mail")
+
+.. note::
+ To customize the notification title using this method, edit the ``"TITLE DEFAULT"`` string in your ``navigatorPush.service.js`` file.
+
+JSON example:
+
+.. code-block:: python
+
+ import json
+ from push_notifications.models import WebPushDevice
+
+ device = WebPushDevice.objects.get(registration_id=wp_reg_id)
+
+ title = "Message Received"
+ message = "You've got mail"
+ data = json.dumps({"title": title, "message": message})
+
+ device.send_message(data)
+
+
Sending messages in bulk
------------------------
.. code-block:: python
@@ -463,7 +267,7 @@
from push_notifications.gcm import send_message
# First param is "None" because no Registration_id is needed, the message will be sent to all devices subscribed to the topic.
- send_message(None, {"body": "Hello members of my_topic!"}, to="/topics/my_topic")
+ send_message(None, {"body": "Hello members of my_topic!"}, cloud_type="FCM", to="/topics/my_topic")
Reference: `FCM Documentation https://firebase.google.com/docs/cloud-messaging/android/topic-messaging`_
@@ -495,6 +299,7 @@
Routes can be added one of two ways:
- Routers_ (include all views)
+
.. _Routers: http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers#using-r...
::
@@ -513,6 +318,7 @@
)
- Using as_view_ (specify which views to include)
+
.. _as_view: http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers#binding...
::
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/docs/WebPush.rst new/django-push-notifications-3.0.2/docs/WebPush.rst
--- old/django-push-notifications-3.0.0/docs/WebPush.rst 1970-01-01 01:00:00.000000000 +0100
+++ new/django-push-notifications-3.0.2/docs/WebPush.rst 2023-10-29 17:59:32.000000000 +0100
@@ -0,0 +1,225 @@
+At a high-level, the key steps for implementing web push notifications after installing django-push-notifications[WP] are:
+ - Configure the VAPID keys, a private and public key for signing your push requests.
+ - Add client side logic to ask the user for permission to send push notifications and then sending returned client identifier information to a django view to create a WebPushDevice.
+ - Use a service worker to receive messages that have been pushed to the device and displaying them as notifications.
+
+These are in addition to the instalation steps for django-push-notifications[WP]
+
+Configure the VAPID keys
+------------------------------
+- Install:
+
+.. code-block:: python
+
+ pip install py-vapid (Only for generating key)
+
+- Generate public and private keys:
+
+.. code-block:: bash
+
+ vapid --gen
+
+ Generating private_key.pem
+ Generating public_key.pem
+
+
+The private key generated is the file to use with the setting ``WP_PRIVATE_KEY``
+The public key will be used in your client side javascript, but first it must be formated as an Application Server Key
+
+- Generate client public key (applicationServerKey)
+
+.. code-block:: bash
+
+ vapid --applicationServerKey
+
+ Application Server Key = <Your Public Key>
+
+
+
+Client Side logic to ask user for permission and subscribe to WebPush
+------------------------------
+The example subscribeUser function is best called in response to a user action, such as a button click. Some browsers will deny the request otherwise.
+
+.. code-block:: javascript
+
+ // Utils functions:
+
+ function urlBase64ToUint8Array (base64String) {
+ var padding = '='.repeat((4 - base64String.length % 4) % 4)
+ var base64 = (base64String + padding)
+ .replace(/\-/g, '+')
+ .replace(/_/g, '/')
+
+ var rawData = window.atob(base64)
+ var outputArray = new Uint8Array(rawData.length)
+
+ for (var i = 0; i < rawData.length; ++i) {
+ outputArray[i] = rawData.charCodeAt(i)
+ }
+ return outputArray;
+ }
+
+ var applicationServerKey = '<Your Public Key>';
+
+ function subscribeUser() {
+ if ('Notification' in window && 'serviceWorker' in navigator) {
+ navigator.serviceWorker.ready.then(function (reg) {
+ reg.pushManager
+ .subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: urlBase64ToUint8Array(
+ applicationServerKey
+ ),
+ })
+ .then(function (sub) {
+ var registration_id = sub.endpoint;
+ var data = {
+ p256dh: btoa(
+ String.fromCharCode.apply(
+ null,
+ new Uint8Array(sub.getKey('p256dh'))
+ )
+ ),
+ auth: btoa(
+ String.fromCharCode.apply(
+ null,
+ new Uint8Array(sub.getKey('auth'))
+ )
+ ),
+ registration_id: registration_id,
+ }
+ requestPOSTToServer(data)
+ })
+ .catch(function (e) {
+ if (Notification.permission === 'denied') {
+ console.warn('Permission for notifications was denied')
+ } else {
+ console.error('Unable to subscribe to push', e)
+ }
+ })
+ })
+ }
+ }
+
+ // Send the subscription data to your server
+ function requestPOSTToServer (data) {
+ const headers = new Headers();
+ headers.set('Content-Type', 'application/json');
+ const requestOptions = {
+ method: 'POST',
+ headers,
+ body: JSON.stringify(data),
+ };
+
+ return (
+ fetch(
+ '<your endpoint url>',
+ requestOptions
+ )
+ ).then((response) => response.json())
+ }
+
+Server Side logic to create webpush
+------------------------------
+Is is up to you how to add a view in your django application that can handle a POST of p256dh, auth, registration_id and create a WebPushDevice with those values assoicated with the appropriate user.
+For example you could use rest_framework
+
+.. code-block:: python
+
+ from rest_framework.routers import SimpleRouter
+ from push_notifications.api.rest_framework import WebPushDeviceViewSet
+ ....
+ api_router = SimpleRouter()
+ api_router.register(r'push/web', WebPushDeviceViewSet, basename='web_push')
+ ...
+ urlpatterns += [
+ # Api
+ re_path('api/v1/', include(api_router.urls)),
+ ...
+ ]
+
+Or a generic function view (add your own boilerplate for errors and protections)
+
+.. code-block:: python
+
+ import json
+ from push_notifications.models import WebPushDevice
+ def register_webpush(request):
+ data = json.loads(request.body)
+ WebPushDevice.objects.create(
+ user=request.user,
+ **data
+ )
+
+
+Service Worker to show messages
+------------------------------
+You will need a service worker registered with your web app that can handle the notfications, for example
+
+.. code-block:: javascript
+
+ // Example navigatorPush.service.js file
+
+ var getTitle = function (title) {
+ if (title === "") {
+ title = "TITLE DEFAULT";
+ }
+ return title;
+ };
+ var getNotificationOptions = function (message, message_tag) {
+ var options = {
+ body: message,
+ icon: '/img/icon_120.png',
+ tag: message_tag,
+ vibrate: [200, 100, 200, 100, 200, 100, 200]
+ };
+ return options;
+ };
+
+ self.addEventListener('install', function (event) {
+ self.skipWaiting();
+ });
+
+ self.addEventListener('push', function(event) {
+ try {
+ // Push is a JSON
+ var response_json = event.data.json();
+ var title = response_json.title;
+ var message = response_json.message;
+ var message_tag = response_json.tag;
+ } catch (err) {
+ // Push is a simple text
+ var title = "";
+ var message = event.data.text();
+ var message_tag = "";
+ }
+ self.registration.showNotification(getTitle(title), getNotificationOptions(message, message_tag));
+ // Optional: Comunicating with our js application. Send a signal
+ self.clients.matchAll({includeUncontrolled: true, type: 'window'}).then(function (clients) {
+ clients.forEach(function (client) {
+ client.postMessage({
+ "data": message_tag,
+ "data_title": title,
+ "data_body": message});
+ });
+ });
+ });
+
+ // Optional: Added to that the browser opens when you click on the notification push web.
+ self.addEventListener('notificationclick', function(event) {
+ // Android doesn't close the notification when you click it
+ // See http://crbug.com/463146
+ event.notification.close();
+ // Check if there's already a tab open with this URL.
+ // If yes: focus on the tab.
+ // If no: open a tab with the URL.
+ event.waitUntil(clients.matchAll({type: 'window', includeUncontrolled: true}).then(function(windowClients) {
+ for (var i = 0; i < windowClients.length; i++) {
+ var client = windowClients[i];
+ if ('focus' in client) {
+ return client.focus();
+ }
+ }
+ })
+ );
+ });
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/api/rest_framework.py new/django-push-notifications-3.0.2/push_notifications/api/rest_framework.py
--- old/django-push-notifications-3.0.0/push_notifications/api/rest_framework.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/api/rest_framework.py 2023-10-29 17:59:32.000000000 +0100
@@ -22,7 +22,7 @@
data = int(data, 16) if type(data) != int else data
except ValueError:
raise ValidationError("Device ID is not a valid hex number")
- return super(HexIntegerField, self).to_internal_value(data)
+ return super().to_internal_value(data)
def to_representation(self, value):
return value
@@ -46,10 +46,10 @@
model = APNSDevice
def validate_registration_id(self, value):
- # iOS device tokens are 256-bit hexadecimal (64 characters). In 2016 Apple is increasing
- # iOS device tokens to 100 bytes hexadecimal (200 characters).
- if hex_re.match(value) is None or len(value) not in (64, 200):
+ # https://developer.apple.com/documentation/uikit/uiapplicationdelegate/162295...
+ # As of 02/2023 APNS tokens (registration_id) "are of variable length. Do not hard-code their size."
+ if hex_re.match(value) is None:
raise ValidationError("Registration ID (device token) is invalid")
return value
@@ -160,12 +160,12 @@
def perform_create(self, serializer):
if self.request.user.is_authenticated:
serializer.save(user=self.request.user)
- return super(DeviceViewSetMixin, self).perform_create(serializer)
+ return super().perform_create(serializer)
def perform_update(self, serializer):
if self.request.user.is_authenticated:
serializer.save(user=self.request.user)
- return super(DeviceViewSetMixin, self).perform_update(serializer)
+ return super().perform_update(serializer)
class AuthorizedMixin:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/conf/app.py new/django-push-notifications-3.0.2/push_notifications/conf/app.py
--- old/django-push-notifications-3.0.0/push_notifications/conf/app.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/conf/app.py 2023-10-29 17:59:32.000000000 +0100
@@ -171,7 +171,7 @@
"""Validate the APNS certificate at startup."""
try:
- with open(certfile, "r") as f:
+ with open(certfile) as f:
content = f.read()
check_apns_certificate(content)
except Exception as e:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/conf/legacy.py new/django-push-notifications-3.0.2/push_notifications/conf/legacy.py
--- old/django-push-notifications-3.0.0/push_notifications/conf/legacy.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/conf/legacy.py 2023-10-29 17:59:32.000000000 +0100
@@ -119,8 +119,7 @@
return self._get_application_settings(application_id, "APNS_USE_SANDBOX", self.msg)
def get_apns_use_alternative_port(self, application_id=None):
- return
- self._get_application_settings(application_id, "APNS_USE_ALTERNATIVE_PORT", self.msg)
+ return self._get_application_settings(application_id, "APNS_USE_ALTERNATIVE_PORT", self.msg)
def get_apns_topic(self, application_id=None):
return self._get_application_settings(application_id, "APNS_TOPIC", self.msg)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/fields.py new/django-push-notifications-3.0.2/push_notifications/fields.py
--- old/django-push-notifications-3.0.0/push_notifications/fields.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/fields.py 2023-10-29 17:59:32.000000000 +0100
@@ -13,7 +13,7 @@
UNSIGNED_64BIT_INT_MAX_VALUE = 2 ** 64 - 1
-hex_re = re.compile(r"^(([0-9A-f])|(0x[0-9A-f]))+$")
+hex_re = re.compile(r"^(0x)?([0-9a-f])+$", re.I)
signed_integer_vendors = [
"postgresql",
"sqlite",
@@ -48,7 +48,7 @@
self.default_validators = [
RegexValidator(hex_re, _("Enter a valid hexadecimal number"), "invalid")
]
- super(HexadecimalField, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
def prepare_value(self, value):
# converts bigint from db to hex before it is displayed in admin
@@ -82,7 +82,7 @@
elif "sqlite" == connection.vendor:
return "UNSIGNED BIG INT"
else:
- return super(HexIntegerField, self).db_type(connection=connection)
+ return super().db_type(connection=connection)
def get_prep_value(self, value):
""" Return the integer value to be stored from the hex string """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/migrations/0001_initial.py new/django-push-notifications-3.0.2/push_notifications/migrations/0001_initial.py
--- old/django-push-notifications-3.0.0/push_notifications/migrations/0001_initial.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/migrations/0001_initial.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from django.conf import settings
from django.db import migrations, models
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/migrations/0002_auto_20160106_0850.py new/django-push-notifications-3.0.2/push_notifications/migrations/0002_auto_20160106_0850.py
--- old/django-push-notifications-3.0.0/push_notifications/migrations/0002_auto_20160106_0850.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/migrations/0002_auto_20160106_0850.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# Generated by Django 1.9.1 on 2016-01-06 08:50
from django.db import migrations, models
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/migrations/0003_wnsdevice.py new/django-push-notifications-3.0.2/push_notifications/migrations/0003_wnsdevice.py
--- old/django-push-notifications-3.0.0/push_notifications/migrations/0003_wnsdevice.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/migrations/0003_wnsdevice.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# Generated by Django 1.9.6 on 2016-06-13 20:46
import django.db.models.deletion
from django.conf import settings
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/migrations/0004_fcm.py new/django-push-notifications-3.0.2/push_notifications/migrations/0004_fcm.py
--- old/django-push-notifications-3.0.0/push_notifications/migrations/0004_fcm.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/migrations/0004_fcm.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# Generated by Django 1.9.6 on 2016-06-13 20:46
from django.conf import settings
from django.db import migrations, models
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/migrations/0005_applicationid.py new/django-push-notifications-3.0.2/push_notifications/migrations/0005_applicationid.py
--- old/django-push-notifications-3.0.0/push_notifications/migrations/0005_applicationid.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/migrations/0005_applicationid.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from django.conf import settings
from django.db import migrations, models
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/migrations/0006_webpushdevice.py new/django-push-notifications-3.0.2/push_notifications/migrations/0006_webpushdevice.py
--- old/django-push-notifications-3.0.0/push_notifications/migrations/0006_webpushdevice.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/migrations/0006_webpushdevice.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
from django.conf import settings
from django.db import migrations, models
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/models.py new/django-push-notifications-3.0.2/push_notifications/models.py
--- old/django-push-notifications-3.0.0/push_notifications/models.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/models.py 2023-10-29 17:59:32.000000000 +0100
@@ -255,6 +255,4 @@
def send_message(self, message, **kwargs):
from .webpush import webpush_send_message
- return webpush_send_message(
- uri=self.registration_id, message=message, browser=self.browser,
- auth=self.auth, p256dh=self.p256dh, application_id=self.application_id, **kwargs)
+ return webpush_send_message(self, message, **kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/push_notifications/webpush.py new/django-push-notifications-3.0.2/push_notifications/webpush.py
--- old/django-push-notifications-3.0.0/push_notifications/webpush.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/push_notifications/webpush.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,3 +1,5 @@
+import warnings
+
from pywebpush import WebPushException, webpush
from .conf import get_manager
@@ -5,9 +7,18 @@
def get_subscription_info(application_id, uri, browser, auth, p256dh):
- url = get_manager().get_wp_post_url(application_id, browser)
+ if uri.startswith("https://"):
+ endpoint = uri
+ else:
+ url = get_manager().get_wp_post_url(application_id, browser)
+ endpoint = "{}/{}".format(url, uri)
+ warnings.warn(
+ "registration_id should be the full endpoint returned from pushManager.subscribe",
+ DeprecationWarning,
+ stacklevel=2,
+ )
return {
- "endpoint": "{}/{}".format(url, uri),
+ "endpoint": endpoint,
"keys": {
"auth": auth,
"p256dh": p256dh,
@@ -15,25 +26,30 @@
}
-def webpush_send_message(
- uri, message, browser, auth, p256dh, application_id=None, **kwargs
-):
- subscription_info = get_subscription_info(application_id, uri, browser, auth, p256dh)
-
+def webpush_send_message(device, message, **kwargs):
+ subscription_info = get_subscription_info(
+ device.application_id, device.registration_id,
+ device.browser, device.auth, device.p256dh)
try:
+ results = {"results": [{"original_registration_id": device.registration_id}]}
response = webpush(
subscription_info=subscription_info,
data=message,
- vapid_private_key=get_manager().get_wp_private_key(application_id),
- vapid_claims=get_manager().get_wp_claims(application_id).copy(),
+ vapid_private_key=get_manager().get_wp_private_key(device.application_id),
+ vapid_claims=get_manager().get_wp_claims(device.application_id).copy(),
**kwargs
)
- results = {"results": [{}]}
- if not response.ok:
- results["results"][0]["error"] = response.content
- results["results"][0]["original_registration_id"] = response.content
- else:
+ if response.ok:
results["success"] = 1
+ else:
+ results["failure"] = 1
+ results["results"][0]["error"] = response.content
return results
except WebPushException as e:
+ if e.response is not None and e.response.status_code in [404, 410]:
+ results["failure"] = 1
+ results["results"][0]["error"] = e.message
+ device.active = False
+ device.save()
+ return results
raise WebPushError(e.message)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/setup.cfg new/django-push-notifications-3.0.2/setup.cfg
--- old/django-push-notifications-3.0.0/setup.cfg 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/setup.cfg 2023-10-29 17:59:32.000000000 +0100
@@ -38,7 +38,6 @@
APNS =
apns2>=0.3.0
importlib-metadata;python_version < "3.8"
- pywebpush>=1.3.0
Django>=2.2
WP = pywebpush>=1.3.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/setup.py new/django-push-notifications-3.0.2/setup.py
--- old/django-push-notifications-3.0.0/setup.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/setup.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,4 +1,11 @@
#!/usr/bin/env python
from setuptools import setup
+from pathlib import Path
-setup(use_scm_version={"version_scheme": "post-release"})
+this_directory = Path(__file__).parent
+long_description = (this_directory / "README.rst").read_text()
+setup(
+ long_description=long_description,
+ long_description_content_type='text/x-rst',
+ use_scm_version={"version_scheme": "post-release"}
+)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/tests/settings.py new/django-push-notifications-3.0.2/tests/settings.py
--- old/django-push-notifications-3.0.0/tests/settings.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/tests/settings.py 2023-10-29 17:59:32.000000000 +0100
@@ -26,5 +26,5 @@
SECRET_KEY = "foobar"
PUSH_NOTIFICATIONS_SETTINGS = {
- "WP_CLAIMS": {"sub": "mailto: jazzband@example.com"}
+ "WP_CLAIMS": {"sub": "mailto:jazzband@example.com"}
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/tests/settings_unique.py new/django-push-notifications-3.0.2/tests/settings_unique.py
--- old/django-push-notifications-3.0.0/tests/settings_unique.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/tests/settings_unique.py 2023-10-29 17:59:32.000000000 +0100
@@ -26,6 +26,6 @@
SECRET_KEY = "foobar"
PUSH_NOTIFICATIONS_SETTINGS = {
- "WP_CLAIMS": {"sub": "mailto: jazzband@example.com"},
+ "WP_CLAIMS": {"sub": "mailto:jazzband@example.com"},
"UNIQUE_REG_ID": True
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/tests/test_fields.py new/django-push-notifications-3.0.2/tests/test_fields.py
--- old/django-push-notifications-3.0.0/tests/test_fields.py 1970-01-01 01:00:00.000000000 +0100
+++ new/django-push-notifications-3.0.2/tests/test_fields.py 2023-10-29 17:59:32.000000000 +0100
@@ -0,0 +1,40 @@
+from django.core.exceptions import ValidationError
+from django.test import SimpleTestCase
+
+from push_notifications.fields import HexadecimalField
+
+
+class HexadecimalFieldTestCase(SimpleTestCase):
+ _INVALID_HEX_VALUES = [
+ "foobar",
+ "GLUTEN",
+ "HeLLo WoRLd",
+ "international",
+ "°!#€%&/()[]{}=?",
+ "0x",
+ ]
+
+ _VALID_HEX_VALUES = {
+ "babe": "babe",
+ "BEEF": "BEEF",
+ " \nfeed \t": "feed",
+ "0x012345789abcdef": "0x012345789abcdef",
+ "012345789aBcDeF": "012345789aBcDeF",
+ }
+
+ def test_clean_invalid_values(self):
+ """Passing invalid values raises ValidationError."""
+ f = HexadecimalField()
+ for invalid in self._INVALID_HEX_VALUES:
+ self.assertRaisesMessage(
+ ValidationError,
+ "'Enter a valid hexadecimal number'",
+ f.clean,
+ invalid,
+ )
+
+ def test_clean_valid_values(self):
+ """Passing valid values returns the expected output."""
+ f = HexadecimalField()
+ for valid, expected in self._VALID_HEX_VALUES.items():
+ self.assertEqual(expected, f.clean(valid))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/tests/test_legacy_config.py new/django-push-notifications-3.0.2/tests/test_legacy_config.py
--- old/django-push-notifications-3.0.0/tests/test_legacy_config.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/tests/test_legacy_config.py 2023-10-29 17:59:32.000000000 +0100
@@ -1,3 +1,5 @@
+from unittest import mock
+
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
@@ -38,9 +40,17 @@
)
def test_immutable_wp_claims(self):
+ self.endpoint = "https://updates.push.services.mozilla.com/wpush/v2/token"
+ self.mock_device = mock.Mock()
+ self.mock_device.application_id = None
+ self.mock_device.registration_id = self.endpoint
+ self.mock_device.auth = "authtest"
+ self.mock_device.p256dh = "p256dhtest"
+ self.mock_device.active = True
+ self.mock_device.save.return_value = True
vapid_claims_pre = get_manager().get_wp_claims(None).copy()
try:
- webpush_send_message("", {}, "CHROME", "", "")
+ webpush_send_message(self.mock_device, "message")
except WebPushError:
pass
vapid_claims_after = get_manager().get_wp_claims(None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/tests/test_models.py new/django-push-notifications-3.0.2/tests/test_models.py
--- old/django-push-notifications-3.0.0/tests/test_models.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/tests/test_models.py 2023-10-29 17:59:32.000000000 +0100
@@ -276,7 +276,7 @@
reg_ids = [obj.registration_id for obj in GCMDevice.objects.all()]
send_bulk_message(reg_ids, {"message": "Hello World"}, "GCM")
p.assert_called_once_with(
- [u"abc", u"abc1"], {"message": "Hello World"}, cloud_type="GCM", application_id=None
+ ["abc", "abc1"], {"message": "Hello World"}, cloud_type="GCM", application_id=None
)
def test_fcm_send_message(self):
@@ -506,7 +506,7 @@
reg_ids = [obj.registration_id for obj in GCMDevice.objects.all()]
send_bulk_message(reg_ids, {"message": "Hello World"}, "FCM")
p.assert_called_once_with(
- [u"abc", u"abc1"], {"message": "Hello World"}, cloud_type="FCM",
+ ["abc", "abc1"], {"message": "Hello World"}, cloud_type="FCM",
application_id=None
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/tests/test_rest_framework.py new/django-push-notifications-3.0.2/tests/test_rest_framework.py
--- old/django-push-notifications-3.0.0/tests/test_rest_framework.py 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/tests/test_rest_framework.py 2023-10-29 17:59:32.000000000 +0100
@@ -5,13 +5,13 @@
)
-GCM_DRF_INVALID_HEX_ERROR = {"device_id": [u"Device ID is not a valid hex number"]}
-GCM_DRF_OUT_OF_RANGE_ERROR = {"device_id": [u"Device ID is out of range"]}
+GCM_DRF_INVALID_HEX_ERROR = {"device_id": ["Device ID is not a valid hex number"]}
+GCM_DRF_OUT_OF_RANGE_ERROR = {"device_id": ["Device ID is out of range"]}
class APNSDeviceSerializerTestCase(TestCase):
def test_validation(self):
- # valid data - 32 bytes upper case
+ # valid data - 64 bytes upper case
serializer = APNSDeviceSerializer(data={
"registration_id": "AEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAE",
"name": "Apple iPhone 6+",
@@ -20,7 +20,7 @@
})
self.assertTrue(serializer.is_valid())
- # valid data - 32 bytes lower case
+ # valid data - 64 bytes lower case
serializer = APNSDeviceSerializer(data={
"registration_id": "aeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeae",
"name": "Apple iPhone 6+",
@@ -31,7 +31,7 @@
# valid data - 100 bytes upper case
serializer = APNSDeviceSerializer(data={
- "registration_id": "AE" * 100,
+ "registration_id": "AE" * 50,
"name": "Apple iPhone 6+",
"device_id": "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
})
@@ -39,7 +39,15 @@
# valid data - 100 bytes lower case
serializer = APNSDeviceSerializer(data={
- "registration_id": "ae" * 100,
+ "registration_id": "ae" * 50,
+ "name": "Apple iPhone 6+",
+ "device_id": "ffffffffffffffffffffffffffffffff",
+ })
+ self.assertTrue(serializer.is_valid())
+
+ # valid data - 200 bytes mixed case
+ serializer = APNSDeviceSerializer(data={
+ "registration_id": "aE" * 100,
"name": "Apple iPhone 6+",
"device_id": "ffffffffffffffffffffffffffffffff",
})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/tests/test_webpush.py new/django-push-notifications-3.0.2/tests/test_webpush.py
--- old/django-push-notifications-3.0.0/tests/test_webpush.py 1970-01-01 01:00:00.000000000 +0100
+++ new/django-push-notifications-3.0.2/tests/test_webpush.py 2023-10-29 17:59:32.000000000 +0100
@@ -0,0 +1,93 @@
+from unittest import mock
+
+from django.test import TestCase
+from pywebpush import WebPushException
+
+from push_notifications.exceptions import WebPushError
+from push_notifications.webpush import (
+ get_subscription_info, webpush_send_message
+)
+
+# Mock Responses
+mock_success_response = mock.MagicMock(status_code=200, ok=True)
+mock_fail_resposne = mock.MagicMock(status_code=400, ok=False, content="Test Error")
+mock_unsubscribe_response = mock.MagicMock(
+ status_code=410, ok=False, content="Unsubscribe")
+mock_unsubscribe_response_404 = mock.MagicMock(
+ status_code=404, ok=False, content="Unsubscribe")
+
+
+class WebPushSendMessageTestCase(TestCase):
+ def setUp(self):
+ self.endpoint = "https://updates.push.services.mozilla.com/wpush/v2/token"
+ self.mock_device = mock.Mock()
+ self.mock_device.application_id = None
+ self.mock_device.registration_id = self.endpoint
+ self.mock_device.auth = "authtest"
+ self.mock_device.p256dh = "p256dhtest"
+ self.mock_device.active = True
+ self.mock_device.save.return_value = True
+
+ def test_get_subscription_info(self):
+ keys = {"auth": "authtest", "p256dh": "p256dhtest"}
+ endpoint = self.endpoint
+ original = get_subscription_info(
+ None, "token", "FIREFOX", keys["auth"], keys["p256dh"]
+ )
+
+ self.assertEqual(
+ original,
+ {
+ "endpoint": endpoint,
+ "keys": keys,
+ },
+ )
+
+ patched = get_subscription_info(
+ None,
+ endpoint,
+ "",
+ keys["auth"],
+ keys["p256dh"],
+ )
+
+ self.assertEqual(
+ patched,
+ {
+ "endpoint": endpoint,
+ "keys": keys,
+ },
+ )
+
+ @mock.patch("push_notifications.webpush.webpush", return_value=mock_success_response)
+ def test_webpush_send_message(self, webpush_mock):
+ results = webpush_send_message(self.mock_device, "message")
+ self.assertEqual(results["success"], 1)
+
+ @mock.patch("push_notifications.webpush.webpush", return_value=mock_fail_resposne)
+ def test_webpush_send_message_failure(self, webpush_mock):
+ results = webpush_send_message(self.mock_device, "message")
+ self.assertEqual(results["failure"], 1)
+
+ @mock.patch(
+ "push_notifications.webpush.webpush",
+ side_effect=WebPushException("Unsubscribe",
+ response=mock_unsubscribe_response))
+ def test_webpush_send_message_unsubscribe(self, webpush_mock):
+ results = webpush_send_message(self.mock_device, "message")
+ self.assertEqual(results["failure"], 1)
+
+ @mock.patch(
+ "push_notifications.webpush.webpush",
+ side_effect=WebPushException("Unsubscribe",
+ response=mock_unsubscribe_response_404))
+ def test_webpush_send_message_404(self, webpush_mock):
+ results = webpush_send_message(self.mock_device, "message")
+ self.assertEqual(results["failure"], 1)
+
+ @mock.patch(
+ "push_notifications.webpush.webpush",
+ side_effect=WebPushException("Error"))
+ def test_webpush_send_message_exception(self, webpush_mock):
+ with self.assertRaises(WebPushError):
+ webpush_send_message(self.mock_device, "message")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/django-push-notifications-3.0.0/tox.ini new/django-push-notifications-3.0.2/tox.ini
--- old/django-push-notifications-3.0.0/tox.ini 2022-02-14 03:43:49.000000000 +0100
+++ new/django-push-notifications-3.0.2/tox.ini 2023-10-29 17:59:32.000000000 +0100
@@ -1,9 +1,9 @@
[tox]
-skipsdist = True
+skipsdist = False
usedevelop = true
envlist =
py{36,37,38,39}-dj{22,32}
- py{38,39}-dj{40,main}
+ py{38,39}-dj{40,405}
flake8
[gh-actions]
@@ -18,7 +18,7 @@
2.2: dj22
3.2: dj32
4.0: dj40
- main: djmain
+ 4.0.5: dj405
[testenv]
usedevelop = true
@@ -38,8 +38,8 @@
djangorestframework
dj22: Django>=2.2,<3.0
dj32: Django>=3.2,<3.3
- dj40: Django>=4.0,<4.1
- djmain: https://github.com/django/django/archive/main.tar.gz
+ dj40: Django>=4.0,<4.0.5
+ dj405: Django>=4.0.5,<4.1
[testenv:flake8]
commands = flake8 --exit-zero