Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add aioapns version of APNS #696

Closed
wants to merge 0 commits into from
Closed

Add aioapns version of APNS #696

wants to merge 0 commits into from

Conversation

pomali
Copy link
Contributor

@pomali pomali commented Nov 2, 2023

Since version python 3.10+ is not supported by apns2 (because of dependency on hyper) we add aioapns version of sending APNs notifications.

We add installation extra [APNS_ASYNC] that installs aioapns and use new version of service if aioapns is installed. Tests are also conditional on installed modules/version of python.

Related to

@pomali
Copy link
Contributor Author

pomali commented Nov 2, 2023

There are some (maybe) controversial decisions, feel free to discuss them

  • choice of aioapns - it looks like only library with active development
  • supporting both apns2 and aioapns - because we don't want to break older code
  • deciding on which version to use in models.py - sure we can move it to apns.py and move apns2 code to apns_legacy.py

@pomali pomali requested a review from jamaalscarlett November 7, 2023 18:16
@sivasankarankb
Copy link

@pomali You have left print() statements in the code. See apns_async.py.

Copy link

codecov bot commented Dec 8, 2023

Codecov Report

Attention: Patch coverage is 11.20690% with 103 lines in your changes missing coverage. Please review.

Project coverage is 64.66%. Comparing base (0f79181) to head (7500caa).
Report is 5 commits behind head on master.

Current head 7500caa differs from pull request most recent head fdc90f8

Please upload reports for the commit fdc90f8 to get more accurate results.

Files Patch % Lines
push_notifications/apns_async.py 4.62% 103 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (0f79181) and HEAD (7500caa). Click for more details.

HEAD has 6 uploads less than BASE | Flag | BASE (0f79181) | HEAD (7500caa) | |------|------|------| ||7|1|
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #696      +/-   ##
==========================================
- Coverage   70.39%   64.66%   -5.73%     
==========================================
  Files          27       28       +1     
  Lines        1101     1214     +113     
  Branches      180      199      +19     
==========================================
+ Hits          775      785      +10     
- Misses        288      391     +103     
  Partials       38       38              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@itayAmza
Copy link

@pomali, any progress for this one?
I had the apns2 0.7.2 requires PyJWT<2.0.0,>=1.4.0, but you have pyjwt 2.1.0 which is incompatible issue, and pointing to your branch (as a temporary solution) completely solves it.

@pomali
Copy link
Contributor Author

pomali commented Jan 25, 2024

I'm not sure what's missing, maybe someone should review it? And then there is code coverage check failing, but most of failing lines are comments, so I am not sure how important it is.

Is there anyone who could point me in right direction?

@itayAmza glad that it helped :)

@itayAmza
Copy link

@jamaalscarlett following this one can you help us figure out who can help review this PR?

@50-Course
Copy link
Member

hopefully, I get to have a look into both PRs, tonight. Thank you for the notification @itayAmza

@jamaalscarlett
Copy link
Member

  1. I can review the PR, but I have never used APNS. Has someone actually run this with an ios/macos app?
  2. Regarding the support for both apns2 and aioapns, I think we should add a deprecation warning with this release then completely remove apns2 with the next release.

@pomali
Copy link
Contributor Author

pomali commented Mar 11, 2024

I ran it with an iOS simulator, but a second pair of eyes with a real device would be good idea

@ssyberg
Copy link

ssyberg commented Mar 13, 2024

I ran it with an iOS simulator, but a second pair of eyes with a real device would be good idea

I just tried this and got this error when trying to send a test message via apns:

ModuleNotFoundError at /admin/push_notifications/apnsdevice/
No module named 'apns2'
Request Method:	POST
Request URL:	https://local.a24-vod:8060/admin/push_notifications/apnsdevice/
Django Version:	4.2
Exception Type:	ModuleNotFoundError
Exception Value:	
No module named 'apns2'

