Merge lp:~paulliu/unity-scope-click/showratings into lp:unity-scope-click

Proposed by Ying-Chun Liu
Status: Superseded
Proposed branch: lp:~paulliu/unity-scope-click/showratings
Merge into: lp:unity-scope-click
Diff against target: 221 lines (+186/-2)
3 files modified
src/Makefile.am (+2/-1)
src/click-scope.vala (+7/-1)
src/rnrclient.vala (+177/-0)
To merge this branch: bzr merge lp:~paulliu/unity-scope-click/showratings
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Needs Fixing
dobey (community) Needs Fixing
Manuel de la Peña (community) Needs Fixing
Review via email: mp+198349@code.launchpad.net

This proposal has been superseded by a proposal from 2013-12-12.

Commit message

Provide reviews/ratings from server in previews.

Description of the change

Provide reviews/ratings from server in previews.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Manuel de la Peña (mandel) wrote :

Can you explain what is the following line doing:

var reviews = yield rnrClient.get_reviews("","any","any","any","any",details.title,"1","helpful");

Is "any" some kind of keyword??? Should we use a constant?

We have the following var names:

node1, node2, gvar1

Can we use names that add some information to the reviewer, I have no context of what the data should be and such names do not help. Maybe something like rootNode, ratingNode etc...?

Should we log something in this exception:

182 + try {
183 + gvar1 = Json.gvariant_deserialize(node1.get_member(node2),null);
184 + } catch (GLib.Error e) {
185 + continue;
186 + }

It might be a good thing to do when debugging and I do not trust the server side not changing things or always working ;)

Why do you use in some cases the var keyword and in others you do not? For example:

143 + Rest.Proxy proxy = new Rest.Proxy(REVIEWS_URL, false);
144 + var call = proxy.new_call();

Could not have used bar in both cases?

In the following code:

257 + try {
258 + parser.load_from_data(testData1);
259 + } catch (GLib.Error e) {
260 + }

It might be a good idea to deal with the exception in case some other developer changes the testData wrongly and has bad results in the tests, what do you think???

All the rest of the code looks great, thx!!!

review: Needs Fixing
Revision history for this message
dobey (dobey) wrote :

48 + --pkg rest-0.7 \

I thought we agreed to not use librest, but just use soup for everything?

Also, the test doesn't seem to actually test anything, as I read it. It's fake data in a JSON dict, and you parse it as such. We don't need to write unit tests for json-glib in our code. It's also a bit weird to define a test method inside a fake class, and simply have the actual test method call that method, doing the assertion inside the fake class. Fake classes should really only be used to override methods which require network or dbus access to other daemons, to prevent such access and isolate the tests. Fake data should probably be placed in external files for use in the tests, or at the very least, encoded in the fake-data.vala source file. And the actual test and assertion should happen within the test function itself, not inside a set of methods in another class.

review: Needs Fixing
Revision history for this message
dobey (dobey) wrote :

118 + const string REVIEWS_URL = "http://reviews.ubuntu.com/reviews/api/1.0";

Also, this needs to be HTTPS, and should be split up such that the base URL can be changed via environment variable, for testing against staging. See click-webservice.vala for how we do this for the SSO base URL.

Revision history for this message
Ying-Chun Liu (paulliu) wrote :

I'll rewrite it to use libsoup because I really cannot make the SSO work with librest.

But maybe we want to use librest for the future at some time as it makes the code cleaner and easier to be maintained?

Revision history for this message
Ying-Chun Liu (paulliu) wrote :

the reviews retriviing doesn't need the SSO. So I'll keep the http here.
The submit reviews function will be implemented in another branch which will use https.

Revision history for this message
dobey (dobey) wrote :

77 + const string REVIEWS_BASE_URL = "http://reviews.ubuntu.com/reviews/api/1.0";

86 + string from_environ (string env_name, string default_value) {
87 + string env_value = Environment.get_variable (env_name);
88 + return env_value != null ? env_value : default_value;
89 + }
90 +
91 + string get_base_url () {
92 + return from_environ ("U1_REVIEWS_BASE_URL", REVIEWS_BASE_URL);
93 + }

This is still wrong. Why are you not using HTTPS here? Also, the _BASE_URL should only be "https://reviews.ubuntu.com/reviews" with "/api/1.0" added after. One shouldn't include the "/api/1.0" portion in the environment variable. If someone passes /api/2.0 for example, the app wouldn't handle it.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
dobey (dobey) wrote :

