Merge lp:~james-w/launchpad/export-code-import into lp:launchpad

Proposed by James Westby
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~james-w/launchpad/export-code-import
Merge into: lp:launchpad
Diff against target: 330 lines (+142/-45)
11 files modified
lib/canonical/launchpad/doc/canonical_url_examples.txt (+10/-0)
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+2/-0)
lib/lp/code/browser/branch.py (+5/-0)
lib/lp/code/browser/codeimport.py (+3/-3)
lib/lp/code/browser/configure.zcml (+5/-0)
lib/lp/code/doc/codeimport.txt (+4/-4)
lib/lp/code/interfaces/branch.py (+4/-2)
lib/lp/code/interfaces/codeimport.py (+48/-36)
lib/lp/code/interfaces/webservice.py (+1/-0)
lib/lp/code/stories/webservice/xx-branch.txt (+1/-0)
lib/lp/code/stories/webservice/xx-code-import.txt (+59/-0)
To merge this branch: bzr merge lp:~james-w/launchpad/export-code-import
Reviewer Review Type Date Requested Status
Tim Penhey (community) Approve
Review via email: mp+21472@code.launchpad.net

Commit message

Expose the main code import attributes read only.

Description of the change

This exports ICodeImport.

It's not intended to be all-singing, all-dancing, just export
the objects, we can do more in future branches.

Thanks,

James

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

I think this is a great start.

review: Approve
Revision history for this message
Francis J. Lacoste (flacoste) wrote :

On March 16, 2010, James Westby wrote:
 + owner = exported(
> + PublicPersonChoice(
> + title=_('Owner'), required=True, readonly=False,
> + vocabulary='ValidPersonOrTeam',
> + description=_("The community contact for this import.")))
>