collapse_id: str = None,
aps_kwargs: dict = {},
message_kwargs: dict = {},
notification_request_kwargs: dict = {},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since notification_request_kwargs argument has a default value and it's content is changed in the function logic, the default state can change during execution. If any previous call has set values expiration, priority or collapse_id, these are persisted to any next call and if the next call does not have these values or has the value set to None, the values are still passed to sent NotificationRequest. A simple test case to demonstrate this is to send first message with expiration and second without expiration set. The value from first call is passed to second call NotificationRequest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved it by creating copy of notification_request_kwargs, but if you consider it code smell I could rework it to having default argument None and replacing them in function with {}. Added your test (test_apns_send_messages_different_priority)

@kupsum
Copy link

kupsum commented Mar 17, 2024

There is a possible bug with default argument pollution. Below test case can demonstrate the issue. I've also added a review comment about this.

	@mock.patch("push_notifications.apns_async.APNs", autospec=True)
	def test_apns_send_messages_different_priority(self, mock_apns):
		self._create_devices(["abc", "def"])
		device_1 = APNSDevice.objects.get(registration_id="abc")
		device_2 = APNSDevice.objects.get(registration_id="def")

		device_1.send_message("Hello world 1", expiration=time.time() + 1, priority=5, collapse_id="1")
		args_1, _ = mock_apns.return_value.send_notification.call_args

		device_2.send_message("Hello world 2")
		args_2, _ = mock_apns.return_value.send_notification.call_args

		req = args_1[0]
		self.assertEqual(req.device_token, "abc")
		self.assertEqual(req.message["aps"]["alert"], "Hello world 1")
		self.assertAlmostEqual(req.time_to_live, 1, places=-1)
		self.assertEqual(req.priority, 5)
		self.assertEqual(req.collapse_key, "1")

		reg_2 = args_2[0]
		self.assertEqual(reg_2.device_token, "def")
		self.assertEqual(reg_2.message["aps"]["alert"], "Hello world 2")
		self.assertIsNone(reg_2.time_to_live, "No time to live should be specified")
		self.assertIsNone(reg_2.priority, "No priority should be specified")
		self.assertIsNone(reg_2.collapse_key, "No collapse key should be specified")

@pomali
Copy link
Contributor Author

pomali commented Mar 18, 2024

I ran it with an iOS simulator, but a second pair of eyes with a real device would be good idea

I just tried this and got this error when trying to send a test message via apns:

ModuleNotFoundError at /admin/push_notifications/apnsdevice/
No module named 'apns2'
Request Method:	POST
Request URL:	https://local.a24-vod:8060/admin/push_notifications/apnsdevice/
Django Version:	4.2
Exception Type:	ModuleNotFoundError
Exception Value:	
No module named 'apns2'

do you have aioapns installed? because that is condition on which we use the new code

added mention to README

pip install django-push-notifications[APNS_ASYNC]

@pomali
Copy link
Contributor Author

pomali commented Mar 18, 2024

There is a possible bug with default argument pollution. Below test case can demonstrate the issue. I've also added a review comment about this.

Thanks for catching this, I added your test and fixed the issue.

@kmasuhr
Copy link

kmasuhr commented Mar 18, 2024

I think it is worth to add python 3.10 as testing target in CI, since it should enable support for version 3.10

python-version: ['3.6', '3.7', '3.8', '3.9']

@ssyberg
Copy link

ssyberg commented Mar 21, 2024

I ran it with an iOS simulator, but a second pair of eyes with a real device would be good idea

I just tried this and got this error when trying to send a test message via apns:

ModuleNotFoundError at /admin/push_notifications/apnsdevice/
No module named 'apns2'
Request Method:	POST
Request URL:	https://local.a24-vod:8060/admin/push_notifications/apnsdevice/
Django Version:	4.2
Exception Type:	ModuleNotFoundError
Exception Value:	
No module named 'apns2'

do you have aioapns installed? because that is condition on which we use the new code

added mention to README

pip install django-push-notifications[APNS_ASYNC]

I'm pretty sure I did when I tested it, but wouldn't that be handled by pip/piptools?

@50-Course
Copy link
Member

Hello @kmasuhr,

I have just updated this PR to bump the version of python being used in CI.
However, it would seem one of its transitive dependencies is not compatible with Python 3.10 - precisely, the hyper==0.7.0
HTTP Client.

Given that the hyper library is not actively maintained, I would like to propose that we replace it with a maintained alternative.
I am open to suggestions, but I would recommend using httpx as a good replacement.

@pomali
Copy link
Contributor Author

pomali commented Mar 26, 2024

I'm pretty sure I did when I tested it, but wouldn't that be handled by pip/piptools?

@ssyberg it should be handled by pip, but it's optional dependency, so maybe it didn't get installed?

@rib3
Copy link

rib3 commented Mar 29, 2024

I have just updated this PR to bump the version of python being used in CI. However, it would seem one of its transitive dependencies is not compatible with Python 3.10 - precisely, the hyper==0.7.0 HTTP Client.

Given that the hyper library is not actively maintained, I would like to propose that we replace it with a maintained alternative. I am open to suggestions, but I would recommend using httpx as a good replacement.

hyper is a dependency of apns2.
This PR adds support for aioapns as an alternative apple push notification library.
(because of the problems with apns2/hyper)

There are some github issues with additional details / background linked in the PR description.

@ssyberg
Copy link

ssyberg commented Apr 8, 2024

I have just updated this PR to bump the version of python being used in CI. However, it would seem one of its transitive dependencies is not compatible with Python 3.10 - precisely, the hyper==0.7.0 HTTP Client.
Given that the hyper library is not actively maintained, I would like to propose that we replace it with a maintained alternative. I am open to suggestions, but I would recommend using httpx as a good replacement.

hyper is a dependency of apns2. This PR adds support for aioapns as an alternative apple push notification library. (because of the problems with apns2/hyper)

There are some github issues with additional details / background linked in the PR description.

If I'm understanding correctly, the inclusion of hyper in this PR is only to stay backwards compatible with apns2 for people who don't want to upgrade to using aioapns in which case they need to stay <3.10 (which if they are using apns2 I think is implied). IMO this could be noted in the docs but shouldn't block merge, a lot of people are waiting for this to get merged and it's blocking dev for anyone on later version setups.

Copy link

@kupsum kupsum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments when trying to install this in Python 3.11 system

@@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.6', '3.7', '3.8', '3.9']
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you consider adding 3.11 too?

Suggested change
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accepted change

setup.cfg Outdated
@@ -22,6 +22,7 @@ classifiers =
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you consider adding Python 3.11? Either way, Python 3.10 was missing

Suggested change
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accepted change

tox.ini Outdated
@@ -41,6 +45,8 @@ deps =
dj32: Django>=3.2,<3.3
dj40: Django>=4.0,<4.0.5
dj405: Django>=4.0.5,<4.1
dj42: Django>=4.2,<4.3
dj42: aioapns>=3.1,<3.2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would make send to have apns2 for Python 3.9 or less and aioapns Python 3.10+

Suggested change
dj42: aioapns>=3.1,<3.2
py{36,37,38,39}: apns2
py{310,311}: aioapns>=3.1,<3.2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated deps, removed apns2 from default leaving it only in py{36,37,38,39}

from apns2.client import NotificationPriority
from push_notifications.apns import _apns_send
from push_notifications.exceptions import APNSUnsupportedPriority
except AttributeError:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apns2 and aioapns are not supported together with conflict

The conflict is caused by:
    aioapns 3.1 depends on pyjwt>=2.0.0
    apns2 0.7.2 depends on PyJWT<2.0.0 and >=1.4.0

If apns2 is not installed, ModuleNotFoundError is raised.

Suggested change
except AttributeError:
expect (AttributeError, ModuleNotFoundError):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accepted change

