launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #05642
[Merge] lp:~wgrant/launchpad/observer-merging into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/observer-merging into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/observer-merging/+merge/82952
Building on the recently landed access policy model, this branch adds support to the branch deleter and person merger. Once these this lands, lp:~wgrant/launchpad/observer-db-2 can land to add the foreign key constraints.
This 4-stage process is necessary due to the requirement that schema changes be deployable without code changes.
--
https://code.launchpad.net/~wgrant/launchpad/observer-merging/+merge/82952
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/observer-merging into lp:launchpad.
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2011-11-17 03:30:34 +0000
+++ database/schema/security.cfg 2011-11-22 00:22:24 +0000
@@ -1980,6 +1980,7 @@
[person-merge-job]
groups=script
+public.accesspolicygrant = SELECT, UPDATE, DELETE
public.account = SELECT, UPDATE
public.announcement = SELECT, UPDATE
public.answercontact = SELECT, UPDATE, DELETE
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2011-09-27 00:57:13 +0000
+++ lib/lp/code/model/branch.py 2011-11-22 00:22:24 +0000
@@ -141,6 +141,7 @@
)
from lp.code.model.seriessourcepackagebranch import SeriesSourcePackageBranch
from lp.codehosting.safe_open import safe_open
+from lp.registry.interfaces.accesspolicy import IAccessPolicyArtifactSource
from lp.registry.interfaces.person import (
validate_person,
validate_public_person,
@@ -1125,6 +1126,7 @@
self._deleteBranchSubscriptions()
self._deleteJobs()
+ getUtility(IAccessPolicyArtifactSource).delete(self)
# Now destroy the branch.
branch_id = self.id
=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py 2011-11-14 20:14:44 +0000
+++ lib/lp/code/model/tests/test_branch.py 2011-11-22 00:22:24 +0000
@@ -114,6 +114,7 @@
from lp.codehosting.safe_open import BadUrl
from lp.registry.interfaces.person import PersonVisibility
from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.registry.model.accesspolicy import AccessPolicyArtifact
from lp.registry.model.sourcepackage import SourcePackage
from lp.services.osutils import override_environ
from lp.services.propertycache import clear_property_cache
@@ -1138,6 +1139,12 @@
branch.canBeDeleted(), True,
"A branch that has a import is deletable.")
+ def test_accessPolicyArtifactDoesntDisableDeletion(self):
+ """A branch referenced by an AccessPolicyArtifact can be deleted."""
+ artifact = self.factory.makeAccessPolicyArtifact(self.branch)
+ self.factory.makeAccessPolicyGrant(object=artifact)
+ self.assertEqual(True, self.branch.canBeDeleted())
+
def test_bugBranchLinkDisablesDeletion(self):
"""A branch linked to a bug cannot be deleted."""
params = CreateBugParams(
@@ -1272,6 +1279,16 @@
BuildQueue, BuildQueue.job == other_job.job)
self.assertNotEqual([], list(other_buildqueue))
+ def test_AccessPolicyArtifact_deleted(self):
+ # Any AccessPolicyArtifact referencing the branch is removed.
+ branch = self.factory.makeAnyBranch()
+ artifact = self.factory.makeAccessPolicyArtifact(branch)
+ self.factory.makeAccessPolicyGrant(object=artifact)
+ store = Store.of(branch)
+ branch.destroySelf()
+ self.assertContentEqual(
+ [], store.find(AccessPolicyArtifact, branch=branch))
+
def test_createsJobToReclaimSpace(self):
# When a branch is deleted from the database, a job to remove the
# branch from disk as well.
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py 2011-11-14 12:23:59 +0000
+++ lib/lp/registry/model/person.py 2011-11-22 00:22:24 +0000
@@ -3656,6 +3656,31 @@
skip.append(
(decorator_table.lower(), person_pointer_column.lower()))
+ def _mergeAccessPolicyGrant(self, cur, from_id, to_id):
+ # Update only the AccessPolicyGrants that will not conflict.
+ cur.execute('''
+ UPDATE AccessPolicyGrant
+ SET grantee=%(to_id)d
+ WHERE grantee = %(from_id)d AND (
+ policy NOT IN
+ (
+ SELECT policy
+ FROM AccessPolicyGrant
+ WHERE grantee = %(to_id)d
+ )
+ OR artifact NOT IN
+ (
+ SELECT artifact
+ FROM AccessPolicyGrant
+ WHERE grantee = %(to_id)d
+ )
+ )
+ ''' % vars())
+ # and delete those left over.
+ cur.execute('''
+ DELETE FROM AccessPolicyGrant WHERE grantee = %(from_id)d
+ ''' % vars())
+
def _mergeBranches(self, from_person, to_person):
# This shouldn't use removeSecurityProxy.
branches = getUtility(IBranchCollection).ownedBy(from_person)
@@ -4209,6 +4234,9 @@
% vars())
skip.append(('gpgkey', 'owner'))
+ self._mergeAccessPolicyGrant(cur, from_id, to_id)
+ skip.append(('accesspolicygrant', 'grantee'))
+
# Update the Branches that will not conflict, and fudge the names of
# ones that *do* conflict.
self._mergeBranches(from_person, to_person)
=== modified file 'lib/lp/registry/tests/test_person.py'
--- lib/lp/registry/tests/test_person.py 2011-11-21 04:08:34 +0000
+++ lib/lp/registry/tests/test_person.py 2011-11-22 00:22:24 +0000
@@ -61,6 +61,7 @@
from lp.registry.interfaces.personnotification import IPersonNotificationSet
from lp.registry.interfaces.pocket import PackagePublishingPocket
from lp.registry.interfaces.product import IProductSet
+from lp.registry.model.accesspolicy import AccessPolicyGrant
from lp.registry.model.karma import (
KarmaCategory,
KarmaTotalCache,
@@ -1185,6 +1186,41 @@
dsp = self.factory.makeDistributionSourcePackage()
self.assertConflictingSubscriptionDeletes(dsp)
+ def test_merge_accesspolicygrants(self):
+ # AccessPolicyGrants are transferred from the duplicate.
+ person = self.factory.makePerson()
+ grant = self.factory.makeAccessPolicyGrant()
+ self._do_premerge(grant.grantee, person)
+ with person_logged_in(person):
+ self._do_merge(grant.grantee, person)
+ self.assertEqual(person, grant.grantee)
+
+ def test_merge_accesspolicygrants_conflicts(self):
+ # Conflicting AccessPolicyGrants are deleted.
+ policy = self.factory.makeAccessPolicy()
+
+ person = self.factory.makePerson()
+ person_grantor = self.factory.makePerson()
+ person_grant = self.factory.makeAccessPolicyGrant(
+ grantee=person, grantor=person_grantor, object=policy)
+
+ duplicate = self.factory.makePerson()
+ duplicate_grantor = self.factory.makePerson()
+ duplicate_grant = self.factory.makeAccessPolicyGrant(
+ grantee=duplicate, grantor=duplicate_grantor, object=policy)
+
+ self._do_premerge(duplicate, person)
+ with person_logged_in(person):
+ self._do_merge(duplicate, person)
+ transaction.commit()
+
+ self.assertEqual(person, person_grant.grantee)
+ self.assertEqual(person_grantor, person_grant.grantor)
+ self.assertIs(
+ None,
+ IStore(AccessPolicyGrant).get(
+ AccessPolicyGrant, duplicate_grant.id))
+
def test_mergeAsync(self):
# mergeAsync() creates a new `PersonMergeJob`.
from_person = self.factory.makePerson()