> + review_status = exported(
> + Choice(
> + title=_("Review Status"), vocabulary=CodeImportReviewStatus,
> + default=CodeImportReviewStatus.NEW,
> + description=_("Before a code import is performed, it is
> reviewed." + " Only reviewed imports are processed.")))
> +

Do we know if the security wrapper were properly defined for the exported field?

> + date_last_successful = exported(
> + Datetime(title=_("Last successful"), required=False))
>

This should probably be a readonly field?

--
Francis J. Lacoste
<email address hidden>

Revision history for this message
James Westby (james-w) wrote :

> On March 16, 2010, James Westby wrote:
> + owner = exported(
> > + PublicPersonChoice(
> > + title=_('Owner'), required=True, readonly=False,
> > + vocabulary='ValidPersonOrTeam',
> > + description=_("The community contact for this import.")))
> >
>
> > + review_status = exported(
> > + Choice(
> > + title=_("Review Status"), vocabulary=CodeImportReviewStatus,
> > + default=CodeImportReviewStatus.NEW,
> > + description=_("Before a code import is performed, it is
> > reviewed." + " Only reviewed imports are processed.")))
> > +
>
> Do we know if the security wrapper were properly defined for the exported
> field?

I don't know what that means, so probably not.

> > + date_last_successful = exported(
> > + Datetime(title=_("Last successful"), required=False))
> >
>
> This should probably be a readonly field?

It is in the latest diff.

Thanks,

James

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/doc/canonical_url_examples.txt'
2--- lib/canonical/launchpad/doc/canonical_url_examples.txt 2010-03-12 18:28:28 +0000
3+++ lib/canonical/launchpad/doc/canonical_url_examples.txt 2010-03-17 21:52:39 +0000
4@@ -356,6 +356,16 @@
5 http://code.launchpad.dev/~name12/gnome-terminal/main/+merge/.../comments/...
6
7
8+== Code Imports ==
9+
10+Code imports have a canonical URL which is a subordinate of the branch
11+that they import to.
12+
13+ >>> from lp.code.interfaces.codeimport import ICodeImportSet
14+ >>> code_import = getUtility(ICodeImportSet).get(1)
15+ >>> print canonical_url(code_import)
16+ http://code.launchpad.dev/~vcs-imports/gnome-terminal/import/+code-import
17+
18 == Specifications ==
19
20 >>> from canonical.launchpad.interfaces import ISpecificationSet
21
22=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
23--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-03-16 05:09:55 +0000
24+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-03-17 21:52:39 +0000
25@@ -40,6 +40,7 @@
26 from lp.code.interfaces.branch import IBranch
27 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
28 from lp.code.interfaces.branchsubscription import IBranchSubscription
29+from lp.code.interfaces.codeimport import ICodeImport
30 from lp.code.interfaces.codereviewcomment import ICodeReviewComment
31 from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
32 from lp.code.interfaces.diff import IPreviewDiff
33@@ -95,6 +96,7 @@
34 patch_plain_parameter_type(
35 IBranch, 'setTarget', 'source_package', ISourcePackage)
36 patch_reference_property(IBranch, 'sourcepackage', ISourcePackage)
37+patch_reference_property(IBranch, 'code_import', ICodeImport)
38
39 IBranch['spec_links'].value_type.schema = ISpecificationBranch
40 IBranch['subscribe'].queryTaggedValue(
41
42=== modified file 'lib/lp/code/browser/branch.py'
43--- lib/lp/code/browser/branch.py 2010-02-24 13:37:51 +0000
44+++ lib/lp/code/browser/branch.py 2010-03-17 21:52:39 +0000
45@@ -181,6 +181,11 @@
46 if proposal.id == id:
47 return proposal
48
49+ @stepto("+code-import")
50+ def traverse_code_import(self):
51+ """Traverses to the `ICodeImport` for the branch."""
52+ return self.context.code_import
53+
54
55 class BranchEditMenu(NavigationMenu):
56 """Edit menu for IBranch."""
57
58=== modified file 'lib/lp/code/browser/codeimport.py'
59--- lib/lp/code/browser/codeimport.py 2010-03-08 19:27:29 +0000
60+++ lib/lp/code/browser/codeimport.py 2010-03-17 21:52:39 +0000
61@@ -435,9 +435,9 @@
62 class EditCodeImportForm(Interface):
63 """The fields presented on the form for editing a code import."""
64
65- use_template(
66- ICodeImport,
67- ['url', 'cvs_root', 'cvs_module'])
68+ url = copy_field(ICodeImport['url'], readonly=False)
69+ cvs_root = copy_field(ICodeImport['cvs_root'], readonly=False)
70+ cvs_module = copy_field(ICodeImport['cvs_module'], readonly=False)
71 whiteboard = copy_field(IBranch['whiteboard'])
72
73
74
75=== modified file 'lib/lp/code/browser/configure.zcml'
76--- lib/lp/code/browser/configure.zcml 2010-03-12 18:28:28 +0000
77+++ lib/lp/code/browser/configure.zcml 2010-03-17 21:52:39 +0000
78@@ -714,6 +714,11 @@
79 template="../templates/codeimport-new.pt"
80 permission="launchpad.AnyPerson"/>
81 </facet>
82+ <browser:url
83+ for="lp.code.interfaces.codeimport.ICodeImport"
84+ attribute_to_parent="branch"
85+ path_expression="string:+code-import"
86+ rootsite="code"/>
87 <browser:navigation
88 module="lp.code.browser.codeimport"
89 classes="
90
91=== modified file 'lib/lp/code/doc/codeimport.txt'
92--- lib/lp/code/doc/codeimport.txt 2010-03-09 12:01:49 +0000
93+++ lib/lp/code/doc/codeimport.txt 2010-03-17 21:52:39 +0000
94@@ -468,14 +468,14 @@
95 >>> print canonical_url(code_import_set)
96 http://code.launchpad.dev/+code-imports
97
98-The code imports themselves are viewed under the branch's URL,
99-and are not traversable in themselves.
100+The code imports themselves have a canonical URL that is subordinate of
101+the branches, though they cannot currently be viewed that way in the webapp,
102+only over the API.
103
104 >>> print canonical_url(svn_import.branch)
105 http://code.launchpad.dev/~no-priv/firefox/trunk-svn
106 >>> print canonical_url(svn_import)
107- Traceback (most recent call last):
108- NoCanonicalUrl: ...
109+ http://code.launchpad.dev/~no-priv/firefox/trunk-svn/+code-import
110
111
112 Modifying CodeImports
113
114=== modified file 'lib/lp/code/interfaces/branch.py'
115--- lib/lp/code/interfaces/branch.py 2010-03-10 18:58:02 +0000
116+++ lib/lp/code/interfaces/branch.py 2010-03-17 21:52:39 +0000
117@@ -855,8 +855,10 @@
118 browse_source_url = Attribute(
119 "The URL of the source browser for this branch.")
120
121- # Don't use Object -- that would cause an import loop with ICodeImport.
122- code_import = Attribute("The associated CodeImport, if any.")
123+ # Really ICodeImport, but that would cause a circular import
124+ code_import = exported(
125+ Reference(
126+ title=_("The associated CodeImport, if any."), schema=Interface))
127
128 bzr_identity = exported(
129 Text(
130
131=== modified file 'lib/lp/code/interfaces/codeimport.py'
132--- lib/lp/code/interfaces/codeimport.py 2010-03-05 03:51:59 +0000
133+++ lib/lp/code/interfaces/codeimport.py 2010-03-17 21:52:39 +0000
134@@ -23,6 +23,9 @@
135 from canonical.launchpad.validators import LaunchpadValidationError
136 from lp.code.enums import CodeImportReviewStatus, RevisionControlSystems
137
138+from lazr.restful.declarations import (
139+ export_as_webservice_entry, exported)
140+
141
142 def validate_cvs_root(cvsroot):
143 try:
144@@ -60,13 +63,17 @@
145 class ICodeImport(Interface):
146 """A code import to a Bazaar Branch."""
147
148+ export_as_webservice_entry()
149+
150 id = Int(readonly=True, required=True)
151 date_created = Datetime(
152 title=_("Date Created"), required=True, readonly=True)
153
154- branch = Choice(
155- title=_('Branch'), required=True, readonly=True, vocabulary='Branch',
156- description=_("The Bazaar branch produced by the import system."))
157+ branch = exported(
158+ Choice(
159+ title=_('Branch'), required=True, readonly=True,
160+ vocabulary='Branch',
161+ description=_("The Bazaar branch produced by the import system.")))
162
163 registrant = PublicPersonChoice(
164 title=_('Registrant'), required=True, readonly=True,
165@@ -94,39 +101,44 @@
166 description=_("The series this import is registered as the "
167 "code for, or None if there is no such series."))
168
169- review_status = Choice(
170- title=_("Review Status"), vocabulary=CodeImportReviewStatus,
171- default=CodeImportReviewStatus.NEW,
172- description=_("Before a code import is performed, it is reviewed."
173- " Only reviewed imports are processed."))
174-
175- rcs_type = Choice(title=_("Type of RCS"),
176- required=True, vocabulary=RevisionControlSystems,
177- description=_(
178- "The version control system to import from. "
179- "Can be CVS or Subversion."))
180-
181- url = URIField(title=_("URL"), required=False,
182- description=_("The URL of the VCS branch."),
183- allowed_schemes=["http", "https", "svn", "git"],
184- allow_userinfo=False, # Only anonymous access is supported.
185- allow_port=True,
186- allow_query=False, # Query makes no sense in Subversion.
187- allow_fragment=False, # Fragment makes no sense in Subversion.
188- trailing_slash=False) # See http://launchpad.net/bugs/56357.
189-
190- cvs_root = TextLine(title=_("Repository"), required=False,
191- constraint=validate_cvs_root,
192- description=_("The CVSROOT. "
193- "Example: :pserver:anonymous@anoncvs.gnome.org:/cvs/gnome"))
194-
195- cvs_module = TextLine(title=_("Module"), required=False,
196- constraint=validate_cvs_module,
197- description=_("The path to import within the repository."
198- " Usually, it is the name of the project."))
199-
200- date_last_successful = Datetime(
201- title=_("Last successful"), required=False)
202+ review_status = exported(
203+ Choice(
204+ title=_("Review Status"), vocabulary=CodeImportReviewStatus,
205+ default=CodeImportReviewStatus.NEW, readonly=True,
206+ description=_("Before a code import is performed, it is reviewed."
207+ " Only reviewed imports are processed.")))
208+
209+ rcs_type = exported(
210+ Choice(title=_("Type of RCS"), readonly=True,
211+ required=True, vocabulary=RevisionControlSystems,
212+ description=_(
213+ "The version control system to import from. "
214+ "Can be CVS or Subversion.")))
215+
216+ url = exported(
217+ URIField(title=_("URL"), required=False, readonly=True,
218+ description=_("The URL of the VCS branch."),
219+ allowed_schemes=["http", "https", "svn", "git"],
220+ allow_userinfo=False, # Only anonymous access is supported.
221+ allow_port=True,
222+ allow_query=False, # Query makes no sense in Subversion.
223+ allow_fragment=False, # Fragment makes no sense in Subversion.
224+ trailing_slash=False)) # See http://launchpad.net/bugs/56357.
225+
226+ cvs_root = exported(
227+ TextLine(title=_("Repository"), required=False, readonly=True,
228+ constraint=validate_cvs_root,
229+ description=_("The CVSROOT. "
230+ "Example: :pserver:anonymous@anoncvs.gnome.org:/cvs/gnome")))
231+
232+ cvs_module = exported(
233+ TextLine(title=_("Module"), required=False, readonly=True,
234+ constraint=validate_cvs_module,
235+ description=_("The path to import within the repository."
236+ " Usually, it is the name of the project.")))
237+
238+ date_last_successful = exported(
239+ Datetime(title=_("Last successful"), required=False, readonly=True))
240
241 update_interval = Timedelta(
242 title=_("Update interval"), required=False, description=_(
243
244=== modified file 'lib/lp/code/interfaces/webservice.py'
245--- lib/lp/code/interfaces/webservice.py 2009-07-17 00:26:05 +0000
246+++ lib/lp/code/interfaces/webservice.py 2010-03-17 21:52:39 +0000
247@@ -6,6 +6,7 @@
248 from lp.code.interfaces.branch import IBranch, IBranchSet
249 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
250 from lp.code.interfaces.branchsubscription import IBranchSubscription
251+from lp.code.interfaces.codeimport import ICodeImport
252 from lp.code.interfaces.codereviewcomment import ICodeReviewComment
253 from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
254 from lp.code.interfaces.diff import IDiff, IPreviewDiff, IStaticDiff
255
256=== modified file 'lib/lp/code/stories/webservice/xx-branch.txt'
257--- lib/lp/code/stories/webservice/xx-branch.txt 2010-02-17 22:34:52 +0000
258+++ lib/lp/code/stories/webservice/xx-branch.txt 2010-03-17 21:52:39 +0000
259@@ -108,6 +108,7 @@
260 branch_format: None
261 branch_type: u'Hosted'
262 bzr_identity: u'lp://dev/~eric/fooix/trunk'
263+ code_import_link: None
264 control_format: None
265 date_created: u'2009-01-01T00:00:00+00:00'
266 date_last_modified: u'2009-01-01T00:00:00+00:00'
267
268=== added file 'lib/lp/code/stories/webservice/xx-code-import.txt'
269--- lib/lp/code/stories/webservice/xx-code-import.txt 1970-01-01 00:00:00 +0000
270+++ lib/lp/code/stories/webservice/xx-code-import.txt 2010-03-17 21:52:39 +0000
271@@ -0,0 +1,59 @@
272+Introduction
273+============
274+
275+Launchpad can tell you about the code imports that power a branch
276+if it is an import branch.
277+
278+ >>> from zope.security.proxy import removeSecurityProxy
279+
280+First we create some objects for use in the tests.
281+
282+ >>> login(ANONYMOUS)
283+ >>> person = factory.makePerson(name='import-owner')
284+ >>> product = factory.makeProduct(name='scruff')
285+ >>> code_import = removeSecurityProxy(factory.makeCodeImport(
286+ ... registrant=person, product=product, branch_name='import'))
287+ >>> no_import_branch = removeSecurityProxy(factory.makeProductBranch(
288+ ... owner=person, product=product, name='no-import'))
289+ >>> logout()
290+
291+If we query a branch with no import then we find that it tells us
292+it doesn't have one.
293+
294+ >>> branch_url = '/' + no_import_branch.unique_name
295+ >>> response = webservice.get(branch_url)
296+ >>> representation = response.jsonBody()
297+ >>> print representation['code_import_link']
298+ None
299+
300+For a branch with an import we get a link to the import entry in its
301+representation.
302+
303+ >>> branch_url = '/' + code_import.branch.unique_name
304+ >>> response = webservice.get(branch_url)
305+ >>> representation = response.jsonBody()
306+ >>> print representation['code_import_link']
307+ http://.../~import-owner/scruff/import/+code-import
308+
309+We can get some information about the import using this URL.
310+
311+ >>> import_url = representation['code_import_link']
312+ >>> response = webservice.get(import_url)
313+ >>> representation = response.jsonBody()
314+ >>> print representation['self_link'] == import_url
315+ True
316+ >>> print representation['branch_link']
317+ http://.../~import-owner/scruff/import
318+ >>> print representation['review_status']
319+ Pending Review
320+ >>> print representation['rcs_type']
321+ Subversion via CSCVS
322+ >>> print representation['url']
323+ http://domain9.domain.com/path10
324+ >>> print representation['cvs_root']
325+ None
326+ >>> print representation['cvs_module']
327+ None
328+ >>> print representation['date_last_successful']
329+ None
330+