class APNSModelTestCase(TestCase):
from push_notifications.exceptions import APNSError
from push_notifications.models import APNSDevice
except AttributeError:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apns2 and aioapns are not supported together with conflict

The conflict is caused by:
    aioapns 3.1 depends on pyjwt>=2.0.0
    apns2 0.7.2 depends on PyJWT<2.0.0 and >=1.4.0

If apns2 is not installed, ModuleNotFoundError is raised.

Suggested change
except AttributeError:
expect (AttributeError, ModuleNotFoundError):

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accepted change

tox.ini Outdated
Comment on lines 6 to 7
py{38,39,310}-dj{40,405}
py311-dj42
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Django 4.2 supports still Python 3.8

Suggested change
py{38,39,310}-dj{40,405}
py311-dj42
py{38,39,310,311}-dj{40,405,42}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accepted change


from . import models
from .conf import get_manager
from .exceptions import APNSServerError

ErrFunc = Optional[Callable[[NotificationRequest, NotificationResult], Awaitable[None]]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of office right now, Awaitable is that some form of Future result?

Copy link
Member

@50-Course 50-Course May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#Update: Interesting,

https://docs.python.org/3/library/typing.html#annotating-callable-objects

I have always took a different approach to this!

Comment on lines 172 to 174
notification_request_kwargs_out["time_to_live"] = expiration - int(
time.time()
)
Copy link
Member

@50-Course 50-Course May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ruff botched this formatting in here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼 looks good to me, can we have a test for this?

@ficast
Copy link

ficast commented Jul 4, 2024

Hey guys, any news on this? Can we get this merged?

@illymarev
Copy link

Hello, are there any updates regarding this issue? Any other changes are needed to merge the PR?

@pomali
Copy link
Contributor Author

pomali commented Jul 19, 2024

I don't see any outstanding issues from my side.

@50-Course
Copy link
Member

haven't followed up much on the development lately, however it would seem we having some conflicts, no?

Copy link
Member

@50-Course 50-Course left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR looks greenlit, still on the lookout what's causing the rebase conflicts.

@illymarev
Copy link

Have you determined what causes rebase conflicts? Do you need any additional changes?

@DataGreed
Copy link
Contributor

Anyone maintaining this package?.. its not usable at the moment on python 3.10+

@ficast
Copy link

ficast commented Oct 1, 2024

let's merge this guys !!! @jamaalscarlett

@vitorguima
Copy link

@50-Course will this PR get merged by any chances?

@50-Course
Copy link
Member

Yes, am currently fixing the re-base conflicts a couple more changes to go... hopefully i after my office hours i could get to it again.

@vitorguima

@50-Course
Copy link
Member

Hi everyone,

I hope this message finds you well. I wanted to provide an update regarding this PR that led to a erroneous closure of the PR.

Unfortunately, while attempting to assist with the updates and re-base conflicts, I inadvertently pushed changes that led to the closure of the original pull request. I sincerely apologize for this error and any confusion it may have caused. My intention was to facilitate the merging of the contributions into the project, and I regret any inconvenience this has created.

To rectify the situation, I have now pushed the latest changes back to the pomali/master branch with proper attribution to the original author. I will be creating a new pull request shortly to ensure that these contributions can be reviewed and merged appropriately.

Thank you for your understanding, and I appreciate your patience during this process. Please feel free to reach out if you have any questions or need further assistance.

regards,
E.A

@50-Course
Copy link
Member

Quick heads-up,

I want to provide an update regarding the changes. Please follow the new PR on #739. The new changes have been fully re-based onto this PR to facilitate the merging process.

Thank you for your patience, and I appreciate your understanding as we work to get this merged.

@50-Course
Copy link
Member

Once again calling on review of the new PR as its now able to get merged. This would allow us successfully close this one out

@50-Course
Copy link
Member

Update to all following this thread, PR been merged. Documentation could use some work here and there due to rebase issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.