Merge lp:~paulliu/unity-scope-click/showratings into lp:unity-scope-click
- showratings
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | dobey |
Approved revision: | 105 |
Merged at revision: | 104 |
Proposed branch: | lp:~paulliu/unity-scope-click/showratings |
Merge into: | lp:unity-scope-click |
Diff against target: |
394 lines (+307/-3) 6 files modified
src/Makefile.am (+2/-1) src/click-scope.vala (+7/-1) src/click-webservice.vala (+7/-1) src/fake-data.vala (+43/-0) src/rnrclient.vala (+201/-0) src/test-click-webservice.vala (+47/-0) |
To merge this branch: | bzr merge lp:~paulliu/unity-scope-click/showratings |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot | continuous-integration | Approve | |
dobey (community) | Approve | ||
Manuel de la Peña (community) | Approve | ||
Review via email: mp+198762@code.launchpad.net |
This proposal supersedes a proposal from 2013-12-10.
Commit message
Get the reviews from the server and pass them to the preview.
Description of the change
Provide reviews/ratings from server in previews.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
Manuel de la Peña (mandel) wrote : Posted in a previous version of this proposal | # |
Can you explain what is the following line doing:
var reviews = yield rnrClient.
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_
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(
144 + var call = proxy.new_call();
Could not have used bar in both cases?
In the following code:
257 + try {
258 + parser.
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!!!
dobey (dobey) wrote : Posted in a previous version of this proposal | # |
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.
dobey (dobey) wrote : Posted in a previous version of this proposal | # |
118 + const string REVIEWS_URL = "http://
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-webservic
Ying-Chun Liu (paulliu) wrote : Posted in a previous version of this proposal | # |
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?
Ying-Chun Liu (paulliu) wrote : Posted in a previous version of this proposal | # |
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.
dobey (dobey) wrote : Posted in a previous version of this proposal | # |
77 + const string REVIEWS_BASE_URL = "http://
86 + string from_environ (string env_name, string default_value) {
87 + string env_value = Environment.
88 + return env_value != null ? env_value : default_value;
89 + }
90 +
91 + string get_base_url () {
92 + return from_environ ("U1_REVIEWS_
93 + }
This is still wrong. Why are you not using HTTPS here? Also, the _BASE_URL should only be "https:/
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:91
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:93
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : Posted in a previous version of this proposal | # |
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.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:94
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:91
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 92. By Ying-Chun Liu
-
remvoe server-status as we don't need to use it.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:92
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
The ReviewFilter object is a little weird (maybe because of the name). You aren't documenting in the code what each element does, or why its value is what it is. For example, it's still not clear why "any" is used in several places, or why you are passing "any" as the version, instead of the version.
A guess suggests this tells the server that we don't care what distroseries/
- 93. By Ying-Chun Liu
-
Add comments.
Michael Nelson (michael.nelson) wrote : | # |
I don't remember why "any" was chosen as the token, but from memory a token was chosen so that the urls would be consistent, flexible and without qparams - at the expense of requiring multiple requests in some cases. So no, there is currently no intelligence for "you requested version X, but we don't have reviews of that, so here are the reviews of X".
Looking at the interface document [1], the get_reviews helper is just enabling requests in the form:
/api/2.
ie. in the first example:
>>> skype_reviews = api.get_
results in a call to:
/api/2.
ie - all reviews of skype across all distroseries, versions etc.
The second example: api.get_
in (an escaped)
/api/2.
ie. all reviews of that version of skype, across distroseries etc.
So it's flexible, but means that multiple requests may be needed to do things like "get me the reviews for skype and somotherapp, or get me reviews of skype versions X and Y, or something intelligent like "You asked for version X, but here's reviews for X-1.
The server could be easily updated to support that if you know exactly what you want. If you have specific requests like this that you know you'll need often, it should be trivial to add those to the API.
Hope that helps!
[1] https:/
Michael Nelson (michael.nelson) wrote : | # |
> I don't remember why "any" was chosen as the token, but from memory a token
> was chosen so that the urls would be consistent, flexible and without qparams
> - at the expense of requiring multiple requests in some cases. So no, there is
> currently no intelligence for "you requested version X, but we don't have
> reviews of that, so here are the reviews of X".
X-1, sorry.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:93
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 94. By Ying-Chun Liu
-
Change origin to "click".
- 95. By Ying-Chun Liu
-
Merge upstream.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:94
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Manuel de la Peña (mandel) wrote : | # |
Looks good to me
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:95
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
+ return get_base_url() + "/api/1.
I'm confused by this. Particularly the "%s%s" in the middle there, and the apparent need for paging to support having the server sort the results.
Why are you combining packagename and appname into a single string there? I don't think we should be doing that. I also don't think we should be using pagination. If the results from the API include the usefulness rating, we can just do the sorting on that locally, and avoid passing the extra weird parameters.
217 + * @origin: Normally comes from "ubuntu". For click packages it is "click".
218 + * The default value "any" to get reviews for any origin.
+ filter.origin = "click";
Why are we using "click" here? The origin is not click. The origin for packages in Ubuntu itself is "ubuntu" because they are packaged as part of the Ubuntu archive, by Ubuntu developers. Packages which are sold via MyApps in the Software Center, do not have an origin of "ubuntu" there. Rather, "steam" for example, has an origin of "valve inc." (which is apparently not a proper origin ID, but that's another issue). The origin is supposed to be who created the package and uploaded it (aka, the Publisher). I don't know that we have a proper ID for publishers though, which is in the app details, and which is not a user-displayable string, but rather is a string without any spaces. However, I'm pretty sure "click" is not what we want to use there.
33 + if (reviews != null) {
34 + preview.
35 + debug("Add reviews "+reviews.
36 + }
4 spaces for indents please, not 2.
dobey (dobey) wrote : | # |
Some tests would also be nice to have here.
- 96. By Ying-Chun Liu
-
fix indent
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:96
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Nelson (michael.nelson) wrote : | # |
> + return get_base_url() +
> "/api/1.
>
> I'm confused by this. Particularly the "%s%s" in the middle there, and the
> apparent need for paging to support having the server sort the results.
Hrm - I assume vala/C++ printf doesn't support a hash of args so you can return something like: ".../filter/
>
> Why are you combining packagename and appname into a single string there? I
> don't think we should be doing that.
Correct - it should just be packagename (according to the urls in rnr server [1]).
You should expect to see a 404 if you combine the packagename+appname like that.
> I also don't think we should be using
> pagination. If the results from the API include the usefulness rating, we can
> just do the sorting on that locally, and avoid passing the extra weird
> parameters.
You don't need to use the pagination unless you want more than the first page of results. By default, the server will return the first batch of results, sorted by usefulness (and the current config of the server has the batch size at 10 results). These were the requirements that we had for USC client, but can be changed as needed. As far as I can tell below, you're never using anything but the default first page with the default sort of usefulness.
>
>
> 217 + * @origin: Normally comes from "ubuntu". For click packages it is
> "click".
> 218 + * The default value "any" to get reviews for any origin.
> + filter.origin = "click";
>
> Why are we using "click" here? The origin is not click. The origin for
> packages in Ubuntu itself is "ubuntu" because they are packaged as part of the
> Ubuntu archive, by Ubuntu developers.
Correct, the origin should be "ubuntu" here, while the distroseries should be "click" (there is no natty/precise/etc. for click packages - at least, that's the decision that was made for exporting the apps from devportal [2]:
[2] http://
I don't see why this unity-scope wouldn't just use the default for ReviewFilter of ubuntu/click?
> Packages which are sold via MyApps in
> the Software Center, do not have an origin of "ubuntu" there. Rather, "steam"
> for example, has an origin of "valve inc." (which is apparently not a proper
> origin ID, but that's another issue). The origin is supposed to be who created
> the package and uploaded it (aka, the Publisher). I don't know that we have a
> proper ID for publishers though, which is in the app details, and which is not
> a user-displayable string, but rather is a string without any spaces. However,
> I'm pretty sure "click" is not what we want to use there.
Right. For USC, the origin is either ubuntu or the private ppa identifier (url-encoded).
Hope that helps!
dobey (dobey) wrote : | # |
> > + return get_base_url() +
> > "/api/1.
> >
> Hrm - I assume vala/C++ printf doesn't support a hash of args so you can
> return something like: ".../filter/
> That would be clearer.
I don't think it would be made clearer. The most confusing part was the "%s%s" (without a slash between them), which we agree is wrong. I think a simple comment that explains the structure of the URL would be enough to clarify what each %s is, in the string. The Python string formatting would obviously be better, but we can't do that in C/Vala, so a comment would be well enough.
> Correct, the origin should be "ubuntu" here, while the distroseries should be
> "click" (there is no natty/precise/etc. for click packages - at least, that's
> the decision that was made for exporting the apps from devportal [2]:
>
> [2] http://
>
> I don't see why this unity-scope wouldn't just use the default for
> ReviewFilter of ubuntu/click?
Well, this doesn't make sense to me either. The origin isn't Ubuntu (ie, these packages aren't part of the Ubuntu archive, but are published by other individuals or organizations). So, I don't think we should be defaulting to anything, but instead getting some value from the details of the package, and using that as the origin. Maybe we need to add something to the package details endpoint of the click-package-index API to do that. Also, I think the distroseries does need to be the actual distroseries here. But maybe we should be using the "click_framework" value from the package details here, rather than saucy/trusty/etc… ?
Michael Nelson (michael.nelson) wrote : | # |
On Thu, Jan 2, 2014 at 4:55 PM, Rodney Dawes <email address hidden> wrote:
>> Correct, the origin should be "ubuntu" here, while the distroseries should be
>> "click" (there is no natty/precise/etc. for click packages - at least, that's
>> the decision that was made for exporting the apps from devportal [2]:
>>
>> [2] http://
>>
>> I don't see why this unity-scope wouldn't just use the default for
>> ReviewFilter of ubuntu/click?
>
> Well, this doesn't make sense to me either. The origin isn't Ubuntu (ie, these packages aren't part of the Ubuntu archive, but are published by other individuals or organizations).
Hrm - true. So for existing reviews, the origin is the ppa identifier,
such as lp-ppa-
For click packages, we have a packagename which includes an 'origin'
of sorts, like:
com.ubuntu.
So I wonder if it'd make sense to use:
origin: com.ubuntu.
packagename: mymaps
in that case? Let me know, as I'll need to update the server (easy enough).
> So, I don't think we should be defaulting to anything, but instead getting some value from the details of the package, and using that as the origin. Maybe we need to add something to the package details endpoint of the click-package-index API to do that.
As above, the 'domain' seems to map 1:1 with the idea of origin?
> Also, I think the distroseries does need to be the actual distroseries here. But maybe we should be using the "click_framework" value from the package details here, rather than saucy/trusty/etc… ?
Yep, +1 to using the click_framework for the distroseries. I can get
an MP ready with those changes for the server. So, to check my
understanding with a summary:
You'll search/filter with origin: any, distroseries: ubuntu-sdk-13.10
You'll submit reviews with:
origin: com.ubuntu.
packagename: mymaps
distroseries: ubuntu-sdk-13.10
Will you need to search for click packages across frameworks? If so,
it'll need a bit more work to make that clean (as origin:any,
distroseries: any will give you not just click packages, but normal
packages also - a result of us just re-using the existing
infrastructure)., but if not, the above should be fine.
Let me know if you want to have a call about this.
Ying-Chun Liu (paulliu) wrote : | # |
> On Thu, Jan 2, 2014 at 4:55 PM, Rodney Dawes <email address hidden>
> wrote:
> >> Correct, the origin should be "ubuntu" here, while the distroseries should
> be
> >> "click" (there is no natty/precise/etc. for click packages - at least,
> that's
> >> the decision that was made for exporting the apps from devportal [2]:
> >>
> >> [2] http://
> >>
> >> I don't see why this unity-scope wouldn't just use the default for
> >> ReviewFilter of ubuntu/click?
> >
> > Well, this doesn't make sense to me either. The origin isn't Ubuntu (ie,
> these packages aren't part of the Ubuntu archive, but are published by other
> individuals or organizations).
>
> Hrm - true. So for existing reviews, the origin is the ppa identifier,
> such as lp-ppa-
>
> For click packages, we have a packagename which includes an 'origin'
> of sorts, like:
>
> com.ubuntu.
>
> So I wonder if it'd make sense to use:
>
> origin: com.ubuntu.
> packagename: mymaps
>
> in that case? Let me know, as I'll need to update the server (easy enough).
>
> > So, I don't think we should be defaulting to anything, but instead getting
> some value from the details of the package, and using that as the origin.
> Maybe we need to add something to the package details endpoint of the click-
> package-index API to do that.
>
> As above, the 'domain' seems to map 1:1 with the idea of origin?
>
yeah, I think this is a good idea.We just need a 1:1 mapping to query the reviews.
I mean, we just give you a key and you give us all of the data.
origin+packagename should be good enough for being a key I think.
dobey (dobey) wrote : | # |
> For click packages, we have a packagename which includes an 'origin'
> of sorts, like:
>
> com.ubuntu.
>
> So I wonder if it'd make sense to use:
>
> origin: com.ubuntu.
> packagename: mymaps
>
> in that case? Let me know, as I'll need to update the server (easy enough).
While this is common, it's not necessarily required to be this way. I'm not sure we want to split this string up in this way at all. The package name is the full string, not just the text after the last '.' character. It's possible that this portion may conflict (multiple people might write a 'flickr' app for example). It could result in requests for existing reviews, returning the wrong things (reviews for the other app with the same name).
Maybe the best solution, given the data we have, is to simply use the package name as the origin and the package name.
> > So, I don't think we should be defaulting to anything, but instead getting
> some value from the details of the package, and using that as the origin.
> Maybe we need to add something to the package details endpoint of the click-
> package-index API to do that.
>
> As above, the 'domain' seems to map 1:1 with the idea of origin?
There is no 'domain' exactly. But I think we can use the package name for both the origin and the package name, to introduce the necessary level of uniqueness.
> So, to check my understanding with a summary:
>
> You'll search/filter with origin: any, distroseries: ubuntu-sdk-13.10
>
> You'll submit reviews with:
> origin: com.ubuntu.
> packagename: mymaps
> distroseries: ubuntu-sdk-13.10
As mentioned above, I don't think we'll do it this way, but we should probably use full package name (com.example.
> Will you need to search for click packages across frameworks? If so,
> it'll need a bit more work to make that clean (as origin:any,
> distroseries: any will give you not just click packages, but normal
> packages also - a result of us just re-using the existing
> infrastructure)., but if not, the above should be fine.
I'd like to say no for now, and we can deal with making that possible later, if necessary.
dobey (dobey) wrote : | # |
98 + return get_base_url() + "/api/1.
Please add a comment above this line, explaining which each %s is for exactly. Also, please switch to using the simpler URL, and avoid the "%s%s" combining the filter.packagename and filter.appname into a single part of the URL's path. We should be using "/api/1.
239 + public string appname = "";
240 + public string page = "1";
241 + public string sort = "helpful";
You can also get rid of these, as we do not need to use any of them, with the above changes.
35 + debug("Add reviews "+reviews.
Let's not just print all the reviews here. It will eventually be a lot of data to be looking at in the log. Maybe just logging "Got %d reviews for %s" with the number of reviews received, and the package name, instead.
And finally, a few tests would be nice. Especially with the above mentioned change to the URL structuring and handling of error cases, to ensure the error cases are being handled correctly, as well as the valid results cases.
Ying-Chun Liu (paulliu) wrote : | # |
> 98 + return get_base_url() +
> "/api/1.
>
> Please add a comment above this line, explaining which each %s is for exactly.
> Also, please switch to using the simpler URL, and avoid the "%s%s" combining
> the filter.packagename and filter.appname into a single part of the URL's
> path. We should be using "/api/1.
> "language, origin, distroseries, version, packagename" as the format values.
> The origin and packagename arguments should be the same here (using the
> package_name value from AppDetails), version should be version from
> AppDetails, and distroseries should be the "framework" value from AppDetails.
> We should also check that origin, distroseries, version, and packagename are
> not the default values of "any" or "", raising an error if they are, and not
> getting any reviews from the server in such a case.
seems to me that we currently don't have framework value in AppDetails. It is a private constant now.
- 97. By Ying-Chun Liu
-
Add some unit tests.
Partly fix the issues in comment 465611
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:97
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
The test you added created a conflict with trunk. Please merge trunk into your branch and ensure there are no conflicts (and that you don't remove any of the existing tests in trunk).
Alexander Sack (asac) wrote : | # |
fwiw, the src/test-
Search for:
+<<<<<<< TREE
- 98. By Ying-Chun Liu
-
Add version/framework
- 99. By Ying-Chun Liu
-
merge trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:99
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 100. By Ying-Chun Liu
-
fix indent.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:100
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 101. By Ying-Chun Liu
-
fix comment for ReviewFilter.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:101
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
67 + keywords: parse_string_list (details, JSON_FIELD_
68 + version: details.
69 + framework: parse_string_list (details, JSON_FIELD_
Fix the spacing here please. The version and framework you added, seem to have tabs and are spaced further to the right, than the previous lines are.
58 + public string[] framework { get; construct; }
69 + framework: parse_string_list (details, JSON_FIELD_
Why are you using this as a string list? The JSON for this seems to be a single string, not a list of strings.
338 +
Can you remove this new empty line you seem to have added?
172 + private string from_environ (string env_name, string default_value) {
177 + private string get_base_url () {
181 + private string get_review_url() {
There's no reason these need to be private. Rather, it would be a good idea to have them be public, and for there to be a few tests for them (especially to test from_environ's success and failure cases).
- 102. By Ying-Chun Liu
-
fix indent.
Add test for from_environ
fix framework.
Ying-Chun Liu (paulliu) wrote : | # |
** (process:3961): DEBUG: click-webservic
I saw this. Is framework really just a string?
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:102
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 103. By Ying-Chun Liu
-
Add 2014 to copyright.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:103
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
Looks like the framework is a list after all in the search API (but is apparently only a string on myapps, hence the confusion). We'll probably need to get the framework to use, from the system instead of the package details, when there is a way to do that.
- 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.
Ying-Chun Liu (paulliu) wrote : | # |
Currently I add a TODO in source.
I've talked to lool and noodles. Found that the system can also provides more than 1 framework on a single device.
To get a list of the framework that system is provided, there's some files in /usr/share/
So we might want to do an intersect of the system's and the AppDetails' and do a multiple query and then sort the result.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:104
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Michael Nelson (michael.nelson) wrote : | # |
TBH, I don't understand why we're using framework(s) for this interaction with the reviews api service at all.
As I understand it:
1) A click package definition can define the *required* frameworks that it needs.
2) A client can support multiple frameworks
3) The client already knows which apps it can install (from querying search.apps.u.c), and will only ever (?) want to retrieve reviews for apps it knows it can install, or submit reviews for apps it has already installed.
The review api service doesn't need to care what framework(s) the app requires (that's what search.apps.u.c is for), or which frameworks the client supports.
If it helps, I can send an email to the list to get more input, but IMO, we should just be sending distroseries=click as we were originally, or what am I missing?
Roberto Alsina (ralsina) wrote : | # |
Good point, there is no way to get to the reviews on a app with the wrong
framework, etc. This is getting way overcomplicated.
On Thu, Jan 9, 2014 at 8:52 AM, Michael Nelson <<email address hidden>
> wrote:
> TBH, I don't understand why we're using framework(s) for this interaction
> with the reviews api service at all.
>
> As I understand it:
>
> 1) A click package definition can define the *required* frameworks that it
> needs.
> 2) A client can support multiple frameworks
> 3) The client already knows which apps it can install (from querying
> search.apps.u.c), and will only ever (?) want to retrieve reviews for apps
> it knows it can install, or submit reviews for apps it has already
> installed.
>
> The review api service doesn't need to care what framework(s) the app
> requires (that's what search.apps.u.c is for), or which frameworks the
> client supports.
>
> If it helps, I can send an email to the list to get more input, but IMO,
> we should just be sending distroseries=click as we were originally, or what
> am I missing?
>
>
> --
>
> https:/
> Your team Ubuntu One hackers is subscribed to branch lp:unity-scope-click.
>
Ying-Chun Liu (paulliu) wrote : | # |
yeah, it is a good point.
But I also can understand what dobey was worrying about.
I'll need to confirm if 1) is true.
Is that field the required framework, or just *supported* framework.
I mean if a click package can run on either 13.10 and 14.04 framework. In that case the user might only wants to get the reviews of specific framework. Because a click package might get negative reviews if it has a bug on one of the framework but good on another.
Roberto Alsina (ralsina) wrote : | # |
Well, that's actually a design/UX question. A quick check on Play tells me
the reviews are not separated by android version, and that makes sense, or
else new versions will show no reviews at all, which is weird.
I'd say let's not filter by framework *at all* at this point.
- 105. By Ying-Chun Liu
-
use "click" as framework.
Michael Nelson (michael.nelson) wrote : | # |
On Thu, Jan 9, 2014 at 2:05 PM, Ying-Chun Liu <email address hidden> wrote:
> yeah, it is a good point.
> But I also can understand what dobey was worrying about.
> I'll need to confirm if 1) is true.
> Is that field the required framework, or just *supported* framework.
Required AIUI. From [1]:
For future expansion (e.g. applications that require multiple
frameworks), the syntax of “framework” is formally that of a Debian
dependency relationship field. Currently, only a simple name is
permitted, e.g. “framework”: “ubuntu-sdk-13.10”.
[1] https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:105
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
dobey (dobey) wrote : | # |
The framework is needed because the reviews API requires distroseries. It's required because the API is for some reason a URL path, and not a POST request to an endpoint, with some JSON to describe what we want.
Using the framework for the distroseries makes sense to me, but we can just as well use "saucy" or "trusty" or whatever the Ubuntu distroseries is, if we have that information on the device. I don't know how the reviews system on Android works or not. But it doesn't really matter, because we're not using their system. We're stuck with using the weird API we have, which requires a distroseries. The way software-center was written, was to get reviews for the "previous" series, if the current series results were empty. That should really be handled on the server side, and we really should have a proper API, but it's not.
Roberto Alsina (ralsina) wrote : | # |
Yes, but we can just as easily use "foo" as the distroseries, forever. We
are trying to implement something here, and we control both sides of the
thing, so leaking the API into the UX is not a foregone conclusion. If
something needs to bend to make the usage reasonable, then it bends.
Having all apps lose all reviews when you update the phone is silly.
Michael Nelson (michael.nelson) wrote : | # |
Hi Rodney, let me know if you'd rather a call, but RE the points below:
On Thu, Jan 9, 2014 at 3:22 PM, Rodney Dawes <email address hidden> wrote:
> The framework is needed because the reviews API requires distroseries. It's required because the API is for some reason a URL path, and not a POST request to an endpoint, with some JSON to describe what we want.
The request for reviews filtered by a package is a GET request to a
URL. The request for creating a new review is a POST to an endpoint
(/api/1.0/reviews/) with some JSON to describe what you want.
And yep, creating a new review requires a distroseries because it was
created for USC. We could use distroseries=
the server to differentiate the reviews easily (distroseries is
already indexed), or we could also quite easily create a separate
resource for click apps which doesn't have these hacks (origin and
distroseries), but this started as a way to re-use what we already
had.
>
> Using the framework for the distroseries makes sense to me, but we can just as well use "saucy" or "trusty" or whatever the Ubuntu distroseries is, if we have that information on the device. I don't know how the reviews system on Android works or not. But it doesn't really matter, because we're not using their system. We're stuck with using the weird API we have, which requires a distroseries. The way software-center was written, was to get reviews for the "previous" series, if the current series results were empty. That should really be handled on the server side, and we really should have a proper API, but it's not.
>
If it's worth creating a separate API for click (with separate specs
from what we already do for USC), then let's do that. If we want to
re-use what we've already got for USC, let's do that. That decision
isn't my call, but I do want to be able to support you whichever way
that decision goes.
Michael Nelson (michael.nelson) wrote : | # |
On Thu, Jan 9, 2014 at 3:41 PM, Michael Nelson
<email address hidden> wrote:
>
> If it's worth creating a separate API for click (with separate specs
> from what we already do for USC), then let's do that. If we want to
> re-use what we've already got for USC, let's do that. That decision
> isn't my call, but I do want to be able to support you whichever way
> that decision goes.
FWIW, I'm doing a quick branch which I'll give to Rodney tomorrow with
an example of a separate click review API (as part of the same
service). It won't take long, and would mean we don't have to release
software on the client that has hacks to fit in with existing services
for which it wasn't designed. It'll also have the benefit that it's
easy to separate later if we need to deploy the click review service
separately.
I'm doing the following, but we can make it exactly as we see fit so
just let me know:
To create a review, it's much the same:
POST /click/
with the json data (same as current, just without origin or distroseries)
To retrieve reviews for a specific application:
GET /click/
Or, if you'd prefer:
GET /click/
(url-encoded)
The original reason that the existing API uses the former
non-query-param filters is because not all caches will reliably cache
uri's with query params, but perhaps that's not so important (or
relevant) now (I'm not a squid expert). The flexibility of the second
option is certainly nicer (and more standard). We can move other
features (moderation, +1's etc.) across easily as we need them too.
As an aside, I mentioned to beuno today that I'm pretty sure we'll
also want to update the search.apps.u.c search results to include the
review stats (at the least, the current rating for each app in the
results so the client can display the stars, and only request reviews
when needed).
Cheers,
Michael
dobey (dobey) wrote : | # |
I don't think we should spend time to build a slightly different API which does basically the same as passing "click" as the distroseries for everything.
dobey (dobey) : | # |
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'src/Makefile.am' | |||
2 | --- src/Makefile.am 2013-12-18 19:19:12 +0000 | |||
3 | +++ src/Makefile.am 2014-01-09 13:55:57 +0000 | |||
4 | @@ -44,7 +44,8 @@ | |||
5 | 44 | click-interface.vala \ | 44 | click-interface.vala \ |
6 | 45 | ubuntuone-credentials.vala \ | 45 | ubuntuone-credentials.vala \ |
7 | 46 | non-click-scope.vala \ | 46 | non-click-scope.vala \ |
9 | 47 | utils.vala | 47 | utils.vala \ |
10 | 48 | rnrclient.vala | ||
11 | 48 | 49 | ||
12 | 49 | config.vala: config.vala.in | 50 | config.vala: config.vala.in |
13 | 50 | sed -e "s|\@DATADIR\@|${datadir}|g" \ | 51 | sed -e "s|\@DATADIR\@|${datadir}|g" \ |
14 | 51 | 52 | ||
15 | === modified file 'src/click-scope.vala' | |||
16 | --- src/click-scope.vala 2013-12-24 00:33:07 +0000 | |||
17 | +++ src/click-scope.vala 2014-01-09 13:55:57 +0000 | |||
18 | @@ -82,6 +82,7 @@ | |||
19 | 82 | public ClickInterface click_if = new ClickInterface (); | 82 | public ClickInterface click_if = new ClickInterface (); |
20 | 83 | public UbuntuoneCredentials u1creds = new UbuntuoneCredentials (); | 83 | public UbuntuoneCredentials u1creds = new UbuntuoneCredentials (); |
21 | 84 | public ClickWebservice webservice = new ClickWebservice (); | 84 | public ClickWebservice webservice = new ClickWebservice (); |
22 | 85 | RNRClient rnrClient = new RNRClient(); | ||
23 | 85 | 86 | ||
24 | 86 | public ClickScope () | 87 | public ClickScope () |
25 | 87 | { | 88 | { |
26 | @@ -123,7 +124,12 @@ | |||
27 | 123 | preview.add_info(new Unity.InfoHint.with_variant(HINT_PUBLISHER, "Publisher", null, new Variant.string(details.publisher))); | 124 | preview.add_info(new Unity.InfoHint.with_variant(HINT_PUBLISHER, "Publisher", null, new Variant.string(details.publisher))); |
28 | 124 | preview.add_info(new Unity.InfoHint.with_variant(HINT_SCREENSHOTS, LABEL_SCREENSHOTS, null, new Variant.strv(details.more_screenshot_urls))); | 125 | preview.add_info(new Unity.InfoHint.with_variant(HINT_SCREENSHOTS, LABEL_SCREENSHOTS, null, new Variant.strv(details.more_screenshot_urls))); |
29 | 125 | preview.add_info(new Unity.InfoHint.with_variant(HINT_KEYWORDS, LABEL_KEYWORDS, null, new Variant.strv(details.keywords))); | 126 | preview.add_info(new Unity.InfoHint.with_variant(HINT_KEYWORDS, LABEL_KEYWORDS, null, new Variant.strv(details.keywords))); |
31 | 126 | // TODO: get the proper ratings and reviews from the rnr web service | 127 | // get the proper ratings and reviews from the rnr web service |
32 | 128 | var reviews = yield rnrClient.get_reviews(details); | ||
33 | 129 | if (reviews != null) { | ||
34 | 130 | preview.add_info(new Unity.InfoHint.with_variant(HINT_REVIEWS, LABEL_REVIEWS, null, reviews)); | ||
35 | 131 | debug("Got %d reviews for %s", (int)reviews.n_children(), details.package_name); | ||
36 | 132 | } | ||
37 | 127 | return preview; | 133 | return preview; |
38 | 128 | } catch (WebserviceError e) { | 134 | } catch (WebserviceError e) { |
39 | 129 | debug ("Error calling webservice: %s", e.message); | 135 | debug ("Error calling webservice: %s", e.message); |
40 | 130 | 136 | ||
41 | === modified file 'src/click-webservice.vala' | |||
42 | --- src/click-webservice.vala 2013-12-19 18:19:27 +0000 | |||
43 | +++ src/click-webservice.vala 2014-01-09 13:55:57 +0000 | |||
44 | @@ -32,6 +32,8 @@ | |||
45 | 32 | const string JSON_FIELD_SCREENSHOT_URLS = "screenshot_urls"; | 32 | const string JSON_FIELD_SCREENSHOT_URLS = "screenshot_urls"; |
46 | 33 | const string JSON_FIELD_DESCRIPTION = "description"; | 33 | const string JSON_FIELD_DESCRIPTION = "description"; |
47 | 34 | const string JSON_FIELD_KEYWORDS = "keywords"; | 34 | const string JSON_FIELD_KEYWORDS = "keywords"; |
48 | 35 | const string JSON_FIELD_VERSION = "version"; | ||
49 | 36 | const string JSON_FIELD_FRAMEWORK = "framework"; | ||
50 | 35 | 37 | ||
51 | 36 | // The size of the cache for click app data | 38 | // The size of the cache for click app data |
52 | 37 | // == SIZE_IN_MB * 1024 * 1024 | 39 | // == SIZE_IN_MB * 1024 * 1024 |
53 | @@ -117,6 +119,8 @@ | |||
54 | 117 | public string main_screenshot_url { get; construct; } | 119 | public string main_screenshot_url { get; construct; } |
55 | 118 | public string[] more_screenshot_urls { get; construct; } | 120 | public string[] more_screenshot_urls { get; construct; } |
56 | 119 | public uint64 binary_filesize { get; construct; } | 121 | public uint64 binary_filesize { get; construct; } |
57 | 122 | public string version { get; construct; } | ||
58 | 123 | public string[] framework { get; construct; } | ||
59 | 120 | 124 | ||
60 | 121 | 125 | ||
61 | 122 | /* TODO: use RnR webservice | 126 | /* TODO: use RnR webservice |
62 | @@ -164,7 +168,9 @@ | |||
63 | 164 | 168 | ||
64 | 165 | title: details.get_string_member(JSON_FIELD_TITLE), | 169 | title: details.get_string_member(JSON_FIELD_TITLE), |
65 | 166 | description: details.get_string_member(JSON_FIELD_DESCRIPTION), | 170 | description: details.get_string_member(JSON_FIELD_DESCRIPTION), |
67 | 167 | keywords: parse_string_list (details, JSON_FIELD_KEYWORDS) | 171 | keywords: parse_string_list (details, JSON_FIELD_KEYWORDS), |
68 | 172 | version: details.get_string_member(JSON_FIELD_VERSION), | ||
69 | 173 | framework: parse_string_list (details, JSON_FIELD_FRAMEWORK) | ||
70 | 168 | ); | 174 | ); |
71 | 169 | } | 175 | } |
72 | 170 | } | 176 | } |
73 | 171 | 177 | ||
74 | === modified file 'src/fake-data.vala' | |||
75 | --- src/fake-data.vala 2013-08-08 19:25:09 +0000 | |||
76 | +++ src/fake-data.vala 2014-01-09 13:55:57 +0000 | |||
77 | @@ -88,3 +88,46 @@ | |||
78 | 88 | } | 88 | } |
79 | 89 | ] | 89 | ] |
80 | 90 | """; | 90 | """; |
81 | 91 | |||
82 | 92 | const string FAKE_RNR_REVIEW_RESULTS = """ | ||
83 | 93 | [ | ||
84 | 94 | { | ||
85 | 95 | "origin": "canonical", | ||
86 | 96 | "rating": 1, | ||
87 | 97 | "hide": false, | ||
88 | 98 | "app_name": "", | ||
89 | 99 | "language": "en", | ||
90 | 100 | "reviewer_username": "gbtest", | ||
91 | 101 | "usefulness_total": 31, | ||
92 | 102 | "usefulness_favorable": 30, | ||
93 | 103 | "review_text": "Cause system hangs a lot", | ||
94 | 104 | "date_deleted": null, | ||
95 | 105 | "summary": "poor linux version", | ||
96 | 106 | "version": "4.2.0.11-0ubuntu0.12.04.2", | ||
97 | 107 | "id": 7678621, | ||
98 | 108 | "date_created": "2013-08-11 13:23:13", | ||
99 | 109 | "reviewer_displayname": "Brian Test", | ||
100 | 110 | "package_name": "skyter", | ||
101 | 111 | "distroseries": "precise" | ||
102 | 112 | }, | ||
103 | 113 | { | ||
104 | 114 | "origin": "canonical", | ||
105 | 115 | "rating": 4, | ||
106 | 116 | "hide": false, | ||
107 | 117 | "app_name": "", | ||
108 | 118 | "language": "en", | ||
109 | 119 | "reviewer_username": "user3844", | ||
110 | 120 | "usefulness_total": 6, | ||
111 | 121 | "usefulness_favorable": 6, | ||
112 | 122 | "review_text": "Great app versions", | ||
113 | 123 | "date_deleted": null, | ||
114 | 124 | "summary": "works stable, but has a bad interface", | ||
115 | 125 | "version": "4.2.0.11-0ubuntu0.12.04.2", | ||
116 | 126 | "id": 78054, | ||
117 | 127 | "date_created": "2013-09-09 18:59:20", | ||
118 | 128 | "reviewer_displayname": "Oron Pista", | ||
119 | 129 | "package_name": "skyter", | ||
120 | 130 | "distroseries": "raring" | ||
121 | 131 | } | ||
122 | 132 | ] | ||
123 | 133 | """; | ||
124 | 91 | 134 | ||
125 | === added file 'src/rnrclient.vala' | |||
126 | --- src/rnrclient.vala 1970-01-01 00:00:00 +0000 | |||
127 | +++ src/rnrclient.vala 2014-01-09 13:55:57 +0000 | |||
128 | @@ -0,0 +1,201 @@ | |||
129 | 1 | /* -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ | ||
130 | 2 | /* | ||
131 | 3 | * Copyright (C) 2013-2014 Canonical, Ltd. | ||
132 | 4 | * | ||
133 | 5 | * This program is free software; you can redistribute it and/or modify | ||
134 | 6 | * it under the terms of the GNU General Public License as published by | ||
135 | 7 | * the Free Software Foundation; version 3. | ||
136 | 8 | * | ||
137 | 9 | * This program is distributed in the hope that it will be useful, | ||
138 | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
139 | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
140 | 12 | * GNU General Public License for more details. | ||
141 | 13 | * | ||
142 | 14 | * You should have received a copy of the GNU General Public License | ||
143 | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
144 | 16 | */ | ||
145 | 17 | |||
146 | 18 | using Json; | ||
147 | 19 | |||
148 | 20 | /** | ||
149 | 21 | * RNRClient: | ||
150 | 22 | * | ||
151 | 23 | * Client for the ratings and reviews server. | ||
152 | 24 | * It is able to change the server address by setting the environment | ||
153 | 25 | * variable "U1_REVIEWS_BASE_URL". | ||
154 | 26 | * | ||
155 | 27 | */ | ||
156 | 28 | public class RNRClient : GLib.Object | ||
157 | 29 | { | ||
158 | 30 | UbuntuoneCredentials u1creds; | ||
159 | 31 | const string CONSUMER_KEY = "consumer_key"; | ||
160 | 32 | const string CONSUMER_SECRET = "consumer_secret"; | ||
161 | 33 | const string TOKEN = "token"; | ||
162 | 34 | const string TOKEN_SECRET = "token_secret"; | ||
163 | 35 | const string REVIEWS_BASE_URL = "https://reviews.ubuntu.com/reviews"; | ||
164 | 36 | |||
165 | 37 | internal Soup.SessionAsync http_session; | ||
166 | 38 | |||
167 | 39 | construct { | ||
168 | 40 | http_session = WebClient.get_webclient(); | ||
169 | 41 | u1creds = new UbuntuoneCredentials (); | ||
170 | 42 | } | ||
171 | 43 | |||
172 | 44 | public string from_environ (string env_name, string default_value) { | ||
173 | 45 | string env_value = Environment.get_variable (env_name); | ||
174 | 46 | return env_value != null ? env_value : default_value; | ||
175 | 47 | } | ||
176 | 48 | |||
177 | 49 | public string get_base_url () { | ||
178 | 50 | return from_environ ("U1_REVIEWS_BASE_URL", REVIEWS_BASE_URL); | ||
179 | 51 | } | ||
180 | 52 | |||
181 | 53 | public string get_review_url() { | ||
182 | 54 | /* /api/1.0/reviews/filter/{LANGUAGE}/{ORIGIN}/{DISTROSERIES}/{VERSION}/{PACKAGENAME}/ */ | ||
183 | 55 | return get_base_url() + "/api/1.0/reviews/filter/%s/%s/%s/%s/%s/"; | ||
184 | 56 | } | ||
185 | 57 | |||
186 | 58 | /** | ||
187 | 59 | * get_reviews: | ||
188 | 60 | * @details: the AppDetails describe the details. | ||
189 | 61 | * | ||
190 | 62 | * The function returns all the reviews based on the application | ||
191 | 63 | * details. It only returns the reviews that matches the system | ||
192 | 64 | * language. | ||
193 | 65 | * | ||
194 | 66 | * Returns: A Variant (list of dictionary) that containts the reviews. | ||
195 | 67 | * | ||
196 | 68 | */ | ||
197 | 69 | public async Variant? get_reviews(AppDetails details) { | ||
198 | 70 | string[] languages = GLib.Intl.get_language_names(); | ||
199 | 71 | string? language = null; | ||
200 | 72 | if (languages.length > 0) { | ||
201 | 73 | language = languages[0]; | ||
202 | 74 | } | ||
203 | 75 | var filter = new ReviewFilter(); | ||
204 | 76 | filter.language = language; | ||
205 | 77 | filter.packagename = details.package_name; | ||
206 | 78 | filter.origin = filter.packagename; | ||
207 | 79 | filter.version = details.version; | ||
208 | 80 | filter.distroseries = "click"; | ||
209 | 81 | return yield get_reviews_by_filter(filter); | ||
210 | 82 | } | ||
211 | 83 | |||
212 | 84 | /** | ||
213 | 85 | * get_reviews_by_filter: | ||
214 | 86 | * @filter: the filter for reviews. | ||
215 | 87 | * | ||
216 | 88 | * The function returns all the reviews based on the filter. | ||
217 | 89 | * | ||
218 | 90 | * Returns: A Variant (list of dictionary) that containts the reviews. | ||
219 | 91 | * | ||
220 | 92 | */ | ||
221 | 93 | public async Variant? get_reviews_by_filter(ReviewFilter filter) { | ||
222 | 94 | Variant? ret = null; | ||
223 | 95 | WebserviceError failure = null; | ||
224 | 96 | |||
225 | 97 | if (filter.language == "" || filter.language == "any" | ||
226 | 98 | || filter.origin == "" || filter.origin == "any" | ||
227 | 99 | || filter.distroseries == "" || filter.distroseries == "any" | ||
228 | 100 | || filter.version == "" || filter.version == "any" | ||
229 | 101 | || filter.packagename == "") { | ||
230 | 102 | debug("filter is not filled"); | ||
231 | 103 | return ret; | ||
232 | 104 | } | ||
233 | 105 | |||
234 | 106 | string url = get_review_url().printf(filter.language, | ||
235 | 107 | filter.origin, | ||
236 | 108 | filter.distroseries, | ||
237 | 109 | filter.version, | ||
238 | 110 | filter.packagename); | ||
239 | 111 | string response=""; | ||
240 | 112 | |||
241 | 113 | var message = new Soup.Message ("GET", url); | ||
242 | 114 | http_session.queue_message(message, (http_session, message) => { | ||
243 | 115 | if (message.status_code != Soup.KnownStatusCode.OK) { | ||
244 | 116 | var msg = "Web request failed: HTTP %u %s".printf( | ||
245 | 117 | message.status_code, message.reason_phrase); | ||
246 | 118 | failure = new WebserviceError.HTTP_ERROR(msg); | ||
247 | 119 | } else { | ||
248 | 120 | message.response_body.flatten (); | ||
249 | 121 | response = (string) message.response_body.data; | ||
250 | 122 | debug ("response is %s", response); | ||
251 | 123 | } | ||
252 | 124 | get_reviews_by_filter.callback(); | ||
253 | 125 | }); | ||
254 | 126 | yield; | ||
255 | 127 | if (failure != null) { | ||
256 | 128 | debug("cannot get reviews error %s", failure.message); | ||
257 | 129 | return ret; | ||
258 | 130 | } | ||
259 | 131 | var parser = new Json.Parser(); | ||
260 | 132 | try { | ||
261 | 133 | parser.load_from_data(response); | ||
262 | 134 | } catch (GLib.Error e) { | ||
263 | 135 | debug ("parse reviews response error %s", e.message); | ||
264 | 136 | return ret; | ||
265 | 137 | } | ||
266 | 138 | |||
267 | 139 | ret = convertJSONtoVariant(parser); | ||
268 | 140 | return ret; | ||
269 | 141 | } | ||
270 | 142 | |||
271 | 143 | public Variant? convertJSONtoVariant(Json.Parser parser) { | ||
272 | 144 | Variant? ret = null; | ||
273 | 145 | var root_object = parser.get_root(); | ||
274 | 146 | var builder = new VariantBuilder(new VariantType("av")); | ||
275 | 147 | |||
276 | 148 | foreach (var node in root_object.get_array().get_elements()) { | ||
277 | 149 | var ht = new VariantBuilder(new VariantType("a{sv}")); | ||
278 | 150 | var node_object = node.get_object(); | ||
279 | 151 | foreach (var node_member in node_object.get_members()) { | ||
280 | 152 | Variant? gvar_member = null; | ||
281 | 153 | try { | ||
282 | 154 | gvar_member = Json.gvariant_deserialize(node_object.get_member(node_member), null); | ||
283 | 155 | } catch (GLib.Error e) { | ||
284 | 156 | debug ("gvariant deserialize error: %s",e.message); | ||
285 | 157 | continue; | ||
286 | 158 | } | ||
287 | 159 | if (gvar_member == null) { | ||
288 | 160 | continue; | ||
289 | 161 | } | ||
290 | 162 | if (gvar_member.is_of_type(VariantType.MAYBE) && gvar_member.get_maybe() == null) { | ||
291 | 163 | continue; | ||
292 | 164 | } | ||
293 | 165 | ht.add("{sv}", node_member, gvar_member); | ||
294 | 166 | } | ||
295 | 167 | Variant dict = ht.end(); | ||
296 | 168 | builder.add("v", dict); | ||
297 | 169 | } | ||
298 | 170 | |||
299 | 171 | ret = builder.end(); | ||
300 | 172 | return ret; | ||
301 | 173 | } | ||
302 | 174 | } | ||
303 | 175 | |||
304 | 176 | /** | ||
305 | 177 | * ReviewFilter: | ||
306 | 178 | * @packagename: the name of package. Required. | ||
307 | 179 | * @language: the language string. Eg: en zh ... The default value "any" to | ||
308 | 180 | * get reviews for any languages | ||
309 | 181 | * @origin: Normally comes from "ubuntu". For click packages it should be | ||
310 | 182 | * as same as packagename. | ||
311 | 183 | * The default value "any" to get reviews for any origin. | ||
312 | 184 | * @distroseries: For example, "natty", "saucy". The default value "any" to | ||
313 | 185 | * get any distroseries. | ||
314 | 186 | * @version: The version of the software. Default value "any" means to get | ||
315 | 187 | * all versions from the server. | ||
316 | 188 | * | ||
317 | 189 | * ReviewFilter is the class used to pass the reviews filter to the server | ||
318 | 190 | * API to get the reviews. The server contains many reviews with different | ||
319 | 191 | * languages, different versions, etc. We should use the filter to get the | ||
320 | 192 | * reviews we want. | ||
321 | 193 | * | ||
322 | 194 | */ | ||
323 | 195 | public class ReviewFilter : GLib.Object { | ||
324 | 196 | public string packagename = ""; | ||
325 | 197 | public string language = "any"; | ||
326 | 198 | public string origin = "any"; | ||
327 | 199 | public string distroseries = "any"; | ||
328 | 200 | public string version = "any"; | ||
329 | 201 | } | ||
330 | 0 | 202 | ||
331 | === modified file 'src/test-click-webservice.vala' | |||
332 | --- src/test-click-webservice.vala 2013-12-24 00:33:07 +0000 | |||
333 | +++ src/test-click-webservice.vala 2014-01-09 13:55:57 +0000 | |||
334 | @@ -601,6 +601,50 @@ | |||
335 | 601 | assert (scope.build_installed_called == (!is_installable && !finds_progress)); | 601 | assert (scope.build_installed_called == (!is_installable && !finds_progress)); |
336 | 602 | } | 602 | } |
337 | 603 | 603 | ||
338 | 604 | public static void test_rnrclient_json_to_variant() { | ||
339 | 605 | RNRClient rnrClient = new RNRClient(); | ||
340 | 606 | var testData1 = FAKE_RNR_REVIEW_RESULTS; | ||
341 | 607 | Json.Parser parser = new Json.Parser(); | ||
342 | 608 | try { | ||
343 | 609 | parser.load_from_data(testData1); | ||
344 | 610 | } catch (GLib.Error e) { | ||
345 | 611 | } | ||
346 | 612 | Variant testVariant = rnrClient.convertJSONtoVariant(parser); | ||
347 | 613 | |||
348 | 614 | assert_cmpint ((int)testVariant.n_children(), OperatorType.EQUAL, 2); | ||
349 | 615 | } | ||
350 | 616 | |||
351 | 617 | public static void test_rnrclient_bad_filter() { | ||
352 | 618 | MainLoop mainloop = new MainLoop (); | ||
353 | 619 | RNRClient rnrClient = new RNRClient(); | ||
354 | 620 | |||
355 | 621 | ReviewFilter filter = new ReviewFilter(); | ||
356 | 622 | filter.version = "any"; | ||
357 | 623 | Variant? testData1 = null; | ||
358 | 624 | rnrClient.get_reviews_by_filter.begin(filter, (obj, res) => { | ||
359 | 625 | mainloop.quit(); | ||
360 | 626 | testData1 = rnrClient.get_reviews_by_filter.end (res); | ||
361 | 627 | }); | ||
362 | 628 | assert (run_with_timeout (mainloop, 10000)); | ||
363 | 629 | assert (testData1 == null); | ||
364 | 630 | } | ||
365 | 631 | |||
366 | 632 | public static void test_rnrclient_from_environ() { | ||
367 | 633 | RNRClient rnrClient = new RNRClient(); | ||
368 | 634 | |||
369 | 635 | string testEnv = "TEST_RNRCLIENT_FROM_ENVIRON_S"; | ||
370 | 636 | string defaultValue = "default"; | ||
371 | 637 | string nonDefaultValue = "nondefault"; | ||
372 | 638 | |||
373 | 639 | Environment.set_variable(testEnv, nonDefaultValue, true); | ||
374 | 640 | string test1 = rnrClient.from_environ(testEnv, defaultValue); | ||
375 | 641 | Environment.unset_variable(testEnv); | ||
376 | 642 | string test2 = rnrClient.from_environ(testEnv, defaultValue); | ||
377 | 643 | |||
378 | 644 | assert_cmpstr (test1, OperatorType.EQUAL, nonDefaultValue); | ||
379 | 645 | assert_cmpstr (test2, OperatorType.EQUAL, defaultValue); | ||
380 | 646 | } | ||
381 | 647 | |||
382 | 604 | public static int main (string[] args) | 648 | public static int main (string[] args) |
383 | 605 | { | 649 | { |
384 | 606 | Test.init (ref args); | 650 | Test.init (ref args); |
385 | @@ -631,6 +675,9 @@ | |||
386 | 631 | test_scope_build_default_preview_finds_progress_is_installable); | 675 | test_scope_build_default_preview_finds_progress_is_installable); |
387 | 632 | Test.add_data_func ("/Unit/ClickChecker/Test_Scope_Build_Default_Preview_Finds_Progress_Not_Installable", | 676 | Test.add_data_func ("/Unit/ClickChecker/Test_Scope_Build_Default_Preview_Finds_Progress_Not_Installable", |
388 | 633 | test_scope_build_default_preview_finds_progress_not_installable); | 677 | test_scope_build_default_preview_finds_progress_not_installable); |
389 | 678 | Test.add_data_func ("/Unit/ClickChecker/Test_RNRClient_JSON_to_Variant", test_rnrclient_json_to_variant); | ||
390 | 679 | Test.add_data_func ("/Unit/ClickChecker/Test_RNRClient_Bad_Filter", test_rnrclient_bad_filter); | ||
391 | 680 | Test.add_data_func ("/Unit/ClickChecker/Test_RNRClient_From_Environ", test_rnrclient_from_environ); | ||
392 | 634 | return Test.run (); | 681 | return Test.run (); |
393 | 635 | } | 682 | } |
394 | 636 | } | 683 | } |
PASSED: Continuous integration, rev:90 jenkins. qa.ubuntu. com/job/ unity-scope- click-ci/ 118/ jenkins. qa.ubuntu. com/job/ unity-scope- click-trusty- amd64-ci/ 16 jenkins. qa.ubuntu. com/job/ unity-scope- click-trusty- armhf-ci/ 16
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/unity- scope-click- ci/118/ rebuild
http://