130 + public async Variant? get_reviews(string packagename, string? language,
131 + string? origin, string? distroseries,
132 + string? version, string appname,
133 + string page, string sort) {

I wonder if we can abstract all these arguments to a new object, or just pass in the AppDetails object here, and get the language internally rather than taking an argument for it. It's better to have an object containing a bunch of args, than to pass many options individually like this.

90. By dobey

Don't reimplement generated rules for vala.
Take advantage of VALA_CHECK_MODULES to find vala packages.
Split non-compatible mdoules to another PKG_CHECK_MODULES.
Build majority of the code as a noinst lib, which click-scope and tests link to.
Update the .bzrignore for an autools project.
. Fixes: https://bugs.launchpad.net/bugs/1258559.

Approved by Roberto Alsina, Mike McCracken, PS Jenkins bot.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
91. By Ying-Chun Liu

get reviews & ratings from server.

92. By Ying-Chun Liu

remvoe server-status as we don't need to use it.

93. By Ying-Chun Liu

Add comments.

94. By Ying-Chun Liu

Change origin to "click".

95. By Ying-Chun Liu

Merge upstream.

96. By Ying-Chun Liu

fix indent

97. By Ying-Chun Liu

Add some unit tests.
Partly fix the issues in comment 465611

98. By Ying-Chun Liu

Add version/framework

99. By Ying-Chun Liu

merge trunk

100. By Ying-Chun Liu

fix indent.

101. By Ying-Chun Liu

fix comment for ReviewFilter.

102. By Ying-Chun Liu

fix indent.
Add test for from_environ
fix framework.

103. By Ying-Chun Liu

Add 2014 to copyright.

104. By Ying-Chun Liu

revert part of the previous commit - change back framework as string list.
Add a TODO to indicate that we should use system's framework.

105. By Ying-Chun Liu

use "click" as framework.

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2013-12-11 16:46:03 +0000
+++ src/Makefile.am 2013-12-12 14:18:57 +0000
@@ -43,7 +43,8 @@
43 click-interface.vala \43 click-interface.vala \
44 ubuntuone-credentials.vala \44 ubuntuone-credentials.vala \
45 non-click-scope.vala \45 non-click-scope.vala \
46 utils.vala46 utils.vala \
47 rnrclient.vala
4748
48click_scope_VALAFLAGS = \49click_scope_VALAFLAGS = \
49 --thread \50 --thread \
5051
=== modified file 'src/click-scope.vala'
--- src/click-scope.vala 2013-12-10 22:21:24 +0000
+++ src/click-scope.vala 2013-12-12 14:18:57 +0000
@@ -80,6 +80,7 @@
80 const string LABEL_COMMENTS = "Comments";80 const string LABEL_COMMENTS = "Comments";
8181
82 ClickInterface click_if = new ClickInterface ();82 ClickInterface click_if = new ClickInterface ();
83 RNRClient rnrClient = new RNRClient();
8384
84 public ClickScope ()85 public ClickScope ()
85 {86 {
@@ -118,7 +119,12 @@
118 preview.add_info(new Unity.InfoHint.with_variant(HINT_PUBLISHER, "Publisher", null, new Variant.string(details.publisher)));119 preview.add_info(new Unity.InfoHint.with_variant(HINT_PUBLISHER, "Publisher", null, new Variant.string(details.publisher)));
119 preview.add_info(new Unity.InfoHint.with_variant(HINT_SCREENSHOTS, LABEL_SCREENSHOTS, null, new Variant.strv(details.more_screenshot_urls)));120 preview.add_info(new Unity.InfoHint.with_variant(HINT_SCREENSHOTS, LABEL_SCREENSHOTS, null, new Variant.strv(details.more_screenshot_urls)));
120 preview.add_info(new Unity.InfoHint.with_variant(HINT_KEYWORDS, LABEL_KEYWORDS, null, new Variant.strv(details.keywords)));121 preview.add_info(new Unity.InfoHint.with_variant(HINT_KEYWORDS, LABEL_KEYWORDS, null, new Variant.strv(details.keywords)));
121 // TODO: get the proper ratings and reviews from the rnr web service122 // get the proper ratings and reviews from the rnr web service
123 var reviews = yield rnrClient.get_reviews(details);
124 if (reviews != null) {
125 preview.add_info(new Unity.InfoHint.with_variant(HINT_REVIEWS, LABEL_REVIEWS, null, reviews));
126 debug("Add reviews "+reviews.print(true));
127 }
122 return preview;128 return preview;
123 } catch (WebserviceError e) {129 } catch (WebserviceError e) {
124 debug ("Error calling webservice: %s", e.message);130 debug ("Error calling webservice: %s", e.message);
125131
=== added file 'src/rnrclient.vala'
--- src/rnrclient.vala 1970-01-01 00:00:00 +0000
+++ src/rnrclient.vala 2013-12-12 14:18:57 +0000
@@ -0,0 +1,177 @@
1/* -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2/*
3 * Copyright (C) 2013 Canonical, Ltd.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18using Json;
19
20class RNRClient : GLib.Object
21{
22 UbuntuoneCredentials u1creds;
23 const string CONSUMER_KEY = "consumer_key";
24 const string CONSUMER_SECRET = "consumer_secret";
25 const string TOKEN = "token";
26 const string TOKEN_SECRET = "token_secret";
27 const string REVIEWS_BASE_URL = "https://reviews.ubuntu.com/reviews";
28
29 internal Soup.SessionAsync http_session;
30
31 construct {
32 http_session = WebClient.get_webclient();
33 u1creds = new UbuntuoneCredentials ();
34 }
35
36 string from_environ (string env_name, string default_value) {
37 string env_value = Environment.get_variable (env_name);
38 return env_value != null ? env_value : default_value;
39 }
40
41 string get_base_url () {
42 return from_environ ("U1_REVIEWS_BASE_URL", REVIEWS_BASE_URL);
43 }
44
45 string get_status_url() {
46 return get_base_url() + "/api/1.0/server-status";
47 }
48
49 string get_review_url() {
50 return get_base_url() + "/api/1.0/reviews/filter/%s/%s/%s/%s/%s%s/page/%s/%s/";
51 }
52
53 public async string server_status() {
54 WebserviceError failure = null;
55 string url = get_status_url();
56 string response="";
57
58 var message = new Soup.Message ("GET", url);
59 http_session.queue_message(message, (http_session, message) => {
60 if (message.status_code != Soup.KnownStatusCode.OK) {
61 var msg = "Web request failed: HTTP %u %s".printf(
62 message.status_code, message.reason_phrase);
63 failure = new WebserviceError.HTTP_ERROR(msg);
64 } else {
65 message.response_body.flatten ();
66 response = (string) message.response_body.data;
67 debug ("response is %s", response);
68 }
69 server_status.callback();
70 });
71 yield;
72 if (failure != null) {
73 return "no";
74 }
75 return response;
76 }
77
78 public async Variant? get_reviews(AppDetails details) {
79 string[] languages = GLib.Intl.get_language_names();
80 string? language = null;
81 if (languages.length > 0) {
82 language = languages[0];
83 }
84 var filter = new ReviewFilter();
85 filter.language = language;
86 filter.appname = details.title;
87 return yield get_reviews_core(filter);
88 }
89
90 public async Variant? get_reviews_core(ReviewFilter filter) {
91 Variant? ret = null;
92 WebserviceError failure = null;
93 string url = get_review_url().printf(filter.language,
94 filter.origin,
95 filter.distroseries,
96 filter.version,
97 filter.packagename,
98 filter.appname,
99 filter.page,
100 filter.sort);
101 string response="";
102
103 debug ("url is %s", url);
104
105 var message = new Soup.Message ("GET", url);
106 http_session.queue_message(message, (http_session, message) => {
107 if (message.status_code != Soup.KnownStatusCode.OK) {
108 var msg = "Web request failed: HTTP %u %s".printf(
109 message.status_code, message.reason_phrase);
110 failure = new WebserviceError.HTTP_ERROR(msg);
111 } else {
112 message.response_body.flatten ();
113 response = (string) message.response_body.data;
114 debug ("response is %s", response);
115 }
116 get_reviews_core.callback();
117 });
118 yield;
119 if (failure != null) {
120 debug("cannot get reviews error %s", failure.message);
121 return ret;
122 }
123 var parser = new Json.Parser();
124 try {
125 parser.load_from_data(response);
126 } catch (GLib.Error e) {
127 debug ("parse reviews response error %s", e.message);
128 return ret;
129 }
130
131 ret = convertJSONtoVariant(parser);
132 return ret;
133 }
134
135 protected Variant? convertJSONtoVariant(Json.Parser parser) {
136 Variant? ret = null;
137 var root_object = parser.get_root();
138 var builder = new VariantBuilder(new VariantType("av"));
139
140 foreach (var node in root_object.get_array().get_elements()) {
141 var ht = new VariantBuilder(new VariantType("a{sv}"));
142 var node_object = node.get_object();
143 foreach (var node_member in node_object.get_members()) {
144 Variant? gvar_member = null;
145 try {
146 gvar_member = Json.gvariant_deserialize(node_object.get_member(node_member), null);
147 } catch (GLib.Error e) {
148 debug ("gvariant deserialize error: %s",e.message);
149 continue;
150 }
151 if (gvar_member == null) {
152 continue;
153 }
154 if (gvar_member.is_of_type(VariantType.MAYBE) && gvar_member.get_maybe() == null) {
155 continue;
156 }
157 ht.add("{sv}", node_member, gvar_member);
158 }
159 Variant dict = ht.end();
160 builder.add("v", dict);
161 }
162
163 ret = builder.end();
164 return ret;
165 }
166}
167
168class ReviewFilter : GLib.Object {
169 public string packagename = "";
170 public string language = "any";
171 public string origin = "any";
172 public string distroseries = "any";
173 public string version = "any";
174 public string appname = "";
175 public string page = "1";
176 public string sort = "helpful";
177}

Subscribers

People subscribed via source and target branches

to all changes: