Merge lp:~stolowski/unity-scope-click/departments into lp:unity-scope-click/devel

Proposed by Paweł Stołowski
Status: Superseded
Proposed branch: lp:~stolowski/unity-scope-click/departments
Merge into: lp:unity-scope-click/devel
Prerequisite: lp:~stolowski/unity-scope-click/update-for-api-0.5.0
Diff against target: 2069 lines (+1475/-36) (has conflicts)
25 files modified
libclickscope/click/CMakeLists.txt (+3/-0)
libclickscope/click/department-lookup.cpp (+84/-0)
libclickscope/click/department-lookup.h (+58/-0)
libclickscope/click/departments.cpp (+173/-0)
libclickscope/click/departments.h (+83/-0)
libclickscope/click/highlights.cpp (+95/-0)
libclickscope/click/highlights.h (+72/-0)
libclickscope/click/index.cpp (+40/-2)
libclickscope/click/index.h (+6/-1)
libclickscope/click/package.h (+1/-0)
libclickscope/click/webclient.cpp (+2/-1)
libclickscope/click/webclient.h (+1/-1)
libclickscope/tests/CMakeLists.txt (+2/-0)
libclickscope/tests/fake_json.h (+271/-0)
libclickscope/tests/mock_network_access_manager.h (+1/-0)
libclickscope/tests/mock_webclient.h (+2/-2)
libclickscope/tests/test_bootstrap.cpp (+73/-0)
libclickscope/tests/test_departments.cpp (+151/-0)
libclickscope/tests/test_index.cpp (+2/-2)
scope/clickstore/store-query.cpp (+238/-1)
scope/clickstore/store-query.h (+16/-3)
scope/clickstore/store-scope.cpp (+31/-3)
scope/clickstore/store-scope.h (+10/-0)
scope/tests/integration/webclient_integration.cpp (+1/-0)
scope/tests/test_query.cpp (+59/-20)
Text conflict in scope/clickstore/store-query.cpp
Text conflict in scope/clickstore/store-scope.cpp
To merge this branch: bzr merge lp:~stolowski/unity-scope-click/departments
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Alejandro J. Cura (community) Needs Fixing
dobey Pending
Review via email: mp+223629@code.launchpad.net

This proposal supersedes a proposal from 2014-05-12.

This proposal has been superseded by a proposal from 2014-06-23.

Description of the change

Add support for departments and highlights.

To post a comment you must log in.
Revision history for this message
dobey (dobey) wrote : Posted in a previous version of this proposal

426 +std::map<std::string, std::string> Index::build_headers(const std::string& language)
438 + {"Accept-Language", language},
486 + QSharedPointer<click::web::Response> response(client->call(
487 + get_base_url() + click::BOOTSTRAP_PATH, "GET", false, build_headers("en"), "", params)); //TODO: language

You don't need to do all this work to pass the language around or set Accept-Language manually. It is already added automatically in web::Client::cal().

review: Needs Fixing
Revision history for this message
dobey (dobey) wrote : Posted in a previous version of this proposal

Can you also add a commit with --fixes=lp:1209224 to link that bug?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
dobey (dobey) wrote : Posted in a previous version of this proposal
review: Needs Fixing
Revision history for this message
Paweł Stołowski (stolowski) wrote :

That should have been fixed now.

257. By Paweł Stołowski

Merged devel.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alejandro J. Cura (alecu) wrote :

On one hand, I like this branch.
I've run it on my desktop against testing, and it's showing the highlights as expected, and seems to be working fine otherwise, so kudos for that.

On the other, I'm a bit disappointed with the length, and a bit more that the unit testing for it seems lacking. Here's a list of what I've found is missing:
- tests for methods in departments.cpp (eg: from_json_root_node)
- tests for failures in departments json parsing
- tests for DepartmentLookup::get_department_info
- tests for parsing Highlights, and for failing to parse them
- tests for Index::bootstrap and Index::departments
- test_query has fixes for existing "search" tests, but it has no new tests for Highlights, Departments

Also, I think Index::bootstrap() could be rewritten as just a call to Index::departments(get_base_url() + click::BOOTSTRAP_PATH, .....)

Before approving I'd like to have jenkins build a proper package to test on the device.
After that, and since we are so close to feature freeze, I'll propose to land this, and to open a bug for the missing tests.

review: Needs Fixing
258. By Paweł Stołowski

Reuse code between bootstrap and departments call.

259. By Paweł Stołowski

Cleanups. Check department href in tests.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
260. By Paweł Stołowski

Test get_department_info().

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
261. By Paweł Stołowski

Added tests for broken departments json.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
262. By Paweł Stołowski

Don't pass departments with search.

263. By Paweł Stołowski

Test for invalid highlights. Cleanups.

264. By Paweł Stołowski

Reuse push_package().

265. By Paweł Stołowski

Fixed comment.

266. By Paweł Stołowski

Test bootstrap call.

267. By Paweł Stołowski

