Merge ~pappacena/launchpad:mp-edit-url into launchpad:master

Proposed by Thiago F. Pappacena
Status: Work in progress
Proposed branch: ~pappacena/launchpad:mp-edit-url
Merge into: launchpad:master
Diff against target: 109 lines (+56/-1)
4 files modified
lib/lp/code/interfaces/gitref.py (+7/-0)
lib/lp/code/model/gitref.py (+6/-0)
lib/lp/code/xmlrpc/git.py (+11/-1)
lib/lp/code/xmlrpc/tests/test_git.py (+32/-0)
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+389990@code.launchpad.net

Commit message

Sending back on XML-RPC git the URL to currently open MP, if any

To post a comment you must log in.

Unmerged commits

0612249... by Thiago F. Pappacena

Sending back on XML-RPC git the URL to currently open MP, if any

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/code/interfaces/gitref.py b/lib/lp/code/interfaces/gitref.py
2index ef51d99..12fae27 100644
3--- a/lib/lp/code/interfaces/gitref.py
4+++ b/lib/lp/code/interfaces/gitref.py
5@@ -273,6 +273,13 @@ class IGitRefView(IHasMergeProposals, IHasRecipes, IPrivacy, IInformationType):
6 Target and prerequisite repositories are preloaded.
7 """
8
9+ def getOpenPrecachedLandingTargets(user):
10+ """Return precached landing targets, excluding those ones in final
11+ states.
12+
13+ Target and prerequisite repositories are preloaded.
14+ """
15+
16 def getPrecachedLandingCandidates(user):
17 """Return precached landing candidates.
18
19diff --git a/lib/lp/code/model/gitref.py b/lib/lp/code/model/gitref.py
20index f7dc142..07a6d09 100644
21--- a/lib/lp/code/model/gitref.py
22+++ b/lib/lp/code/model/gitref.py
23@@ -245,6 +245,12 @@ class GitRefMixin:
24 loader = partial(BranchMergeProposal.preloadDataForBMPs, user=user)
25 return DecoratedResultSet(self.landing_targets, pre_iter_hook=loader)
26
27+ def getOpenPrecachedLandingTargets(self, user):
28+ """See `IGitRef`."""
29+ final_states = BRANCH_MERGE_PROPOSAL_FINAL_STATES
30+ return self.getPrecachedLandingTargets(user).find(
31+ Not(BranchMergeProposal.queue_status.is_in(final_states)))
32+
33 @property
34 def _api_landing_targets(self):
35 return self.getPrecachedLandingTargets(getUtility(ILaunchBag).user)
36diff --git a/lib/lp/code/xmlrpc/git.py b/lib/lp/code/xmlrpc/git.py
37index 0c3a2f1..6991f17 100644
38--- a/lib/lp/code/xmlrpc/git.py
39+++ b/lib/lp/code/xmlrpc/git.py
40@@ -480,8 +480,18 @@ class GitAPI(LaunchpadXMLRPCView):
41 # but it doesn't make sense in the context of an internal service.
42 return None
43
44- # We assemble the URL this way here because the ref may not exist yet.
45 base_url = canonical_url(repository, rootsite='code')
46+
47+ # If we already have exactly one open MP for this branch,
48+ # we should suggest the user to continue editing this MP instead of
49+ # opening a new one.
50+ ref = repository.getRefByPath(six.text_type(branch))
51+ if ref is not None:
52+ mps = ref.getOpenPrecachedLandingTargets(requester)
53+ if mps.count() == 1:
54+ return "%s/+merge/%s/" % (base_url, mps[0].id)
55+
56+ # We assemble the URL this way here because the ref may not exist yet.
57 mp_url = "%s/+ref/%s/+register-merge" % (
58 base_url, quote(branch))
59 return mp_url
60diff --git a/lib/lp/code/xmlrpc/tests/test_git.py b/lib/lp/code/xmlrpc/tests/test_git.py
61index c3b8126..be81181 100644
62--- a/lib/lp/code/xmlrpc/tests/test_git.py
63+++ b/lib/lp/code/xmlrpc/tests/test_git.py
64@@ -29,6 +29,7 @@ from zope.security.proxy import removeSecurityProxy
65 from lp.app.enums import InformationType
66 from lp.buildmaster.enums import BuildStatus
67 from lp.code.enums import (
68+ BranchMergeProposalStatus,
69 GitGranteeType,
70 GitRepositoryStatus,
71 GitRepositoryType,
72@@ -1810,6 +1811,37 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory):
73 self.assertHasMergeProposalURL(repository, pushed_branch,
74 {"uid": requester_non_owner.id})
75
76+ def test_getMergeProposalURL_already_existing_open_mp(self):
77+ mp = self.factory.makeBranchMergeProposalForGit(
78+ set_state=BranchMergeProposalStatus.WORK_IN_PROGRESS)
79+ repository = mp.source_git_ref.repository
80+ requester_owner = mp.source_git_ref.repository.owner
81+ pushed_branch_name = mp.source_git_ref.name
82+
83+ base_url = canonical_url(repository, rootsite='code')
84+ result = self.git_api.getMergeProposalURL(
85+ repository.getInternalPath(), pushed_branch_name,
86+ {"uid": requester_owner.id})
87+
88+ expected_mp_url = "%s/+merge/%s/" % (base_url, mp.id)
89+ self.assertEqual(expected_mp_url, result)
90+
91+ def test_getMergeProposalURL_already_closed_mp(self):
92+ mp = self.factory.makeBranchMergeProposalForGit(
93+ set_state=BranchMergeProposalStatus.MERGED)
94+ repository = mp.source_git_ref.repository
95+ requester_owner = mp.source_git_ref.repository.owner
96+ pushed_branch_name = mp.source_git_ref.name
97+
98+ base_url = canonical_url(repository, rootsite='code')
99+ result = self.git_api.getMergeProposalURL(
100+ repository.getInternalPath(), pushed_branch_name,
101+ {"uid": requester_owner.id})
102+
103+ expected_mp_url = "%s/+ref/%s/+register-merge" % (
104+ base_url, quote(pushed_branch_name))
105+ self.assertEqual(expected_mp_url, result)
106+
107 def test_getMergeProposalURL_user_macaroon(self):
108 # The merge proposal URL is returned by LP for a non-default branch
109 # pushed by a user with a suitable macaroon that