Merge lp:~abentley/charmworld/hide-same-series into lp:~juju-jitsu/charmworld/trunk

Proposed by Aaron Bentley
Status: Merged
Approved by: Curtis Hovey
Approved revision: 256
Merged at revision: 250
Proposed branch: lp:~abentley/charmworld/hide-same-series
Merge into: lp:~juju-jitsu/charmworld/trunk
Diff against target: 206 lines (+83/-16)
5 files modified
Makefile (+1/-1)
charmworld/search.py (+14/-1)
charmworld/tests/test_search.py (+25/-0)
charmworld/views/api.py (+12/-11)
charmworld/views/tests/test_api.py (+31/-3)
To merge this branch: bzr merge lp:~abentley/charmworld/hide-same-series
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+168167@code.launchpad.net

Commit message

Exclude related charms from different series.

Description of the change

It is confusing to show charms from a different series as "related", because it's very unusual to deploy charms from different series. This branch suppresses such results in the "related" endpoint (API 2).

As a driveby, it tweaks the "make test" command to suppress urllib and pyelasticsearch logs, making most debugging easier.

As another driveby, it moves API2._filter_charm_related into API1, since it is only used there.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2013-05-03 15:15:20 +0000
3+++ Makefile 2013-06-07 19:47:31 +0000
4@@ -5,7 +5,7 @@
5 CACHE := download-cache/dist
6 CSS_PATH := charmworld/static/css
7 LESSC := node_modules/less/bin/lessc
8-NOSE := INI="test.ini" bin/nosetests
9+NOSE := INI="test.ini" bin/nosetests --logging-filter=-pyelasticsearch,-requests.packages.urllib3.connectionpool
10 PSERVE := bin/pserve
11 SYSTEM_DEPS := \
12 build-essential bzr charm-tools libyaml-dev python-dev python-virtualenv\
13
14=== modified file 'charmworld/search.py'
15--- charmworld/search.py 2013-06-04 15:28:33 +0000
16+++ charmworld/search.py 2013-06-07 19:47:31 +0000
17@@ -313,11 +313,24 @@
18 'matches_human_readable_estimate': '%d' % len(charms),
19 }
20
21- def related_charms(self, requires, provides):
22+ def related_charms(self, requires, provides, series=None):
23+ """Determine the charms related to the specified interfaces.
24+
25+ Results are of the form:
26+ {'data': {/*actual schema*/}, 'weight': 5.6}
27+
28+ :param requires: A set of interfaces that charms may require.
29+ :param provides: A set of interfaces that charms may provide.
30+ :param series: If provided, limit results to the specified series.
31+ :return: requires, provides, each of which is a dict mapping
32+ interfaces to lists of results.
33+ """
34 and_clauses = [{'or': [
35 {'terms': {'i_provides': provides}},
36 {'terms': {'i_requires': requires}},
37 ]}]
38+ if series is not None:
39+ and_clauses.append({'term': {'series': series}})
40 and_clauses.extend(self.valid_charms_clauses)
41 dsl = {
42 'filtered': {
43
44=== modified file 'charmworld/tests/test_search.py'
45--- charmworld/tests/test_search.py 2013-06-04 16:09:44 +0000
46+++ charmworld/tests/test_search.py 2013-06-07 19:47:31 +0000
47@@ -541,6 +541,31 @@
48 for result in provides['telnet'])
49 self.assertAlmostEqual(by_id[official_id], by_id[unofficial_id] * 10)
50
51+ def test_related_charms_series_filter(self):
52+ def filter_data(result):
53+ return dict((key, [entry['data'] for entry in value])
54+ for key, value in result.items())
55+ alpha_charm = factory.get_charm_json(
56+ series='alpha', provides={'asdf': {'interface': 'foo'}})
57+ beta_charm = factory.get_charm_json(
58+ series='beta', requires={'asdf': {'interface': 'bar'}})
59+ self.index_client.index_charms([alpha_charm, beta_charm])
60+ requires, provides = self.index_client.related_charms({'bar'}, {'foo'})
61+ self.assertEqual({'bar': [beta_charm]}, filter_data(requires))
62+ self.assertEqual({'foo': [alpha_charm]}, filter_data(provides))
63+ requires, provides = self.index_client.related_charms(
64+ {'bar'}, {'foo'}, series='alpha')
65+ self.assertEqual({}, filter_data(requires))
66+ self.assertEqual({'foo': [alpha_charm]}, filter_data(provides))
67+ requires, provides = self.index_client.related_charms(
68+ {'bar'}, {'foo'}, series='beta')
69+ self.assertEqual({'bar': [beta_charm]}, filter_data(requires))
70+ self.assertEqual({}, filter_data(provides))
71+ requires, provides = self.index_client.related_charms(
72+ {'bar'}, {'foo'}, series='gamma')
73+ self.assertEqual({}, filter_data(requires))
74+ self.assertEqual({}, filter_data(provides))
75+
76
77 def put_mapping(client, properties):
78 client._client.put_mapping(
79
80=== modified file 'charmworld/views/api.py'
81--- charmworld/views/api.py 2013-06-07 13:08:39 +0000
82+++ charmworld/views/api.py 2013-06-07 19:47:31 +0000
83@@ -128,14 +128,6 @@
84 }
85
86 @classmethod
87- def _filter_charm_related(cls, charm_name, interfaces, relations):
88- for interface in interfaces:
89- related_charms = [ch for ch in relations.get(interface, [])
90- if ch['name'] != charm_name]
91- if len(related_charms) > 0:
92- yield interface, related_charms
93-
94- @classmethod
95 def _charm_result(cls, charm_data, requires=None, provides=None):
96 # requires and provides are unused, provided only for API1
97 # compatibility.
98@@ -146,7 +138,7 @@
99 }
100 }
101
102- def _related_charms(self, charms):
103+ def _related_charms(self, charms, series=None):
104 charms_provide = set()
105 charms_require = set()
106 for charm in charms:
107@@ -156,7 +148,7 @@
108 # what these charms provide, and the charms that *provide* what these
109 # charms require.
110 r_requires, r_provides = self.request.index_client.related_charms(
111- charms_provide, charms_require)
112+ charms_provide, charms_require, series=series)
113 f_requires = {}
114 for key, value in r_requires.items():
115 f_requires[key] = [
116@@ -332,7 +324,8 @@
117 status_code=200)
118
119 def _charm_related(self, charm):
120- requires, provides = self._related_charms([charm])
121+ requires, provides = self._related_charms(
122+ [charm], series=charm['series'])
123 return {'result': {'requires': requires, 'provides': provides}}
124
125 @staticmethod
126@@ -424,6 +417,14 @@
127 for charm in charms]
128
129 @classmethod
130+ def _filter_charm_related(cls, charm_name, interfaces, relations):
131+ for interface in interfaces:
132+ related_charms = [ch for ch in relations.get(interface, [])
133+ if ch['name'] != charm_name]
134+ if len(related_charms) > 0:
135+ yield interface, related_charms
136+
137+ @classmethod
138 def _charm_result(cls, charm_data, requires, provides):
139 charm = Charm(charm_data)
140 return {
141
142=== modified file 'charmworld/views/tests/test_api.py'
143--- charmworld/views/tests/test_api.py 2013-06-06 19:39:36 +0000
144+++ charmworld/views/tests/test_api.py 2013-06-07 19:47:31 +0000
145@@ -1182,7 +1182,7 @@
146
147
148 class TestAPI2(TestAPI, API2Mixin):
149- """Test API 1."""
150+ """Test API 2."""
151
152 def test_charm_result_excludes_related(self):
153 charm = factory.get_charm_json(promulgated=False, provides={
154@@ -1217,6 +1217,10 @@
155 result = self.api_class._charm_result(charm, requires, provides)
156 self.assertNotIn('related', result['metadata'])
157
158+ def get_charm_response(self, charm, suffix):
159+ remainder = self.api_class._get_api_id(charm) + '/' + suffix
160+ return self.get_response('charm', remainder=remainder).json
161+
162 def test_related(self):
163 self.use_index_client()
164 charm_source = CharmSource.from_request(self)
165@@ -1246,8 +1250,7 @@
166 'bar': {'interface': 'imap'}
167 })
168 self.index_client.index_charm(imap_provider)
169- remainder = self.api_class._get_api_id(charm) + '/related'
170- result_json = self.get_response('charm', remainder=remainder).json
171+ result_json = self.get_charm_response(charm, 'related')
172 self.assertEqual(
173 self.weightless_relation(result_json['result']['requires']),
174 {'imap': [self.related_weightless(imap_user)]})
175@@ -1255,6 +1258,31 @@
176 self.weightless_relation(result_json['result']['provides']),
177 {'ssl': [self.related_weightless(ssl_provider)]})
178
179+ def test_charm_related_excludes_different_series(self):
180+ self.use_index_client()
181+ charm_source = CharmSource.from_request(self)
182+ charm_one = factory.get_charm_json(promulgated=False, provides={
183+ 'cookie-factory': {
184+ 'interface': 'imap',
185+ }
186+ })
187+ charm_source.save(charm_one)
188+ charm_two = factory.get_charm_json(promulgated=False, requires={
189+ 'cookie-factory': {
190+ 'interface': 'imap',
191+ }
192+ }, series=charm_one['series'])
193+ charm_source.save(charm_two)
194+ response = self.get_charm_response(charm_one, 'related')
195+ charm_two_id = self.api_class._get_api_id(charm_two)
196+ self.assertEqual(
197+ [charm_two_id], [charm['id'] for charm
198+ in response['result']['requires']['imap']])
199+ charm_two['series'] = factory.random_string(10)
200+ charm_source.save(charm_two)
201+ response = self.get_charm_response(charm_one, 'related')
202+ self.assertEqual({}, response['result']['requires'])
203+
204
205 class TestAPI1(TestAPI, API1Mixin):
206 """Test API 1."""

Subscribers

People subscribed via source and target branches