Merge lp:~rharding/charmworld/bundle-metadata2 into lp:~juju-jitsu/charmworld/trunk
- bundle-metadata2
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Richard Harding |
Approved revision: | 414 |
Merged at revision: | 406 |
Proposed branch: | lp:~rharding/charmworld/bundle-metadata2 |
Merge into: | lp:~juju-jitsu/charmworld/trunk |
Diff against target: |
383 lines (+232/-8) 5 files modified
charmworld/models.py (+67/-4) charmworld/testing/factory.py (+1/-1) charmworld/tests/test_models.py (+118/-1) charmworld/views/api.py (+22/-0) charmworld/views/tests/test_api.py (+24/-2) |
To merge this branch: | bzr merge lp:~rharding/charmworld/bundle-metadata2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Gui Bot | continuous-integration | Approve | |
Benji York (community) | Approve | ||
Review via email: mp+189406@code.launchpad.net |
Commit message
Add extra file/charm_metadata support to the bundle details api call.
Description of the change
Add extra file/charm_metadata support to the bundle details api call.
The bundle details view needs to provide metadata for the Gui to properly render the token and information about it. This provides the list of files (to find the README for instance) in the bundle as well as charm details of the charms in the bundle.
The charms are keyed off their keys in the bundle services list so that the gui can match from the name used in the bundle to the charm model in the charm_metadata.
The charms are formatted per the normal charm_details method by casting to a Charm object and then running through _format_charm.
QA:
http://
Should output a new large suite of json for each charm in the bundle. It should also show the list of files so that the front end can implement a hasIcon as per the way gui handles charms. It also allows searching for the README file (README, readme.md, etc) just as we do with the charms in the Gui.
Richard Harding (rharding) wrote : | # |
> Lines 44 and 85 of the diff: The name "data" is somewhat
> information-free, maybe bundle_data would be more informative.
Updated. It's actually the services bit from the bundle so went with bundle_charm_info.
> Line 45 of the diff: The docstring summery might be better rendered as
> "Construct a query that specifies a charm from data about that charm."
Updated
> Line 47 of the diff: The bulleted lists in the docstring have
> inconsistent indentation.
Same
> Lines 64 and 77 of the diff: Please capitalize the sentence in the
> comment.
Thanks
> Line 71 of the diff: I would be paranoid and pass "/trunk" to the
> .endswith() call so a branch name like "who-is-
> be matched.
Makes sense.
> Line 196 of the diff: It would be slightly better to use .assertIsNone().
Updated
> Line 357 of the diff: There is an extra space after the colon. I think
> lint will pick that up, so you should run lint to see if there is more.
Thanks, also had the extra line setting the maxdiff to none that lint caught and removed.
> Line 205 of the diff: The TestBundleLoadi
> test that shows what a request with too-little information to find a
> charm will produce.
Thanks! This misbehaved and the test caught that and got it adjusted.
Juju Gui Bot (juju-gui-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
Richard Harding (rharding) wrote : | # |
Updated and sync'd with trunk. Will try to land again.
Juju Gui Bot (juju-gui-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
Juju Gui Bot (juju-gui-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
Juju Gui Bot (juju-gui-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
Juju Gui Bot (juju-gui-bot) : | # |
Preview Diff
1 | === modified file 'charmworld/models.py' | |||
2 | --- charmworld/models.py 2013-10-01 00:38:01 +0000 | |||
3 | +++ charmworld/models.py 2013-10-07 14:35:40 +0000 | |||
4 | @@ -1288,7 +1288,7 @@ | |||
5 | 1288 | class Bundle: | 1288 | class Bundle: |
6 | 1289 | """A model to represent bundles of charms.""" | 1289 | """A model to represent bundles of charms.""" |
7 | 1290 | 1290 | ||
9 | 1291 | _COMMON_REPRESENTATION = { | 1291 | _MONGO_FIELDS = { |
10 | 1292 | 'owner': '', | 1292 | 'owner': '', |
11 | 1293 | 'basket_name': None, | 1293 | 'basket_name': None, |
12 | 1294 | 'basket_revision': None, | 1294 | 'basket_revision': None, |
13 | @@ -1305,7 +1305,7 @@ | |||
14 | 1305 | 'inheriting from other bundles is not supported, the bundle ' | 1305 | 'inheriting from other bundles is not supported, the bundle ' |
15 | 1306 | 'must be preprocessed to incorporate any inheritance') | 1306 | 'must be preprocessed to incorporate any inheritance') |
16 | 1307 | self._raw_representation = data | 1307 | self._raw_representation = data |
18 | 1308 | self._representation = dict(self._COMMON_REPRESENTATION) | 1308 | self._representation = dict(self._MONGO_FIELDS) |
19 | 1309 | self._representation.update(data) | 1309 | self._representation.update(data) |
20 | 1310 | 1310 | ||
21 | 1311 | @classmethod | 1311 | @classmethod |
22 | @@ -1333,7 +1333,7 @@ | |||
23 | 1333 | and self.permanent_url == other.permanent_url) | 1333 | and self.permanent_url == other.permanent_url) |
24 | 1334 | 1334 | ||
25 | 1335 | def __getattr__(self, name): | 1335 | def __getattr__(self, name): |
27 | 1336 | if name in self._COMMON_REPRESENTATION: | 1336 | if name in self._MONGO_FIELDS: |
28 | 1337 | return self._representation[name] | 1337 | return self._representation[name] |
29 | 1338 | else: | 1338 | else: |
30 | 1339 | raise AttributeError | 1339 | raise AttributeError |
31 | @@ -1406,7 +1406,7 @@ | |||
32 | 1406 | """ | 1406 | """ |
33 | 1407 | yield 'id', self.id | 1407 | yield 'id', self.id |
34 | 1408 | yield 'permanent_url', self.permanent_url | 1408 | yield 'permanent_url', self.permanent_url |
36 | 1409 | for key in self._COMMON_REPRESENTATION: | 1409 | for key in self._MONGO_FIELDS: |
37 | 1410 | yield key, self.__getattr__(key) | 1410 | yield key, self.__getattr__(key) |
38 | 1411 | 1411 | ||
39 | 1412 | @property | 1412 | @property |
40 | @@ -1487,6 +1487,69 @@ | |||
41 | 1487 | sort, max_scan, as_class, all_revisions, **kwargs)) | 1487 | sort, max_scan, as_class, all_revisions, **kwargs)) |
42 | 1488 | 1488 | ||
43 | 1489 | 1489 | ||
44 | 1490 | def _build_charm_query_from_bundle_info(bundle_charm_info, series): | ||
45 | 1491 | """Construct a query for a charm based on the info available in the bundle | ||
46 | 1492 | |||
47 | 1493 | Bundles may provide: | ||
48 | 1494 | - branch | ||
49 | 1495 | - charm (name) | ||
50 | 1496 | - If so, the series is required to determine which charm with that | ||
51 | 1497 | name. This also assumes a promulgated charm. | ||
52 | 1498 | - charm (store url) | ||
53 | 1499 | """ | ||
54 | 1500 | charm_query = None | ||
55 | 1501 | branch, rev = None, None | ||
56 | 1502 | charm_branch = bundle_charm_info.get('branch') | ||
57 | 1503 | store_url = bundle_charm_info.get( | ||
58 | 1504 | 'charm', | ||
59 | 1505 | bundle_charm_info.get('charm_url', None)) | ||
60 | 1506 | |||
61 | 1507 | if (store_url and store_url.startswith('cs:')): | ||
62 | 1508 | charm_query = {'store_url': store_url} | ||
63 | 1509 | elif charm_branch: | ||
64 | 1510 | branch, sep, revision = charm_branch.partition('@') | ||
65 | 1511 | |||
66 | 1512 | # Strip the lp:charms to match the fields in the db. | ||
67 | 1513 | if branch.startswith('lp:'): | ||
68 | 1514 | branch = re.sub('^lp:', '', branch) | ||
69 | 1515 | # If the branch url doesn't start with an owner assume it's a | ||
70 | 1516 | # ~charmers branch | ||
71 | 1517 | if branch[0] != '~': | ||
72 | 1518 | branch = '~charmers/' + branch | ||
73 | 1519 | if not branch.endswith('/trunk'): | ||
74 | 1520 | branch = branch + '/trunk' | ||
75 | 1521 | charm_query = {u'branch_spec': str(branch)} | ||
76 | 1522 | if (revision): | ||
77 | 1523 | charm_query['store_data.revision'] = int(revision) | ||
78 | 1524 | elif store_url and series: | ||
79 | 1525 | # The last resort, if there's a series in the bundle config, and we | ||
80 | 1526 | # have a charm name, we can attempt to load the latest version of the | ||
81 | 1527 | # charm with that address. | ||
82 | 1528 | address = 'cs:' + '/'.join([series, store_url]) | ||
83 | 1529 | charm_query = {u'address': address} | ||
84 | 1530 | return charm_query | ||
85 | 1531 | |||
86 | 1532 | |||
87 | 1533 | def resolve_charm_from_bundle_info(db, bundle_charm_info, series): | ||
88 | 1534 | """Find the charm that the bundle is looking for. | ||
89 | 1535 | |||
90 | 1536 | Bundles may provide charm info in several ways. Attempt to find the best | ||
91 | 1537 | charm we can based on that information. | ||
92 | 1538 | |||
93 | 1539 | """ | ||
94 | 1540 | charm_query = _build_charm_query_from_bundle_info( | ||
95 | 1541 | bundle_charm_info, series) | ||
96 | 1542 | |||
97 | 1543 | if charm_query: | ||
98 | 1544 | # Order all results by revision so that we grab the latest match | ||
99 | 1545 | # as we might not always have a revision to work with. | ||
100 | 1546 | sort = [('store_data.revision', pymongo.DESCENDING)] | ||
101 | 1547 | results = db.charms.find(charm_query, sort=sort).limit(1) | ||
102 | 1548 | if results.count() > 0: | ||
103 | 1549 | return results[0] | ||
104 | 1550 | return None | ||
105 | 1551 | |||
106 | 1552 | |||
107 | 1490 | def _find_charms(collection, spec=None, fields=None, skip=0, limit=0, | 1553 | def _find_charms(collection, spec=None, fields=None, skip=0, limit=0, |
108 | 1491 | timeout=True, snapshot=False, tailable=False, sort=None, | 1554 | timeout=True, snapshot=False, tailable=False, sort=None, |
109 | 1492 | max_scan=None, as_class=None, all_revisions=False, **kwargs): | 1555 | max_scan=None, as_class=None, all_revisions=False, **kwargs): |
110 | 1493 | 1556 | ||
111 | === modified file 'charmworld/testing/factory.py' | |||
112 | --- charmworld/testing/factory.py 2013-10-03 17:13:56 +0000 | |||
113 | +++ charmworld/testing/factory.py 2013-10-07 14:35:40 +0000 | |||
114 | @@ -256,7 +256,7 @@ | |||
115 | 256 | charm = dict(payload) | 256 | charm = dict(payload) |
116 | 257 | add_store_data(charm, promulgated, charm_error, store_revision) | 257 | add_store_data(charm, promulgated, charm_error, store_revision) |
117 | 258 | charm.update({ | 258 | charm.update({ |
119 | 259 | '_id': construct_charm_id(charm), | 259 | '_id': construct_charm_id(charm, revision), |
120 | 260 | 'address': get_address(charm, promulgated), | 260 | 'address': get_address(charm, promulgated), |
121 | 261 | "branch_dir": os.path.join(branch_root, charm['name']), | 261 | "branch_dir": os.path.join(branch_root, charm['name']), |
122 | 262 | "categories": categories, | 262 | "categories": categories, |
123 | 263 | 263 | ||
124 | === modified file 'charmworld/tests/test_models.py' | |||
125 | --- charmworld/tests/test_models.py 2013-09-10 14:45:25 +0000 | |||
126 | +++ charmworld/tests/test_models.py 2013-10-07 14:35:40 +0000 | |||
127 | @@ -27,6 +27,7 @@ | |||
128 | 27 | 27 | ||
129 | 28 | from charmworld.models import ( | 28 | from charmworld.models import ( |
130 | 29 | acquire_session_secret, | 29 | acquire_session_secret, |
131 | 30 | _build_charm_query_from_bundle_info, | ||
132 | 30 | Bundle, | 31 | Bundle, |
133 | 31 | Charm, | 32 | Charm, |
134 | 32 | CharmFile, | 33 | CharmFile, |
135 | @@ -40,6 +41,7 @@ | |||
136 | 40 | options_to_storage, | 41 | options_to_storage, |
137 | 41 | QAData, | 42 | QAData, |
138 | 42 | QADataSource, | 43 | QADataSource, |
139 | 44 | resolve_charm_from_bundle_info, | ||
140 | 43 | slurp_files, | 45 | slurp_files, |
141 | 44 | storage_to_options, | 46 | storage_to_options, |
142 | 45 | store_bundles, | 47 | store_bundles, |
143 | @@ -1644,7 +1646,7 @@ | |||
144 | 1644 | bundle_data = {} | 1646 | bundle_data = {} |
145 | 1645 | bundle = Bundle(bundle_data) | 1647 | bundle = Bundle(bundle_data) |
146 | 1646 | self.assertIs(bundle_data, bundle._raw_representation) | 1648 | self.assertIs(bundle_data, bundle._raw_representation) |
148 | 1647 | self.assertEqual(bundle._COMMON_REPRESENTATION, bundle._representation) | 1649 | self.assertEqual(bundle._MONGO_FIELDS, bundle._representation) |
149 | 1648 | 1650 | ||
150 | 1649 | def test_init_with_minimal_data(self): | 1651 | def test_init_with_minimal_data(self): |
151 | 1650 | bundle_data = { | 1652 | bundle_data = { |
152 | @@ -1852,6 +1854,51 @@ | |||
153 | 1852 | '~bac/sample_bundle/1', | 1854 | '~bac/sample_bundle/1', |
154 | 1853 | bundle.basket_id) | 1855 | bundle.basket_id) |
155 | 1854 | 1856 | ||
156 | 1857 | def test_charm_resolving_from_bundle_branch(self): | ||
157 | 1858 | service_data = { | ||
158 | 1859 | 'charm': 'mediawiki', | ||
159 | 1860 | 'branch': 'lp:charms/precise/mediawiki' | ||
160 | 1861 | } | ||
161 | 1862 | query = _build_charm_query_from_bundle_info(service_data, None) | ||
162 | 1863 | self.assertEqual( | ||
163 | 1864 | '~charmers/charms/precise/mediawiki/trunk', | ||
164 | 1865 | query['branch_spec']) | ||
165 | 1866 | self.assertNotIn('store_data.revision', query) | ||
166 | 1867 | |||
167 | 1868 | # And supports a version. | ||
168 | 1869 | service_data['branch'] = 'lp:charms/precise/mediawiki@7' | ||
169 | 1870 | query = _build_charm_query_from_bundle_info(service_data, None) | ||
170 | 1871 | self.assertEqual( | ||
171 | 1872 | '~charmers/charms/precise/mediawiki/trunk', | ||
172 | 1873 | query['branch_spec']) | ||
173 | 1874 | self.assertEqual(7, query['store_data.revision']) | ||
174 | 1875 | |||
175 | 1876 | def test_charm_resolving_from_bundle_charm_csurl(self): | ||
176 | 1877 | service_data = { | ||
177 | 1878 | 'charm': 'cs:precise/mediawiki-7' | ||
178 | 1879 | } | ||
179 | 1880 | query = _build_charm_query_from_bundle_info(service_data, None) | ||
180 | 1881 | self.assertEqual( | ||
181 | 1882 | 'cs:precise/mediawiki-7', | ||
182 | 1883 | query['store_url']) | ||
183 | 1884 | |||
184 | 1885 | def test_charm_resolving_from_bundle_charm_and_series(self): | ||
185 | 1886 | service_data = { | ||
186 | 1887 | 'charm': 'mediawiki' | ||
187 | 1888 | } | ||
188 | 1889 | series = 'precise' | ||
189 | 1890 | query = _build_charm_query_from_bundle_info(service_data, series) | ||
190 | 1891 | self.assertEqual( | ||
191 | 1892 | 'cs:precise/mediawiki', | ||
192 | 1893 | query['address']) | ||
193 | 1894 | |||
194 | 1895 | def test_charm_resolving_from_bundle_empty_missing_info(self): | ||
195 | 1896 | service_data = { | ||
196 | 1897 | 'charm': 'mediawiki' | ||
197 | 1898 | } | ||
198 | 1899 | query = _build_charm_query_from_bundle_info(service_data, None) | ||
199 | 1900 | self.assertIsNone(query) | ||
200 | 1901 | |||
201 | 1855 | 1902 | ||
202 | 1856 | class TestMakeBundleDoc(TestCase): | 1903 | class TestMakeBundleDoc(TestCase): |
203 | 1857 | 1904 | ||
204 | @@ -1953,6 +2000,76 @@ | |||
205 | 1953 | self.assertEqual(expected, filelist) | 2000 | self.assertEqual(expected, filelist) |
206 | 1954 | 2001 | ||
207 | 1955 | 2002 | ||
208 | 2003 | class TestBundleLoadingCharms(MongoTestBase): | ||
209 | 2004 | |||
210 | 2005 | def setUp(self): | ||
211 | 2006 | """Create 3 of the same charms at different revisions""" | ||
212 | 2007 | super(TestBundleLoadingCharms, self).setUp() | ||
213 | 2008 | charm_args = { | ||
214 | 2009 | 'name': 'wikipedia', | ||
215 | 2010 | 'series': 'precise', | ||
216 | 2011 | 'owner': 'charmers', | ||
217 | 2012 | 'promulgated': True | ||
218 | 2013 | } | ||
219 | 2014 | charm_args['revision'] = 1 | ||
220 | 2015 | charm_args['store_revision'] = 1 | ||
221 | 2016 | factory.makeCharm(self.db, **charm_args) | ||
222 | 2017 | charm_args['revision'] = 2 | ||
223 | 2018 | charm_args['store_revision'] = 2 | ||
224 | 2019 | factory.makeCharm(self.db, **charm_args) | ||
225 | 2020 | charm_args['revision'] = 3 | ||
226 | 2021 | charm_args['store_revision'] = 3 | ||
227 | 2022 | factory.makeCharm(self.db, **charm_args) | ||
228 | 2023 | |||
229 | 2024 | def test_resolve_by_store_url(self): | ||
230 | 2025 | data = { | ||
231 | 2026 | 'charm': 'cs:precise/wikipedia-1' | ||
232 | 2027 | } | ||
233 | 2028 | charm = resolve_charm_from_bundle_info(self.db, data, None) | ||
234 | 2029 | self.assertIsNotNone(charm) | ||
235 | 2030 | self.assertEqual('~charmers/precise/wikipedia/1', charm['_id']) | ||
236 | 2031 | self.assertEqual('wikipedia', charm['name']) | ||
237 | 2032 | self.assertEqual(1, charm['store_data']['revision']) | ||
238 | 2033 | self.assertEqual(1, charm['revision']) | ||
239 | 2034 | |||
240 | 2035 | def test_resolve_by_name_and_series(self): | ||
241 | 2036 | data = { | ||
242 | 2037 | 'charm': 'wikipedia' | ||
243 | 2038 | } | ||
244 | 2039 | charm = resolve_charm_from_bundle_info(self.db, data, 'precise') | ||
245 | 2040 | self.assertIsNotNone(charm) | ||
246 | 2041 | self.assertEqual('wikipedia', charm['name']) | ||
247 | 2042 | self.assertEqual(3, charm['store_data']['revision']) | ||
248 | 2043 | self.assertEqual(3, charm['revision']) | ||
249 | 2044 | |||
250 | 2045 | def test_resolve_by_branch(self): | ||
251 | 2046 | data = { | ||
252 | 2047 | 'branch': 'lp:charms/precise/wikipedia' | ||
253 | 2048 | } | ||
254 | 2049 | charm = resolve_charm_from_bundle_info(self.db, data, 'precise') | ||
255 | 2050 | self.assertIsNotNone(charm) | ||
256 | 2051 | self.assertEqual('wikipedia', charm['name']) | ||
257 | 2052 | self.assertEqual(3, charm['store_data']['revision']) | ||
258 | 2053 | self.assertEqual(3, charm['revision']) | ||
259 | 2054 | |||
260 | 2055 | def test_resolve_by_branch_with_revision(self): | ||
261 | 2056 | data = { | ||
262 | 2057 | 'branch': 'lp:charms/precise/wikipedia@2' | ||
263 | 2058 | } | ||
264 | 2059 | charm = resolve_charm_from_bundle_info(self.db, data, 'precise') | ||
265 | 2060 | self.assertIsNotNone(charm) | ||
266 | 2061 | self.assertEqual('wikipedia', charm['name']) | ||
267 | 2062 | self.assertEqual(2, charm['store_data']['revision']) | ||
268 | 2063 | self.assertEqual(2, charm['revision']) | ||
269 | 2064 | |||
270 | 2065 | def test_not_enough_info(self): | ||
271 | 2066 | data = { | ||
272 | 2067 | 'name': 'wikipedia' | ||
273 | 2068 | } | ||
274 | 2069 | charm = resolve_charm_from_bundle_info(self.db, data, None) | ||
275 | 2070 | self.assertIsNone(charm) | ||
276 | 2071 | |||
277 | 2072 | |||
278 | 1956 | class TestFeaturedSource(MongoTestBase): | 2073 | class TestFeaturedSource(MongoTestBase): |
279 | 1957 | 2074 | ||
280 | 1958 | def test_set_and_get_work(self): | 2075 | def test_set_and_get_work(self): |
281 | 1959 | 2076 | ||
282 | === modified file 'charmworld/views/api.py' | |||
283 | --- charmworld/views/api.py 2013-10-03 17:13:56 +0000 | |||
284 | +++ charmworld/views/api.py 2013-10-07 14:35:40 +0000 | |||
285 | @@ -29,6 +29,7 @@ | |||
286 | 29 | FeaturedSource, | 29 | FeaturedSource, |
287 | 30 | getfs, | 30 | getfs, |
288 | 31 | QADataSource, | 31 | QADataSource, |
289 | 32 | resolve_charm_from_bundle_info, | ||
290 | 32 | ) | 33 | ) |
291 | 33 | from charmworld.search import ( | 34 | from charmworld.search import ( |
292 | 34 | BUNDLE, | 35 | BUNDLE, |
293 | @@ -427,6 +428,25 @@ | |||
294 | 427 | query, sort=[('store_data.revision', pymongo.DESCENDING)]) | 428 | query, sort=[('store_data.revision', pymongo.DESCENDING)]) |
295 | 428 | return charm_id, trailing, charm | 429 | return charm_id, trailing, charm |
296 | 429 | 430 | ||
297 | 431 | def _build_bundle_metadata(self, bundle, db): | ||
298 | 432 | bundle_dict = dict(bundle) | ||
299 | 433 | # Add the list of files in the bundle. | ||
300 | 434 | bundle_dict['files'] = bundle.get_file_list(db) | ||
301 | 435 | bundle_dict['charm_metadata'] = {} | ||
302 | 436 | # Now load the charm information we require for the services in the | ||
303 | 437 | # bundle. | ||
304 | 438 | for service, data in bundle.data['services'].iteritems(): | ||
305 | 439 | charm = resolve_charm_from_bundle_info( | ||
306 | 440 | db, | ||
307 | 441 | data, | ||
308 | 442 | bundle_dict.get('series') | ||
309 | 443 | ) | ||
310 | 444 | if charm: | ||
311 | 445 | formatted = self._format_charm(Charm(charm)) | ||
312 | 446 | bundle_dict['charm_metadata'][service] = formatted | ||
313 | 447 | |||
314 | 448 | return bundle_dict | ||
315 | 449 | |||
316 | 430 | def _find_bundle(self, path): | 450 | def _find_bundle(self, path): |
317 | 431 | try: | 451 | try: |
318 | 432 | bundle_id, trailing, bundle_bits = self._parse_bundle_id(path) | 452 | bundle_id, trailing, bundle_bits = self._parse_bundle_id(path) |
319 | @@ -463,6 +483,7 @@ | |||
320 | 463 | if charm_data is None: | 483 | if charm_data is None: |
321 | 464 | return json_response( | 484 | return json_response( |
322 | 465 | 404, {'type': 'no_such_charm', 'charm_id': charm_id}) | 485 | 404, {'type': 'no_such_charm', 'charm_id': charm_id}) |
323 | 486 | |||
324 | 466 | charm = Charm(charm_data) | 487 | charm = Charm(charm_data) |
325 | 467 | if trailing is None: | 488 | if trailing is None: |
326 | 468 | return self._charm_details(charm_data) | 489 | return self._charm_details(charm_data) |
327 | @@ -503,6 +524,7 @@ | |||
328 | 503 | result = {'type': 'no_such_bundle', 'bundle_id': '/'.join(path)} | 524 | result = {'type': 'no_such_bundle', 'bundle_id': '/'.join(path)} |
329 | 504 | return json_response(status, result) | 525 | return json_response(status, result) |
330 | 505 | if not trailing: | 526 | if not trailing: |
331 | 527 | bundle = self._build_bundle_metadata(bundle, self.request.db) | ||
332 | 506 | return json_response(200, bundle) | 528 | return json_response(200, bundle) |
333 | 507 | elif trailing and trailing[0] == self.ICON_TRAILING: | 529 | elif trailing and trailing[0] == self.ICON_TRAILING: |
334 | 508 | return self._bundle_file(bundle, filename) | 530 | return self._bundle_file(bundle, filename) |
335 | 509 | 531 | ||
336 | === modified file 'charmworld/views/tests/test_api.py' | |||
337 | --- charmworld/views/tests/test_api.py 2013-10-03 17:13:56 +0000 | |||
338 | +++ charmworld/views/tests/test_api.py 2013-10-07 14:35:40 +0000 | |||
339 | @@ -779,7 +779,20 @@ | |||
340 | 779 | self.assertEqual(200, response.status_code) | 779 | self.assertEqual(200, response.status_code) |
341 | 780 | 780 | ||
342 | 781 | def test_results_match(self): | 781 | def test_results_match(self): |
344 | 782 | self.makeBundle(name='bat', owner='bac', basket_with_rev='byobu/4') | 782 | # Make the charm that we'll use as a service. |
345 | 783 | _id, charm = factory.makeCharm( | ||
346 | 784 | self.db, | ||
347 | 785 | description='' | ||
348 | 786 | ) | ||
349 | 787 | services = { | ||
350 | 788 | u'charm': { | ||
351 | 789 | u'branch': charm['branch_spec'], | ||
352 | 790 | u'charm': charm['name'] | ||
353 | 791 | } | ||
354 | 792 | } | ||
355 | 793 | self.makeBundle( | ||
356 | 794 | name='bat', owner='bac', | ||
357 | 795 | basket_with_rev='byobu/4', services=services) | ||
358 | 783 | response = self.get_response('bundle', '~bac/byobu/4/bat') | 796 | response = self.get_response('bundle', '~bac/byobu/4/bat') |
359 | 784 | self.assertEqual(200, response.status_code) | 797 | self.assertEqual(200, response.status_code) |
360 | 785 | self.assertEqual( | 798 | self.assertEqual( |
361 | @@ -788,12 +801,21 @@ | |||
362 | 788 | u'basket_name': u'byobu', | 801 | u'basket_name': u'byobu', |
363 | 789 | u'basket_revision': 4, | 802 | u'basket_revision': 4, |
364 | 790 | u'branch_deleted': False, | 803 | u'branch_deleted': False, |
365 | 804 | u'charm_metadata': { | ||
366 | 805 | u'charm': self.api_class._format_charm(Charm(charm)) | ||
367 | 806 | }, | ||
368 | 791 | u'data': { | 807 | u'data': { |
369 | 792 | u'series': u'precise', | 808 | u'series': u'precise', |
371 | 793 | u'services': {}, | 809 | u'services': { |
372 | 810 | u'charm': { | ||
373 | 811 | u'branch': charm['branch_spec'], | ||
374 | 812 | u'charm': charm['name'] | ||
375 | 813 | } | ||
376 | 814 | }, | ||
377 | 794 | u'relations': {}, | 815 | u'relations': {}, |
378 | 795 | }, | 816 | }, |
379 | 796 | u'description': u'', | 817 | u'description': u'', |
380 | 818 | u'files': [], | ||
381 | 797 | u'id': u'~bac/byobu/4/bat', | 819 | u'id': u'~bac/byobu/4/bat', |
382 | 798 | u'name': u'bat', | 820 | u'name': u'bat', |
383 | 799 | u'owner': u'bac', | 821 | u'owner': u'bac', |
The branch looks good. I saw a few little things that deserve your
attention.
Lines 44 and 85 of the diff: The name "data" is somewhat
information-free, maybe bundle_data would be more informative.
Line 45 of the diff: The docstring summery might be better rendered as
"Construct a query that specifies a charm from data about that charm."
Line 47 of the diff: The bulleted lists in the docstring have
inconsistent indentation.
Lines 64 and 77 of the diff: Please capitalize the sentence in the
comment.
Line 71 of the diff: I would be paranoid and pass "/trunk" to the that-in- the-trunk" won't
.endswith() call so a branch name like "who-is-
be matched.
Line 196 of the diff: It would be slightly better to use .assertIsNone().
Line 357 of the diff: There is an extra space after the colon. I think
lint will pick that up, so you should run lint to see if there is more.
Line 205 of the diff: The TestBundleLoadi ngCharms test case could use a
test that shows what a request with too-little information to find a
charm will produce.