Merged devel.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libclickscope/click/CMakeLists.txt'
2--- libclickscope/click/CMakeLists.txt 2014-06-20 20:00:53 +0000
3+++ libclickscope/click/CMakeLists.txt 2014-06-23 10:26:18 +0000
4@@ -12,6 +12,9 @@
5 add_library(${SCOPE_LIB_NAME} STATIC
6 configuration.cpp
7 download-manager.cpp
8+ department-lookup.cpp
9+ departments.cpp
10+ highlights.cpp
11 index.cpp
12 interface.cpp
13 key_file_locator.cpp
14
15=== added file 'libclickscope/click/department-lookup.cpp'
16--- libclickscope/click/department-lookup.cpp 1970-01-01 00:00:00 +0000
17+++ libclickscope/click/department-lookup.cpp 2014-06-23 10:26:18 +0000
18@@ -0,0 +1,84 @@
19+/*
20+ * Copyright (C) 2014 Canonical Ltd.
21+ *
22+ * This program is free software: you can redistribute it and/or modify it
23+ * under the terms of the GNU General Public License version 3, as published
24+ * by the Free Software Foundation.
25+ *
26+ * This program is distributed in the hope that it will be useful, but
27+ * WITHOUT ANY WARRANTY; without even the implied warranties of
28+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
29+ * PURPOSE. See the GNU General Public License for more details.
30+ *
31+ * You should have received a copy of the GNU General Public License along
32+ * with this program. If not, see <http://www.gnu.org/licenses/>.
33+ *
34+ * In addition, as a special exception, the copyright holders give
35+ * permission to link the code of portions of this program with the
36+ * OpenSSL library under certain conditions as described in each
37+ * individual source file, and distribute linked combinations
38+ * including the two.
39+ * You must obey the GNU General Public License in all respects
40+ * for all of the code used other than OpenSSL. If you modify
41+ * file(s) with this exception, you may extend this exception to your
42+ * version of the file(s), but you are not obligated to do so. If you
43+ * do not wish to do so, delete this exception statement from your
44+ * version. If you delete this exception statement from all source
45+ * files in the program, then also delete it here.
46+ */
47+
48+#include "department-lookup.h"
49+
50+namespace click
51+{
52+
53+DepartmentLookup::DepartmentLookup()
54+{
55+}
56+
57+void DepartmentLookup::rebuild(const Department::SPtr& dept)
58+{
59+ departments[dept->id()] = dept;
60+ for (auto const& subdep: dept->sub_departments())
61+ {
62+ parent_lut[subdep->id()] = dept;
63+ rebuild(subdep);
64+ }
65+}
66+
67+void DepartmentLookup::rebuild(const std::list<Department::SPtr>& root_departments)
68+{
69+ parent_lut.clear();
70+ departments.clear();
71+ for (auto const& dep: root_departments)
72+ {
73+ rebuild(dep);
74+ }
75+}
76+
77+Department::SPtr DepartmentLookup::get_parent(const std::string& department_id) const
78+{
79+ auto it = parent_lut.find(department_id);
80+ if (it != parent_lut.end())
81+ {
82+ return it->second;
83+ }
84+ return Department::SPtr(nullptr);
85+}
86+
87+Department::SPtr DepartmentLookup::get_department_info(const std::string& department_id) const
88+{
89+ auto it = departments.find(department_id);
90+ if (it != departments.end())
91+ {
92+ return it->second;
93+ }
94+ return nullptr;
95+}
96+
97+int DepartmentLookup::size() const
98+{
99+ return parent_lut.size();
100+}
101+
102+}
103
104=== added file 'libclickscope/click/department-lookup.h'
105--- libclickscope/click/department-lookup.h 1970-01-01 00:00:00 +0000
106+++ libclickscope/click/department-lookup.h 2014-06-23 10:26:18 +0000
107@@ -0,0 +1,58 @@
108+/*
109+ * Copyright (C) 2014 Canonical Ltd.
110+ *
111+ * This program is free software: you can redistribute it and/or modify it
112+ * under the terms of the GNU General Public License version 3, as published
113+ * by the Free Software Foundation.
114+ *
115+ * This program is distributed in the hope that it will be useful, but
116+ * WITHOUT ANY WARRANTY; without even the implied warranties of
117+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
118+ * PURPOSE. See the GNU General Public License for more details.
119+ *
120+ * You should have received a copy of the GNU General Public License along
121+ * with this program. If not, see <http://www.gnu.org/licenses/>.
122+ *
123+ * In addition, as a special exception, the copyright holders give
124+ * permission to link the code of portions of this program with the
125+ * OpenSSL library under certain conditions as described in each
126+ * individual source file, and distribute linked combinations
127+ * including the two.
128+ * You must obey the GNU General Public License in all respects
129+ * for all of the code used other than OpenSSL. If you modify
130+ * file(s) with this exception, you may extend this exception to your
131+ * version of the file(s), but you are not obligated to do so. If you
132+ * do not wish to do so, delete this exception statement from your
133+ * version. If you delete this exception statement from all source
134+ * files in the program, then also delete it here.
135+ */
136+
137+#ifndef CLICK_DEPARTMENT_LOOKUP_H
138+#define CLICK_DEPARTMENT_LOOKUP_H
139+
140+#include "departments.h"
141+#include <string>
142+#include <memory>
143+#include <map>
144+
145+namespace click
146+{
147+
148+class DepartmentLookup
149+{
150+ public:
151+ DepartmentLookup();
152+ void rebuild(const std::list<Department::SPtr>& root_departments);
153+ Department::SPtr get_parent(const std::string& department_id) const;
154+ Department::SPtr get_department_info(const std::string& department_id) const;
155+ int size() const;
156+
157+ private:
158+ void rebuild(const Department::SPtr& dept);
159+ std::map<std::string, Department::SPtr> parent_lut;
160+ std::map<std::string, Department::SPtr> departments;
161+};
162+
163+}
164+
165+#endif
166
167=== added file 'libclickscope/click/departments.cpp'
168--- libclickscope/click/departments.cpp 1970-01-01 00:00:00 +0000
169+++ libclickscope/click/departments.cpp 2014-06-23 10:26:18 +0000
170@@ -0,0 +1,173 @@
171+/*
172+ * Copyright (C) 2014 Canonical Ltd.
173+ *
174+ * This program is free software: you can redistribute it and/or modify it
175+ * under the terms of the GNU General Public License version 3, as published
176+ * by the Free Software Foundation.
177+ *
178+ * This program is distributed in the hope that it will be useful, but
179+ * WITHOUT ANY WARRANTY; without even the implied warranties of
180+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
181+ * PURPOSE. See the GNU General Public License for more details.
182+ *
183+ * You should have received a copy of the GNU General Public License along
184+ * with this program. If not, see <http://www.gnu.org/licenses/>.
185+ *
186+ * In addition, as a special exception, the copyright holders give
187+ * permission to link the code of portions of this program with the
188+ * OpenSSL library under certain conditions as described in each
189+ * individual source file, and distribute linked combinations
190+ * including the two.
191+ * You must obey the GNU General Public License in all respects
192+ * for all of the code used other than OpenSSL. If you modify
193+ * file(s) with this exception, you may extend this exception to your
194+ * version of the file(s), but you are not obligated to do so. If you
195+ * do not wish to do so, delete this exception statement from your
196+ * version. If you delete this exception statement from all source
197+ * files in the program, then also delete it here.
198+ */
199+
200+#include "departments.h"
201+#include <iostream>
202+
203+namespace click
204+{
205+
206+Department::Department(const std::string& id, const std::string &name, const std::string& href, bool has_children)
207+ : id_(id),
208+ name_(name),
209+ href_(href),
210+ has_children_flag_(has_children)
211+{
212+}
213+
214+std::string Department::id() const
215+{
216+ return id_;
217+}
218+
219+std::string Department::name() const
220+{
221+ return name_;
222+}
223+
224+std::string Department::href() const
225+{
226+ return href_;
227+}
228+
229+bool Department::has_children_flag() const
230+{
231+ return has_children_flag_;
232+}
233+
234+void Department::set_subdepartments(const std::list<Department::SPtr>& deps)
235+{
236+ sub_departments_ = deps;
237+}
238+
239+std::list<Department::SPtr> Department::sub_departments() const
240+{
241+ return sub_departments_;
242+}
243+
244+Json::Value Department::check_mandatory_attribute(const Json::Value& item, const std::string& name, Json::ValueType valtype)
245+{
246+ if (!item.isMember(name)) {
247+ throw std::runtime_error("Missing '" + name + "' node");
248+ }
249+ auto const val = item[name];
250+ if (val.type() != valtype) {
251+ throw std::runtime_error("Invalid type of '" + name + "' node");
252+ }
253+ return val;
254+}
255+
256+std::list<Department::SPtr> Department::from_json_node(const Json::Value& node)
257+{
258+ std::list<Department::SPtr> deps;
259+
260+ for (uint i = 0; i < node.size(); i++)
261+ {
262+ try
263+ {
264+ auto const item = node[i];
265+
266+ auto name = check_mandatory_attribute(item, Department::JsonKeys::name, Json::ValueType::stringValue).asString();
267+ const bool has_children = item.isMember(Department::JsonKeys::has_children) ? item[Department::JsonKeys::has_children].asBool() : false;
268+
269+ auto const links = check_mandatory_attribute(item, Department::JsonKeys::links, Json::ValueType::objectValue);
270+ auto const self = check_mandatory_attribute(links, Department::JsonKeys::self, Json::ValueType::objectValue);
271+ auto const href = check_mandatory_attribute(self, Department::JsonKeys::href, Json::ValueType::stringValue).asString();
272+
273+ auto dep = std::make_shared<Department>(name, name, href, has_children); //FIXME: id
274+ if (item.isObject() && item.isMember(Department::JsonKeys::embedded))
275+ {
276+ auto const emb = item[Department::JsonKeys::embedded];
277+ if (emb.isObject() && emb.isMember(Department::JsonKeys::department))
278+ {
279+ auto const ditem = emb[Department::JsonKeys::department];
280+ auto const subdeps = from_json_node(ditem);
281+ dep->set_subdepartments(subdeps);
282+ }
283+ }
284+ deps.push_back(dep);
285+ }
286+ catch (const std::runtime_error& e)
287+ {
288+ std::cerr << "Invalid department #" << i << std::endl;
289+ }
290+ }
291+
292+ return deps;
293+}
294+
295+std::list<Department::SPtr> Department::from_json_root_node(const Json::Value& root)
296+{
297+ if (root.isObject() && root.isMember(Department::JsonKeys::embedded))
298+ {
299+ auto const emb = root[Department::JsonKeys::embedded];
300+ if (emb.isObject() && emb.isMember(Department::JsonKeys::department))
301+ {
302+ auto const ditem = emb[Department::JsonKeys::department];
303+ return from_json_node(ditem);
304+ }
305+ }
306+
307+ return std::list<Department::SPtr>();
308+}
309+
310+std::list<Department::SPtr> Department::from_json(const std::string& json)
311+{
312+ Json::Reader reader;
313+ Json::Value root;
314+
315+ try
316+ {
317+ if (!reader.parse(json, root)) {
318+ throw std::runtime_error(reader.getFormattedErrorMessages());
319+ }
320+
321+ if (root.isObject() && root.isMember(Department::JsonKeys::embedded))
322+ {
323+ auto const emb = root[Department::JsonKeys::embedded];
324+ if (emb.isObject() && emb.isMember(Department::JsonKeys::department))
325+ {
326+ auto const ditem = emb[Department::JsonKeys::department];
327+ return from_json_node(ditem);
328+ }
329+ }
330+ }
331+ catch (const std::exception& e)
332+ {
333+ std::cerr << "Error parsing departments: " << e.what() << std::endl;
334+ }
335+ catch (...)
336+ {
337+ std::cerr << "Unknown error when parsing departments" << std::endl;
338+ }
339+
340+ return std::list<Department::SPtr>();
341+}
342+
343+}
344
345=== added file 'libclickscope/click/departments.h'
346--- libclickscope/click/departments.h 1970-01-01 00:00:00 +0000
347+++ libclickscope/click/departments.h 2014-06-23 10:26:18 +0000
348@@ -0,0 +1,83 @@
349+/*
350+ * Copyright (C) 2014 Canonical Ltd.
351+ *
352+ * This program is free software: you can redistribute it and/or modify it
353+ * under the terms of the GNU General Public License version 3, as published
354+ * by the Free Software Foundation.
355+ *
356+ * This program is distributed in the hope that it will be useful, but
357+ * WITHOUT ANY WARRANTY; without even the implied warranties of
358+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
359+ * PURPOSE. See the GNU General Public License for more details.
360+ *
361+ * You should have received a copy of the GNU General Public License along
362+ * with this program. If not, see <http://www.gnu.org/licenses/>.
363+ *
364+ * In addition, as a special exception, the copyright holders give
365+ * permission to link the code of portions of this program with the
366+ * OpenSSL library under certain conditions as described in each
367+ * individual source file, and distribute linked combinations
368+ * including the two.
369+ * You must obey the GNU General Public License in all respects
370+ * for all of the code used other than OpenSSL. If you modify
371+ * file(s) with this exception, you may extend this exception to your
372+ * version of the file(s), but you are not obligated to do so. If you
373+ * do not wish to do so, delete this exception statement from your
374+ * version. If you delete this exception statement from all source
375+ * files in the program, then also delete it here.
376+ */
377+
378+#ifndef CLICK_DEPARTMENTS_H
379+#define CLICK_DEPARTMENTS_H
380+
381+#include <string>
382+#include <list>
383+#include <memory>
384+#include <json/json.h>
385+
386+namespace click
387+{
388+
389+class Department
390+{
391+ public:
392+ typedef std::shared_ptr<Department> SPtr;
393+ typedef std::shared_ptr<Department const> SCPtr;
394+
395+ struct JsonKeys
396+ {
397+ JsonKeys() = delete;
398+ constexpr static const char* name {"name"};
399+ constexpr static const char* embedded {"_embedded"};
400+ constexpr static const char* department {"clickindex:department"};
401+ constexpr static const char* has_children {"has_children"};
402+ constexpr static const char* links {"_links"};
403+ constexpr static const char* self {"self"};
404+ constexpr static const char* href {"href"};
405+ };
406+
407+ Department(const std::string &id, const std::string &name, const std::string& href, bool has_children);
408+ std::string id() const;
409+ std::string name() const;
410+ std::string href() const;
411+ bool has_children_flag() const;
412+ void set_subdepartments(const std::list<Department::SPtr>& deps);
413+ std::list<Department::SPtr> sub_departments() const;
414+ static std::list<Department::SPtr> from_json(const std::string& json);
415+ static std::list<Department::SPtr> from_json_root_node(const Json::Value& val);
416+
417+ private:
418+ static std::list<Department::SPtr> from_json_node(const Json::Value& val);
419+ static Json::Value check_mandatory_attribute(const Json::Value& item, const std::string& name, Json::ValueType valtype);
420+ std::string id_;
421+ std::string name_;
422+ std::string href_;
423+ bool has_children_flag_;
424+ std::list<Department::SPtr> sub_departments_;
425+};
426+
427+typedef std::list<Department::SPtr> DepartmentList;
428+
429+}
430+
431+#endif
432
433=== added file 'libclickscope/click/highlights.cpp'
434--- libclickscope/click/highlights.cpp 1970-01-01 00:00:00 +0000
435+++ libclickscope/click/highlights.cpp 2014-06-23 10:26:18 +0000
436@@ -0,0 +1,95 @@
437+/*
438+ * Copyright (C) 2014 Canonical Ltd.
439+ *
440+ * This program is free software: you can redistribute it and/or modify it
441+ * under the terms of the GNU General Public License version 3, as published
442+ * by the Free Software Foundation.
443+ *
444+ * This program is distributed in the hope that it will be useful, but
445+ * WITHOUT ANY WARRANTY; without even the implied warranties of
446+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
447+ * PURPOSE. See the GNU General Public License for more details.
448+ *
449+ * You should have received a copy of the GNU General Public License along
450+ * with this program. If not, see <http://www.gnu.org/licenses/>.
451+ *
452+ * In addition, as a special exception, the copyright holders give
453+ * permission to link the code of portions of this program with the
454+ * OpenSSL library under certain conditions as described in each
455+ * individual source file, and distribute linked combinations
456+ * including the two.
457+ * You must obey the GNU General Public License in all respects
458+ * for all of the code used other than OpenSSL. If you modify
459+ * file(s) with this exception, you may extend this exception to your
460+ * version of the file(s), but you are not obligated to do so. If you
461+ * do not wish to do so, delete this exception statement from your
462+ * version. If you delete this exception statement from all source
463+ * files in the program, then also delete it here.
464+ */
465+
466+#include "highlights.h"
467+#include <iostream>
468+
469+namespace click
470+{
471+
472+Highlight::Highlight(const std::string& name)
473+ : name_(name)
474+{
475+}
476+
477+Highlight::Highlight(const std::string& name, const Packages& pkgs)
478+ : name_(name),
479+ packages_(pkgs)
480+{
481+}
482+
483+void Highlight::add_package(const Package& pkg)
484+{
485+ packages_.push_back(pkg);
486+}
487+
488+std::string Highlight::name() const
489+{
490+ return name_;
491+}
492+
493+Packages Highlight::packages() const
494+{
495+ return packages_;
496+}
497+
498+std::list<Highlight> Highlight::from_json_node(const Json::Value& node)
499+{
500+ std::list<Highlight> highlights;
501+
502+ for (uint i = 0; i < node.size(); i++)
503+ {
504+ auto const item = node[i];
505+ if (item.isObject() && item.isMember(Highlight::JsonKeys::name))
506+ {
507+ auto name = item[Highlight::JsonKeys::name].asString();
508+ auto pkgs = package_list_from_json_node(item);
509+ highlights.push_back(Highlight(name, pkgs));
510+ }
511+ }
512+
513+ return highlights;
514+}
515+
516+std::list<Highlight> Highlight::from_json_root_node(const Json::Value& root)
517+{
518+ if (root.isObject() && root.isMember(Highlight::JsonKeys::embedded))
519+ {
520+ auto const emb = root[Highlight::JsonKeys::embedded];
521+ if (emb.isObject() && emb.isMember(Highlight::JsonKeys::highlight))
522+ {
523+ auto const hl = emb[Highlight::JsonKeys::highlight];
524+ return from_json_node(hl);
525+ }
526+ }
527+
528+ return std::list<Highlight>();
529+}
530+
531+}
532
533=== added file 'libclickscope/click/highlights.h'
534--- libclickscope/click/highlights.h 1970-01-01 00:00:00 +0000
535+++ libclickscope/click/highlights.h 2014-06-23 10:26:18 +0000
536@@ -0,0 +1,72 @@
537+/*
538+ * Copyright (C) 2014 Canonical Ltd.
539+ *
540+ * This program is free software: you can redistribute it and/or modify it
541+ * under the terms of the GNU General Public License version 3, as published
542+ * by the Free Software Foundation.
543+ *
544+ * This program is distributed in the hope that it will be useful, but
545+ * WITHOUT ANY WARRANTY; without even the implied warranties of
546+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
547+ * PURPOSE. See the GNU General Public License for more details.
548+ *
549+ * You should have received a copy of the GNU General Public License along
550+ * with this program. If not, see <http://www.gnu.org/licenses/>.
551+ *
552+ * In addition, as a special exception, the copyright holders give
553+ * permission to link the code of portions of this program with the
554+ * OpenSSL library under certain conditions as described in each
555+ * individual source file, and distribute linked combinations
556+ * including the two.
557+ * You must obey the GNU General Public License in all respects
558+ * for all of the code used other than OpenSSL. If you modify
559+ * file(s) with this exception, you may extend this exception to your
560+ * version of the file(s), but you are not obligated to do so. If you
561+ * do not wish to do so, delete this exception statement from your
562+ * version. If you delete this exception statement from all source
563+ * files in the program, then also delete it here.
564+ */
565+
566+#ifndef CLICK_HIGHLIGHTS_H
567+#define CLICK_HIGHLIGHTS_H
568+
569+#include <string>
570+#include <list>
571+#include <json/json.h>
572+#include <click/package.h>
573+
574+namespace click
575+{
576+
577+class Highlight
578+{
579+public:
580+ struct JsonKeys
581+ {
582+ JsonKeys() = delete;
583+ constexpr static const char* name {"name"};
584+ constexpr static const char* embedded {"_embedded"};
585+ constexpr static const char* highlight {"clickindex:highlight"};
586+ };
587+
588+ Highlight(const std::string& name);
589+ Highlight(const std::string& name, const Packages& pkgs);
590+ void add_package(const Package& pkg);
591+
592+ std::string name() const;
593+ Packages packages() const;
594+
595+ static std::list<Highlight> from_json_root_node(const Json::Value& val);
596+
597+private:
598+ static std::list<Highlight> from_json_node(const Json::Value& val);
599+
600+ std::string name_;
601+ Packages packages_;
602+};
603+
604+typedef std::list<Highlight> HighlightList;
605+
606+}
607+
608+#endif
609
610=== modified file 'libclickscope/click/index.cpp'
611--- libclickscope/click/index.cpp 2014-06-12 21:05:25 +0000
612+++ libclickscope/click/index.cpp 2014-06-23 10:26:18 +0000
613@@ -88,11 +88,15 @@
614
615 }
616
617-std::string Index::build_index_query(const std::string& query)
618+std::string Index::build_index_query(const std::string& query, const std::string& department)
619 {
620 std::stringstream result;
621
622 result << query;
623+ if (!department.empty()) {
624+ result << ",department:" << department;
625+ }
626+
627 return result.str();
628 }
629
630@@ -113,7 +117,7 @@
631 click::web::Cancellable Index::search (const std::string& query, std::function<void(click::Packages)> callback)
632 {
633 click::web::CallParams params;
634- const std::string built_query(build_index_query(query));
635+ const std::string built_query(build_index_query(query, ""));
636 params.add(click::QUERY_ARGNAME, built_query.c_str());
637 QSharedPointer<click::web::Response> response(client->call(
638 get_base_url() + click::SEARCH_PATH, "GET", false, build_headers(), "", params));
639@@ -139,6 +143,40 @@
640 return click::web::Cancellable(response);
641 }
642
643+click::web::Cancellable Index::bootstrap(std::function<void(const click::DepartmentList&, const click::HighlightList&, Error, int)> callback)
644+{
645+ return departments(get_base_url() + click::BOOTSTRAP_PATH, callback);
646+}
647+
648+click::web::Cancellable Index::departments(const std::string& department_href, std::function<void(const DepartmentList&, const HighlightList&, Error, int)> callback)
649+{
650+ click::web::CallParams params;
651+ QSharedPointer<click::web::Response> response(client->call(
652+ department_href, "GET", false, build_headers(), "", params));
653+
654+ QObject::connect(response.data(), &click::web::Response::finished, [=](QString reply) {
655+ qDebug() << "departments request finished";
656+ Json::Reader reader;
657+ Json::Value root;
658+
659+ click::DepartmentList depts;
660+ click::HighlightList highlights;
661+ if (reader.parse(reply.toUtf8().constData(), root)) {
662+ depts = Department::from_json_root_node(root);
663+ highlights = Highlight::from_json_root_node(root);
664+ }
665+ callback(depts, highlights, click::Index::Error::NoError, 0);
666+ });
667+ QObject::connect(response.data(), &click::web::Response::error, [=](QString /*description*/, int error_code) {
668+ qWarning() << "departments call failed due to network error";
669+ const click::DepartmentList depts;
670+ const click::HighlightList highlights;
671+ qDebug() << "departments: calling callback";
672+ callback(depts, highlights, click::Index::Error::NetworkError, error_code);
673+ });
674+ return click::web::Cancellable(response);
675+}
676+
677 click::web::Cancellable Index::get_details (const std::string& package_name, std::function<void(PackageDetails, click::Index::Error)> callback)
678 {
679 QSharedPointer<click::web::Response> response = client->call
680
681=== modified file 'libclickscope/click/index.h'
682--- libclickscope/click/index.h 2014-06-12 21:05:25 +0000
683+++ libclickscope/click/index.h 2014-06-23 10:26:18 +0000
684@@ -38,6 +38,8 @@
685 #include <click/webclient.h>
686
687 #include "package.h"
688+#include <click/departments.h>
689+#include <click/highlights.h>
690
691
692 namespace click {
693@@ -47,6 +49,7 @@
694 const std::string SEARCH_BASE_URL_ENVVAR = "U1_SEARCH_BASE_URL";
695 const std::string SEARCH_BASE_URL = "https://search.apps.ubuntu.com/";
696 const std::string SEARCH_PATH = "api/v1/search";
697+const std::string BOOTSTRAP_PATH = "api/v1";
698 const std::string SUPPORTED_FRAMEWORKS = "framework:ubuntu-sdk-13.10";
699 const std::string QUERY_ARGNAME = "q";
700 const std::string ARCHITECTURE = "architecture:";
701@@ -64,7 +67,7 @@
702 protected:
703 QSharedPointer<web::Client> client;
704 QSharedPointer<Configuration> configuration;
705- virtual std::string build_index_query(const std::string& query);
706+ virtual std::string build_index_query(const std::string& query, const std::string& department);
707 virtual std::map<std::string, std::string> build_headers();
708
709 public:
710@@ -73,6 +76,8 @@
711 const QSharedPointer<Configuration> configuration=QSharedPointer<Configuration>(new Configuration()));
712 virtual click::web::Cancellable search (const std::string& query, std::function<void(Packages)> callback);
713 virtual click::web::Cancellable get_details(const std::string& package_name, std::function<void(PackageDetails, Error)> callback);
714+ virtual click::web::Cancellable bootstrap(std::function<void(const DepartmentList&, const HighlightList&, Error, int)> callback);
715+ virtual click::web::Cancellable departments(const std::string& department_href, std::function<void(const DepartmentList&, const HighlightList&, Error, int)> callback);
716 virtual ~Index();
717
718 static std::string get_base_url ();
719
720=== modified file 'libclickscope/click/package.h'
721--- libclickscope/click/package.h 2014-06-13 22:42:02 +0000
722+++ libclickscope/click/package.h 2014-06-23 10:26:18 +0000
723@@ -34,6 +34,7 @@
724 #include <string>
725 #include <unordered_set>
726 #include <vector>
727+#include <functional>
728
729 #include <json/json.h>
730
731
732=== modified file 'libclickscope/click/webclient.cpp'
733--- libclickscope/click/webclient.cpp 2014-05-23 18:45:01 +0000
734+++ libclickscope/click/webclient.cpp 2014-06-23 10:26:18 +0000
735@@ -176,7 +176,8 @@
736 {
737 auto message = reply->errorString() + QString(" (%1)").arg(network_error);
738 qWarning() << "Network error:" << message << "\n" << reply->readAll();
739- emit error(message);
740+ int error_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
741+ emit error(message, error_code);
742 }
743
744 void click::web::Response::abort()
745
746=== modified file 'libclickscope/click/webclient.h'
747--- libclickscope/click/webclient.h 2014-05-23 18:45:01 +0000
748+++ libclickscope/click/webclient.h 2014-06-23 10:26:18 +0000
749@@ -84,7 +84,7 @@
750
751 signals:
752 void finished(QByteArray result);
753- void error(QString description);
754+ void error(QString description, int error_code);
755
756 private:
757 QSharedPointer<click::network::Reply> reply;
758
759=== modified file 'libclickscope/tests/CMakeLists.txt'
760--- libclickscope/tests/CMakeLists.txt 2014-05-26 14:27:31 +0000
761+++ libclickscope/tests/CMakeLists.txt 2014-06-23 10:26:18 +0000
762@@ -28,6 +28,8 @@
763 test_reviews.cpp
764 test_smartconnect.cpp
765 test_webclient.cpp
766+ test_bootstrap.cpp
767+ test_departments.cpp
768
769 ${CMAKE_CURRENT_BINARY_DIR}/test_data.cpp
770 )
771
772=== modified file 'libclickscope/tests/fake_json.h'
773--- libclickscope/tests/fake_json.h 2014-06-13 23:18:41 +0000
774+++ libclickscope/tests/fake_json.h 2014-06-23 10:26:18 +0000
775@@ -150,6 +150,277 @@
776 }
777 )foo";
778
779+const std::string FAKE_JSON_BOOTSTRAP = R"(
780+ {
781+ "_embedded": {
782+ "clickindex:department": [
783+ {
784+ "has_children": false,
785+ "_links": {
786+ "self": {
787+ "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-subdepartment"}
788+ },
789+ "name": "Fake Subdepartment", "slug": "fake-subdepartment"}
790+ ],
791+ "clickindex:highlight": [
792+ {
793+ "_embedded": {
794+ "clickindex:package": [
795+ {
796+ "publisher": "Awesome Widget Company",
797+ "name": "org.example.awesomelauncher",
798+ "title": "Awesome Launcher",
799+ "price": 1.99,
800+ "_links": {
801+ "self": {
802+ "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher"}
803+ },
804+ "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png"
805+ },
806+ {
807+ "publisher": "Awesome Widget Company",
808+ "name": "org.example.awesomewidget",
809+ "title": "Awesome Widget", "price": 1.99,
810+ "_links": {
811+ "self": {
812+ "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget"
813+ }
814+ },
815+ "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png"}
816+ ]
817+ },
818+ "_links": {
819+ "self": {
820+ "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/top-apps"
821+ }
822+ },
823+ "name": "Top Apps", "slug": "top-apps"
824+ },
825+ {
826+ "_embedded": {
827+ "clickindex:package": [
828+ {
829+ "publisher": "Awesome Widget Company",
830+ "name": "org.example.awesomelauncher",
831+ "title": "Awesome Launcher",
832+ "price": 1.99,
833+ "_links": {
834+ "self": {
835+ "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher"
836+ }
837+ },
838+ "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png"
839+ },
840+ {
841+ "publisher": "Awesome Widget Company",
842+ "name": "org.example.awesomewidget",
843+ "title": "Awesome Widget",
844+ "price": 1.99,
845+ "_links": {
846+ "self": {
847+ "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget"
848+ }
849+ },
850+ "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png"
851+ }
852+ ]
853+ },
854+ "_links": {
855+ "self": {
856+ "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/most-purchased"
857+ }
858+ },
859+ "name": "Most Purchased",
860+ "slug": "most-purchased"
861+ },
862+ {
863+ "_embedded": {
864+ "clickindex:package": [
865+ {
866+ "publisher": "Awesome Widget Company",
867+ "name": "org.example.awesomelauncher",
868+ "title": "Awesome Launcher",
869+ "price": 1.99,
870+ "_links": {
871+ "self": {
872+ "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomelauncher"
873+ }
874+ },
875+ "icon": "http://example.org/media/org.example.awesomelauncher/icons/icon16.png"
876+ },
877+ {
878+ "publisher": "Awesome Widget Company",
879+ "name": "org.example.awesomewidget",
880+ "title": "Awesome Widget",
881+ "price": 1.99,
882+ "_links": {
883+ "self": {
884+ "href": "https://search.apps.staging.ubuntu.com/api/v1/package/org.example.awesomewidget"
885+ }
886+ },
887+ "icon": "http://example.org/media/org.example.awesomewidget/icons/icon16.png"
888+ }
889+ ]
890+ },
891+ "_links": {
892+ "self": {
893+ "href": "https://search.apps.staging.ubuntu.com/api/v1/highlights/new-releases"
894+ }
895+ },
896+ "name": "New Releases",
897+ "slug": "new-releases"
898+ }
899+ ]
900+ }, "has_children": true,
901+ "_links": {
902+ "curies": [
903+ {
904+ "href": "https://search.apps.staging.ubuntu.com/docs/v1/relations.html{#rel}",
905+ "name": "clickindex", "templated": true
906+ }
907+ ],
908+ "self": {
909+ "href": "https://search.apps.staging.ubuntu.com/api/v1/departments/fake-department-with-subdepartments"
910+ },
911+ "collection": {
912+ "href": "https://search.apps.staging.ubuntu.com/api/v1/departments"
913+ }
914+ },
915+ "name": "Fake Department With Subdepartments",
916+ "slug": "fake-department-with-subdepartments"
917+ })";
918+
919+const std::string FAKE_JSON_DEPARTMENTS_ONLY = R"(
920+ {
921+ "_links": {
922+ "self": {
923+ "href": "https://search.apps.ubuntu.com/api/v1/departments"
924+ },
925+ "curies": [
926+ {
927+ "name": "clickindex",
928+ "href": "https://search.apps.ubuntu.com/docs/v1/relations.html{#rel}",
929+ "templated": true
930+ }
931+ ]
932+ },
933+ "_embedded": {
934+ "clickindex:department": [
935+ {
936+ "name": "Games",
937+ "_links": {
938+ "self": {
939+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Games"
940+ }
941+ },
942+ "_embedded": {
943+ "clickindex:department": [
944+ {
945+ "name": "Board Games",
946+ "_links": {
947+ "self": {
948+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Games/Board+Games"
949+ }
950+ }
951+ }
952+ ]
953+ }
954+ },
955+ {
956+ "name": "Graphics",
957+ "_links": {
958+ "self": {
959+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Graphics"
960+ }
961+ },
962+ "_embedded": {
963+ "clickindex:department": [
964+ {
965+ "name": "Drawing",
966+ "_links": {
967+ "self": {
968+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Graphics/Drawing"
969+ }
970+ }
971+ }
972+ ]
973+ }
974+ },
975+ {
976+ "name": "Internet",
977+ "_links": {
978+ "self": {
979+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet"
980+ }
981+ },
982+ "_embedded": {
983+ "clickindex:department": [
984+ {
985+ "name": "Chat",
986+ "_links": {
987+ "self": {
988+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Chat"
989+ }
990+ }
991+ },
992+ {
993+ "name": "Mail",
994+ "_links": {
995+ "self": {
996+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Mail"
997+ }
998+ }
999+ },
1000+ {
1001+ "name": "Web Browsers",
1002+ "_links": {
1003+ "self": {
1004+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Internet/Web+Browsers"
1005+ }
1006+ }
1007+ }
1008+ ]
1009+ }
1010+ }
1011+ ]
1012+ }
1013+})";
1014+
1015+const std::string FAKE_JSON_BROKEN_DEPARTMENTS = R"(
1016+ {
1017+ "_links": {
1018+ "self": {
1019+ "href": "https://search.apps.ubuntu.com/api/v1/departments"
1020+ },
1021+ "curies": [
1022+ {
1023+ "name": "clickindex",
1024+ "href": "https://search.apps.ubuntu.com/docs/v1/relations.html{#rel}",
1025+ "templated": true
1026+ }
1027+ ]
1028+ },
1029+ "_embedded": {
1030+ "clickindex:department": [
1031+ {
1032+ "name": "Games",
1033+ "_links": {
1034+ "self": {
1035+ "href": "https://search.apps.ubuntu.com/api/v1/departments/Games"
1036+ }
1037+ },
1038+ "_embedded": {
1039+ "clickindex:department": [
1040+ {
1041+ "name": "Broken department"
1042+ }
1043+ ]
1044+ }
1045+ }
1046+ ]
1047+ }
1048+ })";
1049+
1050 const std::string FAKE_JSON_MANIFEST_REMOVABLE = R"foo(
1051 {
1052 "_removable": 1,
1053
1054=== modified file 'libclickscope/tests/mock_network_access_manager.h'
1055--- libclickscope/tests/mock_network_access_manager.h 2014-05-20 19:33:41 +0000
1056+++ libclickscope/tests/mock_network_access_manager.h 2014-06-23 10:26:18 +0000
1057@@ -48,6 +48,7 @@
1058 {
1059 // Set a default value for QByteArray-returning mocked methods.
1060 ::testing::DefaultValue<QByteArray>::Set(QByteArray(""));
1061+ ON_CALL(*this, attribute(::testing::_)).WillByDefault(::testing::Return(0));
1062 }
1063
1064 MOCK_METHOD0(abort, void());
1065
1066=== modified file 'libclickscope/tests/mock_webclient.h'
1067--- libclickscope/tests/mock_webclient.h 2014-05-23 18:45:01 +0000
1068+++ libclickscope/tests/mock_webclient.h 2014-06-23 10:26:18 +0000
1069@@ -86,7 +86,7 @@
1070 const click::web::CallParams& params));
1071 QSharedPointer<click::web::Response> call(
1072 const std::string& iri,
1073- const click::web::CallParams& params=click::web::CallParams()) {
1074+ const click::web::CallParams& params=click::web::CallParams()) override {
1075 return callImpl(iri, "GET", false,
1076 std::map<std::string, std::string>(), "", params);
1077 }
1078@@ -96,7 +96,7 @@
1079 bool sign = false,
1080 const std::map<std::string, std::string>& headers = std::map<std::string, std::string>(),
1081 const std::string& data = "",
1082- const click::web::CallParams& params=click::web::CallParams()) {
1083+ const click::web::CallParams& params=click::web::CallParams()) override {
1084 return callImpl(iri, method, sign, headers, data, params);
1085 }
1086 };
1087
1088=== added file 'libclickscope/tests/test_bootstrap.cpp'
1089--- libclickscope/tests/test_bootstrap.cpp 1970-01-01 00:00:00 +0000
1090+++ libclickscope/tests/test_bootstrap.cpp 2014-06-23 10:26:18 +0000
1091@@ -0,0 +1,73 @@
1092+/*
1093+ * Copyright (C) 2014 Canonical Ltd.
1094+ *
1095+ * This program is free software: you can redistribute it and/or modify it
1096+ * under the terms of the GNU General Public License version 3, as published
1097+ * by the Free Software Foundation.
1098+ *
1099+ * This program is distributed in the hope that it will be useful, but
1100+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1101+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1102+ * PURPOSE. See the GNU General Public License for more details.
1103+ *
1104+ * You should have received a copy of the GNU General Public License along
1105+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1106+ *
1107+ * In addition, as a special exception, the copyright holders give
1108+ * permission to link the code of portions of this program with the
1109+ * OpenSSL library under certain conditions as described in each
1110+ * individual source file, and distribute linked combinations
1111+ * including the two.
1112+ * You must obey the GNU General Public License in all respects
1113+ * for all of the code used other than OpenSSL. If you modify
1114+ * file(s) with this exception, you may extend this exception to your
1115+ * version of the file(s), but you are not obligated to do so. If you
1116+ * do not wish to do so, delete this exception statement from your
1117+ * version. If you delete this exception statement from all source
1118+ * files in the program, then also delete it here.
1119+ */
1120+
1121+#include <gtest/gtest.h>
1122+#include "fake_json.h"
1123+#include <json/reader.h>
1124+#include <json/value.h>
1125+#include <click/highlights.h>
1126+#include <click/departments.h>
1127+
1128+class BootstrapTest: public ::testing::Test
1129+{
1130+ protected:
1131+ void SetUp() override
1132+ {
1133+ }
1134+};
1135+
1136+TEST_F(BootstrapTest, testParsing)
1137+{
1138+ Json::Reader reader;
1139+ Json::Value root;
1140+
1141+ EXPECT_TRUE(reader.parse(FAKE_JSON_BOOTSTRAP, root));
1142+
1143+ {
1144+ auto highlights = click::Highlight::from_json_root_node(root);
1145+ EXPECT_EQ(3u, highlights.size());
1146+ auto it = highlights.begin();
1147+ EXPECT_EQ("Top Apps", it->name());
1148+ EXPECT_EQ(2u, it->packages().size());
1149+ ++it;
1150+ EXPECT_EQ("Most Purchased", it->name());
1151+ EXPECT_EQ(2u, it->packages().size());
1152+ ++it;
1153+ EXPECT_EQ("New Releases", it->name());
1154+ EXPECT_EQ(2u, it->packages().size());
1155+ }
1156+ {
1157+ auto depts = click::Department::from_json_root_node(root);
1158+ EXPECT_EQ(1u, depts.size());
1159+ auto it = depts.begin();
1160+ EXPECT_EQ("Fake Subdepartment", (*it)->name());
1161+ EXPECT_FALSE((*it)->has_children_flag());
1162+ EXPECT_EQ("https://search.apps.staging.ubuntu.com/api/v1/departments/fake-subdepartment", (*it)->href());
1163+ }
1164+}
1165
1166=== added file 'libclickscope/tests/test_departments.cpp'
1167--- libclickscope/tests/test_departments.cpp 1970-01-01 00:00:00 +0000
1168+++ libclickscope/tests/test_departments.cpp 2014-06-23 10:26:18 +0000
1169@@ -0,0 +1,151 @@
1170+/*
1171+ * Copyright (C) 2014 Canonical Ltd.
1172+ *
1173+ * This program is free software: you can redistribute it and/or modify it
1174+ * under the terms of the GNU General Public License version 3, as published
1175+ * by the Free Software Foundation.
1176+ *
1177+ * This program is distributed in the hope that it will be useful, but
1178+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1179+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1180+ * PURPOSE. See the GNU General Public License for more details.
1181+ *
1182+ * You should have received a copy of the GNU General Public License along
1183+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1184+ *
1185+ * In addition, as a special exception, the copyright holders give
1186+ * permission to link the code of portions of this program with the
1187+ * OpenSSL library under certain conditions as described in each
1188+ * individual source file, and distribute linked combinations
1189+ * including the two.
1190+ * You must obey the GNU General Public License in all respects
1191+ * for all of the code used other than OpenSSL. If you modify
1192+ * file(s) with this exception, you may extend this exception to your
1193+ * version of the file(s), but you are not obligated to do so. If you
1194+ * do not wish to do so, delete this exception statement from your
1195+ * version. If you delete this exception statement from all source
1196+ * files in the program, then also delete it here.
1197+ */
1198+
1199+#include <gtest/gtest.h>
1200+#include "fake_json.h"
1201+#include <click/departments.h>
1202+#include <click/department-lookup.h>
1203+
1204+class DepartmentsTest : public ::testing::Test
1205+{
1206+ protected:
1207+ void SetUp() override
1208+ {
1209+ }
1210+};
1211+
1212+TEST_F(DepartmentsTest, testParsing)
1213+{
1214+ const std::string jsonstr(FAKE_JSON_DEPARTMENTS_ONLY);
1215+ auto depts = click::Department::from_json(jsonstr);
1216+ EXPECT_EQ(3u, depts.size());
1217+ auto it = depts.cbegin();
1218+ {
1219+ auto dep = *it;
1220+ EXPECT_EQ("Games", dep->id());
1221+ EXPECT_EQ("Games", dep->name());
1222+ EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Games", dep->href());
1223+ EXPECT_FALSE(dep->has_children_flag());
1224+ auto subdepts = dep->sub_departments();
1225+ EXPECT_EQ(1u, subdepts.size());
1226+ auto sit = subdepts.cbegin();
1227+ EXPECT_EQ("Board Games", (*sit)->name());
1228+ }
1229+ {
1230+ ++it;
1231+ auto dep = *it;
1232+ EXPECT_EQ("Graphics", dep->id());
1233+ EXPECT_EQ("Graphics", dep->name());
1234+ EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Graphics", dep->href());
1235+ EXPECT_FALSE(dep->has_children_flag());
1236+ auto subdepts = dep->sub_departments();
1237+ EXPECT_EQ(1u, subdepts.size());
1238+ auto sit = subdepts.cbegin();
1239+ EXPECT_EQ("Drawing", (*sit)->name());
1240+ }
1241+ {
1242+ ++it;
1243+ auto dep = *it;
1244+ EXPECT_EQ("Internet", dep->id());
1245+ EXPECT_EQ("Internet", dep->name());
1246+ EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet", dep->href());
1247+ EXPECT_FALSE(dep->has_children_flag());
1248+ auto subdepts = dep->sub_departments();
1249+ EXPECT_EQ(3u, subdepts.size());
1250+ auto sit = subdepts.cbegin();
1251+ auto subdep = *sit;
1252+ EXPECT_EQ("Chat", subdep->name());
1253+ EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet/Chat", subdep->href());
1254+ subdep = *(++sit);
1255+ EXPECT_EQ("Mail", subdep->name());
1256+ EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet/Mail", subdep->href());
1257+ subdep = *(++sit);
1258+ EXPECT_EQ("Web Browsers", subdep->name());
1259+ EXPECT_EQ("https://search.apps.ubuntu.com/api/v1/departments/Internet/Web+Browsers", subdep->href());
1260+ }
1261+}
1262+
1263+TEST_F(DepartmentsTest, testParsingErrors)
1264+{
1265+ // invalid json
1266+ {
1267+ const std::string jsonstr("{{{");
1268+ auto depts = click::Department::from_json(jsonstr);
1269+ EXPECT_EQ(0, depts.size());
1270+ }
1271+ // one of the departments is invalid
1272+ {
1273+ const std::string jsonstr(FAKE_JSON_BROKEN_DEPARTMENTS);
1274+ auto depts = click::Department::from_json(jsonstr);
1275+ EXPECT_EQ(1, depts.size());
1276+ }
1277+}
1278+
1279+TEST_F(DepartmentsTest, testLookup)
1280+{
1281+ auto dep_games = std::make_shared<click::Department>("games", "Games", "http://foobar.com/", false);
1282+ auto dep_rpg = std::make_shared<click::Department>("rpg", "RPG", "http://ubuntu.com/", false);
1283+ auto dep_strategy = std::make_shared<click::Department>("strategy", "Strategy", "", false);
1284+ const std::list<click::Department::SPtr> departments {dep_rpg, dep_strategy};
1285+ dep_games->set_subdepartments(departments);
1286+
1287+ const std::list<click::Department::SPtr> root {dep_games};
1288+ click::DepartmentLookup lut;
1289+ lut.rebuild(root);
1290+
1291+ {
1292+ EXPECT_EQ(2u, lut.size());
1293+ EXPECT_EQ("games", lut.get_parent("strategy")->id());
1294+ EXPECT_EQ("games", lut.get_parent("rpg")->id());
1295+ EXPECT_EQ(nullptr, lut.get_parent("games"));
1296+ }
1297+ {
1298+ auto info = lut.get_department_info("games");
1299+ EXPECT_EQ("games", info->id());
1300+ EXPECT_EQ("Games", info->name());
1301+ EXPECT_EQ("http://foobar.com/", info->href());
1302+ EXPECT_EQ(false, info->has_children_flag());
1303+
1304+ auto sub = info->sub_departments();
1305+ EXPECT_EQ(2u, sub.size());
1306+
1307+ auto it = sub.begin();
1308+ EXPECT_EQ("rpg", (*it)->id());
1309+ EXPECT_EQ("RPG", (*it)->name());
1310+ EXPECT_EQ("http://ubuntu.com/", (*it)->href());
1311+ ++it;
1312+ EXPECT_EQ("strategy", (*it)->id());
1313+ EXPECT_EQ("Strategy", (*it)->name());
1314+ }
1315+ {
1316+ lut.rebuild(root);
1317+ EXPECT_EQ(2u, lut.size());
1318+ }
1319+}
1320+
1321
1322=== modified file 'libclickscope/tests/test_index.cpp'
1323--- libclickscope/tests/test_index.cpp 2014-06-12 21:05:25 +0000
1324+++ libclickscope/tests/test_index.cpp 2014-06-23 10:26:18 +0000
1325@@ -52,8 +52,8 @@
1326 click::Index(client, configuration)
1327 {
1328 }
1329- MOCK_METHOD1(build_index_query, std::string(const std::string&));
1330 MOCK_METHOD0(build_headers, std::map<std::string, std::string>());
1331+ MOCK_METHOD2(build_index_query, std::string(const std::string&, const std::string&));
1332 };
1333
1334 class MockConfiguration : public click::Configuration {
1335@@ -116,7 +116,7 @@
1336 .Times(1)
1337 .WillOnce(Return(response));
1338
1339- EXPECT_CALL(*indexPtr, build_index_query(FAKE_QUERY))
1340+ EXPECT_CALL(*indexPtr, build_index_query(FAKE_QUERY, ""))
1341 .Times(1)
1342 .WillOnce(Return(FAKE_BUILT_QUERY));
1343
1344
1345=== modified file 'scope/clickstore/store-query.cpp'
1346--- scope/clickstore/store-query.cpp 2014-06-18 16:23:50 +0000
1347+++ scope/clickstore/store-query.cpp 2014-06-23 10:26:18 +0000
1348@@ -30,6 +30,7 @@
1349 #include <click/application.h>
1350 #include <click/interface.h>
1351 #include "store-query.h"
1352+#include "store-scope.h"
1353 #include <click/qtbridge.h>
1354
1355 #include <click/key_file_locator.h>
1356@@ -37,6 +38,7 @@
1357 #include <unity/scopes/Annotation.h>
1358 #include <unity/scopes/CategoryRenderer.h>
1359 #include <unity/scopes/CategorisedResult.h>
1360+#include <unity/scopes/Department.h>
1361 #include <unity/scopes/CannedQuery.h>
1362 #include <unity/scopes/SearchReply.h>
1363 #include <unity/scopes/SearchMetadata.h>
1364@@ -93,19 +95,37 @@
1365
1366 struct click::Query::Private
1367 {
1368+<<<<<<< TREE
1369 Private(click::Index& index, const scopes::SearchMetadata& metadata)
1370 : index(index),
1371+=======
1372+ Private(click::Index& index, click::DepartmentLookup& depts,
1373+ click::HighlightList& highlights, const scopes::SearchMetadata& metadata)
1374+ : index(index),
1375+ department_lookup(depts),
1376+ highlights(highlights),
1377+>>>>>>> MERGE-SOURCE
1378 meta(metadata)
1379 {
1380 }
1381 click::Index& index;
1382+ click::DepartmentLookup& department_lookup;
1383+ click::HighlightList& highlights;
1384 scopes::SearchMetadata meta;
1385 click::web::Cancellable search_operation;
1386 };
1387
1388+<<<<<<< TREE
1389 click::Query::Query(unity::scopes::CannedQuery const& query, click::Index& index, scopes::SearchMetadata const& metadata)
1390 : unity::scopes::SearchQueryBase(query, metadata),
1391 impl(new Private(index, metadata))
1392+=======
1393+click::Query::Query(unity::scopes::CannedQuery const& query, click::Index& index, click::DepartmentLookup& depts,
1394+ click::HighlightList& highlights,
1395+ scopes::SearchMetadata const& metadata)
1396+ : unity::scopes::SearchQueryBase(query, metadata),
1397+ impl(new Private(index, depts, highlights, metadata))
1398+>>>>>>> MERGE-SOURCE
1399 {
1400 }
1401
1402@@ -154,6 +174,166 @@
1403 });
1404 }
1405
1406+//
1407+// creates department menu with narrowed-down list of subdepartments of current department, as
1408+// returned by server call
1409+void click::Query::populate_departments(const click::DepartmentList& subdepts, const std::string& current_dep_id, unity::scopes::Department::SPtr &root)
1410+{
1411+ unity::scopes::DepartmentList departments;
1412+
1413+ // create a list of subdepartments of current department
1414+ foreach (auto d, subdepts)
1415+ {
1416+ unity::scopes::Department::SPtr department = unity::scopes::Department::create(d->id(), query(), d->name());
1417+ if (d->has_children_flag())
1418+ {
1419+ department->set_has_subdepartments();
1420+ }
1421+ departments.push_back(department);
1422+ }
1423+
1424+ if (current_dep_id != "")
1425+ {
1426+ auto curr_dpt = impl->department_lookup.get_department_info(current_dep_id);
1427+ if (curr_dpt != nullptr)
1428+ {
1429+ unity::scopes::Department::SPtr current = unity::scopes::Department::create(current_dep_id, query(), curr_dpt->name());
1430+ if (departments.size() > 0) // this may be a leaf department
1431+ {
1432+ current->set_subdepartments(departments);
1433+ }
1434+
1435+ auto parent_info = impl->department_lookup.get_parent(current_dep_id);
1436+ if (parent_info != nullptr)
1437+ {
1438+ root = unity::scopes::Department::create(parent_info->id(), query(), parent_info->name());
1439+ root->set_subdepartments({current});
1440+ return;
1441+ }
1442+ else
1443+ {
1444+ root = unity::scopes::Department::create("", query(), _("All departments"));
1445+ root->set_subdepartments({current});
1446+ return;
1447+ }
1448+ }
1449+ else
1450+ {
1451+ qWarning() << "Unknown department:" << QString::fromStdString(current_dep_id);
1452+ }
1453+ }
1454+
1455+ root = unity::scopes::Department::create("", query(), _("All departments"));
1456+ root->set_subdepartments(departments);
1457+}
1458+
1459+void click::Query::push_package(const scopes::SearchReplyProxy& searchReply, scopes::Category::SCPtr category, const PackageSet &installedPackages, const Package& pkg)
1460+{
1461+ qDebug() << "pushing result" << QString::fromStdString(pkg.name);
1462+ try {
1463+ scopes::CategorisedResult res(category);
1464+ res.set_title(pkg.title);
1465+ res.set_art(pkg.icon_url);
1466+ res.set_uri(pkg.url);
1467+ res[click::Query::ResultKeys::NAME] = pkg.name;
1468+ auto installed = installedPackages.find(pkg);
1469+ if (installed != installedPackages.end()) {
1470+ res[click::Query::ResultKeys::INSTALLED] = true;
1471+ res["subtitle"] = _("✔ INSTALLED");
1472+ res[click::Query::ResultKeys::VERSION] = installed->version;
1473+ } else {
1474+ res[click::Query::ResultKeys::INSTALLED] = false;
1475+ // TODO: get the real price from the webservice (upcoming branch)
1476+ res["subtitle"] = _("FREE");
1477+ }
1478+
1479+ this->push_result(searchReply, res);
1480+ } catch(const std::exception& e){
1481+ qDebug() << "PackageDetails::loadJson: Exception thrown while decoding JSON: " << e.what() ;
1482+ } catch(...){
1483+ qDebug() << "no reason to catch";
1484+ }
1485+}
1486+
1487+void click::Query::push_highlights(const scopes::SearchReplyProxy& searchReply, const HighlightList& highlights, const PackageSet &locallyInstalledApps)
1488+{
1489+ std::string categoryTemplate = CATEGORY_APPS_DISPLAY; //FIXME
1490+ scopes::CategoryRenderer renderer(categoryTemplate);
1491+
1492+ for (auto const& hl: highlights)
1493+ {
1494+ auto category = register_category(searchReply, hl.name(), hl.name(), "", renderer); //FIXME: highlight slug
1495+ for (auto const& pkg: hl.packages())
1496+ {
1497+ push_package(searchReply, category, locallyInstalledApps, pkg);
1498+ }
1499+ }
1500+ qDebug() << "Highlights pushed";
1501+}
1502+
1503+void click::Query::push_departments(const scopes::SearchReplyProxy& searchReply, const scopes::Department::SCPtr& root)
1504+{
1505+ if (root != nullptr)
1506+ {
1507+ try
1508+ {
1509+ qDebug() << "pushing departments";
1510+ searchReply->register_departments(root);
1511+ }
1512+ catch (const std::exception& e)
1513+ {
1514+ qWarning() << "Failed to register departments for query " << QString::fromStdString(query().query_string()) <<
1515+ ", current department " << QString::fromStdString(query().department_id()) << ": " << e.what();
1516+ }
1517+ }
1518+ else
1519+ {
1520+ qWarning() << "No departments data for query " << QString::fromStdString(query().query_string()) <<
1521+ "', current department " << QString::fromStdString(query().department_id());
1522+ }
1523+}
1524+
1525+//
1526+// push highlights and departments
1527+// use cached highlights for root department, otherwise run an async job for highlights of current department.
1528+void click::Query::add_highlights(scopes::SearchReplyProxy const& searchReply, const PackageSet& locallyInstalledApps)
1529+{
1530+ auto curdep = impl->department_lookup.get_department_info(query().department_id());
1531+ assert(curdep);
1532+ auto subdepts = curdep->sub_departments();
1533+ if (query().department_id() == "") // top-level departments
1534+ {
1535+ unity::scopes::Department::SPtr root;
1536+ populate_departments(subdepts, query().department_id(), root);
1537+ push_departments(searchReply, root);
1538+
1539+ qDebug() << "pushing cached highlights";
1540+ push_highlights(searchReply, impl->highlights, locallyInstalledApps);
1541+ this->finished(searchReply); //FIXME: this shouldn't be needed
1542+ }
1543+ else
1544+ {
1545+ qDebug() << "starting departments call for department" << QString::fromStdString(curdep->id()) << ", href" << QString::fromStdString(curdep->href());
1546+ impl->search_operation = impl->index.departments(curdep->href(), [this, locallyInstalledApps, searchReply](const DepartmentList& depts,
1547+ const HighlightList& highlights, Index::Error error, int)
1548+ {
1549+ if (error == click::Index::Error::NoError)
1550+ {
1551+ qDebug() << "departments call completed";
1552+ unity::scopes::Department::SPtr root;
1553+ populate_departments(depts, query().department_id(), root);
1554+ push_departments(searchReply, root);
1555+ push_highlights(searchReply, highlights, locallyInstalledApps);
1556+ }
1557+ else
1558+ {
1559+ qWarning() << "departments call failed";
1560+ }
1561+ this->finished(searchReply); //FIXME: this shouldn't be needed
1562+ });
1563+ }
1564+}
1565+
1566 void click::Query::add_available_apps(scopes::SearchReplyProxy const& searchReply,
1567 const PackageSet& installedPackages,
1568 const std::string& categoryTemplate)
1569@@ -161,12 +341,14 @@
1570 scopes::CategoryRenderer categoryRenderer(categoryTemplate);
1571 auto category = register_category(searchReply, "appstore", _("Available"), "", categoryRenderer);
1572
1573+ assert(searchReply);
1574+
1575 run_under_qt([=]()
1576 {
1577 auto search_cb = [this, searchReply, category, installedPackages](Packages packages) {
1578 qDebug("search callback");
1579
1580- // handle packages data
1581+ // handle packages data; FIXME: use push_package()
1582 foreach (auto p, packages) {
1583 qDebug() << "pushing result" << QString::fromStdString(p.name);
1584 try {
1585@@ -194,11 +376,66 @@
1586 }
1587 }
1588 qDebug() << "search completed";
1589+<<<<<<< TREE
1590 this->finished(searchReply);
1591 };
1592
1593 qDebug() << "starting search of" << QString::fromStdString(query().query_string());
1594 impl->search_operation = impl->index.search(query().query_string(), search_cb);
1595+=======
1596+ this->finished(searchReply); //FIXME: this shouldn't be needed
1597+ };
1598+
1599+ // this is the case when we do bootstrap for the first time, or it failed last time
1600+ if (impl->department_lookup.size() == 0 && !click::Scope::use_old_api())
1601+ {
1602+ qDebug() << "performing bootstrap request";
1603+ impl->search_operation = impl->index.bootstrap([this, search_cb, searchReply, installedPackages](const DepartmentList& deps, const
1604+ HighlightList& highlights, click::Index::Error error, int error_code) {
1605+ if (error == click::Index::Error::NoError)
1606+ {
1607+ qDebug() << "bootstrap request completed";
1608+ auto root = std::make_shared<click::Department>("", "All Departments", "", true);
1609+ root->set_subdepartments(deps);
1610+ DepartmentList rdeps { root };
1611+ impl->department_lookup.rebuild(rdeps);
1612+ impl->highlights = highlights;
1613+ qDebug() << "Total number of departments:" << impl->department_lookup.size() << ", highlights:" << highlights.size();
1614+ }
1615+ else
1616+ {
1617+ qWarning() << "bootstrap request failed";
1618+ if (error_code == 405) // method not allowed
1619+ {
1620+ qDebug() << "bootstrap not available, using old API";
1621+ click::Scope::set_use_old_api();
1622+ }
1623+ }
1624+
1625+ if (query().query_string().empty() && !click::Scope::use_old_api())
1626+ {
1627+ add_highlights(searchReply, installedPackages);
1628+ }
1629+ else
1630+ {
1631+ qDebug() << "starting search of" << QString::fromStdString(query().query_string());
1632+ impl->search_operation = impl->index.search(query().query_string(), search_cb);
1633+ }
1634+ });
1635+ }
1636+ else
1637+ {
1638+ if (query().query_string().empty() && !click::Scope::use_old_api())
1639+ {
1640+ add_highlights(searchReply, installedPackages);
1641+ }
1642+ else // normal search
1643+ {
1644+ qDebug() << "starting search of" << QString::fromStdString(query().query_string());
1645+ impl->search_operation = impl->index.search(query().query_string(), search_cb);
1646+ }
1647+ }
1648+>>>>>>> MERGE-SOURCE
1649 });
1650 }
1651
1652
1653=== modified file 'scope/clickstore/store-query.h'
1654--- scope/clickstore/store-query.h 2014-06-16 14:09:21 +0000
1655+++ scope/clickstore/store-query.h 2014-06-23 10:26:18 +0000
1656@@ -32,10 +32,16 @@
1657
1658
1659 #include <unity/scopes/SearchQueryBase.h>
1660+#include <unity/scopes/Department.h>
1661
1662 namespace scopes = unity::scopes;
1663
1664 #include <QSharedPointer>
1665+#include <set>
1666+
1667+#include <click/department-lookup.h>
1668+#include <click/package.h>
1669+#include <click/highlights.h>
1670 #include <click/interface.h>
1671
1672 namespace click
1673@@ -43,6 +49,7 @@
1674
1675 class Application;
1676 class Index;
1677+class DepartmentLookup;
1678
1679 class Query : public scopes::SearchQueryBase
1680 {
1681@@ -69,7 +76,8 @@
1682 constexpr static const char* VERSION{"version"};
1683 };
1684
1685- Query(unity::scopes::CannedQuery const& query, click::Index& index, scopes::SearchMetadata const& metadata);
1686+ Query(unity::scopes::CannedQuery const& query, click::Index& index, click::DepartmentLookup& dept_lookup, click::HighlightList& highlights,
1687+ scopes::SearchMetadata const& metadata);
1688 virtual ~Query();
1689
1690 virtual void cancelled() override;
1691@@ -77,8 +85,10 @@
1692 virtual void run(scopes::SearchReplyProxy const& reply) override;
1693
1694 protected:
1695- virtual void add_available_apps(const scopes::SearchReplyProxy &searchReply,
1696- const PackageSet &installedPackages, const std::string &category);
1697+ virtual void populate_departments(const click::DepartmentList& depts, const std::string& current_department_id, unity::scopes::Department::SPtr &root);
1698+ virtual void push_departments(const scopes::SearchReplyProxy& searchReply, const scopes::Department::SCPtr& root);
1699+ virtual void add_highlights(scopes::SearchReplyProxy const& searchReply, const PackageSet& installedPackages);
1700+ virtual void add_available_apps(const scopes::SearchReplyProxy &searchReply, const PackageSet &installedPackages, const std::string &category);
1701 virtual click::Interface& clickInterfaceInstance();
1702 virtual PackageSet get_installed_packages();
1703 virtual bool push_result(const scopes::SearchReplyProxy &searchReply, scopes::CategorisedResult const& res);
1704@@ -88,6 +98,9 @@
1705 std::string const& title,
1706 std::string const& icon,
1707 scopes::CategoryRenderer const& renderer_template);
1708+ virtual void push_package(const scopes::SearchReplyProxy& searchReply, scopes::Category::SCPtr category, const PackageSet &locallyInstalledApps,
1709+ const click::Package& pkg);
1710+ virtual void push_highlights(const scopes::SearchReplyProxy& searchReply, const HighlightList& highlights, const PackageSet &locallyInstalledApps);
1711 virtual void run_under_qt(const std::function<void()> &task);
1712
1713 private:
1714
1715=== modified file 'scope/clickstore/store-scope.cpp'
1716--- scope/clickstore/store-scope.cpp 2014-06-19 17:05:34 +0000
1717+++ scope/clickstore/store-scope.cpp 2014-06-23 10:26:18 +0000
1718@@ -28,6 +28,7 @@
1719 */
1720
1721 #include <click/qtbridge.h>
1722+#include <click/department-lookup.h>
1723 #include "store-scope.h"
1724 #include "store-query.h"
1725 #include <click/preview.h>
1726@@ -42,19 +43,36 @@
1727
1728 #include <logging.h>
1729
1730+bool click::Scope::old_api = false;
1731
1732 click::Scope::Scope()
1733 {
1734 nam.reset(new click::network::AccessManager());
1735 client.reset(new click::web::Client(nam));
1736 index.reset(new click::Index(client));
1737+ depts.reset(new click::DepartmentLookup());
1738+ highlights.reset(new click::HighlightList());
1739 }
1740
1741 click::Scope::~Scope()
1742 {
1743 }
1744
1745-void click::Scope::start(std::string const&, scopes::RegistryProxy const&)
1746+<<<<<<< TREE
1747+void click::Scope::start(std::string const&, scopes::RegistryProxy const&)
1748+=======
1749+void click::Scope::set_use_old_api()
1750+{
1751+ old_api = true;
1752+}
1753+
1754+bool click::Scope::use_old_api()
1755+{
1756+ return old_api;
1757+}
1758+
1759+void click::Scope::start(std::string const&, scopes::RegistryProxy const&)
1760+>>>>>>> MERGE-SOURCE
1761 {
1762 setlocale(LC_ALL, "");
1763 // FIXME: This is wrong, but needed for json-cpp workaround.
1764@@ -68,7 +86,6 @@
1765 static const int zero = 0;
1766 auto emptyCb = [this]()
1767 {
1768-
1769 };
1770
1771 qt::core::world::build_and_run(zero, nullptr, emptyCb);
1772@@ -81,7 +98,7 @@
1773
1774 scopes::SearchQueryBase::UPtr click::Scope::search(unity::scopes::CannedQuery const& q, scopes::SearchMetadata const& metadata)
1775 {
1776- return scopes::SearchQueryBase::UPtr(new click::Query(q, *index, metadata));
1777+ return scopes::SearchQueryBase::UPtr(new click::Query(q, *index, *depts, *highlights, metadata));
1778 }
1779
1780
1781@@ -91,10 +108,21 @@
1782 return scopes::PreviewQueryBase::UPtr{new click::Preview(result, metadata, client, nam)};
1783 }
1784
1785+<<<<<<< TREE
1786 unity::scopes::ActivationQueryBase::UPtr click::Scope::perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& /* widget_id */, std::string const& action_id)
1787+=======
1788+unity::scopes::ActivationQueryBase::UPtr click::Scope::perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata,
1789+ std::string const& widget_id, std::string const& _action_id)
1790+>>>>>>> MERGE-SOURCE
1791 {
1792+<<<<<<< TREE
1793 auto activation = new ScopeActivation(result, metadata);
1794 qDebug() << "perform_action called with action_id" << QString().fromStdString(action_id);
1795+=======
1796+ std::string action_id = _action_id;
1797+ qDebug() << "perform_action called with widget_id" << QString::fromStdString(widget_id) << "and action_id:" << QString::fromStdString(action_id);
1798+ auto activation = new ScopeActivation(result, metadata);
1799+>>>>>>> MERGE-SOURCE
1800
1801 // if the purchase is completed, do the install
1802 if (action_id == "purchaseCompleted") {
1803
1804=== modified file 'scope/clickstore/store-scope.h'
1805--- scope/clickstore/store-scope.h 2014-06-18 14:42:47 +0000
1806+++ scope/clickstore/store-scope.h 2014-06-23 10:26:18 +0000
1807@@ -30,6 +30,7 @@
1808 #ifndef CLICK_SCOPE_H
1809 #define CLICK_SCOPE_H
1810
1811+#include <memory>
1812 #include <click/network_access_manager.h>
1813 #include <click/webclient.h>
1814
1815@@ -43,6 +44,9 @@
1816
1817 namespace click
1818 {
1819+
1820+class DepartmentLookup;
1821+
1822 class Scope : public scopes::ScopeBase
1823 {
1824 public:
1825@@ -60,12 +64,18 @@
1826
1827 virtual unity::scopes::ActivationQueryBase::UPtr perform_action(unity::scopes::Result const& result, unity::scopes::ActionMetadata const& metadata, std::string const& widget_id, std::string const& action_id) override;
1828
1829+ static void set_use_old_api();
1830+ static bool use_old_api();
1831+
1832 private:
1833 QSharedPointer<click::network::AccessManager> nam;
1834 QSharedPointer<click::web::Client> client;
1835 QSharedPointer<click::Index> index;
1836+ std::shared_ptr<click::DepartmentLookup> depts;
1837+ std::shared_ptr<click::HighlightList> highlights;
1838
1839 std::string installApplication(unity::scopes::Result const& result);
1840+ static bool old_api;
1841 };
1842 }
1843 #endif // CLICK_SCOPE_H
1844
1845=== modified file 'scope/tests/integration/webclient_integration.cpp'
1846--- scope/tests/integration/webclient_integration.cpp 2014-06-12 21:05:25 +0000
1847+++ scope/tests/integration/webclient_integration.cpp 2014-06-23 10:26:18 +0000
1848@@ -30,6 +30,7 @@
1849 #include <click/network_access_manager.h>
1850 #include <click/webclient.h>
1851 #include <click/index.h>
1852+#include <click/departments.h>
1853
1854 #include <QCoreApplication>
1855 #include <QDebug>
1856
1857=== modified file 'scope/tests/test_query.cpp'
1858--- scope/tests/test_query.cpp 2014-06-12 22:17:42 +0000
1859+++ scope/tests/test_query.cpp 2014-06-23 10:26:18 +0000
1860@@ -45,6 +45,7 @@
1861 #include <unity/scopes/CannedQuery.h>
1862 #include <unity/scopes/ScopeBase.h>
1863 #include <unity/scopes/SearchReply.h>
1864+#include <unity/scopes/testing/MockSearchReply.h>
1865
1866 using namespace ::testing;
1867 using namespace click;
1868@@ -57,10 +58,17 @@
1869
1870 class MockIndex : public click::Index {
1871 click::Packages packages;
1872+ click::DepartmentList departments;
1873+ click::DepartmentList bootstrap_departments;
1874+ click::HighlightList bootstrap_highlights;
1875 public:
1876- MockIndex(click::Packages packages = click::Packages())
1877+ MockIndex(click::Packages packages = click::Packages(),
1878+ click::DepartmentList departments = click::DepartmentList(),
1879+ click::DepartmentList boot_departments = click::DepartmentList())
1880 : Index(QSharedPointer<click::web::Client>()),
1881- packages(packages)
1882+ packages(packages),
1883+ departments(departments),
1884+ bootstrap_departments(boot_departments)
1885 {
1886
1887 }
1888@@ -72,15 +80,21 @@
1889 return click::web::Cancellable();
1890 }
1891
1892- MOCK_METHOD2(do_search,
1893- void(const std::string&,
1894- std::function<void(click::Packages)>));
1895+ click::web::Cancellable bootstrap(std::function<void(const click::DepartmentList&, const click::HighlightList&, Error, int)> callback) override
1896+ {
1897+ callback(bootstrap_departments, bootstrap_highlights, click::Index::Error::NoError, 0);
1898+ return click::web::Cancellable();
1899+ }
1900+
1901+ MOCK_METHOD2(do_search, void(const std::string&, std::function<void(click::Packages)>));
1902 };
1903
1904 class MockQueryBase : public click::Query {
1905 public:
1906 MockQueryBase(const unity::scopes::CannedQuery& query, click::Index& index,
1907- scopes::SearchMetadata const& metadata) : click::Query(query, index, metadata)
1908+ click::DepartmentLookup& depts,
1909+ click::HighlightList& highlights,
1910+ scopes::SearchMetadata const& metadata) : click::Query(query, index, depts, highlights, metadata)
1911 {
1912
1913 }
1914@@ -94,7 +108,9 @@
1915 class MockQuery : public MockQueryBase {
1916 public:
1917 MockQuery(const unity::scopes::CannedQuery& query, click::Index& index,
1918- scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, metadata)
1919+ click::DepartmentLookup& depts,
1920+ click::HighlightList& highlights,
1921+ scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, depts, highlights, metadata)
1922 {
1923
1924 }
1925@@ -118,7 +134,9 @@
1926 class MockQueryRun : public MockQueryBase {
1927 public:
1928 MockQueryRun(const unity::scopes::CannedQuery& query, click::Index& index,
1929- scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, metadata)
1930+ click::DepartmentLookup& depts,
1931+ click::HighlightList& highlights,
1932+ scopes::SearchMetadata const& metadata) : MockQueryBase(query, index, depts, highlights, metadata)
1933 {
1934
1935 }
1936@@ -147,12 +165,16 @@
1937 TEST(QueryTest, testAddAvailableAppsCallsClickIndex)
1938 {
1939 MockIndex mock_index;
1940+ click::DepartmentLookup dept_lookup;
1941+ click::HighlightList highlights;
1942 scopes::SearchMetadata metadata("en_EN", "phone");
1943 PackageSet no_installed_packages;
1944 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1945- MockQuery q(query, mock_index, metadata);
1946+ MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1947 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _)).Times(1);
1948- scopes::SearchReplyProxy reply;
1949+
1950+ scopes::testing::MockSearchReply mock_reply;
1951+ scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){});
1952
1953 scopes::CategoryRenderer renderer("{}");
1954 auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1955@@ -166,17 +188,21 @@
1956 {"name", "title", 0.0, "icon", "uri"}
1957 };
1958 MockIndex mock_index(packages);
1959+ click::DepartmentLookup dept_lookup;
1960+ click::HighlightList highlights;
1961 scopes::SearchMetadata metadata("en_EN", "phone");
1962 PackageSet no_installed_packages;
1963 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1964- MockQuery q(query, mock_index, metadata);
1965+ MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1966 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1967
1968 scopes::CategoryRenderer renderer("{}");
1969 auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1970 EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
1971
1972- scopes::SearchReplyProxy reply;
1973+ scopes::testing::MockSearchReply mock_reply;
1974+ scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){});
1975+
1976 auto expected_title = packages.front().title;
1977 EXPECT_CALL(q, push_result(_, Property(&scopes::CategorisedResult::title, expected_title)));
1978 q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE);
1979@@ -188,17 +214,20 @@
1980 {"name", "title", 0.0, "icon", "uri"}
1981 };
1982 MockIndex mock_index(packages);
1983+ click::DepartmentLookup dept_lookup;
1984+ click::HighlightList highlights;
1985 scopes::SearchMetadata metadata("en_EN", "phone");
1986 PackageSet no_installed_packages;
1987 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
1988- MockQuery q(query, mock_index, metadata);
1989+ MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
1990 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
1991
1992 scopes::CategoryRenderer renderer("{}");
1993 auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
1994 EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
1995
1996- scopes::SearchReplyProxy reply;
1997+ scopes::testing::MockSearchReply mock_reply;
1998+ scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){});
1999 EXPECT_CALL(q, finished(_));
2000 q.wrap_add_available_apps(reply, no_installed_packages, FAKE_CATEGORY_TEMPLATE);
2001 }
2002@@ -209,10 +238,12 @@
2003 {"name", "title", 0.0, "icon", "uri"}
2004 };
2005 MockIndex mock_index(packages);
2006+ click::DepartmentLookup dept_lookup;
2007+ click::HighlightList highlights;
2008 scopes::SearchMetadata metadata("en_EN", "phone");
2009 PackageSet no_installed_packages;
2010 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
2011- MockQueryRun q(query, mock_index, metadata);
2012+ MockQueryRun q(query, mock_index, dept_lookup, highlights, metadata);
2013 auto reply = scopes::SearchReplyProxy();
2014 EXPECT_CALL(q, get_installed_packages()).WillOnce(Return(no_installed_packages));
2015 EXPECT_CALL(q, add_available_apps(reply, no_installed_packages, _));
2016@@ -234,15 +265,18 @@
2017 PackageSet one_installed_package {
2018 {"org.example.app2", "0.2"}
2019 };
2020+ click::DepartmentLookup dept_lookup;
2021+ click::HighlightList highlights;
2022 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
2023- MockQuery q(query, mock_index, metadata);
2024+ MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
2025 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
2026
2027 scopes::CategoryRenderer renderer("{}");
2028 auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
2029 EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
2030
2031- scopes::SearchReplyProxy reply;
2032+ scopes::testing::MockSearchReply mock_reply;
2033+ scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){});
2034 auto expected_name1 = packages.front().name;
2035 EXPECT_CALL(q, push_result(_, HasPackageName(expected_name1)));
2036 auto expected_name2 = packages.back().name;
2037@@ -261,15 +295,18 @@
2038 PackageSet one_installed_package {
2039 {"org.example.app2", "0.2"}
2040 };
2041+ click::DepartmentLookup dept_lookup;
2042+ click::HighlightList highlights;
2043 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
2044- MockQuery q(query, mock_index, metadata);
2045+ MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
2046 EXPECT_CALL(mock_index, do_search(FAKE_QUERY, _));
2047
2048 scopes::CategoryRenderer renderer("{}");
2049 auto ptrCat = std::make_shared<FakeCategory>("id", "", "", renderer);
2050 EXPECT_CALL(q, register_category(_, _, _, _, _)).WillOnce(Return(ptrCat));
2051
2052- scopes::SearchReplyProxy reply;
2053+ scopes::testing::MockSearchReply mock_reply;
2054+ scopes::SearchReplyProxy reply(&mock_reply, [](unity::scopes::SearchReply*){});
2055 EXPECT_CALL(q, push_result(_, IsInstalled(true)));
2056 EXPECT_CALL(q, push_result(_, IsInstalled(false)));
2057 q.wrap_add_available_apps(reply, one_installed_package, FAKE_CATEGORY_TEMPLATE);
2058@@ -288,8 +325,10 @@
2059 };
2060 MockIndex mock_index(uninstalled_packages);
2061 scopes::SearchMetadata metadata("en_EN", "phone");
2062+ click::DepartmentLookup dept_lookup;
2063+ click::HighlightList highlights;
2064 const unity::scopes::CannedQuery query("foo.scope", FAKE_QUERY, "");
2065- MockQuery q(query, mock_index, metadata);
2066+ MockQuery q(query, mock_index, dept_lookup, highlights, metadata);
2067 PackageSet installed_packages{{"package_1", "0.1"}};
2068
2069 FakeInterface fake_interface;

Subscribers

People subscribed via source and target branches

to all changes: