Merge lp:~mandel/unity-lens-music/musicstore-purchase into lp:unity-lens-music
- musicstore-purchase
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~mandel/unity-lens-music/musicstore-purchase |
Merge into: | lp:unity-lens-music |
Diff against target: |
1138 lines (+461/-199) 10 files modified
configure.ac (+1/-0) debian/changelog (+8/-0) debian/control (+2/-1) src/Makefile.am (+2/-1) src/album.vala (+1/-0) src/musicstore-collection.vala (+13/-0) src/musicstore-daemon.vala (+9/-3) src/musicstore-scope.vala (+255/-13) src/ubuntuone-webservices.vala (+104/-65) tests/unit/test-ubuntuone-purchases.vala (+66/-116) |
To merge this branch: | bzr merge lp:~mandel/unity-lens-music/musicstore-purchase |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Unity Team | Pending | ||
Review via email: mp+156513@code.launchpad.net |
Commit message
Description of the change
Adds support for music purchase from the musicstore scope.
- 135. By Manuel de la Peña
-
Add u1 icon to action.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 136. By Manuel de la Peña
-
Removed cached password.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:136
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://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Unmerged revisions
- 136. By Manuel de la Peña
-
Removed cached password.
- 135. By Manuel de la Peña
-
Add u1 icon to action.
- 134. By Manuel de la Peña
-
Fixed debian changelog.
- 133. By Manuel de la Peña
-
Added payment code to the music store.
Preview Diff
1 | === modified file 'configure.ac' | |||
2 | --- configure.ac 2013-02-15 13:06:28 +0000 | |||
3 | +++ configure.ac 2013-04-17 10:13:28 +0000 | |||
4 | @@ -75,6 +75,7 @@ | |||
5 | 75 | sqlite3 >= 3.7.7 | 75 | sqlite3 >= 3.7.7 |
6 | 76 | gee-1.0 | 76 | gee-1.0 |
7 | 77 | json-glib-1.0 | 77 | json-glib-1.0 |
8 | 78 | libnotify | ||
9 | 78 | libsoup-2.4 | 79 | libsoup-2.4 |
10 | 79 | oauth | 80 | oauth |
11 | 80 | unity >= 6.90.0 | 81 | unity >= 6.90.0 |
12 | 81 | 82 | ||
13 | === modified file 'debian/changelog' | |||
14 | --- debian/changelog 2013-03-04 04:01:17 +0000 | |||
15 | +++ debian/changelog 2013-04-17 10:13:28 +0000 | |||
16 | @@ -1,3 +1,11 @@ | |||
17 | 1 | unity-lens-music (6.8.1daily13.03.04-0ubuntu3) UNRELEASED; urgency=low | ||
18 | 2 | |||
19 | 3 | [ Alejandro J. Cura ] | ||
20 | 4 | * debian/control: | ||
21 | 5 | - added libnotify-dev build dependency | ||
22 | 6 | |||
23 | 7 | -- Alejandro J. Cura <alecu@canonical.com> Tue, 19 Mar 2013 18:45:38 -0300 | ||
24 | 8 | |||
25 | 1 | unity-lens-music (6.8.1daily13.03.04-0ubuntu1) raring; urgency=low | 9 | unity-lens-music (6.8.1daily13.03.04-0ubuntu1) raring; urgency=low |
26 | 2 | 10 | ||
27 | 3 | [ Pawel Stolowski ] | 11 | [ Pawel Stolowski ] |
28 | 4 | 12 | ||
29 | === modified file 'debian/control' | |||
30 | --- debian/control 2012-12-12 16:12:30 +0000 | |||
31 | +++ debian/control 2013-04-17 10:13:28 +0000 | |||
32 | @@ -11,11 +11,12 @@ | |||
33 | 11 | libglib2.0-dev (>= 2.32), | 11 | libglib2.0-dev (>= 2.32), |
34 | 12 | libgee-dev, | 12 | libgee-dev, |
35 | 13 | libjson-glib-dev, | 13 | libjson-glib-dev, |
36 | 14 | libnotify-dev, | ||
37 | 14 | libsoup2.4-dev, | 15 | libsoup2.4-dev, |
38 | 15 | liboauth-dev, | 16 | liboauth-dev, |
39 | 16 | libdee-dev (>= 1.0.7), | 17 | libdee-dev (>= 1.0.7), |
40 | 17 | libsqlite3-dev (>= 3.7.7), | 18 | libsqlite3-dev (>= 3.7.7), |
42 | 18 | libunity-dev (>= 6.90.0), | 19 | libunity-dev (>= 6.9), |
43 | 19 | libtdb-dev (>= 1.2.6), | 20 | libtdb-dev (>= 1.2.6), |
44 | 20 | libgstreamer1.0-dev, | 21 | libgstreamer1.0-dev, |
45 | 21 | libgstreamer-plugins-base1.0-dev | 22 | libgstreamer-plugins-base1.0-dev |
46 | 22 | 23 | ||
47 | === modified file 'src/Makefile.am' | |||
48 | --- src/Makefile.am 2013-02-15 13:06:28 +0000 | |||
49 | +++ src/Makefile.am 2013-04-17 10:13:28 +0000 | |||
50 | @@ -72,8 +72,9 @@ | |||
51 | 72 | 72 | ||
52 | 73 | unity_musicstore_daemon_VALAFLAGS = \ | 73 | unity_musicstore_daemon_VALAFLAGS = \ |
53 | 74 | --pkg json-glib-1.0 \ | 74 | --pkg json-glib-1.0 \ |
54 | 75 | --pkg libnotify \ | ||
55 | 76 | --pkg libsoup-2.4 \ | ||
56 | 75 | --pkg oauth \ | 77 | --pkg oauth \ |
57 | 76 | --pkg libsoup-2.4 \ | ||
58 | 77 | $(unity_music_daemon_VALAFLAGS) | 78 | $(unity_music_daemon_VALAFLAGS) |
59 | 78 | 79 | ||
60 | 79 | unity_musicstore_daemon_LDADD = \ | 80 | unity_musicstore_daemon_LDADD = \ |
61 | 80 | 81 | ||
62 | === modified file 'src/album.vala' | |||
63 | --- src/album.vala 2012-09-11 12:50:34 +0000 | |||
64 | +++ src/album.vala 2013-04-17 10:13:28 +0000 | |||
65 | @@ -28,5 +28,6 @@ | |||
66 | 28 | public string uri { get; set; } | 28 | public string uri { get; set; } |
67 | 29 | public string artwork_path { get; set; } | 29 | public string artwork_path { get; set; } |
68 | 30 | public string formatted_price { get; set; } | 30 | public string formatted_price { get; set; } |
69 | 31 | public string purchase_sku { get; set; } | ||
70 | 31 | } | 32 | } |
71 | 32 | } | 33 | } |
72 | 33 | \ No newline at end of file | 34 | \ No newline at end of file |
73 | 34 | 35 | ||
74 | === modified file 'src/musicstore-collection.vala' | |||
75 | --- src/musicstore-collection.vala 2012-12-06 19:29:07 +0000 | |||
76 | +++ src/musicstore-collection.vala 2013-04-17 10:13:28 +0000 | |||
77 | @@ -104,6 +104,18 @@ | |||
78 | 104 | track.duration = (int)root_obj.get_member ("duration").get_int (); | 104 | track.duration = (int)root_obj.get_member ("duration").get_int (); |
79 | 105 | tracks.append (track); | 105 | tracks.append (track); |
80 | 106 | } | 106 | } |
81 | 107 | |||
82 | 108 | if (root_obj.has_member ("id")) | ||
83 | 109 | { | ||
84 | 110 | album.purchase_sku = root_obj.get_string_member ("id"); | ||
85 | 111 | } | ||
86 | 112 | else | ||
87 | 113 | { | ||
88 | 114 | // U1MS does not allow purchases of an album with no purchase_sku | ||
89 | 115 | album.purchase_sku = ""; | ||
90 | 116 | warning ("Json has no purchase_sku in '%s'", details_uri); | ||
91 | 117 | } | ||
92 | 118 | |||
93 | 107 | } | 119 | } |
94 | 108 | else | 120 | else |
95 | 109 | { | 121 | { |
96 | @@ -125,6 +137,7 @@ | |||
97 | 125 | { | 137 | { |
98 | 126 | var timer = new Timer (); | 138 | var timer = new Timer (); |
99 | 127 | debug ("Searching %s", file.get_uri ()); | 139 | debug ("Searching %s", file.get_uri ()); |
100 | 140 | var empty_asv = new Variant.array (VariantType.VARDICT.element (), {}); | ||
101 | 128 | 141 | ||
102 | 129 | try { | 142 | try { |
103 | 130 | var stream = yield file.read_async (Priority.DEFAULT, cancellable); | 143 | var stream = yield file.read_async (Priority.DEFAULT, cancellable); |
104 | 131 | 144 | ||
105 | === modified file 'src/musicstore-daemon.vala' | |||
106 | --- src/musicstore-daemon.vala 2012-09-04 09:53:02 +0000 | |||
107 | +++ src/musicstore-daemon.vala 2013-04-17 10:13:28 +0000 | |||
108 | @@ -25,6 +25,8 @@ | |||
109 | 25 | static Application? app = null; | 25 | static Application? app = null; |
110 | 26 | static MusicStoreScopeProxy? daemon = null; | 26 | static MusicStoreScopeProxy? daemon = null; |
111 | 27 | 27 | ||
112 | 28 | static const string BUS_NAME = "com.canonical.Unity.Scope.MusicStore"; | ||
113 | 29 | |||
114 | 28 | /* Check if a given well known DBus is owned. | 30 | /* Check if a given well known DBus is owned. |
115 | 29 | * WARNING: This does sync IO! */ | 31 | * WARNING: This does sync IO! */ |
116 | 30 | public static bool dbus_name_has_owner (string name) | 32 | public static bool dbus_name_has_owner (string name) |
117 | @@ -65,7 +67,7 @@ | |||
118 | 65 | * making it race against GDBus' worker thread to export our | 67 | * making it race against GDBus' worker thread to export our |
119 | 66 | * objects on the bus before/after owning our name and receiving | 68 | * objects on the bus before/after owning our name and receiving |
120 | 67 | * method calls on our objects (which may not yet be up!)*/ | 69 | * method calls on our objects (which may not yet be up!)*/ |
122 | 68 | if (dbus_name_has_owner ("com.canonical.Unity.Scope.MusicStore")) | 70 | if (dbus_name_has_owner (BUS_NAME)) |
123 | 69 | { | 71 | { |
124 | 70 | print ("Another instance of the UbuntuOne Music Daemon " + | 72 | print ("Another instance of the UbuntuOne Music Daemon " + |
125 | 71 | "already appears to be running.\nBailing out.\n"); | 73 | "already appears to be running.\nBailing out.\n"); |
126 | @@ -75,10 +77,14 @@ | |||
127 | 75 | /* Now register our DBus objects *before* acquiring the name! | 77 | /* Now register our DBus objects *before* acquiring the name! |
128 | 76 | * See above for reasons */ | 78 | * See above for reasons */ |
129 | 77 | daemon = new MusicStoreScopeProxy (); | 79 | daemon = new MusicStoreScopeProxy (); |
130 | 80 | try { | ||
131 | 81 | daemon.scope.export (); | ||
132 | 82 | } catch (GLib.IOError e) { | ||
133 | 83 | stdout.printf ("error %s\n", e.message); | ||
134 | 84 | } | ||
135 | 78 | 85 | ||
136 | 79 | /* Use GApplication directly for single instance app functionality */ | 86 | /* Use GApplication directly for single instance app functionality */ |
139 | 80 | app = new Application ("com.canonical.Unity.Scope.MusicStore", | 87 | app = new Application (BUS_NAME, ApplicationFlags.IS_SERVICE); |
138 | 81 | ApplicationFlags.IS_SERVICE); | ||
140 | 82 | try { | 88 | try { |
141 | 83 | app.register (); | 89 | app.register (); |
142 | 84 | } catch (Error e) { | 90 | } catch (Error e) { |
143 | 85 | 91 | ||
144 | === modified file 'src/musicstore-scope.vala' | |||
145 | --- src/musicstore-scope.vala 2013-02-28 09:48:46 +0000 | |||
146 | +++ src/musicstore-scope.vala 2013-04-17 10:13:28 +0000 | |||
147 | @@ -18,15 +18,30 @@ | |||
148 | 18 | */ | 18 | */ |
149 | 19 | 19 | ||
150 | 20 | using GLib; | 20 | using GLib; |
151 | 21 | using Notify; | ||
152 | 22 | using Unity; | ||
153 | 23 | using Gdk; | ||
154 | 24 | using Ubuntuone.Webservice; | ||
155 | 21 | 25 | ||
156 | 22 | namespace Unity.MusicLens { | 26 | namespace Unity.MusicLens { |
157 | 27 | private const string FORGOTTEN_PASSWORD_URL = "https://login.ubuntu.com/+forgot_password"; | ||
158 | 28 | private const string CHANGE_PAYMENT_METHOD_URL = "https://pay.ubuntu.com/account/"; | ||
159 | 29 | private const string ERROR_MESSAGE_NOT_LOGGED_IN = _("It seems you don't have an Ubuntu One account, or you are not logged in. To continue, please login and visit the Ubuntu One online store."); | ||
160 | 30 | private const string ERROR_MESSAGE_NO_PAYMENT_METHOD = _("It seems you haven't set yet your preferred Ubuntu One payment method. To add a payment method, please visit the Ubuntu One online store."); | ||
161 | 31 | private const string ERROR_MESSAGE_TECHNICAL_PROBLEM = _("Sorry, we have encountered a technical problem. No money has been taken from your account. To try your purchase again, please visit the Ubuntu One online store."); | ||
162 | 32 | private const string U1_SSO_UI = "u1_login_register"; | ||
163 | 23 | 33 | ||
164 | 24 | public class MusicStoreScopeProxy : SimpleScope | 34 | public class MusicStoreScopeProxy : SimpleScope |
165 | 25 | { | 35 | { |
166 | 36 | const string NO_CREDENTIALS_LABEL_TEXT = _("Before you can purchase music you need to log in to the Ubuntu One app."); | ||
167 | 26 | private MusicStoreCollection collection; | 37 | private MusicStoreCollection collection; |
168 | 27 | private Unity.Extras.PreviewPlayerController preview_player; | 38 | private Unity.Extras.PreviewPlayerController preview_player; |
169 | 28 | private Unity.MusicPreview? music_preview; | 39 | private Unity.MusicPreview? music_preview; |
170 | 29 | private PreferencesManager preferences = PreferencesManager.get_default (); | 40 | private PreferencesManager preferences = PreferencesManager.get_default (); |
171 | 41 | private HashTable<string, Album> album_map; | ||
172 | 42 | private PurchaseService purchase_service; | ||
173 | 43 | private Notification notification; | ||
174 | 44 | private bool have_credentials = false; | ||
175 | 30 | 45 | ||
176 | 31 | public MusicStoreScopeProxy () | 46 | public MusicStoreScopeProxy () |
177 | 32 | { | 47 | { |
178 | @@ -40,7 +55,11 @@ | |||
179 | 40 | 55 | ||
180 | 41 | base.initialize (); | 56 | base.initialize (); |
181 | 42 | 57 | ||
182 | 58 | Notify.init ("Music Store Scope"); | ||
183 | 59 | notification = new Notification("Album name", _("Purchase started"), ""); | ||
184 | 43 | collection = new MusicStoreCollection (); | 60 | collection = new MusicStoreCollection (); |
185 | 61 | album_map = new HashTable<string, Album>(str_hash, str_equal); | ||
186 | 62 | purchase_service = new PurchaseService (); | ||
187 | 44 | 63 | ||
188 | 45 | preferences.notify["remote-content-search"].connect((obj, pspec) => { scope.queue_search_changed(SearchType.DEFAULT); }); | 64 | preferences.notify["remote-content-search"].connect((obj, pspec) => { scope.queue_search_changed(SearchType.DEFAULT); }); |
189 | 46 | 65 | ||
190 | @@ -65,11 +84,11 @@ | |||
191 | 65 | else | 84 | else |
192 | 66 | { | 85 | { |
193 | 67 | warning ("Failed to generate preview for %s", uri); | 86 | warning ("Failed to generate preview for %s", uri); |
195 | 68 | return download_album (uri); | 87 | return open_uri (uri); |
196 | 69 | } | 88 | } |
197 | 70 | } | 89 | } |
198 | 71 | 90 | ||
200 | 72 | public Unity.ActivationResponse download_album (string uri) | 91 | public Unity.ActivationResponse open_uri (string uri) |
201 | 73 | { | 92 | { |
202 | 74 | /* launch the music store streaming client or the webstore or whatevz */ | 93 | /* launch the music store streaming client or the webstore or whatevz */ |
203 | 75 | try { | 94 | try { |
204 | @@ -89,6 +108,7 @@ | |||
205 | 89 | 108 | ||
206 | 90 | if (album != null) | 109 | if (album != null) |
207 | 91 | { | 110 | { |
208 | 111 | album_map.insert (uri, album); | ||
209 | 92 | File cover_file = File.new_for_uri (album.artwork_path); //artwork path is a remote uri | 112 | File cover_file = File.new_for_uri (album.artwork_path); //artwork path is a remote uri |
210 | 93 | var cover = new FileIcon (cover_file); | 113 | var cover = new FileIcon (cover_file); |
211 | 94 | 114 | ||
212 | @@ -111,12 +131,19 @@ | |||
213 | 111 | } | 131 | } |
214 | 112 | } | 132 | } |
215 | 113 | 133 | ||
222 | 114 | GLib.Icon? icon = new GLib.FileIcon (File.new_for_path (Config.DATADIR + "/icons/unity-icon-theme/places/svg/service-u1.svg")); | 134 | if (have_credentials) { |
223 | 115 | var download_action = new Unity.PreviewAction ("download_album", _("Download"), icon); | 135 | GLib.Icon? icon = new GLib.FileIcon (File.new_for_path (Config.DATADIR + "/icons/unity-icon-theme/places/svg/service-u1.svg")); |
224 | 116 | if (album.formatted_price != null) | 136 | var download_action = new Unity.PreviewAction ("show_purchase_preview", _("Download"), icon); |
225 | 117 | download_action.extra_text = album.formatted_price; | 137 | if (album.formatted_price != null) |
226 | 118 | download_action.activated.connect (download_album); | 138 | download_action.extra_text = album.formatted_price; |
227 | 119 | music_preview.add_action (download_action); | 139 | download_action.activated.connect (show_purchase_preview); |
228 | 140 | music_preview.add_action (download_action); | ||
229 | 141 | } else { | ||
230 | 142 | var data = new HashTable<string, Variant>(str_hash, str_equal); | ||
231 | 143 | data["no_credentials_label"] = NO_CREDENTIALS_LABEL_TEXT; | ||
232 | 144 | InfoHint info_hint = new InfoHint.with_variant("music_preview", "", null, data); | ||
233 | 145 | music_preview.add_info(info_hint); | ||
234 | 146 | } | ||
235 | 120 | } | 147 | } |
236 | 121 | return music_preview; | 148 | return music_preview; |
237 | 122 | } | 149 | } |
238 | @@ -142,12 +169,227 @@ | |||
239 | 142 | } | 169 | } |
240 | 143 | 170 | ||
241 | 144 | try { | 171 | try { |
245 | 145 | debug ("model has %u rows before search", search.results_model.get_n_rows ()); | 172 | yield purchase_service.fetch_credentials (); |
246 | 146 | yield collection.search (search, search_type, (owned) filters, max_results, cancellable); | 173 | have_credentials = true; |
247 | 147 | debug ("model has %u rows after search", search.results_model.get_n_rows ()); | 174 | } catch (PurchaseError e) { |
248 | 175 | // this is not a serious error, just missing credentials | ||
249 | 176 | have_credentials = false; | ||
250 | 177 | } | ||
251 | 178 | |||
252 | 179 | try { | ||
253 | 180 | debug ("model has %u rows before search", search.results_model.get_n_rows ()); | ||
254 | 181 | yield collection.search (search, search_type, (owned) filters, max_results, cancellable); | ||
255 | 182 | debug ("model has %u rows after search", search.results_model.get_n_rows ()); | ||
256 | 148 | } catch (IOError e) { | 183 | } catch (IOError e) { |
259 | 149 | warning ("Failed to search for '%s': %s", search.search_string, e.message); | 184 | warning ("Failed to search for '%s': %s", search.search_string, e.message); |
260 | 150 | } | 185 | } |
261 | 186 | |||
262 | 187 | try { | ||
263 | 188 | yield purchase_service.fetch_account_info (); | ||
264 | 189 | debug ("retrieved account info: %s %s", purchase_service.nickname, purchase_service.email); | ||
265 | 190 | } catch (PurchaseError e) { | ||
266 | 191 | debug ("can't get account info: %s", e.message); | ||
267 | 192 | } | ||
268 | 193 | } | ||
269 | 194 | |||
270 | 195 | delegate Unity.ActivationResponse LinkHandler (string uri); | ||
271 | 196 | |||
272 | 197 | private Unity.ActivationResponse build_error_preview (string uri, string error_header, string link_text, LinkHandler link_handler) | ||
273 | 198 | { | ||
274 | 199 | Album album = null; | ||
275 | 200 | SList<Track> tracks = null; | ||
276 | 201 | collection.get_album_details (uri, out album, out tracks); | ||
277 | 202 | debug ("album art uri: %s", album.artwork_path); | ||
278 | 203 | File cover_file = File.new_for_uri (album.artwork_path); //artwork path is a remote uri | ||
279 | 204 | var cover = new FileIcon (cover_file); | ||
280 | 205 | var error_preview = new Unity.PaymentPreview.for_error(album.title, album.artist, cover); | ||
281 | 206 | error_preview.header = error_header; | ||
282 | 207 | |||
283 | 208 | var cancel_action = new Unity.PreviewAction ("cancel", _("Cancel"), null); | ||
284 | 209 | cancel_action.activated.connect (() => cancel_purchase (uri)); | ||
285 | 210 | error_preview.add_action (cancel_action); | ||
286 | 211 | |||
287 | 212 | GLib.Icon? icon = new GLib.FileIcon (File.new_for_path (Config.DATADIR + "/icons/unity-icon-theme/places/svg/service-u1.svg")); | ||
288 | 213 | var error_action = new Unity.PreviewAction ("go_to_u1", link_text, icon); | ||
289 | 214 | error_action.activated.connect (() => link_handler (uri)); | ||
290 | 215 | error_preview.add_action (error_action); | ||
291 | 216 | return new Unity.ActivationResponse.with_preview (error_preview); | ||
292 | 217 | } | ||
293 | 218 | |||
294 | 219 | public Unity.ActivationResponse open_forgot_password_url (string uri) | ||
295 | 220 | { | ||
296 | 221 | return open_uri (FORGOTTEN_PASSWORD_URL); | ||
297 | 222 | } | ||
298 | 223 | |||
299 | 224 | public Unity.ActivationResponse change_payment_method (string uri) | ||
300 | 225 | { | ||
301 | 226 | if (purchase_service.open_url != null) | ||
302 | 227 | { | ||
303 | 228 | debug ("Open url is %s", purchase_service.open_url); | ||
304 | 229 | return open_uri (purchase_service.open_url); | ||
305 | 230 | } | ||
306 | 231 | return open_uri (CHANGE_PAYMENT_METHOD_URL); | ||
307 | 232 | } | ||
308 | 233 | |||
309 | 234 | public Unity.ActivationResponse cancel_purchase (string uri) | ||
310 | 235 | { | ||
311 | 236 | return new Unity.ActivationResponse.with_preview (preview (uri)); | ||
312 | 237 | } | ||
313 | 238 | |||
314 | 239 | public Unity.ActivationResponse open_sso_login (string uri) | ||
315 | 240 | { | ||
316 | 241 | Album album = null; | ||
317 | 242 | SList<Track> tracks = null; | ||
318 | 243 | collection.get_album_details (uri, out album, out tracks); | ||
319 | 244 | |||
320 | 245 | var ui_path = Environment.find_program_in_path (U1_SSO_UI); | ||
321 | 246 | GLib.Pid child_pid; | ||
322 | 247 | string argv[11] = { | ||
323 | 248 | ui_path, | ||
324 | 249 | "--album", album.title, | ||
325 | 250 | "--artist", album.artist, | ||
326 | 251 | "--price", album.formatted_price, | ||
327 | 252 | "--picture", album.artwork_path, | ||
328 | 253 | "--url", uri | ||
329 | 254 | }; | ||
330 | 255 | if (ui_path != null) { | ||
331 | 256 | try { | ||
332 | 257 | bool was_started = Process.spawn_async (null, argv, null, 0, null, out child_pid); | ||
333 | 258 | // hide dash | ||
334 | 259 | if (was_started) { | ||
335 | 260 | return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH); | ||
336 | 261 | } | ||
337 | 262 | } catch (GLib.SpawnError e) { | ||
338 | 263 | debug ("Failed to start u1_sso_ui for uri %s", uri); | ||
339 | 264 | } | ||
340 | 265 | } | ||
341 | 266 | return open_uri (uri); | ||
342 | 267 | } | ||
343 | 268 | |||
344 | 269 | public Unity.ActivationResponse show_purchase_preview (string uri) | ||
345 | 270 | { | ||
346 | 271 | if (purchase_service.got_credentials () == false) | ||
347 | 272 | { | ||
348 | 273 | debug ("no credentials available, opening sso login. %s", uri); | ||
349 | 274 | return build_error_preview (uri, ERROR_MESSAGE_NOT_LOGGED_IN, _("Continue"), open_sso_login); | ||
350 | 275 | } | ||
351 | 276 | |||
352 | 277 | Album album = null; | ||
353 | 278 | SList<Track> tracks = null; | ||
354 | 279 | collection.get_album_details (uri, out album, out tracks); | ||
355 | 280 | if (album != null) | ||
356 | 281 | { | ||
357 | 282 | try { | ||
358 | 283 | purchase_service.fetch_payment_info (album.purchase_sku); | ||
359 | 284 | debug ("retrieved payment method: %s", purchase_service.selected_payment_method); | ||
360 | 285 | return new Unity.ActivationResponse.with_preview (purchase_preview (uri, null)); | ||
361 | 286 | } catch (PurchaseError e) { | ||
362 | 287 | debug ("can't get default payment method: %s", e.message); | ||
363 | 288 | return build_error_preview (uri, ERROR_MESSAGE_NO_PAYMENT_METHOD, _("Choose Payment Method"), change_payment_method); | ||
364 | 289 | } | ||
365 | 290 | } | ||
366 | 291 | return open_uri (uri); | ||
367 | 292 | } | ||
368 | 293 | |||
369 | 294 | public Unity.ActivationResponse purchase_album (Unity.PreviewAction action, string uri) | ||
370 | 295 | { | ||
371 | 296 | var password = action.hints["password"].get_string(); | ||
372 | 297 | |||
373 | 298 | if (password == null) { | ||
374 | 299 | var preview = purchase_preview (uri, _("Please enter your password")); | ||
375 | 300 | debug ("empty password."); | ||
376 | 301 | return new Unity.ActivationResponse.with_preview (preview); | ||
377 | 302 | } | ||
378 | 303 | |||
379 | 304 | var album = album_map.get (uri); | ||
380 | 305 | |||
381 | 306 | File cover_file = File.new_for_uri (album.artwork_path); //artwork path is a remote uri | ||
382 | 307 | try { | ||
383 | 308 | var cover_pixbuf = new Pixbuf.from_stream (cover_file.read ()); | ||
384 | 309 | notification.set_icon_from_pixbuf (cover_pixbuf); | ||
385 | 310 | } catch (GLib.Error e) { | ||
386 | 311 | debug ("Cannot set notification icon from uri %s", uri); | ||
387 | 312 | } | ||
388 | 313 | |||
389 | 314 | notification.summary = album.title; | ||
390 | 315 | notification.body = _("Authorizing purchase"); | ||
391 | 316 | try { | ||
392 | 317 | notification.show (); | ||
393 | 318 | } catch (GLib.Error e) { | ||
394 | 319 | debug ("Error while showing notification: %s", e.message); | ||
395 | 320 | } | ||
396 | 321 | |||
397 | 322 | try { | ||
398 | 323 | purchase_service.purchase (album.purchase_sku, password); | ||
399 | 324 | debug ("purchase completed."); | ||
400 | 325 | notification.update (album.title, _("Purchase completed"), ""); | ||
401 | 326 | try { | ||
402 | 327 | notification.show (); | ||
403 | 328 | } catch (GLib.Error e) { | ||
404 | 329 | debug ("Error while showing notification: %s", e.message); | ||
405 | 330 | } | ||
406 | 331 | return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH); | ||
407 | 332 | } catch (PurchaseError e) { | ||
408 | 333 | if (e is PurchaseError.WRONG_PASSWORD_ERROR) { | ||
409 | 334 | debug ("wrong password error: %s", e.message); | ||
410 | 335 | return new Unity.ActivationResponse.with_preview (purchase_preview (uri, _("Wrong password"))); | ||
411 | 336 | } else { | ||
412 | 337 | debug ("got purchase error: %s", e.message); | ||
413 | 338 | return build_error_preview (uri, ERROR_MESSAGE_TECHNICAL_PROBLEM, _("Continue"), open_uri); | ||
414 | 339 | } | ||
415 | 340 | } | ||
416 | 341 | } | ||
417 | 342 | |||
418 | 343 | public Unity.Preview purchase_preview (string uri, string? error_message) | ||
419 | 344 | { | ||
420 | 345 | Unity.PaymentPreview album_purchase_preview = null; | ||
421 | 346 | Album album = null; | ||
422 | 347 | SList<Track> tracks = null; | ||
423 | 348 | collection.get_album_details (uri, out album, out tracks); | ||
424 | 349 | |||
425 | 350 | if (album != null) | ||
426 | 351 | { | ||
427 | 352 | File cover_file = File.new_for_uri (album.artwork_path); //artwork path is a remote uri | ||
428 | 353 | |||
429 | 354 | var cover = new FileIcon (cover_file); | ||
430 | 355 | album_purchase_preview = new Unity.PaymentPreview.for_music(album.title, album.artist, cover); | ||
431 | 356 | |||
432 | 357 | album_purchase_preview.header = _("Hi %s, you purchased in the past from Ubuntu One," | ||
433 | 358 | + " would you like to use the same payment details? Please review your order.").printf (purchase_service.nickname); | ||
434 | 359 | album_purchase_preview.email = purchase_service.email; | ||
435 | 360 | album_purchase_preview.payment_method = purchase_service.selected_payment_method; | ||
436 | 361 | album_purchase_preview.purchase_prize = album.formatted_price; | ||
437 | 362 | album_purchase_preview.purchase_type = _("Digital CD"); | ||
438 | 363 | |||
439 | 364 | // data | ||
440 | 365 | |||
441 | 366 | var data = new HashTable<string, Variant>(str_hash, str_equal); | ||
442 | 367 | if (error_message != null) { | ||
443 | 368 | data["error_message"] = error_message; | ||
444 | 369 | } | ||
445 | 370 | |||
446 | 371 | InfoHint info_hint = new InfoHint.with_variant("album_purchase_preview", "", null, data); | ||
447 | 372 | album_purchase_preview.add_info(info_hint); | ||
448 | 373 | |||
449 | 374 | // actions | ||
450 | 375 | |||
451 | 376 | var purchase_action = new Unity.PreviewAction ("purchase_album", _("Purchase"), null); | ||
452 | 377 | purchase_action.activated.connect (purchase_album); | ||
453 | 378 | album_purchase_preview.add_action (purchase_action); | ||
454 | 379 | |||
455 | 380 | var forgot_password_action = new Unity.PreviewAction ("forgot_password", _("forgotten your Ubuntu One password?"), null); | ||
456 | 381 | forgot_password_action.activated.connect (open_forgot_password_url); | ||
457 | 382 | album_purchase_preview.add_action (forgot_password_action); | ||
458 | 383 | |||
459 | 384 | var cancel_action = new Unity.PreviewAction ("cancel_purchase", _("Cancel"), null); | ||
460 | 385 | cancel_action.activated.connect (cancel_purchase); | ||
461 | 386 | album_purchase_preview.add_action (cancel_action); | ||
462 | 387 | |||
463 | 388 | var change_payment_method_action = new Unity.PreviewAction ("change_payment_method", _("change payment method"), null); | ||
464 | 389 | change_payment_method_action.activated.connect (change_payment_method); | ||
465 | 390 | album_purchase_preview.add_action (change_payment_method_action); | ||
466 | 391 | } | ||
467 | 392 | return album_purchase_preview; | ||
468 | 151 | } | 393 | } |
469 | 152 | 394 | ||
470 | 153 | } | 395 | } |
471 | 154 | 396 | ||
472 | === modified file 'src/ubuntuone-webservices.vala' | |||
473 | --- src/ubuntuone-webservices.vala 2012-12-06 15:39:38 +0000 | |||
474 | +++ src/ubuntuone-webservices.vala 2013-04-17 10:13:28 +0000 | |||
475 | @@ -21,26 +21,29 @@ | |||
476 | 21 | 21 | ||
477 | 22 | [DBus (name = "com.ubuntuone.CredentialsManagement")] | 22 | [DBus (name = "com.ubuntuone.CredentialsManagement")] |
478 | 23 | interface CredentialsManagement : GLib.Object { | 23 | interface CredentialsManagement : GLib.Object { |
479 | 24 | public signal void authorization_denied (); | ||
480 | 24 | public signal void credentials_found (HashTable <string, string> info); | 25 | public signal void credentials_found (HashTable <string, string> info); |
481 | 26 | public signal void credentials_not_found (); | ||
482 | 25 | public signal void credentials_error (); | 27 | public signal void credentials_error (); |
483 | 26 | 28 | ||
484 | 27 | [DBus (name = "find_credentials")] | 29 | [DBus (name = "find_credentials")] |
485 | 28 | public abstract void find_credentials () throws IOError; | 30 | public abstract void find_credentials () throws IOError; |
486 | 29 | } | 31 | } |
487 | 30 | 32 | ||
493 | 31 | const string WEBAPI_URI = "https://edge.one.ubuntu.com/"; | 33 | const string WEBAPI_SERVER = "https://one.ubuntu.com/"; |
494 | 32 | const string ACCOUNT_URI = WEBAPI_URI + "api/account"; | 34 | const string ACCOUNT_PATH = "api/account"; |
495 | 33 | const string PAYMENT_METHOD_URI = WEBAPI_URI + "music-store/api/1/user/retrieve-payment-method?purchase_sku=%s"; | 35 | const string PAYMENT_METHOD_PATH = "music-store-up/api/1/user/retrieve-payment-method?purchase_sku=%s"; |
496 | 34 | const string PURCHASE_WITH_DEFAULT_PAYMENT_URI = WEBAPI_URI + "music-store/api/1/user/purchase-with-default-payment?purchase_sku=%s&authentication=%s"; | 36 | const string PURCHASE_WITH_DEFAULT_PAYMENT_PATH = "music-store-up/api/1/user/purchase-with-default-payment?purchase_sku=%s&authentication=%s"; |
497 | 35 | const string AUTHENTICATION_URI = "https://login.staging.ubuntu.com/api/1.1/authentications"; | 37 | const string AUTHENTICATION_SERVER = "https://login.ubuntu.com/"; |
498 | 38 | const string AUTHENTICATION_PATH = "api/1.1/authentications"; | ||
499 | 36 | const string AUTHENTICATE_PARAMS = "ws.op=authenticate&token_name=Purchase_Token"; | 39 | const string AUTHENTICATE_PARAMS = "ws.op=authenticate&token_name=Purchase_Token"; |
502 | 37 | const int PASSWORD_CACHE_SECONDS = 300; | 40 | |
501 | 38 | const int PAYMENT_METHOD_CACHE_SECONDS = 120; | ||
503 | 39 | 41 | ||
504 | 40 | namespace Ubuntuone.Webservice | 42 | namespace Ubuntuone.Webservice |
505 | 41 | { | 43 | { |
506 | 42 | public errordomain PurchaseError | 44 | public errordomain PurchaseError |
507 | 43 | { | 45 | { |
508 | 46 | MISSING_CREDENTIALS_ERROR, | ||
509 | 44 | PURCHASE_ERROR, | 47 | PURCHASE_ERROR, |
510 | 45 | WRONG_PASSWORD_ERROR, | 48 | WRONG_PASSWORD_ERROR, |
511 | 46 | UNSPECIFIED_ERROR | 49 | UNSPECIFIED_ERROR |
512 | @@ -56,17 +59,13 @@ | |||
513 | 56 | public string selected_payment_method { get; internal set; default = null; } | 59 | public string selected_payment_method { get; internal set; default = null; } |
514 | 57 | public string consumer_key { get; private set; default = null; } | 60 | public string consumer_key { get; private set; default = null; } |
515 | 58 | public string token { get; private set; default = null; } | 61 | public string token { get; private set; default = null; } |
518 | 59 | internal DateTime _selected_payment_method_expiry; | 62 | public string open_url { get; private set; default = null; } |
517 | 60 | DateTime _password_expiry; | ||
519 | 61 | internal HashTable <string, string> _ubuntuone_credentials = null; | 63 | internal HashTable <string, string> _ubuntuone_credentials = null; |
520 | 62 | 64 | ||
521 | 63 | construct { | 65 | construct { |
522 | 64 | http_session = build_http_session (); | 66 | http_session = build_http_session (); |
523 | 65 | http_session_sso = build_http_session (); | 67 | http_session_sso = build_http_session (); |
524 | 66 | 68 | ||
525 | 67 | reset_payment_method_cache (0); | ||
526 | 68 | reset_password_cache (0); | ||
527 | 69 | |||
528 | 70 | credentials_management = build_credentials_management (); | 69 | credentials_management = build_credentials_management (); |
529 | 71 | } | 70 | } |
530 | 72 | 71 | ||
531 | @@ -77,24 +76,40 @@ | |||
532 | 77 | return session; | 76 | return session; |
533 | 78 | } | 77 | } |
534 | 79 | 78 | ||
553 | 80 | internal DateTime now () | 79 | string webapi_server () |
554 | 81 | { | 80 | { |
555 | 82 | return new DateTime.now_utc (); | 81 | string staging_webapi = Environment.get_variable ("U1_STAGING_WEBAPI"); |
556 | 83 | } | 82 | return staging_webapi != null ? staging_webapi : WEBAPI_SERVER; |
557 | 84 | 83 | } | |
558 | 85 | internal bool expired_payment_method_cache () | 84 | |
559 | 86 | { | 85 | string account_uri () |
560 | 87 | return _selected_payment_method_expiry.compare (now ()) <= 0; | 86 | { |
561 | 88 | } | 87 | return webapi_server() + ACCOUNT_PATH; |
562 | 89 | 88 | } | |
563 | 90 | internal void reset_payment_method_cache (int seconds_from_now) | 89 | |
564 | 91 | { | 90 | string payment_method_uri () |
565 | 92 | _selected_payment_method_expiry = now ().add_seconds (seconds_from_now); | 91 | { |
566 | 93 | } | 92 | return webapi_server() + PAYMENT_METHOD_PATH; |
567 | 94 | 93 | } | |
568 | 95 | internal void reset_password_cache (int seconds_from_now) | 94 | |
569 | 96 | { | 95 | string purchase_with_default_payment_uri () |
570 | 97 | _password_expiry = now ().add_seconds (seconds_from_now); | 96 | { |
571 | 97 | return webapi_server() + PURCHASE_WITH_DEFAULT_PAYMENT_PATH; | ||
572 | 98 | } | ||
573 | 99 | |||
574 | 100 | string authentication_server () | ||
575 | 101 | { | ||
576 | 102 | string staging_authentication = Environment.get_variable ("U1_STAGING_AUTHENTICATION"); | ||
577 | 103 | return staging_authentication != null ? staging_authentication : AUTHENTICATION_SERVER; | ||
578 | 104 | } | ||
579 | 105 | |||
580 | 106 | string authentication_uri () | ||
581 | 107 | { | ||
582 | 108 | return authentication_server() + AUTHENTICATION_PATH; | ||
583 | 109 | } | ||
584 | 110 | |||
585 | 111 | public bool got_credentials () { | ||
586 | 112 | return _ubuntuone_credentials != null; | ||
587 | 98 | } | 113 | } |
588 | 99 | 114 | ||
589 | 100 | internal virtual CredentialsManagement build_credentials_management () | 115 | internal virtual CredentialsManagement build_credentials_management () |
590 | @@ -125,28 +140,36 @@ | |||
591 | 125 | email = root_object.get_string_member("email"); | 140 | email = root_object.get_string_member("email"); |
592 | 126 | } | 141 | } |
593 | 127 | 142 | ||
595 | 128 | internal void parse_payment_method_json (string json_string) throws GLib.Error | 143 | internal void parse_payment_method_json (string json_string) throws GLib.Error, PurchaseError |
596 | 129 | { | 144 | { |
597 | 130 | var root_object = parse_json (json_string); | 145 | var root_object = parse_json (json_string); |
600 | 131 | Json.Object payload = root_object.get_object_member("payload"); | 146 | if (root_object.has_member ("selected_payment_method")) { |
601 | 132 | selected_payment_method = payload.get_string_member("selected_payment_method"); | 147 | selected_payment_method = root_object.get_string_member("selected_payment_method"); |
602 | 148 | } else { | ||
603 | 149 | open_url = root_object.get_string_member ("open_url"); | ||
604 | 150 | var error_message = root_object.get_string_member ("error_message"); | ||
605 | 151 | throw new PurchaseError.PURCHASE_ERROR (error_message); | ||
606 | 152 | } | ||
607 | 133 | } | 153 | } |
608 | 134 | 154 | ||
609 | 135 | internal void parse_authentication_json (string json_string) throws GLib.Error | 155 | internal void parse_authentication_json (string json_string) throws GLib.Error |
610 | 136 | { | 156 | { |
611 | 137 | var root_object = parse_json (json_string); | 157 | var root_object = parse_json (json_string); |
614 | 138 | consumer_key = root_object.get_string_member("consumer_key"); | 158 | consumer_key = root_object.get_string_member ("consumer_key"); |
615 | 139 | token = root_object.get_string_member("token"); | 159 | token = root_object.get_string_member ("token"); |
616 | 140 | } | 160 | } |
617 | 141 | 161 | ||
618 | 142 | internal string parse_purchase_json (string json_string) throws GLib.Error | 162 | internal string parse_purchase_json (string json_string) throws GLib.Error |
619 | 143 | { | 163 | { |
620 | 144 | var root_object = parse_json (json_string); | 164 | var root_object = parse_json (json_string); |
623 | 145 | Json.Object payload = root_object.get_object_member("payload"); | 165 | if (root_object.has_member ("open_url")) { |
624 | 146 | return payload.get_string_member("open_url"); | 166 | return root_object.get_string_member("open_url"); |
625 | 167 | } else { | ||
626 | 168 | return ""; | ||
627 | 169 | } | ||
628 | 147 | } | 170 | } |
629 | 148 | 171 | ||
631 | 149 | internal virtual async void fetch_credentials () throws PurchaseError | 172 | public virtual async void fetch_credentials () throws PurchaseError |
632 | 150 | { | 173 | { |
633 | 151 | PurchaseError error = null; | 174 | PurchaseError error = null; |
634 | 152 | 175 | ||
635 | @@ -155,8 +178,12 @@ | |||
636 | 155 | debug ("got credentials"); | 178 | debug ("got credentials"); |
637 | 156 | fetch_credentials.callback (); | 179 | fetch_credentials.callback (); |
638 | 157 | }); | 180 | }); |
639 | 181 | ulong not_found_handler = credentials_management.credentials_not_found.connect ((credentials) => { | ||
640 | 182 | error = new PurchaseError.MISSING_CREDENTIALS_ERROR ("Can't get Ubuntu One tokens."); | ||
641 | 183 | fetch_credentials.callback (); | ||
642 | 184 | }); | ||
643 | 158 | ulong error_handler = credentials_management.credentials_error.connect (() => { | 185 | ulong error_handler = credentials_management.credentials_error.connect (() => { |
645 | 159 | error = new PurchaseError.PURCHASE_ERROR ("Can't get Ubuntu One tokens."); | 186 | error = new PurchaseError.MISSING_CREDENTIALS_ERROR ("Can't get Ubuntu One tokens."); |
646 | 160 | fetch_credentials.callback (); | 187 | fetch_credentials.callback (); |
647 | 161 | }); | 188 | }); |
648 | 162 | 189 | ||
649 | @@ -164,10 +191,11 @@ | |||
650 | 164 | credentials_management.find_credentials (); | 191 | credentials_management.find_credentials (); |
651 | 165 | yield; | 192 | yield; |
652 | 166 | } catch (IOError e) { | 193 | } catch (IOError e) { |
654 | 167 | error = new PurchaseError.PURCHASE_ERROR ("Can't get Ubuntu One tokens: %s", e.message); | 194 | error = new PurchaseError.MISSING_CREDENTIALS_ERROR ("Can't get Ubuntu One tokens: %s", e.message); |
655 | 168 | } | 195 | } |
656 | 169 | 196 | ||
657 | 170 | credentials_management.disconnect (found_handler); | 197 | credentials_management.disconnect (found_handler); |
658 | 198 | credentials_management.disconnect (not_found_handler); | ||
659 | 171 | credentials_management.disconnect (error_handler); | 199 | credentials_management.disconnect (error_handler); |
660 | 172 | 200 | ||
661 | 173 | if (error != null) { | 201 | if (error != null) { |
662 | @@ -208,7 +236,7 @@ | |||
663 | 208 | internal virtual async void fetch_account () throws PurchaseError | 236 | internal virtual async void fetch_account () throws PurchaseError |
664 | 209 | { | 237 | { |
665 | 210 | string response; | 238 | string response; |
667 | 211 | PurchaseError error = yield call_api ("GET", ACCOUNT_URI, out response); | 239 | PurchaseError error = yield call_api ("GET", account_uri(), out response); |
668 | 212 | 240 | ||
669 | 213 | if (error != null) { | 241 | if (error != null) { |
670 | 214 | debug ("Error while fetching U1 account: %s.", error.message); | 242 | debug ("Error while fetching U1 account: %s.", error.message); |
671 | @@ -224,43 +252,40 @@ | |||
672 | 224 | } | 252 | } |
673 | 225 | } | 253 | } |
674 | 226 | 254 | ||
676 | 227 | internal virtual async void fetch_payment_method (string purchase_sku) throws PurchaseError | 255 | internal virtual void fetch_payment_method (string purchase_sku) throws PurchaseError |
677 | 228 | { | 256 | { |
681 | 229 | string response; | 257 | var uri = payment_method_uri().printf (purchase_sku); |
679 | 230 | var uri = PAYMENT_METHOD_URI.printf (purchase_sku); | ||
680 | 231 | PurchaseError error = yield call_api ("GET", uri, out response); | ||
682 | 232 | 258 | ||
686 | 233 | if (error != null) { | 259 | var message = send_signed_webservice_call ("GET", uri); |
687 | 234 | debug ("Error while fetching payment method: %s.", error.message); | 260 | if (message.status_code != Soup.KnownStatusCode.OK) { |
688 | 235 | throw error; | 261 | debug ("Purchase request failed: HTTP %u", message.status_code); |
689 | 262 | debug ("Reason: %s", message.reason_phrase); | ||
690 | 263 | try { | ||
691 | 264 | message.response_body.flatten (); | ||
692 | 265 | debug ("body: ------\n%s\n------\n", (string) message.response_body.data); | ||
693 | 266 | } catch (Error e) { | ||
694 | 267 | } | ||
695 | 268 | throw new PurchaseError.PURCHASE_ERROR ("Retrieve payment method failed: %s".printf (message.reason_phrase)); | ||
696 | 236 | } | 269 | } |
697 | 237 | |||
698 | 238 | try { | 270 | try { |
701 | 239 | parse_payment_method_json (response); | 271 | message.response_body.flatten (); |
702 | 240 | debug ("got payment method"); | 272 | var result = (string) message.response_body.data; |
703 | 273 | parse_payment_method_json (result); | ||
704 | 241 | } catch (GLib.Error e) { | 274 | } catch (GLib.Error e) { |
705 | 242 | debug ("Error while getting payment method: %s.", e.message); | 275 | debug ("Error while getting payment method: %s.", e.message); |
706 | 243 | throw new PurchaseError.PURCHASE_ERROR (e.message); | 276 | throw new PurchaseError.PURCHASE_ERROR (e.message); |
707 | 244 | } | 277 | } |
708 | 245 | } | 278 | } |
709 | 246 | 279 | ||
711 | 247 | internal virtual async void refetch_payment_info (string purchase_sku) throws PurchaseError | 280 | public virtual async void fetch_account_info () throws PurchaseError |
712 | 248 | { | 281 | { |
713 | 249 | yield fetch_credentials (); | 282 | yield fetch_credentials (); |
714 | 250 | yield fetch_account (); | 283 | yield fetch_account (); |
715 | 251 | yield fetch_payment_method (purchase_sku); | ||
716 | 252 | reset_payment_method_cache (PAYMENT_METHOD_CACHE_SECONDS); | ||
717 | 253 | } | 284 | } |
718 | 254 | 285 | ||
720 | 255 | public async void fetch_payment_info (string purchase_sku) throws PurchaseError | 286 | public void fetch_payment_info (string purchase_sku) throws PurchaseError |
721 | 256 | { | 287 | { |
729 | 257 | if (expired_payment_method_cache ()) | 288 | fetch_payment_method (purchase_sku); |
723 | 258 | { | ||
724 | 259 | debug ("refetching"); | ||
725 | 260 | yield refetch_payment_info (purchase_sku); | ||
726 | 261 | } else { | ||
727 | 262 | debug ("in cache"); | ||
728 | 263 | } | ||
730 | 264 | } | 289 | } |
731 | 265 | 290 | ||
732 | 266 | internal virtual void _do_sso_webcall (Soup.Message message, string password) | 291 | internal virtual void _do_sso_webcall (Soup.Message message, string password) |
733 | @@ -286,6 +311,11 @@ | |||
734 | 286 | if (message.status_code == Soup.KnownStatusCode.UNAUTHORIZED) { | 311 | if (message.status_code == Soup.KnownStatusCode.UNAUTHORIZED) { |
735 | 287 | throw new PurchaseError.WRONG_PASSWORD_ERROR ("Wrong password"); | 312 | throw new PurchaseError.WRONG_PASSWORD_ERROR ("Wrong password"); |
736 | 288 | } | 313 | } |
737 | 314 | try { | ||
738 | 315 | message.response_body.flatten (); | ||
739 | 316 | debug ("body: ------\n%s\n------\n", (string) message.response_body.data); | ||
740 | 317 | } catch (Error e) { | ||
741 | 318 | } | ||
742 | 289 | throw new PurchaseError.PURCHASE_ERROR (message.reason_phrase); | 319 | throw new PurchaseError.PURCHASE_ERROR (message.reason_phrase); |
743 | 290 | } | 320 | } |
744 | 291 | message.response_body.flatten (); | 321 | message.response_body.flatten (); |
745 | @@ -294,12 +324,13 @@ | |||
746 | 294 | 324 | ||
747 | 295 | internal virtual string get_purchase_token (string password) throws PurchaseError | 325 | internal virtual string get_purchase_token (string password) throws PurchaseError |
748 | 296 | { | 326 | { |
750 | 297 | var result = authenticated_sso_webcall ("POST", AUTHENTICATION_URI, AUTHENTICATE_PARAMS, password); | 327 | var result = authenticated_sso_webcall ("POST", authentication_uri(), AUTHENTICATE_PARAMS, password); |
751 | 298 | try { | 328 | try { |
752 | 299 | parse_authentication_json (result); | 329 | parse_authentication_json (result); |
753 | 300 | } catch (GLib.Error e) { | 330 | } catch (GLib.Error e) { |
754 | 301 | throw new PurchaseError.PURCHASE_ERROR (e.message); | 331 | throw new PurchaseError.PURCHASE_ERROR (e.message); |
755 | 302 | } | 332 | } |
756 | 333 | |||
757 | 303 | return "%s:%s".printf (consumer_key, token); | 334 | return "%s:%s".printf (consumer_key, token); |
758 | 304 | } | 335 | } |
759 | 305 | 336 | ||
760 | @@ -311,14 +342,20 @@ | |||
761 | 311 | return message; | 342 | return message; |
762 | 312 | } | 343 | } |
763 | 313 | 344 | ||
765 | 314 | internal virtual void purchase_with_default_payment (string sku, string purchase_token) throws PurchaseError | 345 | internal virtual void purchase_with_default_payment (string album_id, string purchase_token) throws PurchaseError |
766 | 315 | { | 346 | { |
768 | 316 | var uri = PURCHASE_WITH_DEFAULT_PAYMENT_URI.printf (sku, purchase_token); | 347 | var uri = purchase_with_default_payment_uri().printf (album_id, purchase_token); |
769 | 348 | |||
770 | 317 | var message = send_signed_webservice_call ("GET", uri); | 349 | var message = send_signed_webservice_call ("GET", uri); |
771 | 318 | 350 | ||
772 | 319 | if (message.status_code != Soup.KnownStatusCode.OK) { | 351 | if (message.status_code != Soup.KnownStatusCode.OK) { |
773 | 320 | debug ("Purchase request failed: HTTP %u", message.status_code); | 352 | debug ("Purchase request failed: HTTP %u", message.status_code); |
774 | 321 | debug ("Reason: %s", message.reason_phrase); | 353 | debug ("Reason: %s", message.reason_phrase); |
775 | 354 | try { | ||
776 | 355 | message.response_body.flatten (); | ||
777 | 356 | debug ("body: ------\n%s\n------\n", (string) message.response_body.data); | ||
778 | 357 | } catch (Error e) { | ||
779 | 358 | } | ||
780 | 322 | throw new PurchaseError.PURCHASE_ERROR ("Purchase failed: %s".printf (message.reason_phrase)); | 359 | throw new PurchaseError.PURCHASE_ERROR ("Purchase failed: %s".printf (message.reason_phrase)); |
781 | 323 | } | 360 | } |
782 | 324 | try { | 361 | try { |
783 | @@ -336,7 +373,9 @@ | |||
784 | 336 | public void purchase (string album_id, string password) throws PurchaseError | 373 | public void purchase (string album_id, string password) throws PurchaseError |
785 | 337 | { | 374 | { |
786 | 338 | var purchase_token = get_purchase_token (password); | 375 | var purchase_token = get_purchase_token (password); |
787 | 376 | debug ("purchasing..."); | ||
788 | 339 | purchase_with_default_payment (album_id, purchase_token); | 377 | purchase_with_default_payment (album_id, purchase_token); |
789 | 378 | debug ("purchase completed."); | ||
790 | 340 | } | 379 | } |
791 | 341 | } | 380 | } |
792 | 342 | } | 381 | } |
793 | 343 | 382 | ||
794 | === modified file 'tests/unit/test-ubuntuone-purchases.vala' | |||
795 | --- tests/unit/test-ubuntuone-purchases.vala 2012-12-06 15:39:38 +0000 | |||
796 | +++ tests/unit/test-ubuntuone-purchases.vala 2013-04-17 10:13:28 +0000 | |||
797 | @@ -25,7 +25,7 @@ | |||
798 | 25 | 25 | ||
799 | 26 | const string FAKE_URL = "http://fake/url"; | 26 | const string FAKE_URL = "http://fake/url"; |
800 | 27 | const string FAKE_PASSWORD = "PezEspada"; | 27 | const string FAKE_PASSWORD = "PezEspada"; |
802 | 28 | const string FAKE_SKU = "fake_store:fake_album:id"; | 28 | const string FAKE_SKU = "7digital:fake_album:id"; |
803 | 29 | const string FAKE_TOKEN = "a fake token"; | 29 | const string FAKE_TOKEN = "a fake token"; |
804 | 30 | 30 | ||
805 | 31 | const string BROKEN_JSON = """ | 31 | const string BROKEN_JSON = """ |
806 | @@ -45,14 +45,17 @@ | |||
807 | 45 | 45 | ||
808 | 46 | const string FAKE_JSON_PAYMENT_METHOD = """ | 46 | const string FAKE_JSON_PAYMENT_METHOD = """ |
809 | 47 | { | 47 | { |
818 | 48 | "meta": { | 48 | "open_url": "", |
819 | 49 | "status": "Ok" | 49 | "user_email": "mr@be.an", |
820 | 50 | }, | 50 | "selected_payment_method": "Visa 1234" |
821 | 51 | "payload": { | 51 | } |
822 | 52 | "open_url": "", | 52 | """; |
823 | 53 | "user_email": "mr@be.an", | 53 | |
824 | 54 | "selected_payment_method": "Visa 1234" | 54 | |
825 | 55 | } | 55 | const string FAKE_JSON_PAYMENT_METHOD_ERROR = """ |
826 | 56 | { | ||
827 | 57 | "open_url": "http://somewhere/else", | ||
828 | 58 | "error_message": "No default payment method selected." | ||
829 | 56 | } | 59 | } |
830 | 57 | """; | 60 | """; |
831 | 58 | 61 | ||
832 | @@ -69,30 +72,18 @@ | |||
833 | 69 | 72 | ||
834 | 70 | const string FAKE_JSON_PURCHASE = """ | 73 | const string FAKE_JSON_PURCHASE = """ |
835 | 71 | { | 74 | { |
845 | 72 | "meta": { | 75 | "order_id": "1111", |
846 | 73 | "status": "Ok" | 76 | "order_status": "OK" |
838 | 74 | }, | ||
839 | 75 | "payload": { | ||
840 | 76 | "order_id": "1111", | ||
841 | 77 | "order_status": "OK", | ||
842 | 78 | "order_detail": "TBD", | ||
843 | 79 | "open_url": "" | ||
844 | 80 | } | ||
847 | 81 | } | 77 | } |
848 | 82 | """; | 78 | """; |
849 | 83 | 79 | ||
850 | 84 | 80 | ||
851 | 85 | const string FAKE_JSON_PURCHASE_FAILURE = """ | 81 | const string FAKE_JSON_PURCHASE_FAILURE = """ |
852 | 86 | { | 82 | { |
862 | 87 | "meta": { | 83 | "order_id": "1111", |
863 | 88 | "status": "Ok" | 84 | "order_status": "PAYMENT_FAILURE", |
864 | 89 | }, | 85 | "order_detail": "TBD", |
865 | 90 | "payload": { | 86 | "open_url": "http://slashdot.org/" |
857 | 91 | "order_id": "1111", | ||
858 | 92 | "order_status": "PAYMENT_FAILURE", | ||
859 | 93 | "order_detail": "TBD", | ||
860 | 94 | "open_url": "http://slashdot.org/" | ||
861 | 95 | } | ||
866 | 96 | } | 87 | } |
867 | 97 | """; | 88 | """; |
868 | 98 | 89 | ||
869 | @@ -113,12 +104,11 @@ | |||
870 | 113 | Test.add_data_func ("/Unit/PurchaseChecker/ParseAccount", test_parse_account); | 104 | Test.add_data_func ("/Unit/PurchaseChecker/ParseAccount", test_parse_account); |
871 | 114 | Test.add_data_func ("/Unit/PurchaseChecker/ParseBrokenAccount", test_parse_broken_account); | 105 | Test.add_data_func ("/Unit/PurchaseChecker/ParseBrokenAccount", test_parse_broken_account); |
872 | 115 | Test.add_data_func ("/Unit/PurchaseChecker/ParsePaymentMethod", test_parse_payment_method); | 106 | Test.add_data_func ("/Unit/PurchaseChecker/ParsePaymentMethod", test_parse_payment_method); |
873 | 107 | Test.add_data_func ("/Unit/PurchaseChecker/ParsePaymentMethodError", test_parse_payment_method_error); | ||
874 | 116 | Test.add_data_func ("/Unit/PurchaseChecker/ParseAuthentication", test_parse_authentication); | 108 | Test.add_data_func ("/Unit/PurchaseChecker/ParseAuthentication", test_parse_authentication); |
875 | 117 | Test.add_data_func ("/Unit/PurchaseChecker/ReadyToPurchase", test_ready_to_purchase); | 109 | Test.add_data_func ("/Unit/PurchaseChecker/ReadyToPurchase", test_ready_to_purchase); |
880 | 118 | Test.add_data_func ("/Unit/PurchaseChecker/FetchPaymentInvalidated", test_fetch_payment_invalidated); | 110 | Test.add_data_func ("/Unit/PurchaseChecker/FetchAccountInfo", test_fetch_account_info); |
881 | 119 | Test.add_data_func ("/Unit/PurchaseChecker/FetchPaymentCached", test_fetch_payment_cached); | 111 | Test.add_data_func ("/Unit/PurchaseChecker/FetchPaymentInfo", test_fetch_payment_info); |
878 | 120 | Test.add_data_func ("/Unit/PurchaseChecker/ReFetchPayment", test_refetch_payment); | ||
879 | 121 | Test.add_data_func ("/Unit/PurchaseChecker/ReFetchPaymentSetsCache", test_refetch_payment_sets_cache); | ||
882 | 122 | Test.add_data_func ("/Unit/PurchaseChecker/FetchCredentials", test_fetch_credentials); | 112 | Test.add_data_func ("/Unit/PurchaseChecker/FetchCredentials", test_fetch_credentials); |
883 | 123 | Test.add_data_func ("/Unit/PurchaseChecker/FetchCredentialsFails", test_fetch_credentials_fails); | 113 | Test.add_data_func ("/Unit/PurchaseChecker/FetchCredentialsFails", test_fetch_credentials_fails); |
884 | 124 | Test.add_data_func ("/Unit/PurchaseChecker/FetchCredentialsFailsExtra", test_fetch_credentials_fails_extra); | 114 | Test.add_data_func ("/Unit/PurchaseChecker/FetchCredentialsFailsExtra", test_fetch_credentials_fails_extra); |
885 | @@ -163,11 +153,12 @@ | |||
886 | 163 | async void irl_test_async () | 153 | async void irl_test_async () |
887 | 164 | { | 154 | { |
888 | 165 | var purchase_service = new PurchaseService(); | 155 | var purchase_service = new PurchaseService(); |
890 | 166 | var purchase_sku = "1"; | 156 | var purchase_sku = "7digital:album:1347423:WORLD"; |
891 | 167 | try { | 157 | try { |
893 | 168 | yield purchase_service.fetch_payment_info (purchase_sku); | 158 | yield purchase_service.fetch_account_info (); |
894 | 159 | purchase_service.fetch_payment_info (purchase_sku); | ||
895 | 169 | debug ("data was available: %s %s %s", purchase_service.nickname, purchase_service.email, purchase_service.selected_payment_method); | 160 | debug ("data was available: %s %s %s", purchase_service.nickname, purchase_service.email, purchase_service.selected_payment_method); |
897 | 170 | var real_user_password = "****fake_password_in_bzr***"; | 161 | var real_user_password = FAKE_PASSWORD; |
898 | 171 | purchase_service.purchase (purchase_sku, real_user_password); | 162 | purchase_service.purchase (purchase_sku, real_user_password); |
899 | 172 | debug ("purchase completed."); | 163 | debug ("purchase completed."); |
900 | 173 | } catch (PurchaseError e) { | 164 | } catch (PurchaseError e) { |
901 | @@ -186,7 +177,7 @@ | |||
902 | 186 | }); | 177 | }); |
903 | 187 | return false; | 178 | return false; |
904 | 188 | }); | 179 | }); |
906 | 189 | assert (run_with_timeout (loop, 10000)); | 180 | assert (run_with_timeout (loop, 60000)); |
907 | 190 | } | 181 | } |
908 | 191 | 182 | ||
909 | 192 | class FakeCredentialsManagement : GLib.Object, CredentialsManagement | 183 | class FakeCredentialsManagement : GLib.Object, CredentialsManagement |
910 | @@ -290,6 +281,19 @@ | |||
911 | 290 | assert_cmpstr (purchase_service.selected_payment_method, OperatorType.EQUAL, "Visa 1234"); | 281 | assert_cmpstr (purchase_service.selected_payment_method, OperatorType.EQUAL, "Visa 1234"); |
912 | 291 | } | 282 | } |
913 | 292 | 283 | ||
914 | 284 | private static void test_parse_payment_method_error () | ||
915 | 285 | { | ||
916 | 286 | var purchase_service = new BaseTestPurchaseService (); | ||
917 | 287 | try | ||
918 | 288 | { | ||
919 | 289 | purchase_service.parse_payment_method_json (FAKE_JSON_PAYMENT_METHOD_ERROR); | ||
920 | 290 | assert_not_reached (); | ||
921 | 291 | } catch (PurchaseError e) | ||
922 | 292 | { | ||
923 | 293 | assert_cmpstr (purchase_service.open_url, OperatorType.EQUAL, "http://somewhere/else"); | ||
924 | 294 | } | ||
925 | 295 | } | ||
926 | 296 | |||
927 | 293 | private static void test_parse_authentication () | 297 | private static void test_parse_authentication () |
928 | 294 | { | 298 | { |
929 | 295 | var purchase_service = new BaseTestPurchaseService (); | 299 | var purchase_service = new BaseTestPurchaseService (); |
930 | @@ -313,30 +317,6 @@ | |||
931 | 313 | assert (purchase_service.ready_to_purchase == true); | 317 | assert (purchase_service.ready_to_purchase == true); |
932 | 314 | } | 318 | } |
933 | 315 | 319 | ||
934 | 316 | class TestFetchPurchaseService : BaseTestPurchaseService { | ||
935 | 317 | internal bool fetch_called = false; | ||
936 | 318 | internal override async void refetch_payment_info (string purchase_sku) | ||
937 | 319 | { | ||
938 | 320 | fetch_called = true; | ||
939 | 321 | } | ||
940 | 322 | } | ||
941 | 323 | |||
942 | 324 | private static void test_fetch_payment_invalidated () | ||
943 | 325 | { | ||
944 | 326 | var purchase_service = new TestFetchPurchaseService (); | ||
945 | 327 | purchase_service._selected_payment_method_expiry = new DateTime.now_utc (); | ||
946 | 328 | purchase_service.fetch_payment_info ("fake_sku"); | ||
947 | 329 | assert (purchase_service.fetch_called); | ||
948 | 330 | } | ||
949 | 331 | |||
950 | 332 | private static void test_fetch_payment_cached () | ||
951 | 333 | { | ||
952 | 334 | var purchase_service = new TestFetchPurchaseService (); | ||
953 | 335 | purchase_service._selected_payment_method_expiry = new DateTime.now_utc ().add_seconds (60); | ||
954 | 336 | purchase_service.fetch_payment_info ("fake_sku"); | ||
955 | 337 | assert (purchase_service.fetch_called == false); | ||
956 | 338 | } | ||
957 | 339 | |||
958 | 340 | class TestRefetchPurchaseService : BaseTestPurchaseService { | 320 | class TestRefetchPurchaseService : BaseTestPurchaseService { |
959 | 341 | internal bool credentials_fetched = false; | 321 | internal bool credentials_fetched = false; |
960 | 342 | internal bool account_fetched = false; | 322 | internal bool account_fetched = false; |
961 | @@ -360,26 +340,21 @@ | |||
962 | 360 | }); | 340 | }); |
963 | 361 | yield; | 341 | yield; |
964 | 362 | } | 342 | } |
966 | 363 | internal override async void fetch_payment_method (string purchase_sku) | 343 | internal override void fetch_payment_method (string purchase_sku) |
967 | 364 | { | 344 | { |
974 | 365 | Idle.add (() => { | 345 | payment_method_fetched = true; |
969 | 366 | payment_method_fetched = true; | ||
970 | 367 | fetch_payment_method.callback (); | ||
971 | 368 | return false; | ||
972 | 369 | }); | ||
973 | 370 | yield; | ||
975 | 371 | } | 346 | } |
976 | 372 | } | 347 | } |
977 | 373 | 348 | ||
979 | 374 | private static void test_refetch_payment () | 349 | private static void test_fetch_account_info () |
980 | 375 | { | 350 | { |
981 | 376 | var purchase_service = new TestRefetchPurchaseService (); | 351 | var purchase_service = new TestRefetchPurchaseService (); |
982 | 377 | 352 | ||
983 | 378 | MainLoop mainloop = new MainLoop (); | 353 | MainLoop mainloop = new MainLoop (); |
985 | 379 | purchase_service.refetch_payment_info.begin("fake_sku", (obj, res) => { | 354 | purchase_service.fetch_account_info.begin((obj, res) => { |
986 | 380 | mainloop.quit (); | 355 | mainloop.quit (); |
987 | 381 | try { | 356 | try { |
989 | 382 | purchase_service.refetch_payment_info.end (res); | 357 | purchase_service.fetch_account_info.end (res); |
990 | 383 | } catch (PurchaseError e) { | 358 | } catch (PurchaseError e) { |
991 | 384 | error ("Can't fetch payment info: %s", e.message); | 359 | error ("Can't fetch payment info: %s", e.message); |
992 | 385 | } | 360 | } |
993 | @@ -388,29 +363,19 @@ | |||
994 | 388 | 363 | ||
995 | 389 | assert (purchase_service.credentials_fetched); | 364 | assert (purchase_service.credentials_fetched); |
996 | 390 | assert (purchase_service.account_fetched); | 365 | assert (purchase_service.account_fetched); |
997 | 366 | } | ||
998 | 367 | |||
999 | 368 | private static void test_fetch_payment_info () | ||
1000 | 369 | { | ||
1001 | 370 | var purchase_service = new TestRefetchPurchaseService (); | ||
1002 | 371 | try { | ||
1003 | 372 | purchase_service.fetch_payment_info("fake_sku"); | ||
1004 | 373 | } catch (PurchaseError e) { | ||
1005 | 374 | error ("Can't fetch payment info: %s", e.message); | ||
1006 | 375 | } | ||
1007 | 391 | assert (purchase_service.payment_method_fetched); | 376 | assert (purchase_service.payment_method_fetched); |
1008 | 392 | } | 377 | } |
1009 | 393 | 378 | ||
1010 | 394 | private static void test_refetch_payment_sets_cache () | ||
1011 | 395 | { | ||
1012 | 396 | var purchase_service = new TestRefetchPurchaseService (); | ||
1013 | 397 | |||
1014 | 398 | assert (true == purchase_service.expired_payment_method_cache ()); | ||
1015 | 399 | |||
1016 | 400 | MainLoop mainloop = new MainLoop (); | ||
1017 | 401 | purchase_service.refetch_payment_info.begin("fake_sku", (obj, res) => { | ||
1018 | 402 | mainloop.quit (); | ||
1019 | 403 | try { | ||
1020 | 404 | purchase_service.refetch_payment_info.end (res); | ||
1021 | 405 | } catch (PurchaseError e) { | ||
1022 | 406 | error ("Can't fetch payment info: %s", e.message); | ||
1023 | 407 | } | ||
1024 | 408 | }); | ||
1025 | 409 | assert (run_with_timeout (mainloop, 1000)); | ||
1026 | 410 | |||
1027 | 411 | assert (false == purchase_service.expired_payment_method_cache ()); | ||
1028 | 412 | } | ||
1029 | 413 | |||
1030 | 414 | private static void test_fetch_credentials () | 379 | private static void test_fetch_credentials () |
1031 | 415 | { | 380 | { |
1032 | 416 | var purchase_service = new BaseTestPurchaseService (); | 381 | var purchase_service = new BaseTestPurchaseService (); |
1033 | @@ -431,7 +396,7 @@ | |||
1034 | 431 | 396 | ||
1035 | 432 | private static void test_fetch_credentials_fails () | 397 | private static void test_fetch_credentials_fails () |
1036 | 433 | { | 398 | { |
1038 | 434 | bool failed = false; | 399 | PurchaseError failure = null; |
1039 | 435 | var purchase_service = new FailingCredentialsPurchaseService (); | 400 | var purchase_service = new FailingCredentialsPurchaseService (); |
1040 | 436 | 401 | ||
1041 | 437 | MainLoop mainloop = new MainLoop (); | 402 | MainLoop mainloop = new MainLoop (); |
1042 | @@ -440,17 +405,16 @@ | |||
1043 | 440 | try { | 405 | try { |
1044 | 441 | purchase_service.fetch_credentials.end (res); | 406 | purchase_service.fetch_credentials.end (res); |
1045 | 442 | } catch (PurchaseError e) { | 407 | } catch (PurchaseError e) { |
1047 | 443 | failed = true; | 408 | failure = e; |
1048 | 444 | } | 409 | } |
1049 | 445 | }); | 410 | }); |
1050 | 446 | assert (run_with_timeout (mainloop, 1000)); | 411 | assert (run_with_timeout (mainloop, 1000)); |
1053 | 447 | 412 | assert (failure is PurchaseError.MISSING_CREDENTIALS_ERROR); | |
1052 | 448 | assert (failed); | ||
1054 | 449 | } | 413 | } |
1055 | 450 | 414 | ||
1056 | 451 | private static void test_fetch_credentials_fails_extra () | 415 | private static void test_fetch_credentials_fails_extra () |
1057 | 452 | { | 416 | { |
1059 | 453 | bool failed = false; | 417 | PurchaseError failure = null; |
1060 | 454 | var purchase_service = new ExtraFailingCredentialsPurchaseService (); | 418 | var purchase_service = new ExtraFailingCredentialsPurchaseService (); |
1061 | 455 | 419 | ||
1062 | 456 | MainLoop mainloop = new MainLoop (); | 420 | MainLoop mainloop = new MainLoop (); |
1063 | @@ -459,12 +423,11 @@ | |||
1064 | 459 | try { | 423 | try { |
1065 | 460 | purchase_service.fetch_credentials.end (res); | 424 | purchase_service.fetch_credentials.end (res); |
1066 | 461 | } catch (PurchaseError e) { | 425 | } catch (PurchaseError e) { |
1068 | 462 | failed = true; | 426 | failure = e; |
1069 | 463 | } | 427 | } |
1070 | 464 | }); | 428 | }); |
1071 | 465 | assert (run_with_timeout (mainloop, 1000)); | 429 | assert (run_with_timeout (mainloop, 1000)); |
1074 | 466 | 430 | assert (failure is PurchaseError.MISSING_CREDENTIALS_ERROR); | |
1073 | 467 | assert (failed); | ||
1075 | 468 | } | 431 | } |
1076 | 469 | 432 | ||
1077 | 470 | class FakeWebcallPurchaseService : BaseTestPurchaseService | 433 | class FakeWebcallPurchaseService : BaseTestPurchaseService |
1078 | @@ -565,21 +528,7 @@ | |||
1079 | 565 | { | 528 | { |
1080 | 566 | var purchase_service = new FakeWebcallPurchaseService (_status_code, _found_json); | 529 | var purchase_service = new FakeWebcallPurchaseService (_status_code, _found_json); |
1081 | 567 | purchase_service._ubuntuone_credentials = new HashTable <string, string> (str_hash, str_equal); | 530 | purchase_service._ubuntuone_credentials = new HashTable <string, string> (str_hash, str_equal); |
1097 | 568 | 531 | purchase_service.fetch_payment_method("fake_sku"); | |
1083 | 569 | PurchaseError error = null; | ||
1084 | 570 | MainLoop mainloop = new MainLoop (); | ||
1085 | 571 | purchase_service.fetch_payment_method.begin("fake_sku", (obj, res) => { | ||
1086 | 572 | mainloop.quit (); | ||
1087 | 573 | try { | ||
1088 | 574 | purchase_service.fetch_payment_method.end (res); | ||
1089 | 575 | } catch (PurchaseError e) { | ||
1090 | 576 | error = e; | ||
1091 | 577 | } | ||
1092 | 578 | }); | ||
1093 | 579 | assert (run_with_timeout (mainloop, 1000)); | ||
1094 | 580 | if (error != null) { | ||
1095 | 581 | throw error; | ||
1096 | 582 | } | ||
1098 | 583 | return purchase_service; | 532 | return purchase_service; |
1099 | 584 | } | 533 | } |
1100 | 585 | 534 | ||
1101 | @@ -616,7 +565,7 @@ | |||
1102 | 616 | class TestPurchaseService : BaseTestPurchaseService | 565 | class TestPurchaseService : BaseTestPurchaseService |
1103 | 617 | { | 566 | { |
1104 | 618 | internal string password; | 567 | internal string password; |
1106 | 619 | internal string sku; | 568 | internal string album_id; |
1107 | 620 | internal string purchase_token; | 569 | internal string purchase_token; |
1108 | 621 | 570 | ||
1109 | 622 | internal override string get_purchase_token (string password) | 571 | internal override string get_purchase_token (string password) |
1110 | @@ -624,9 +573,9 @@ | |||
1111 | 624 | this.password = password; | 573 | this.password = password; |
1112 | 625 | return FAKE_TOKEN; | 574 | return FAKE_TOKEN; |
1113 | 626 | } | 575 | } |
1115 | 627 | internal override void purchase_with_default_payment (string sku, string purchase_token) | 576 | internal override void purchase_with_default_payment (string album_id, string purchase_token) |
1116 | 628 | { | 577 | { |
1118 | 629 | this.sku = sku; | 578 | this.album_id = album_id; |
1119 | 630 | this.purchase_token = purchase_token; | 579 | this.purchase_token = purchase_token; |
1120 | 631 | } | 580 | } |
1121 | 632 | } | 581 | } |
1122 | @@ -637,7 +586,7 @@ | |||
1123 | 637 | var purchase_service = new TestPurchaseService (); | 586 | var purchase_service = new TestPurchaseService (); |
1124 | 638 | purchase_service.purchase (FAKE_SKU, FAKE_PASSWORD); | 587 | purchase_service.purchase (FAKE_SKU, FAKE_PASSWORD); |
1125 | 639 | assert_cmpstr (purchase_service.password, OperatorType.EQUAL, FAKE_PASSWORD); | 588 | assert_cmpstr (purchase_service.password, OperatorType.EQUAL, FAKE_PASSWORD); |
1127 | 640 | assert_cmpstr (purchase_service.sku, OperatorType.EQUAL, FAKE_SKU); | 589 | assert_cmpstr (purchase_service.album_id, OperatorType.EQUAL, FAKE_SKU); |
1128 | 641 | assert_cmpstr (purchase_service.purchase_token, OperatorType.EQUAL, FAKE_TOKEN); | 590 | assert_cmpstr (purchase_service.purchase_token, OperatorType.EQUAL, FAKE_TOKEN); |
1129 | 642 | } catch (Error e) { | 591 | } catch (Error e) { |
1130 | 643 | assert_not_reached (); | 592 | assert_not_reached (); |
1131 | @@ -722,6 +671,7 @@ | |||
1132 | 722 | try { | 671 | try { |
1133 | 723 | purchase_service.purchase_with_default_payment (FAKE_SKU, FAKE_TOKEN); | 672 | purchase_service.purchase_with_default_payment (FAKE_SKU, FAKE_TOKEN); |
1134 | 724 | } catch (PurchaseError e) { | 673 | } catch (PurchaseError e) { |
1135 | 674 | warning (e.message); | ||
1136 | 725 | assert_not_reached (); | 675 | assert_not_reached (); |
1137 | 726 | } | 676 | } |
1138 | 727 | } | 677 | } |
FAILED: Continuous integration, rev:135 /code.launchpad .net/~mandel/ unity-lens- music/musicstor e-purchase/ +merge/ 156513/ +edit-commit- message
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:// jenkins. qa.ubuntu. com/job/ unity-lens- music-ci/ 2/ jenkins. qa.ubuntu. com/job/ unity-lens- music-raring- amd64-ci/ 2 jenkins. qa.ubuntu. com/job/ unity-lens- music-raring- armhf-ci/ 2 jenkins. qa.ubuntu. com/job/ unity-lens- music-raring- i386-ci/ 2
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ unity-lens- music-ci/ 2/rebuild
http://