notification.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import celery
  2. import json
  3. import pagure
  4. import rdflib
  5. from .. import activitypub
  6. from .. import model
  7. from .. import settings
  8. from . import broker
  9. from . import broker_url
  10. from . import database_session
  11. log = celery.utils.log.get_task_logger(__name__)
  12. log.setLevel(settings.LOG_LEVEL)
  13. @broker.task
  14. def handle_pagure_signal(topic, message):
  15. """
  16. This task receives notifications from Pagure about events that happen on
  17. the instance, creates a new activity, and schedules their delivery.
  18. """
  19. log.debug('New notification from Pagure: ' + topic)
  20. log.debug(json.dumps(message, indent=4, sort_keys=True))
  21. with database_session() as (pagure_db, forgefed_graph):
  22. if topic == 'issue.new':
  23. # A user has created a new issue
  24. person = pagure_db \
  25. .query(model.Person) \
  26. .filter(model.Person.user == message['issue']['user']['name']) \
  27. .one_or_none()
  28. project = pagure_db \
  29. .query(model.Projects) \
  30. .filter(model.Projects.id == message['project']['id']) \
  31. .one_or_none()
  32. issue = pagure_db \
  33. .query(pagure.lib.model.Issue) \
  34. .filter(pagure.lib.model.Issue.id == message['issue']['id'],
  35. pagure.lib.model.Issue.project_id == message['project']['id']) \
  36. .one_or_none()
  37. # This should never raise an error otherwise there's a bug in Pagure
  38. assert person and issue and project, \
  39. 'User or or project doesn\'t exist.'
  40. tracker_url = project.uri + '/issues'
  41. # Check if this tracker is only used to interact with a remote one
  42. remote_tracker = forgefed_graph.value(rdflib.URIRef(tracker_url),
  43. rdflib.OWL.sameAs)
  44. if not remote_tracker:
  45. log.debug('Not a remote tracker. Nothing to send.')
  46. if remote_tracker:
  47. log.debug('Sending new issue to remote tracker...')
  48. ticket = pagure_db \
  49. .query(model.Ticket) \
  50. .filter(model.Ticket.id == issue.id,
  51. model.Ticket.project_id == issue.project_id) \
  52. .one_or_none()
  53. # Offer a new ticket to the remote tracker
  54. person.offer(ticket.jsonld, to=remote_tracker)
  55. if topic == 'issue.comment.added':
  56. # A user has commented on an issue
  57. # The Pagure notification contains *all* the comments of the issue
  58. # in an ordered list, so we extract the last one from the list of
  59. # comments.
  60. comment = pagure_db \
  61. .query(model.TicketComment) \
  62. .filter(model.TicketComment.id == message['issue']['comments'][-1]['id']) \
  63. .one_or_none()
  64. person = pagure_db \
  65. .query(model.Person) \
  66. .filter(model.Person.id == comment.user.id) \
  67. .one_or_none()
  68. project = pagure_db \
  69. .query(model.Projects) \
  70. .filter(model.Projects.id == message['project']['id']) \
  71. .one_or_none()
  72. # Our local ticket
  73. ticket = pagure_db \
  74. .query(model.Ticket) \
  75. .filter(model.Ticket.id == message['issue']['id'],
  76. model.Ticket.project_id == message['project']['id']) \
  77. .one_or_none()
  78. tracker_url = project.uri + '/issues'
  79. # Check if this tracker is only used to interact with a remote one
  80. remote_tracker = forgefed_graph.value(rdflib.URIRef(tracker_url),
  81. rdflib.OWL.sameAs)
  82. if remote_tracker:
  83. log.debug('Sending new comment to remote tracker...')
  84. assert ticket.remote_uri, 'Ticket not linked to remote ticket.'
  85. # Notify the remote tracker about the new comment
  86. person.create(comment.jsonld, to=remote_tracker)
  87. else:
  88. log.debug('Not a remote tracker. Will send new comment to tracker followers '
  89. + 'and watchers.')
  90. # Retrieve the pagure "watchlist"
  91. watchlist = pagure.lib.query.get_watch_list(pagure_db, ticket)
  92. actors = []
  93. for username in watchlist:
  94. actor = pagure_db \
  95. .query(model.Person) \
  96. .filter(model.Person.user == username) \
  97. .one_or_none()
  98. actors.append(actor.uri)
  99. # Send the Activity
  100. person.create(comment.jsonld, to=actors)
  101. if topic == 'issue.edit':
  102. # A user has edited an issue
  103. # The Ticket that was modified
  104. ticket = pagure_db \
  105. .query(model.Ticket) \
  106. .filter(model.Ticket.id == message['issue']['id'],
  107. model.Ticket.project_id == message['project']['id']) \
  108. .one_or_none()
  109. # The user that edited the Ticket
  110. person = pagure_db \
  111. .query(model.Person) \
  112. .filter(model.Person.user == message['agent']) \
  113. .one_or_none()
  114. project = pagure_db \
  115. .query(model.Projects) \
  116. .filter(model.Projects.id == message['project']['id']) \
  117. .one_or_none()
  118. # Let's fetch the JSONLD object of the Ticket that has been edited
  119. ticket_jsonld = activitypub.fetch(ticket.uri)
  120. # Let's replace the fields that have changed
  121. if 'status' in message['fields']:
  122. status = message['fields']['status'].upper()
  123. if status == 'CLOSED':
  124. ticket_jsonld['status'] = 'Closed'
  125. if status == 'OPEN':
  126. ticket_jsonld['status'] = 'Open'
  127. if 'content' in message['fields']:
  128. ticket_jsonld['content'] = content
  129. # If the user has edited the Ticket of a remote tracker, we just
  130. # send the Activity to the tracker
  131. if ticket.is_remote:
  132. log.debug('Local user has edited the remote Ticket {}'.format(ticket_jsonld['id']))
  133. person.update(ticket_jsonld, to=project.uri)
  134. # otherwise we send the Activity to the Tickets' watchlist
  135. else:
  136. log.debug('Local user has edited the local Ticket {}'.format(ticket_jsonld['id']))
  137. # Retrieve the pagure "watchlist"
  138. watchlist = pagure.lib.query.get_watch_list(pagure_db, ticket)
  139. actors = []
  140. for username in watchlist:
  141. actor = pagure_db \
  142. .query(model.Person) \
  143. .filter(model.Person.user == username) \
  144. .one_or_none()
  145. actors.append(actor.uri)
  146. # Send the Activity
  147. person.update(ticket_jsonld, to=actors)