project.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import celery
  2. import flask
  3. import pagure
  4. import requests
  5. import urllib.parse
  6. from .. import activitypub
  7. from .. import model
  8. from .. import settings
  9. from . import broker
  10. from . import broker_url
  11. from . import database_session
  12. log = celery.utils.log.get_task_logger(__name__)
  13. log.setLevel(settings.LOG_LEVEL)
  14. @broker.task
  15. def handle_incoming_activity(project_id, activity):
  16. """
  17. A person has received a new Activity in its INBOX. This task is scheduled
  18. in order to triage the activity and decide how the actor should react to it.
  19. """
  20. activity = activitypub.Document(activity)
  21. with database_session() as (pagure_db, forgefed_db):
  22. project = pagure_db.query(model.Project) \
  23. .filter(model.Project.id == project_id) \
  24. .one_or_none()
  25. log.debug('Project {} now handling Activity {}'.format(project.local_uri, activity['id']))
  26. if not project:
  27. log.info('Actor id(' + project_id + ') doesn\'t exist. Incoming '
  28. 'Activity will be ignored.')
  29. return
  30. if activity['type'] == 'Create':
  31. """
  32. The Project has received a Create Activity.
  33. """
  34. # Dereference the actor
  35. # actor = activitypub.fetch(activity['actor'])
  36. # Retrieve the object of the Activity
  37. object = activity.node('object')
  38. if object['type'] == 'Note':
  39. log.debug('Somebody has Created a new Note.')
  40. # We're only interested in Notes that have a "context" property
  41. # because that contains the URI of the Ticket or MergeRequest
  42. if 'context' not in object:
  43. log.debug('The Note does not contain a "context". Ignoring.')
  44. return
  45. model.test_or_set_remote_comment(pagure_db, forgefed_db, object['id'])
  46. # DONE
  47. if activity['type'] == 'Follow':
  48. """
  49. Actor has received a Follow Aactivity. Somebody wants to follow this Project?
  50. """
  51. if activity['object'] != project.local_uri:
  52. log.info('Actor ' + project.local_uri + ' has received a Follow '
  53. 'request but the "object" is not this actor. '
  54. 'Activity will be ignored.')
  55. return
  56. # Check if the remote actor is already following the local actor
  57. if forgefed_db.query(
  58. forgefed_db.query(database.Collection) \
  59. .filter(database.Collection.uri == project.followers_uri) \
  60. .filter(database.Collection.item == activity['actor']) \
  61. .exists()
  62. ).scalar():
  63. log.info('Actor {} is already following {}. Will ignore Follow request.'.format(project.local_uri, activity['actor']))
  64. return
  65. # Add the remote object to our followers collection
  66. forgefed_db.add(database.Collection(uri = project.followers_uri, item = activity['actor']))
  67. # Commit before accepting the Activity because we want the new
  68. # follower in the Followers collection before sending out the
  69. # Accept Activity.
  70. forgefed_db.commit()
  71. # Automatically accept Follow requests.
  72. project.accept(activity)
  73. if activity['type'] == 'Offer':
  74. """
  75. Somebody is offering something to the Project, for example a Ticket.
  76. """
  77. # Retrieve the object of the Offer
  78. object = activity.node('object')
  79. if object['type'] == 'Ticket':
  80. log.debug('Request to create a new Ticket.')
  81. # Make sure we have a local actor representing the remote actor
  82. actor = model.test_or_set_remote_actor(pagure_db, forgefed_db, activity['actor'])
  83. # Create a new local issue in Pagure
  84. issue = pagure.lib.query.new_issue(
  85. pagure_db,
  86. project,
  87. object['summary'],
  88. object['content'],
  89. actor.username)
  90. # Send an "Accept" request to notify the remote actor that the
  91. # ticket was created
  92. issue_uri = '{}/issue/{}'.format(project.local_uri, issue.id)
  93. project.accept(
  94. activity,
  95. to=activity['actor'],
  96. result=issue_uri)
  97. if activity['type'] == 'Resolve' \
  98. and activity.node('object')['type'] == 'Ticket':
  99. """
  100. Somebody has resolved a Ticket.
  101. """
  102. # Retrieve the Ticket object
  103. ticket = model.from_local_uri(pagure_db, activity['object']['id'])
  104. if not ticket:
  105. log.debug('Ticket is not local: {}'.format(activity['object']['id']))
  106. return
  107. # Create a new object (or retrieve existing one) for the remote user
  108. actor = model.test_or_set_remote_actor(pagure_db, forgefed_db, activity['actor'])
  109. log.debug('Ticket resolved: {}'.format(activity['object']['id']))
  110. # Edit the pagure Issue object
  111. pagure.lib.query.edit_issue(
  112. session=pagure_db,
  113. issue=ticket,
  114. user=actor.username,
  115. status='Closed')