1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201 |
- """
- ForgeFed plugin for Pagure.
- Copyright (C) 2020-2021 zPlus <zplus@peers.community>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with this program; if not, see <https://www.gnu.org/licenses/>.
- SPDX-FileCopyrightText: 2020-2021 zPlus <zplus@peers.community>
- SPDX-License-Identifier: GPL-2.0-only
- """
- import celery
- import json
- import pagure.lib.query
- import rdflib
- import re
- import requests
- import requests_http_signature
- from .. import APP_URL
- from .. import activitypub
- from .. import feeds
- from .. import model
- from .. import settings
- from . import broker_url
- from . import broker
- from . import database_session
- log = celery.utils.log.get_task_logger(__name__)
- log.setLevel(settings.LOG_LEVEL)
- # The following is a decorator that accepts a dictionary as input and adds it to
- # _PATTERNS_ together with the decorated function. The dictionary is used as a
- # pattern for matching incoming Activities. If an incoming Activity matches one
- # of the patters in _PATTERNS_, then the corresponding function is executed.
- _PATTERNS_ = []
- def pattern(activity_pattern):
- def closure(func):
- def decorator(*args, **kwargs):
- func(*args, **kwargs)
- global _PATTERNS_
- _PATTERNS_.append((activity_pattern, decorator))
- return decorator
- return closure
- # The firs one is used to match *any* value in the Activity, or in other words to
- # make sure that the key exists irregardless of its value.
- # The other two are used to match a local or remote object only
- _ = activitypub.Document.AnyType()
- _remote = re.compile('^(?!'+APP_URL+')')
- _local = re.compile('^'+APP_URL)
- @broker.task
- def perform(activity):
- """
- This task is responsible for accepting an incoming Activity after it's been
- validated (see tasks.delivery) and decide what to do with it.
- :param activity: the Activity that was sent to the Actor
- """
- activity = activitypub.Activity.from_dict(activity)
- for activity_pattern, function in _PATTERNS_:
- if activity.match(activity_pattern):
- with database_session() as database:
- return function(database, activity)
- log.debug('Activity did not match any pattern. Ignoring incoming Activity: {}'.format(
- json.dumps(activity['id'], indent=4, sort_keys=True)))
- # Repository
- # -----------------------------------------------------------------------------
- @pattern({
- 'type': 'Create',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'TagRef',
- 'id': _,
- 'name': _,
- 'context': {
- 'type': 'Repository',
- 'id': _
- }
- },
- })
- def create_repository_tag(database, activity):
- """
- Somebody has Created a Tag reference in a repository.
- """
- log.debug('Handling incoming Create(TagRef) Activity.')
- tagref = activity['object']
- repository = tagref['context']
- # Get the list of Actors that are Following the Repository
- items = database \
- .query(model.Collection) \
- .filter(model.Collection.uri == repository['followers']) \
- .all()
- # Only the local Actor
- followers = [ result.item for result in items if result.item.startswith(APP_URL) ]
- # Add a feed to recipients
- for follower in followers:
- actor = model.from_uri(database, follower)
- if not actor:
- continue
- database.add(model.Feed(actor.uri, feeds.create_tagref(repository, tagref)))
- # Follow
- # -----------------------------------------------------------------------------
- @pattern({
- 'type': 'Follow',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'id': _
- },
- })
- def follow(database, activity):
- """
- An Actor has Followed another Actor.
- """
- log.debug('Handling incoming Follow Activity.')
- followed = model.from_uri(database, activity['object']['id'])
- if not followed:
- log.debug('Followed Actor {} does not exist on this instance.'.format(followed.uri))
- return
- if followed.is_remote:
- log.debug('Ignoring Follow Activity {} received for a remote Actor {}'.format(
- activity['id'], activity['object']['id']))
- return
- # Check if the local actor is already following the remote actor
- if database.query(
- database.query(model.Collection) \
- .filter(model.Collection.uri == activity['object']['following']) \
- .filter(model.Collection.item == activity['actor']['id']) \
- .exists()
- ).scalar():
- log.info('Actor {} is already following {}. Ignoring Follow request.'.format(followed.uri, follower.uri))
- return
- # Automatically Accept any Follow request
- if followed.is_local:
- # Update the followers/following collections
- database.merge(model.Collection(
- uri = activity['object']['following'],
- item = activity['actor']['id']))
- database.merge(model.Collection(
- uri = activity['actor']['following'],
- item = activity['object']['id']))
- database.merge(model.Collection(
- uri = activity['object']['followers'],
- item = activity['actor']['id']))
- database.merge(model.Collection(
- uri = activity['actor']['followers'],
- item = activity['object']['id']))
- # Send the Accept Activity
- activitypub.Activity(
- type = 'Accept',
- actor = followed.uri,
- object = activity['id']
- ).distribute()
- # Cache the remote Actor
- database.merge(model.Resource(
- uri = activity['actor']['id'],
- document = json.dumps(activity['actor'])))
- # Add a feed
- database.add(model.Feed(followed.uri, feeds.follow(activity['actor'], activity['object'])))
- database.add(model.Feed(followed.uri, feeds.accept_follow(activity['actor'], activity['object'])))
- @pattern({
- 'type': 'Accept',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Follow',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'id': _
- }
- },
- })
- def accept_follow(database, activity):
- """
- Somebody has Accepted of a Follow request.
- """
- log.debug('Handling incoming Accept(Follow) Activity.')
- follow = activity['object']
- if follow['object']['id'] != activity['actor']['id']:
- log.debug('Only target Actor can Accept a Follow request.\n{}'.format(json.dumps(activity, indent=4)))
- return
- log.debug('{} has accepted Follow request from {}'.format(
- follow['object']['id'], follow['actor']['id']))
- # We cache a copy of the Actors for quick lookups. This is used for
- # example when listing "following/followers" collections. If we only
- # stored the actors' URI we would need to GET the JSON data every time
- # for getting its name.
- database.merge(model.Resource(
- uri = follow['actor']['id'],
- document = json.dumps(activity['actor'])))
- database.merge(model.Resource(
- uri = follow['object']['id'],
- document = json.dumps(follow['object'])))
- # Update the followers/following collections
- database.merge(model.Collection(
- uri = follow['actor']['following'],
- item = follow['object']['id']))
- database.merge(model.Collection(
- uri = follow['object']['following'],
- item = follow['actor']['id']))
- database.merge(model.Collection(
- uri = follow['actor']['followers'],
- item = follow['object']['id']))
- database.merge(model.Collection(
- uri = follow['object']['followers'],
- item = follow['actor']['id']))
- # If the follower is local
- if follow['actor']['id'].startswith(APP_URL):
- database.add(model.Feed(follow['actor']['id'],
- feeds.accept_follow(follow['actor'], follow['object'])))
- # Ticket
- # -----------------------------------------------------------------------------
- @pattern({
- 'type': 'Create',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Ticket',
- 'id': _,
- 'context': _,
- 'attributedTo': _,
- 'summary': _,
- 'content': _
- },
- })
- def create_ticket(database, activity):
- """
- Remote user has created a new Ticket for one of our TicketTracker.
- """
- log.debug('Handling incoming Create(Ticket) Activity.')
- ticket = activity['object']
- ticket_tracker = model.from_uri(database, ticket['context'])
- if not ticket_tracker:
- log.debug('Received a new Ticket for nonexistent TicketTracker {}'.format(ticket['context']))
- return
- log.debug('Request to create a new Ticket.')
- # Make sure we have a local actor representing the remote actor
- actor = model.Person.test_or_set(database, ticket['attributedTo'])
- # Create a new local issue in Pagure
- issue = pagure.lib.query.new_issue(
- database,
- ticket_tracker,
- ticket['summary'],
- ticket['content'],
- actor.username)
- if ticket_tracker.is_local:
- # Send an "Accept" request to notify the Actor that the Ticket was
- # Accepted (ticket are accepted automatically)
- activitypub.Activity(
- type = 'Accept',
- actor = ticket_tracker.local_uri,
- object = activity['id'],
- result = issue.uri
- ).distribute()
- else:
- # If it's a remote issue, we just need to keep track of it (we don't
- # need to Accept anything)
- issue.remote_uri = ticket['id']
- @pattern({
- 'type': 'Accept',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Create',
- 'id': _,
- 'actor': {},
- 'object': {
- 'type': 'Ticket',
- 'id': _,
- 'context': _,
- 'attributedTo': _,
- 'summary': _,
- 'content': _
- }
- },
- 'result': _
- })
- def accept_ticket(database, activity):
- """
- A Ticket was Accepted.
- """
- log.debug('Handling incoming Accept(Create(Ticket)) Activity.')
- ticket = activity['object']['object']
- database.add(model.SameAs(
- local_uri = ticket['id'],
- remote_uri = activity['result']
- ))
- database.add(model.Feed(ticket['attributedTo'], feeds.accept_ticket(activity['actor'], ticket)))
- @pattern({
- 'type': 'Update',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Ticket',
- 'id': _local
- },
- 'result': {}
- })
- def update_local_ticket(database, activity):
- """
- Remote user has updated a Ticket belonging to this instance.
- """
- log.debug('Handling incoming Update(Ticket) Activity.')
- remote_ticket = activity['object']
- ticket = model.from_uri(database, remote_ticket['id'])
- if not ticket:
- log.debug('Ticket does not exist: {}'.format(remote_ticket['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- new_title = activity['result']['summary'] if 'summary' in activity['result'] else None
- new_content = activity['result']['content'] if 'content' in activity['result'] else None
- # Edit the pagure Issue object
- pagure.lib.query.edit_issue(
- session=database,
- issue=ticket,
- user=actor.username,
- title=new_title,
- content=new_content)
- log.debug('Ticket updated: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Update',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Ticket',
- 'id': _remote
- },
- 'result': {}
- })
- def update_remote_ticket(database, activity):
- """
- Remote user has updated a remote Ticket not belonging to this instance.
- """
- log.debug('Handling incoming Update(Ticket) Activity.')
- remote_ticket = activity['object']
- ticket = model.from_uri(database, remote_ticket['id'])
- if not ticket:
- log.debug('Ticket {} does not exist on this instance.'.format(remote_ticket['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- new_title = activity['result']['summary'] if 'summary' in activity['result'] else None
- new_content = activity['result']['content'] if 'content' in activity['result'] else None
- # Edit the pagure Issue object
- pagure.lib.query.edit_issue(
- session=database,
- issue=ticket,
- user=actor.username,
- title=new_title,
- content=new_content)
- log.debug('Ticket updated: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Create',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Note',
- 'id': _remote,
- 'context': {
- 'id': _,
- 'type': 'Ticket'
- },
- 'attributedTo': {
- 'id': _
- },
- 'content': _
- },
- })
- def create_ticket_comment(database, activity):
- """
- Remote user has created a new TicketComment for one of our TicketTracker.
- """
- log.debug('Handling incoming Create(Note(Ticket)) Activity.')
- note = activity['object']
- # Check if we already have a local object for this remote comment
- if database.query(
- database.query(model.SameAs)
- .filter(model.SameAs.remote_uri == note['id']).exists()
- ).scalar():
- log.debug('The note {} is already stored in the database. Will not create a new one.'.format(note['id']))
- return
- author = model.Person.test_or_set(database, note['attributedTo']['id'])
- # Check if there is any local Ticket in the pagure database that is used
- # to track this Ticket
- ticket = model.from_uri(database, note['context']['id'])
- if not ticket:
- log.debug('No local Ticket for {}. Ignoring Note.'.format(note['context']['id']))
- return
- # Create the new comment to the ticket
- pagure.lib.query.add_issue_comment(
- session = database,
- issue = ticket,
- comment = note['content'],
- user = author.username)
- @pattern({
- 'type': 'Resolve',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Ticket',
- 'id': _local
- },
- })
- def resolve_local_ticket(database, activity):
- """
- Remote user has closed a Ticket belonging to this instance.
- """
- log.debug('Handling incoming Resolve(Ticket) Activity.')
- ticket = model.from_uri(database, activity['object']['id'])
- if not ticket:
- log.debug('Ticket does not exist: {}'.format(activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Edit the pagure Issue object
- pagure.lib.query.edit_issue(
- session=database,
- issue=ticket,
- user=actor.username,
- status='Closed')
- log.debug('Ticket resolved: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Resolve',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Ticket',
- 'id': _remote
- },
- })
- def resolve_remote_ticket(database, activity):
- """
- Remote user has closed a remote Ticket not belonging to this instance.
- """
- log.debug('Handling incoming Resolve(Ticket) Activity.')
- ticket = model.from_uri(database, activity['object']['id'])
- # Nothing to do because we don't have any local TicketTracker tracking
- # this remote Ticket
- if not ticket:
- log.debug('Untracked Ticket resolved: {}'.format(activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Edit the pagure Issue object
- pagure.lib.query.edit_issue(
- session=database,
- issue=ticket,
- user=actor.username,
- status='Closed')
- log.debug('Ticket resolved: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Reopen',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Ticket',
- 'id': _local
- },
- })
- def reopen_local_ticket(database, activity):
- """
- Remote user has reopened a Ticket belonging to this instance.
- """
- log.debug('Handling incoming Reopen(Ticket) Activity.')
- ticket = model.from_uri(database, activity['object']['id'])
- if not ticket:
- log.debug('Ticket does not exist: {}'.format(activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Edit the pagure Issue object
- pagure.lib.query.edit_issue(
- session=database,
- issue=ticket,
- user=actor.username,
- status='Open')
- log.debug('Ticket reopened: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Reopen',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Ticket',
- 'id': _remote
- },
- })
- def reopen_remote_ticket(database, activity):
- """
- Remote user has reopened a remote Ticket not belonging to this instance.
- """
- log.debug('Handling incoming Reopen(Ticket) Activity.')
- ticket = model.from_uri(database, activity['object']['id'])
- # Nothing to do because we don't have any local TicketTracker tracking
- # this remote Ticket
- if not ticket:
- log.debug('Untracked Ticket reopened: {}'.format(activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Edit the pagure Issue object
- pagure.lib.query.edit_issue(
- session=database,
- issue=ticket,
- user=actor.username,
- status='Open')
- log.debug('Ticket reopened: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Delete',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': _,
- 'origin': {
- 'id': _,
- 'type': 'TicketTracker'
- }
- })
- def delete_ticket(database, activity):
- """
- Remote user has deleted a remote Ticket not belonging to this instance.
- """
- log.debug('Handling incoming Delete(Ticket) Activity.')
- object = activity['object']
- if object.startswith(APP_URL):
- log.debug('Actor not allowed to delete local Ticket {}'.format(ticket.uri))
- return
- # The Actor that has deleted the Ticket
- activity_actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Retrieve the local URI of the object
- same_as = database \
- .query(model.SameAs) \
- .filter(model.SameAs.remote_uri == object) \
- .all()
- uri_list = [ row.local_uri for row in same_as ]
- # Delete all remote_uri from the SameAs table
- database \
- .query(model.SameAs) \
- .filter(model.SameAs.remote_uri == object) \
- .delete()
- for local_uri in uri_list:
- ticket = model.from_uri(database, local_uri)
- if not ticket:
- continue
- pagure.lib.query.drop_issue(database, ticket, activity_actor.username)
- log.debug('Deleted Ticket {}'.format(local_uri))
- # MergeRequest
- # -----------------------------------------------------------------------------
- @pattern({
- 'type': 'Create',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _,
- 'attributedTo': _,
- 'upstream': {
- 'branch': _,
- 'repository': {
- 'id': _local,
- 'type': 'Repository'
- }
- },
- 'downstream': {
- 'branch': _,
- 'repository': {
- 'id': _,
- 'type': 'Repository'
- }
- },
- 'summary': _,
- 'content': _,
- 'commit_start': _,
- 'commit_stop': _,
- },
- })
- def create_merge_request(database, activity):
- """
- Remote user has created a new MergeRequest for one of our Repositories.
- """
- log.debug('Handling incoming Create(MergeRequest) Activity.')
- mergerequest = activity['object']
- repository = model.from_uri(database, mergerequest['upstream']['repository']['id'])
- if not repository:
- log.debug('Received a new MergeRequest for nonexistent Repository {}'.format(mergerequest['upstream']['id']))
- return
- log.debug('Request to create a new MergeRequest.')
- # First of all, let's pull down the remote repository (synchronously)
- pagure.lib.tasks.pull_remote_repo.apply(kwargs={
- 'remote_git': mergerequest['downstream']['repository']['id'],
- 'branch_from': mergerequest['downstream']['branch']
- })
- # Make sure we have a local actor representing the remote actor
- actor = model.Person.test_or_set(database, mergerequest['attributedTo'])
- # Create a new local PullRequest in Pagure
- mergerequest = pagure.lib.query.new_pull_request(
- session=database,
- branch_from=mergerequest['downstream']['branch'],
- repo_to=repository,
- branch_to=mergerequest['upstream']['branch'],
- title=mergerequest['summary'],
- user=actor.username,
- initial_comment=mergerequest['content'],
- repo_from=None,
- remote_git=mergerequest['downstream']['repository']['id'],
- commit_start=mergerequest['commit_start'],
- commit_stop=mergerequest['commit_stop'])
- # Send an "Accept" request to notify the remote actor that the
- # MergeRequest was accepted
- activitypub.Activity(
- type = 'Accept',
- actor = repository.local_uri,
- object = activity['id'],
- result = mergerequest.uri
- ).distribute()
- @pattern({
- 'type': 'Accept',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Create',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _,
- 'attributedTo': _,
- 'upstream': {
- 'branch': _,
- 'repository': {
- 'id': _,
- 'type': 'Repository'
- }
- },
- 'downstream': {
- 'branch': _,
- 'repository': {
- 'id': _local,
- 'type': 'Repository'
- }
- },
- },
- },
- 'result': _
- })
- def accept_merge_request(database, activity):
- """
- A MergeRequest was Accepted.
- """
- log.debug('Handling incoming Accept(Create(MergeRequest)) Activity.')
- mergerequest = activity['object']['object']
- database.add(model.SameAs(
- local_uri = mergerequest['id'],
- remote_uri = activity['result']
- ))
- database.add(model.Feed(mergerequest['attributedTo'],
- feeds.accept_mergerequest(mergerequest)))
- @pattern({
- 'type': 'Update',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _local
- },
- 'result': {}
- })
- def update_local_mergerequest(database, activity):
- """
- Remote user has updated a MergeRequest belonging to this instance.
- """
- log.debug('Handling incoming Update(MergeRequest) Activity.')
- mergerequest_jsonld = activity['object']
- mergerequest = model.from_uri(database, mergerequest_jsonld['id'])
- if not mergerequest:
- log.debug('MergeRequest does not exist: {}'.format(mergerequest_jsonld['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- new_title = activity['result']['summary'] if 'summary' in activity['result'] else None
- new_content = activity['result']['content'] if 'content' in activity['result'] else None
- # Edit the pagure PullRequest object
- mergerequest.title = new_title
- mergerequest.initial_comment = new_content
- log.debug('MergeRequest updated: {}'.format(mergerequest_jsonld['id']))
- @pattern({
- 'type': 'Update',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _remote
- },
- 'result': {}
- })
- def update_remote_mergerequest(database, activity):
- """
- Remote user has updated a remote MergeRequest not belonging to this instance.
- """
- log.debug('Handling incoming Update(MergeRequest) Activity.')
- mergerequest_jsonld = activity['object']
- mergerequest = model.from_uri(database, mergerequest_jsonld['id'])
- if not mergerequest:
- log.debug('MergeRequest does not exist: {}'.format(mergerequest_jsonld['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- new_title = activity['result']['summary'] if 'summary' in activity['result'] else None
- new_content = activity['result']['content'] if 'content' in activity['result'] else None
- # Edit the pagure PullRequest object
- mergerequest.title = new_title
- mergerequest.initial_comment = new_content
- log.debug('MergeRequest updated: {}'.format(mergerequest_jsonld['id']))
- @pattern({
- 'type': 'Resolve',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _local
- },
- })
- def merge_local_mergerequest(database, activity):
- """
- Remote user has merged a MergeRequest belonging to this instance.
- """
- pass
- @pattern({
- 'type': 'Resolve',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _remote,
- },
- })
- def merge_remote_mergerequest(database, activity):
- """
- Remote user has merged a remote MergeRequest not belonging to this instance.
- """
- log.debug('Handling incoming Resolve(MergeRequest) Activity.')
- mergerequest = model.from_uri(database, activity['object']['id'])
- # Nothing to do because we don't have any local MergeRequest tracking
- # this remote object
- if not mergerequest:
- log.debug('Untracked MergeRequest has been merged: {}'.format(activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Edit the pagure PullRequest object
- pagure.lib.query.close_pull_request(
- session=database,
- request=mergerequest,
- user=actor.username,
- merged=True)
- log.debug('MergeRequest merged: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Close',
- 'id': _,
- 'actor': {
- 'id': _remote
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _local
- },
- })
- def close_local_mergerequest(database, activity):
- """
- Remote user has closed a MergeRequest belonging to this instance.
- """
- log.debug('Handling incoming Close(MergeRequest) Activity.')
- mergerequest = model.from_uri(database, activity['object']['id'])
- # Nothing to do because we don't have any local MergeRequest with this URI
- if not mergerequest:
- log.debug('Remote user {} tried to close nonexistent MergeRequest {}'.format(activity['actor']['id'], activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Check if the remote actor has the right to close the MergeRequest
- if mergerequest.user.remote_uri != actor.remote_uri:
- log.debug('Remote actor {} cannot close local MergeRequest {}'.format(actor.remote_uri, mergerequest.local_uri))
- return
- # Edit the pagure PullRequest object
- pagure.lib.query.close_pull_request(
- session=database,
- request=mergerequest,
- user=actor.username,
- merged=False)
- log.debug('MergeRequest closed: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Close',
- 'id': _,
- 'actor': {
- 'id': _remote
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _remote
- },
- })
- def close_remote_mergerequest(database, activity):
- """
- Remote user has closed a remote MergeRequest not belonging to this instance.
- """
- log.debug('Handling incoming Close(MergeRequest) Activity.')
- mergerequest = model.from_uri(database, activity['object']['id'])
- # Nothing to do because we don't have any local MergeRequest tracking
- # this remote object
- if not mergerequest:
- log.debug('Untracked MergeRequest has been closed: {}'.format(activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Edit the pagure PullRequest object
- pagure.lib.query.close_pull_request(
- session=database,
- request=mergerequest,
- user=actor.username,
- merged=False)
- log.debug('MergeRequest closed: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Reopen',
- 'id': _,
- 'actor': {
- 'id': _remote,
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _local
- },
- })
- def reopen_local_mergerequest(database, activity):
- """
- Remote user has reopened a MergeRequest belonging to this instance.
- """
- log.debug('Handling incoming Reopen(MergeRequest) Activity.')
- mergerequest = model.from_uri(database, activity['object']['id'])
- # Nothing to do because we don't have any local MergeRequest with this URI
- if not mergerequest:
- log.debug('Remote user {} tried to reopen nonexistent MergeRequest {}'.format(activity['actor']['id'], activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Check if the remote actor has the right to reopen the MergeRequest
- if mergerequest.user.remote_uri != actor.remote_uri:
- log.debug('Remote actor {} cannot reopen local MergeRequest {}'.format(actor.remote_uri, mergerequest.local_uri))
- return
- # Edit the pagure PullRequest object
- pagure.lib.query.reopen_pull_request(
- session=database,
- request=mergerequest,
- user=actor.username)
- log.debug('MergeRequest reopened: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Reopen',
- 'id': _,
- 'actor': {
- 'id': _remote
- },
- 'object': {
- 'type': 'MergeRequest',
- 'id': _remote
- },
- })
- def reopen_remote_mergerequest(database, activity):
- """
- Remote user has reopened a remote MergeRequest not belonging to this instance.
- """
- log.debug('Handling incoming Reopen(MergeRequest) Activity.')
- mergerequest = model.from_uri(database, activity['object']['id'])
- # Nothing to do because we don't have any local MergeRequest tracking
- # this remote object
- if not mergerequest:
- log.debug('Untracked MergeRequest has been reopened: {}'.format(activity['object']['id']))
- return
- actor = model.Person.test_or_set(database, activity['actor']['id'])
- # Edit the pagure PullRequest object
- pagure.lib.query.reopen_pull_request(
- session=database,
- request=mergerequest,
- user=actor.username)
- log.debug('MergeRequest reopened: {}'.format(activity['object']['id']))
- @pattern({
- 'type': 'Create',
- 'id': _,
- 'actor': {
- 'id': _
- },
- 'object': {
- 'type': 'Note',
- 'id': _remote,
- 'context': {
- 'type': 'MergeRequest',
- 'id': _
- },
- 'attributedTo': {
- 'id': _
- },
- 'content': _
- },
- })
- def create_mergerequest_comment(database, activity):
- """
- Remote user has created a new TicketComment for one of our TicketTracker.
- """
- log.debug('Handling incoming Create(Note(MergeRequest)) Activity.')
- note = activity['object']
- # Check if we already have a local object for this remote comment
- if database.query(
- database.query(model.SameAs)
- .filter(model.SameAs.remote_uri == note['id']).exists()
- ).scalar():
- log.debug('The note {} is already stored in the database. Will not create a new one.'.format(note['id']))
- return
- author = model.Person.test_or_set(database, note['attributedTo']['id'])
- # If the "context" of the Note is a local URL, it means somebody has created a
- # Note for a Repository in our instance. This is the case for example when
- # a remote user is contributing a comment to a local project.
- if note['context']['id'].startswith(APP_URL):
- mergerequest = model.from_uri(database, note['context']['id'])
- # If the "context" of the Note is not a local Ticket, this note was created
- # for a remote object. This is the case for example when a user has sent a
- # comment to a remote ticket, and we have received the Activity notification
- # because we are following that ticket.
- else:
- # Check if there is any local Ticket in the pagure database that is used
- # to track this remote Ticket
- mergerequest = model.from_uri(database, note['context']['id'])
- # This is a new Note for a remote Ticket
- if not mergerequest:
- log.debug('No local MergeRequest for {}'.format(note['context']['id']))
- return
- # Create the new comment to the ticket
- pagure.lib.query.add_pull_request_comment(
- session=database,
- request=mergerequest,
- commit=None,
- tree_id=None,
- filename=None,
- row=None,
- comment=note['content'],
- user=author.username)
|