Merge lp:~dobey/unity-scope-click/submit-reviews-backend into lp:unity-scope-click/devel
- submit-reviews-backend
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Paweł Stołowski |
Approved revision: | 220 |
Merged at revision: | 221 |
Proposed branch: | lp:~dobey/unity-scope-click/submit-reviews-backend |
Merge into: | lp:unity-scope-click/devel |
Diff against target: |
199 lines (+125/-3) 6 files modified
scope/click/reviews.cpp (+39/-0) scope/click/reviews.h (+3/-0) scope/click/webclient.cpp (+1/-1) scope/click/webclient.h (+4/-1) scope/tests/test_reviews.cpp (+77/-0) scope/tests/test_webclient.cpp (+1/-1) |
To merge this branch: | bzr merge lp:~dobey/unity-scope-click/submit-reviews-backend |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Paweł Stołowski (community) | Approve | ||
Alejandro J. Cura (community) | Approve | ||
Review via email: mp+217122@code.launchpad.net |
This proposal supersedes a proposal from 2014-04-21.
Commit message
Add a backend method to submit a review to the server.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:214
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : Posted in a previous version of this proposal | # |
26 + data << "{"
27 + << " 'package_name': '" << package.
28 + << " 'version': '" << package.
29 + << " 'rating': " << rating << ","
30 + << " 'review_text': '" << review_text << "',"
31 + << " 'summary': ''"
32 + << " }";
IMHO constructing json like that is a bad idea... What about proper escaping so that the resulting json is stil valid if review text contains an apostrophe? I think we should be using Json::FastWriter from jsoncpp instead.
Also, question - what is the purpose of the empty 'summary' attribute?
dobey (dobey) wrote : Posted in a previous version of this proposal | # |
> 26 + data << "{"
> 27 + << " 'package_name': '" << package.
> 28 + << " 'version': '" << package.
> 29 + << " 'rating': " << rating << ","
> 30 + << " 'review_text': '" << review_text << "',"
> 31 + << " 'summary': ''"
> 32 + << " }";
>
> IMHO constructing json like that is a bad idea... What about proper escaping
> so that the resulting json is stil valid if review text contains an
> apostrophe? I think we should be using Json::FastWriter from jsoncpp instead.
Is there any good example of how to use that API? I spent some time trying to find how to construct a JSON string in C++, and best I found was to use a string stream.
> Also, question - what is the purpose of the empty 'summary' attribute?
The 'summary' is required by the server, but we have no UI to enter a summary (and summaries don't really make sense), so we just send an empty string for it.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:215
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
> IMHO constructing json like that is a bad idea... What about proper escaping
> so that the resulting json is stil valid if review text contains an
> apostrophe? I think we should be using Json::FastWriter from jsoncpp instead.
Discovered Json::Value to use here. No need for FastWriter AFAICT, just creating a root Value, adding the members, and converting it with .toStyledString() appears to be all we need.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:217
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alejandro J. Cura (alecu) wrote : | # |
Branch looks good, but please add some test that checks that the json is being built right, and that both unicode and json special characters are escaped properly.
For this, it might make sense to refactor into a new function the bits that create the json, including the root.toStyledSt
Paweł Stołowski (stolowski) wrote : | # |
> > IMHO constructing json like that is a bad idea... What about proper escaping
> > so that the resulting json is stil valid if review text contains an
> > apostrophe? I think we should be using Json::FastWriter from jsoncpp
> instead.
>
> Discovered Json::Value to use here. No need for FastWriter AFAICT, just
> creating a root Value, adding the members, and converting it with
> .toStyledString() appears to be all we need.
Right, but if you take a look at the implementation of .toStyledString(), it just creates StyledWriter that outputs human-friendly JSON (i.e. with line breaks and indentation), whereas FastWriter outputs JSON without any formatting, which saves a little bit of bandwidth ;). Creating a FastWriter and passing it the root value yourself is just one extra line of code - see http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:219
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alejandro J. Cura (alecu) wrote : | # |
Looks good, +1.
ps: small typo here:
+ // NOTE: gmock replaes the \" above as \\\" for HasSubstr(), so we have
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:220
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'scope/click/reviews.cpp' |
2 | --- scope/click/reviews.cpp 2014-04-09 18:49:03 +0000 |
3 | +++ scope/click/reviews.cpp 2014-04-25 18:38:08 +0000 |
4 | @@ -33,6 +33,9 @@ |
5 | #include <boost/property_tree/json_parser.hpp> |
6 | #include <boost/foreach.hpp> |
7 | |
8 | +#include <json/value.h> |
9 | +#include <json/writer.h> |
10 | + |
11 | #include "reviews.h" |
12 | |
13 | namespace click |
14 | @@ -123,6 +126,42 @@ |
15 | return click::web::Cancellable(response); |
16 | } |
17 | |
18 | +click::web::Cancellable Reviews::submit_review (const PackageDetails& package, |
19 | + int rating, |
20 | + const std::string& review_text) |
21 | +{ |
22 | + std::map<std::string, std::string> headers({ |
23 | + {click::web::CONTENT_TYPE_HEADER, click::web::CONTENT_TYPE_JSON}, |
24 | + }); |
25 | + // TODO: Need to get language for the package/review. |
26 | + Json::Value root(Json::ValueType::objectValue); |
27 | + root["package_name"] = package.package.name; |
28 | + root["version"] = package.package.version; |
29 | + root["rating"] = rating; |
30 | + root["review_text"] = review_text; |
31 | + root["arch_tag"] = click::Configuration().get_architecture(); |
32 | + |
33 | + // NOTE: "summary" is a required field, but we don't have one. Use "". |
34 | + root["summary"] = ""; |
35 | + |
36 | + qDebug() << "Rating" << package.package.name.c_str() << rating; |
37 | + |
38 | + QSharedPointer<click::web::Response> response = client->call |
39 | + (get_base_url() + click::REVIEWS_API_PATH, "POST", true, |
40 | + headers, Json::FastWriter().write(root), click::web::CallParams()); |
41 | + |
42 | + QObject::connect(response.data(), &click::web::Response::finished, |
43 | + [=](QString) { |
44 | + qDebug() << "Review submitted for:" << package.package.name.c_str(); |
45 | + }); |
46 | + QObject::connect(response.data(), &click::web::Response::error, |
47 | + [=](QString) { |
48 | + qCritical() << "Network error submitting a reviews for:" << package.package.name.c_str(); |
49 | + }); |
50 | + |
51 | + return click::web::Cancellable(response); |
52 | +} |
53 | + |
54 | std::string Reviews::get_base_url () |
55 | { |
56 | const char *env_url = getenv(REVIEWS_BASE_URL_ENVVAR.c_str()); |
57 | |
58 | === modified file 'scope/click/reviews.h' |
59 | --- scope/click/reviews.h 2014-04-09 18:08:56 +0000 |
60 | +++ scope/click/reviews.h 2014-04-25 18:38:08 +0000 |
61 | @@ -78,6 +78,9 @@ |
62 | |
63 | click::web::Cancellable fetch_reviews (const std::string& package_name, |
64 | std::function<void(ReviewList, Error)> callback); |
65 | + click::web::Cancellable submit_review (const PackageDetails& package, |
66 | + int rating, |
67 | + const std::string& review_text); |
68 | |
69 | static std::string get_base_url (); |
70 | }; |
71 | |
72 | === modified file 'scope/click/webclient.cpp' |
73 | --- scope/click/webclient.cpp 2014-04-01 13:27:09 +0000 |
74 | +++ scope/click/webclient.cpp 2014-04-25 18:38:08 +0000 |
75 | @@ -117,7 +117,7 @@ |
76 | [=, &request](const UbuntuOne::Token& token) { |
77 | QString auth_header = token.signUrl(url.toString(), |
78 | method.c_str()); |
79 | - request.setRawHeader(AUTHORIZATION.c_str(), auth_header.toUtf8()); |
80 | + request.setRawHeader(AUTHORIZATION_HEADER.c_str(), auth_header.toUtf8()); |
81 | doConnect(); |
82 | }); |
83 | sc.connect(impl->sso.data(), &click::CredentialsService::credentialsNotFound, |
84 | |
85 | === modified file 'scope/click/webclient.h' |
86 | --- scope/click/webclient.h 2014-04-01 13:27:09 +0000 |
87 | +++ scope/click/webclient.h 2014-04-25 18:38:08 +0000 |
88 | @@ -49,7 +49,10 @@ |
89 | namespace web |
90 | { |
91 | |
92 | -const std::string AUTHORIZATION = "Authorization"; |
93 | +const std::string AUTHORIZATION_HEADER = "Authorization"; |
94 | +const std::string CONTENT_TYPE_HEADER = "Content-Type"; |
95 | + |
96 | +const std::string CONTENT_TYPE_JSON = "application/json"; |
97 | |
98 | class Client; |
99 | |
100 | |
101 | === modified file 'scope/tests/test_reviews.cpp' |
102 | --- scope/tests/test_reviews.cpp 2014-03-25 18:49:16 +0000 |
103 | +++ scope/tests/test_reviews.cpp 2014-04-25 18:38:08 +0000 |
104 | @@ -219,6 +219,83 @@ |
105 | fetch_reviews_op.cancel(); |
106 | } |
107 | |
108 | +TEST_F(ReviewsTest, testSubmitReviewIsCancellable) |
109 | +{ |
110 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
111 | + auto response = responseForReply(reply.asSharedPtr()); |
112 | + |
113 | + click::PackageDetails fake_details { |
114 | + { |
115 | + "ar.com.beuno.wheather-touch", |
116 | + "\u1F4A9 Weather", |
117 | + 1.99, |
118 | + "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png", |
119 | + "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", |
120 | + "0.2", |
121 | + }, |
122 | + "\u1F4A9 Weather\nA weather application.", |
123 | + "https://public.apps.staging.ubuntu.com/download/ar.com.beuno/wheather-touch/ar.com.beuno.wheather-touch-0.2", |
124 | + 3.5, |
125 | + "these, are, key, words", |
126 | + "tos", |
127 | + "Proprietary", |
128 | + "Beuno", |
129 | + "sshot0", |
130 | + {"sshot1", "sshot2"}, |
131 | + 177582, |
132 | + "0.2", |
133 | + "None" |
134 | + }; |
135 | + EXPECT_CALL(*clientPtr, callImpl(_, "POST", true, _, _, _)) |
136 | + .Times(1) |
137 | + .WillOnce(Return(response)); |
138 | + |
139 | + auto submit_op = reviewsPtr->submit_review(fake_details, 3, "blah"); |
140 | + EXPECT_CALL(reply.instance, abort()).Times(1); |
141 | + submit_op.cancel(); |
142 | +} |
143 | + |
144 | +TEST_F(ReviewsTest, testSubmitReviewUtf8) |
145 | +{ |
146 | + LifetimeHelper<click::network::Reply, MockNetworkReply> reply; |
147 | + auto response = responseForReply(reply.asSharedPtr()); |
148 | + |
149 | + click::PackageDetails fake_details { |
150 | + { |
151 | + "com.example.weather", |
152 | + "Weather", |
153 | + 1.99, |
154 | + "http://developer.staging.ubuntu.com/site_media/appmedia/2013/07/weather-icone-6797-64.png", |
155 | + "https://public.apps.staging.ubuntu.com/download/com.example/weather/com.example.weather-0.2", |
156 | + "0.2", |
157 | + }, |
158 | + "Weather application.", |
159 | + "https://public.apps.staging.ubuntu.com/download/com.example/weather/com.example.weather-0.2", |
160 | + 3.5, |
161 | + "these, are, key, words", |
162 | + "tos", |
163 | + "Proprietary", |
164 | + "Beuno", |
165 | + "sshot0", |
166 | + {"sshot1", "sshot2"}, |
167 | + 177582, |
168 | + "0.2", |
169 | + "None" |
170 | + }; |
171 | + std::string review_utf8 = "'\"小海嚴選"; |
172 | + |
173 | + // NOTE: gmock replaces the \" above as \\\" for HasSubstr(), so we have |
174 | + // to manually copy the string into the expected result. |
175 | + std::string expected_review_text = "\"review_text\":\"'\\\"小海嚴選\""; |
176 | + |
177 | + EXPECT_CALL(*clientPtr, callImpl(_, "POST", true, _, |
178 | + HasSubstr(expected_review_text), _)) |
179 | + .Times(1) |
180 | + .WillOnce(Return(response)); |
181 | + |
182 | + auto submit_op = reviewsPtr->submit_review(fake_details, 3, review_utf8); |
183 | +} |
184 | + |
185 | TEST_F(ReviewsTest, testGetBaseUrl) |
186 | { |
187 | const char *value = getenv(click::REVIEWS_BASE_URL_ENVVAR.c_str()); |
188 | |
189 | === modified file 'scope/tests/test_webclient.cpp' |
190 | --- scope/tests/test_webclient.cpp 2014-04-01 13:27:09 +0000 |
191 | +++ scope/tests/test_webclient.cpp 2014-04-25 18:38:08 +0000 |
192 | @@ -46,7 +46,7 @@ |
193 | |
194 | MATCHER_P(IsValidOAuthHeader, refOAuth, "") |
195 | { |
196 | - return arg.hasRawHeader("Authorization") && arg.rawHeader(click::web::AUTHORIZATION.c_str()) |
197 | + return arg.hasRawHeader(click::web::AUTHORIZATION_HEADER.c_str()) && arg.rawHeader(click::web::AUTHORIZATION_HEADER.c_str()) |
198 | .startsWith("OAuth "); |
199 | } |
200 |
PASSED: Continuous integration, rev:213 jenkins. qa.ubuntu. com/job/ unity-scope- click-ci/ 440/ jenkins. qa.ubuntu. com/job/ unity-scope- click-trusty- amd64-ci/ 341 jenkins. qa.ubuntu. com/job/ unity-scope- click-trusty- armhf-ci/ 338 jenkins. qa.ubuntu. com/job/ unity-scope- click-trusty- armhf-ci/ 338/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scope-click- ci/440/ rebuild
http://