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

[API] Added REST API for Notifications #48

Merged
merged 2 commits into from
Jun 26, 2020
Merged

Conversation

pandafy
Copy link
Member

@pandafy pandafy commented Jun 17, 2020

Bumps openwisp-utils to version >=0.5
Implements API for listing marking notification as read
Closes #3

@pandafy pandafy added this to Open Pull-Requests in [GSOC20] Notifications via automation Jun 17, 2020
notification_id = uuid.UUID(notification_id)
notification = queryset.get(id=notification_id)
except ValueError:
return Response(status=status.HTTP_400_BAD_REQUEST)
Copy link

@NoumbissiValere NoumbissiValere Jun 17, 2020

Choose a reason for hiding this comment

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

@pandafy will it not be nice for us to give users some more information as to why their response didn't go through? It this case; it was caused by invalid notification_id

except ValueError:
return Response(status=status.HTTP_400_BAD_REQUEST)
except Notification.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

Choose a reason for hiding this comment

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

Same here

@pandafy pandafy changed the title Issues/3 rest api [API] Added REST API for Notifications Jun 17, 2020
@pandafy pandafy marked this pull request as ready for review June 18, 2020 08:41
@pandafy pandafy moved this from Open Pull-Requests to Ready for Review/Test in [GSOC20] Notifications Jun 18, 2020
Copy link

@NoumbissiValere NoumbissiValere left a comment

Choose a reason for hiding this comment

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

Great work @pandafy 👍
But I think instead of having a mark_read patch api, we could make this a details api for a specific notification. remember from the js widget, we should be able to read a notification. so replacing this with a get API for a specific notification will ease that work as getting a specific notification from the api will mark it as read.
Also, I think we should have an API to list all read notifications as well unread notifications, this could be use as a filter on the js widget.

@pandafy
Copy link
Member Author

pandafy commented Jun 19, 2020

Also, I think we should have an API to list all read notifications as well unread notifications, this could be use as a filter on the js widget.

@NoumbissiValere Instead of adding them as individual endpoints, I have set them as filters using django-filters.

Copy link

@NoumbissiValere NoumbissiValere left a comment

Choose a reason for hiding this comment

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

Great job @pandafy.
see some minor requests below

url(
r'^openwisp_notifications/notifications/$',
views.list_notifications,
name='api_list_notifications',

Choose a reason for hiding this comment

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

I think it will be good if we have this as list_notifications

url(
r'^openwisp_notifications/notifications/(?P<notification_id>[0-9A-Fa-f-]+)/$',
views.read_notification,
name='api_read_notifications',

Choose a reason for hiding this comment

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

same here read_notification. since we are getting a single notification, keep away the s

queryset = Notification.objects.all()
serializer_class = NotificationSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['unread']
Copy link

@NoumbissiValere NoumbissiValere Jun 19, 2020

Choose a reason for hiding this comment

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

I don't think filter_backends and filterset_fields are needed in NotificationReadView so they should be removed from the BaseNotificationView and send to the NotificationListView`

Copy link
Member Author

@pandafy pandafy Jun 19, 2020

Choose a reason for hiding this comment

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

Great catch! 👍

[GSOC20] Notifications automation moved this from Ready for Review/Test to Open Pull-Requests Jun 19, 2020
@pandafy pandafy force-pushed the issues/3-rest-api branch 4 times, most recently from 64dab81 to ff37070 Compare June 21, 2020 11:55
Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

Good progress! See my comments

README.rst Outdated

.. code-block:: text

GET /api/v1/openwisp_notifications/notifications/
Copy link
Member

Choose a reason for hiding this comment

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

shorten openwisp_notifications/notifications/ to just notifications/

README.rst Outdated

.. code-block:: text

PATCH /api/v1/openwisp_notifications/notifications/{id}/
Copy link
Member

Choose a reason for hiding this comment

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

same here

app_name = 'openwisp_notifications'

urlpatterns = [
url(
Copy link
Member

Choose a reason for hiding this comment

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

time to start using path instead of url, see the django docs

name='list_notifications',
),
url(
r'^openwisp_notifications/notifications/(?P<notification_id>[0-9A-Fa-f-]+)/$',
Copy link
Member

Choose a reason for hiding this comment

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

use path with uuid converter, eg: notifications/<uuid:pk>/.

Use pk instead of id which is more generic.


urlpatterns = [
url(
r'^api/v1/sample_notifications/notifications/$',
Copy link
Member

Choose a reason for hiding this comment

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

find a way to avoid repeating the url/path patterns, see how @atb00ker is doing it in the other modules he's working on

tests/openwisp2/urls.py Show resolved Hide resolved
return self.list(request, *args, **kwargs)


class NotificationReadView(BaseNotificationView):
Copy link
Member

Choose a reason for hiding this comment

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

I would call this NotificationDetailView

Copy link
Member Author

Choose a reason for hiding this comment

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

This view does not provide details about a particular notification. It just mark them as read.

Copy link
Member

@nemesifier nemesifier Jun 21, 2020

Choose a reason for hiding this comment

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

I understand, I misread this.

Marking notifications as read does not need to return anything, because the consumer of the API is supposed to already have the list of notifications and we don't need to send back anything.

I would do the following:

  • GET /api/v1/notifications/ to get the list of notifications
  • GET /api/v1/notifications/<pk>/ to get the details of a specific notification, we add this just to make the API more complete
  • POST /api/v1/notifications/read/ to flag all notifications as read, make it return just 200 OK and empty response body.

README.rst Outdated
Authentication
~~~~~~~~~~~~~~

The API authentication is based on session based authentication via REST framework.
Copy link
Member

Choose a reason for hiding this comment

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

Why isn't this as https://github.com/openwisp/openwisp-firmware-upgrader/#authentication
How to authenticate is explained in openwisp-users to avoid duplicating the information in every module.
When we'll have the comprehensive developer documentation we'll be linking a different section of the same documentation instead of a different repository, that will be the best, but for now, linking a different repo is the best option we have (duplicating the info in all the modules is unmaintainable for us right now).

Copy link
Member Author

Choose a reason for hiding this comment

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

I have used session based authentication as in openwisp-ipam. I didn't thought it would be necessary to use use token based authentication.

Copy link
Member

Choose a reason for hiding this comment

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

openwisp-ipam is not up to date, see openwisp/openwisp-ipam#33

Base your work by following openwisp-firmware-upgrader, which is up to date.

And yes, please add support for bearer authentication, we want to make sure the API can be used also outside of django easily. Beware of this bug in openwisp-users: openwisp/openwisp-users#126 @atb00ker should fix that soon, coordinate with him if you have any issue.

Copy link
Member

@atb00ker atb00ker Jun 21, 2020

Choose a reason for hiding this comment

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

README.rst Outdated
Browsable web interface
~~~~~~~~~~~~~~~~~~~~~~~

.. image:: docs/images/api-ui.png
Copy link
Member

Choose a reason for hiding this comment

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

great to see you added screenshots, we'll have to update them when the URLs are changed as requested

return self.list(request, *args, **kwargs)


class NotificationReadView(BaseNotificationView):
Copy link
Member

@nemesifier nemesifier Jun 21, 2020

Choose a reason for hiding this comment

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

I understand, I misread this.

Marking notifications as read does not need to return anything, because the consumer of the API is supposed to already have the list of notifications and we don't need to send back anything.

I would do the following:

  • GET /api/v1/notifications/ to get the list of notifications
  • GET /api/v1/notifications/<pk>/ to get the details of a specific notification, we add this just to make the API more complete
  • POST /api/v1/notifications/read/ to flag all notifications as read, make it return just 200 OK and empty response body.

@pandafy pandafy force-pushed the issues/3-rest-api branch 3 times, most recently from 6892050 to 78dd814 Compare June 23, 2020 12:57
@pandafy pandafy force-pushed the issues/3-rest-api branch 4 times, most recently from 6a85043 to 1bbd411 Compare June 23, 2020 14:36
@pandafy pandafy moved this from Open Pull-Requests to Ready for Review/Test in [GSOC20] Notifications Jun 23, 2020
@pandafy pandafy requested a review from nemesifier June 23, 2020 19:43
@nemesifier nemesifier mentioned this pull request Jun 24, 2020
Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

Did a local test and it looks good (haven't been able to create test notifications in the dev env quicky though).

See below for further improvements and we'll be ready to move on.

README.rst Outdated
Live documentation
~~~~~~~~~~~~~~~~~~

.. image:: docs/images/api-docs.png
Copy link
Member

Choose a reason for hiding this comment

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

I forgot to mention that relative paths don't work when the README is published elsewhere (eg: pypi).
with @atb00ker we had the same issue before.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, using the complete links for now https://github.com/......api-docs.png should fix the issue! 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, I was not aware of this. 😄

class BaseNotificationView(GenericAPIView):
model = Notification
authentication_classes = [BearerAuthentication, SessionAuthentication]
permission_classes = [DjangoModelPermissions]
Copy link
Member

Choose a reason for hiding this comment

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

spotted, I think we can simplify this and just use IsAuthenticated, because we want all users to be able to access their notifications if this module is enabled, regardless of the permissions (django model permissions are not really useful in this case). Using IsAuthenticated will result in less load on the server when retrieving notifications (permission checks are not free).

tests/openwisp2/urls.py Show resolved Hide resolved
NOT_FOUND_ERROR = ErrorDetail(string='Not found.', code='not_found')


class TestNotificationApi(TestCase, TestOrganizationMixin, AuthenticationMixin):
Copy link
Member

Choose a reason for hiding this comment

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

can you add a test which does the following:

  • given 2 users, Joe and Karen
  • ensure Joe cannot GET, PATCH or DELETE a notification ID that is assigned to Karen

Given the current code, it looks to me that this should work, but someone in the future may inadvertently change the code and break it.

Better safe than sorry.

README.rst Outdated
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include(('openwisp_users.api.urls', 'users'), namespace='users')),
path('',include('openwisp_notifications.urls', namespace='openwisp_notifications')),
Copy link
Member

Choose a reason for hiding this comment

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

missing space before include

[GSOC20] Notifications automation moved this from Ready for Review/Test to Open Pull-Requests Jun 24, 2020
@pandafy pandafy force-pushed the issues/3-rest-api branch 2 times, most recently from d65d099 to 0784a8f Compare June 24, 2020 15:33
@pandafy pandafy requested a review from nemesifier June 24, 2020 15:37
@pandafy pandafy moved this from Open Pull-Requests to Ready for Review/Test in [GSOC20] Notifications Jun 24, 2020
Added API endpoint for notifications:
List, retrieve, delete and flag notifications as read.
self.assertEqual(response.status_code, 404)
self.assertDictEqual(response.data, {'detail': NOT_FOUND_ERROR})
# Check Karen's notification is still unread
n = Notification.objects.first()
Copy link
Member

Choose a reason for hiding this comment

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

did you know you can use n.refresh_from_db() ?

@nemesifier nemesifier merged commit f90ae68 into master Jun 26, 2020
[GSOC20] Notifications automation moved this from Ready for Review/Test to Done Jun 26, 2020
@pandafy pandafy deleted the issues/3-rest-api branch July 3, 2020 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

[feature] Develop API endpoints for listing notifications.
4 participants