Merge lp:~julian-edwards/launchpad/api-commercial-ppas-bug-597211 into lp:launchpad/db-devel

Proposed by Julian Edwards on 2010-06-30
Status: Merged
Merged at revision: 9512
Proposed branch: lp:~julian-edwards/launchpad/api-commercial-ppas-bug-597211
Merge into: lp:launchpad/db-devel
Diff against target: 193 lines (+122/-0)
7 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+2/-0)
lib/lp/registry/interfaces/distribution.py (+12/-0)
lib/lp/registry/model/distribution.py (+6/-0)
lib/lp/soyuz/doc/archive-commercial.txt (+56/-0)
lib/lp/soyuz/interfaces/archive.py (+8/-0)
lib/lp/soyuz/model/archive.py (+10/-0)
lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt (+28/-0)
To merge this branch: bzr merge lp:~julian-edwards/launchpad/api-commercial-ppas-bug-597211
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) 2010-06-30 Approve on 2010-06-30
Review via email: mp+28877@code.launchpad.net

Description of the Change

= Summary =
Add IDistribution.getCommercialPPAs() and export it on the webservice

== Implementation details ==
Trivial change!

== Tests ==
bin/test -cvvt archive-commercial.txt -t xx-archive-commercial.txt

== Demo and Q/A ==
Will set up a commercial PPA on staging and query it with the webservice.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/soyuz/interfaces/archive.py
  lib/lp/soyuz/doc/archive-commercial.txt
  lib/lp/registry/interfaces/distribution.py
  lib/lp/soyuz/model/archive.py
  lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt
  lib/canonical/launchpad/interfaces/_schema_circular_imports.py
  lib/lp/registry/model/distribution.py

== Pylint notices ==

<deletetia> - madness ensues with lint checking on interfaces with exports

To post a comment you must log in.
Jeroen T. Vermeulen (jtv) wrote :

Nice branch. Just a few remarks as per IRC:

 * "Lets" in the doctest is a typo for "Let's."

 * Starting doctest paragraphs with "Let's" usually indicates you're passing off setup as documentation. You can just say "we have a" or "foo is a."

 * Use IStore instead of IStoreSelector... it saves typing.

That's it. Approving.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
2--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-06-21 19:29:34 +0000
3+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-06-30 13:55:45 +0000
4@@ -301,6 +301,8 @@
5 IDistribution, 'getSourcePackage', IDistributionSourcePackage)
6 patch_collection_return_type(
7 IDistribution, 'searchSourcePackages', IDistributionSourcePackage)
8+patch_collection_return_type(
9+ IDistribution, 'getCommercialPPAs', IArchive)
10 patch_reference_property(
11 IDistribution, 'main_archive', IArchive)
12 IDistribution['all_distro_archives'].value_type.schema = IArchive
13
14=== modified file 'lib/lp/registry/interfaces/distribution.py'
15--- lib/lp/registry/interfaces/distribution.py 2010-06-11 10:11:28 +0000
16+++ lib/lp/registry/interfaces/distribution.py 2010-06-30 13:55:45 +0000
17@@ -516,6 +516,18 @@
18 def getAllPPAs():
19 """Return all PPAs for this distribution."""
20
21+ # Really returns IArchive, see
22+ # _schema_circular_imports.py.
23+ @operation_returns_collection_of(Interface)
24+ @export_read_operation()
25+ def getCommercialPPAs():
26+ """Return all commercial PPAs.
27+
28+ Commercial PPAs are private, but explicitly flagged up as commercial
29+ so that they are discoverable by people who wish to buy items
30+ from them.
31+ """
32+
33 def searchPPAs(text=None, show_inactive=False):
34 """Return all PPAs matching the given text in this distribution.
35
36
37=== modified file 'lib/lp/registry/model/distribution.py'
38--- lib/lp/registry/model/distribution.py 2010-06-21 21:33:53 +0000
39+++ lib/lp/registry/model/distribution.py 2010-06-30 13:55:45 +0000
40@@ -1259,6 +1259,12 @@
41 distribution=self,
42 purpose=ArchivePurpose.PPA).order_by('id')
43
44+ def getCommercialPPAs(self):
45+ """See `IDistribution`."""
46+ # If we ever see non-Ubuntu PPAs, this will return more than
47+ # just the PPAs for the Ubuntu context.
48+ return getUtility(IArchiveSet).getCommercialPPAs()
49+
50 def searchPPAs(self, text=None, show_inactive=False, user=None):
51 """See `IDistribution`."""
52 clauses = ["""
53
54=== added file 'lib/lp/soyuz/doc/archive-commercial.txt'
55--- lib/lp/soyuz/doc/archive-commercial.txt 1970-01-01 00:00:00 +0000
56+++ lib/lp/soyuz/doc/archive-commercial.txt 2010-06-30 13:55:45 +0000
57@@ -0,0 +1,56 @@
58+===============
59+Commercial PPAs
60+===============
61+
62+Commercial PPAs are to all intents and purposes identical to private PPAs.
63+They contain a 'commercial' flag which indicates that the PPA is actively
64+selling software and should be more discoverable.
65+
66+We have a private PPA for demonstration purposes:
67+
68+ >>> ppa = factory.makeArchive(private=True, name="pppa")
69+
70+
71+Discovering Commercial PPAs
72+---------------------------
73+
74+IArchive.getCommercialPPAs() retrieves the subset of all private PPAs that are
75+marked for commercial usage.
76+
77+ >>> from lp.soyuz.interfaces.archive import IArchiveSet
78+ >>> archive_set = getUtility(IArchiveSet)
79+ >>> for commercial_ppa in archive_set.getCommercialPPAs():
80+ ... print commercial_ppa.name
81+
82+There are none yet - if we mark "ppa" as commercial it will be
83+returned:
84+
85+ >>> # Setting 'commercial' is restricted to admins but we don't want to
86+ >>> # worry about that here. See lib/lp/soyuz/tests/test_archive.py
87+ >>> from zope.security.proxy import removeSecurityProxy
88+ >>> removeSecurityProxy(ppa).commercial = True
89+ >>> for commercial_ppa in archive_set.getCommercialPPAs():
90+ ... print commercial_ppa.name
91+ pppa
92+
93+
94+Security
95+--------
96+
97+Anyone is allowed to retrieve the list of commercial PPAs. Normal security
98+restrictions apply to individual PPA access though.
99+
100+ >>> login(ANONYMOUS)
101+ >>> ppas = archive_set.getCommercialPPAs()
102+
103+'name is a public attribute:
104+
105+ >>> print ppas[0].name
106+ pppa
107+
108+'description' is not, however.
109+
110+ >>> ppas[0].description
111+ Traceback (most recent call last):
112+ ...
113+ Unauthorized:...
114
115=== modified file 'lib/lp/soyuz/interfaces/archive.py'
116--- lib/lp/soyuz/interfaces/archive.py 2010-06-25 15:03:10 +0000
117+++ lib/lp/soyuz/interfaces/archive.py 2010-06-30 13:55:45 +0000
118@@ -1557,6 +1557,14 @@
119 def getPrivatePPAs():
120 """Return a result set containing all private PPAs."""
121
122+ def getCommercialPPAs():
123+ """Return a result set containing all commercial PPAs.
124+
125+ Commercial PPAs are private, but explicitly flagged up as commercial
126+ so that they are discoverable by people who wish to buy items
127+ from them.
128+ """
129+
130 def getPublicationsInArchives(source_package_name, archive_list,
131 distribution):
132 """Return a result set of publishing records for the source package.
133
134=== modified file 'lib/lp/soyuz/model/archive.py'
135--- lib/lp/soyuz/model/archive.py 2010-06-29 09:37:26 +0000
136+++ lib/lp/soyuz/model/archive.py 2010-06-30 13:55:45 +0000
137@@ -95,6 +95,8 @@
138 from lp.soyuz.model.binarypackagerelease import BinaryPackageRelease
139 from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
140 from lp.soyuz.scripts.packagecopier import do_copy
141+
142+from canonical.launchpad.interfaces.lpstorm import IStore
143 from canonical.launchpad.webapp.interfaces import (
144 IStoreSelector, MAIN_STORE, DEFAULT_FLAVOR)
145 from canonical.launchpad.webapp.url import urlappend
146@@ -1927,6 +1929,14 @@
147 Archive.private == True,
148 Archive.purpose == ArchivePurpose.PPA)
149
150+ def getCommercialPPAs(self):
151+ """See `IArchiveSet`."""
152+ store = IStore(Archive)
153+ return store.find(
154+ Archive,
155+ Archive.commercial == True,
156+ Archive.purpose == ArchivePurpose.PPA)
157+
158 def getArchivesForDistribution(self, distribution, name=None,
159 purposes=None, user=None,
160 exclude_disabled=True):
161
162=== added file 'lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt'
163--- lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt 1970-01-01 00:00:00 +0000
164+++ lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt 2010-06-30 13:55:45 +0000
165@@ -0,0 +1,28 @@
166+=============================================
167+Using the webservice with commercial archives
168+=============================================
169+
170+(See also soyuz/stories/webservice/xx-archive.txt and
171+soyuz/doc/archive-commercial.txt)
172+
173+Start by making a commercial PPA that we can fetch via the webservice:
174+
175+ >>> login("admin@canonical.com")
176+ >>> ppa = factory.makeArchive(private=True, name="cpppa")
177+ >>> from zope.security.proxy import removeSecurityProxy
178+ >>> removeSecurityProxy(ppa).commercial = True
179+ >>> import transaction
180+ >>> transaction.commit()
181+ >>> logout()
182+
183+Commercial archives can be found with a custom method 'getCommercialPPAs'
184+that lives on the distribution object.
185+
186+Using webservice directly:
187+
188+ >>> ubuntu = webservice.get("/ubuntu").jsonBody()
189+ >>> cppas = webservice.named_get(
190+ ... ubuntu['self_link'], 'getCommercialPPAs').jsonBody()
191+ >>> for entry in cppas['entries']:
192+ ... print entry['name']
193+ cpppa

Subscribers

People subscribed via source and target branches

to status/vote changes: