Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Subscriptions Service Fetcher
This CL introduces CommerceSubscriptionsServiceProxy which abstracts the communication between the client and the subscriptions backend. The CL also extends BuyableProductPageAnnotation to expose main offer id. Bug: 1195241 Change-Id: Ia53cf18ce0310bd02d18f4e6b2725732de7866ef Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2801032 Reviewed-by: David Trainor <dtrainor@chromium.org> Reviewed-by: Yue Zhang <yuezhanggg@chromium.org> Reviewed-by: David Maunder <davidjm@chromium.org> Commit-Queue: Ayman Almadhoun <ayman@chromium.org> Cr-Commit-Position: refs/heads/master@{#869061}
- Loading branch information
Ayman Almadhoun
authored and
Chromium LUCI CQ
committed
Apr 5, 2021
1 parent
4534b37
commit 37c1f35
Showing
21 changed files
with
770 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64 changes: 64 additions & 0 deletions
64
...ava/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionJsonSerializer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright 2021 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
package org.chromium.chrome.browser.subscriptions; | ||
|
||
import org.json.JSONException; | ||
import org.json.JSONObject; | ||
|
||
import org.chromium.base.Log; | ||
|
||
import java.util.Locale; | ||
|
||
/** | ||
* Helpers for serializing and deserializing {@link CommerceSubscription} objects. | ||
*/ | ||
class CommerceSubscriptionJsonSerializer { | ||
private static final String TAG = "CSJS"; | ||
private static final String SUBSCRIPTION_TYPE_KEY = "type"; | ||
private static final String SUBSCRIPTION_IDENTIFIER_KEY = "identifier"; | ||
private static final String SUBSCRIPTION_IDENTIFIER_TYPE_KEY = "identifierType"; | ||
private static final String SUBSCRIPTION_MANAGEMENT_TYPE_KEY = "managementType"; | ||
private static final String SUBSCRIPTION_TIMESTAMP_KEY = "eventTimestampMicros"; | ||
|
||
/** Creates a {@link CommerceSubscription} from a {@link JSONObject}. */ | ||
public static CommerceSubscription deserialize(JSONObject json) { | ||
try { | ||
return new CommerceSubscription(json.getString(SUBSCRIPTION_TYPE_KEY), | ||
json.getString(SUBSCRIPTION_IDENTIFIER_KEY), | ||
json.getString(SUBSCRIPTION_MANAGEMENT_TYPE_KEY), | ||
json.getString(SUBSCRIPTION_IDENTIFIER_TYPE_KEY), | ||
Long.parseLong(json.getString(SUBSCRIPTION_TIMESTAMP_KEY))); | ||
|
||
} catch (JSONException e) { | ||
Log.e(TAG, | ||
String.format(Locale.US, | ||
"Failed to deserialize CommerceSubscription. Details: %s", | ||
e.getMessage())); | ||
} | ||
return null; | ||
} | ||
|
||
/** Creates a {@link JSONObject}from a {@link CommerceSubscription}. */ | ||
public static JSONObject serialize(CommerceSubscription subscription) { | ||
try { | ||
JSONObject subscriptionJson = new JSONObject(); | ||
subscriptionJson.put(SUBSCRIPTION_TYPE_KEY, subscription.getType()); | ||
subscriptionJson.put( | ||
SUBSCRIPTION_MANAGEMENT_TYPE_KEY, subscription.getManagementType()); | ||
subscriptionJson.put( | ||
SUBSCRIPTION_IDENTIFIER_TYPE_KEY, subscription.getTrackingIdType()); | ||
subscriptionJson.put(SUBSCRIPTION_IDENTIFIER_KEY, subscription.getTrackingId()); | ||
|
||
return subscriptionJson; | ||
} catch (JSONException e) { | ||
Log.e(TAG, | ||
String.format(Locale.US, | ||
"Failed to serialize CommerceSubscription. Details: %s", | ||
e.getMessage())); | ||
} | ||
|
||
return null; | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
...ava/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// Copyright 2021 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
package org.chromium.chrome.browser.subscriptions; | ||
|
||
import org.chromium.chrome.browser.flags.ChromeFeatureList; | ||
import org.chromium.chrome.browser.flags.IntCachedFieldTrialParameter; | ||
import org.chromium.chrome.browser.flags.StringCachedFieldTrialParameter; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
|
||
/** Flag configuration for Commerce Subscriptions Service. */ | ||
public class CommerceSubscriptionsServiceConfig { | ||
private static final String BASE_URL_PARAM = "subscriptions_service_base_url"; | ||
private static final String DEFAULT_BASE_URL = | ||
"https://memex-pa.googleapis.com/v1/shopping/subscriptions"; | ||
|
||
private static final String STALE_TAB_LOWER_BOUND_SECONDS_PARAM = | ||
"price_tracking_stale_tab_lower_bound_seconds"; | ||
|
||
public static final StringCachedFieldTrialParameter SUBSCRIPTIONS_SERVICE_BASE_URL = | ||
new StringCachedFieldTrialParameter( | ||
ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, BASE_URL_PARAM, DEFAULT_BASE_URL); | ||
|
||
public static final IntCachedFieldTrialParameter STALE_TAB_LOWER_BOUND_SECONDS = | ||
new IntCachedFieldTrialParameter(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID, | ||
STALE_TAB_LOWER_BOUND_SECONDS_PARAM, (int) TimeUnit.DAYS.toSeconds(1)); | ||
} |
184 changes: 184 additions & 0 deletions
184
...java/src/org/chromium/chrome/browser/subscriptions/CommerceSubscriptionsServiceProxy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
// Copyright 2021 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
package org.chromium.chrome.browser.subscriptions; | ||
|
||
import org.json.JSONArray; | ||
import org.json.JSONException; | ||
import org.json.JSONObject; | ||
|
||
import org.chromium.base.Callback; | ||
import org.chromium.base.Log; | ||
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher; | ||
import org.chromium.chrome.browser.profiles.Profile; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Locale; | ||
|
||
/** | ||
* Wrapper around CommerceSubscriptions Web APIs. | ||
*/ | ||
public final class CommerceSubscriptionsServiceProxy { | ||
private static final String TAG = "CSSP"; | ||
private static final long HTTPS_REQUEST_TIMEOUT_MS = 1000L; | ||
private static final String GET_HTTPS_METHOD = "GET"; | ||
private static final String POST_HTTPS_METHOD = "POST"; | ||
private static final String CONTENT_TYPE = "application/json; charset=UTF-8"; | ||
private static final String EMPTY_POST_DATA = ""; | ||
private static final String[] OAUTH_SCOPE = | ||
new String[] {"https://www.googleapis.com/auth/chromememex"}; | ||
private static final String OAUTH_NAME = "susbcriptions_svc"; | ||
private static final String STATUS_KEY = "status"; | ||
private static final String STATUS_CODE_KEY = "code"; | ||
private static final String REMOVE_SUBSCRIPTIONS_REQUEST_PARAMS_KEY = | ||
"removeShoppingSubscriptionsParams"; | ||
private static final String CREATE_SUBSCRIPTIONS_REQUEST_PARAMS_KEY = | ||
"createShoppingSubscriptionsParams"; | ||
private static final String EVENT_TIMESTAMP_MICROS_KEY = "eventTimestampMicros"; | ||
private static final String SUBSCRIPTIONS_KEY = "subscriptions"; | ||
private static final String GET_SUBSCRIPTIONS_QUERY_PARAMS_TEMPLATE = | ||
"?requestParams.subscriptionType=%s"; | ||
private static final int BACKEND_CANONICAL_CODE_SUCCESS = 0; | ||
|
||
/** | ||
* Makes an HTTPS call to the backend in order to create the provided subscriptions. | ||
* @param subscriptions list of {@link CommerceSubscription} to create. | ||
* @param callback indicates whether or not the operation succeeded on the backend. | ||
*/ | ||
public void create(List<CommerceSubscription> subscriptions, Callback<Boolean> callback) { | ||
manageSubscriptions(getCreateSubscriptionsRequestParams(subscriptions), callback); | ||
} | ||
|
||
/** | ||
* Makes an HTTPS call to the backend to delete the provided list of subscriptions. | ||
* @param subscriptions list of {@link CommerceSubscription} to delete. | ||
* @param callback indicates whether or not the operation succeeded on the backend. | ||
*/ | ||
public void delete(List<CommerceSubscription> subscriptions, Callback<Boolean> callback) { | ||
manageSubscriptions(getRemoveSubscriptionsRequestParams(subscriptions), callback); | ||
} | ||
|
||
/** | ||
* Fetches all subscriptions that match the provided type from the backend. | ||
* @param type the type of subscriptions to fetch. | ||
* @param callback contains the list of subscriptions returned from the server. | ||
*/ | ||
public void get(@CommerceSubscription.CommerceSubscriptionType String type, | ||
Callback<List<CommerceSubscription>> callback) { | ||
// TODO(crbug.com/1195469) Accept Profile instance from SubscriptionsManager. | ||
EndpointFetcher.fetchUsingOAuth( | ||
(response) | ||
-> { | ||
callback.onResult(createCommerceSubscriptions(response.getResponseString())); | ||
}, | ||
Profile.getLastUsedRegularProfile(), OAUTH_NAME, | ||
CommerceSubscriptionsServiceConfig.SUBSCRIPTIONS_SERVICE_BASE_URL.getValue() | ||
+ String.format(GET_SUBSCRIPTIONS_QUERY_PARAMS_TEMPLATE, type), | ||
GET_HTTPS_METHOD, CONTENT_TYPE, OAUTH_SCOPE, EMPTY_POST_DATA, | ||
HTTPS_REQUEST_TIMEOUT_MS); | ||
} | ||
|
||
private void manageSubscriptions(JSONObject requestPayload, Callback<Boolean> callback) { | ||
// TODO(crbug.com/1195469) Accept Profile instance from SubscriptionsManager. | ||
EndpointFetcher.fetchUsingOAuth( | ||
(response) | ||
-> { | ||
callback.onResult( | ||
didManageSubscriptionCallSucceed(response.getResponseString())); | ||
}, | ||
Profile.getLastUsedRegularProfile(), OAUTH_NAME, | ||
CommerceSubscriptionsServiceConfig.SUBSCRIPTIONS_SERVICE_BASE_URL.getValue(), | ||
POST_HTTPS_METHOD, CONTENT_TYPE, OAUTH_SCOPE, requestPayload.toString(), | ||
HTTPS_REQUEST_TIMEOUT_MS); | ||
} | ||
|
||
private boolean didManageSubscriptionCallSucceed(String responseString) { | ||
try { | ||
JSONObject response = new JSONObject(responseString); | ||
JSONObject statusJson = response.getJSONObject(STATUS_KEY); | ||
int statusCode = statusJson.getInt(STATUS_CODE_KEY); | ||
return statusCode == BACKEND_CANONICAL_CODE_SUCCESS; | ||
} catch (JSONException e) { | ||
Log.e(TAG, | ||
String.format(Locale.US, | ||
"Failed to create CreateSubscriptionRequestParams. Details: %s", | ||
e.getMessage())); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private JSONObject getCreateSubscriptionsRequestParams( | ||
List<CommerceSubscription> subscriptions) { | ||
JSONObject container = new JSONObject(); | ||
JSONArray subscriptionsJsonArray = new JSONArray(); | ||
try { | ||
for (CommerceSubscription subscription : subscriptions) { | ||
subscriptionsJsonArray.put( | ||
CommerceSubscriptionJsonSerializer.serialize(subscription)); | ||
} | ||
|
||
JSONObject subscriptionsObject = new JSONObject(); | ||
subscriptionsObject.put(SUBSCRIPTIONS_KEY, subscriptionsJsonArray); | ||
|
||
container.put(CREATE_SUBSCRIPTIONS_REQUEST_PARAMS_KEY, subscriptionsObject); | ||
} catch (JSONException e) { | ||
Log.e(TAG, | ||
String.format(Locale.US, | ||
"Failed to create CreateSubscriptionRequestParams. Details: %s", | ||
e.getMessage())); | ||
} | ||
|
||
return container; | ||
} | ||
|
||
private JSONObject getRemoveSubscriptionsRequestParams( | ||
List<CommerceSubscription> subscriptions) { | ||
JSONObject container = new JSONObject(); | ||
try { | ||
JSONObject removeSubscriptionsParamsJson = new JSONObject(); | ||
JSONArray subscriptionsTimestamps = new JSONArray(); | ||
for (CommerceSubscription subscription : subscriptions) { | ||
if (subscription.getTimestamp() == CommerceSubscription.UNSAVED_SUBSCRIPTION) { | ||
continue; | ||
} | ||
subscriptionsTimestamps.put(subscription.getTimestamp()); | ||
} | ||
removeSubscriptionsParamsJson.put(EVENT_TIMESTAMP_MICROS_KEY, subscriptionsTimestamps); | ||
container.put(REMOVE_SUBSCRIPTIONS_REQUEST_PARAMS_KEY, removeSubscriptionsParamsJson); | ||
} catch (JSONException e) { | ||
Log.e(TAG, | ||
String.format(Locale.US, | ||
"Failed to create RemoveSubscriptionsRequestParams. Details: %s", | ||
e.getMessage())); | ||
} | ||
|
||
return container; | ||
} | ||
|
||
private List<CommerceSubscription> createCommerceSubscriptions(String responseString) { | ||
List<CommerceSubscription> subscriptions = new ArrayList<>(); | ||
try { | ||
JSONObject response = new JSONObject(responseString); | ||
JSONArray subscriptionsJsonArray = response.getJSONArray(SUBSCRIPTIONS_KEY); | ||
|
||
for (int i = 0; i < subscriptionsJsonArray.length(); i++) { | ||
JSONObject subscriptionJson = subscriptionsJsonArray.getJSONObject(i); | ||
CommerceSubscription subscription = | ||
CommerceSubscriptionJsonSerializer.deserialize(subscriptionJson); | ||
if (subscription != null) { | ||
subscriptions.add(subscription); | ||
} | ||
} | ||
} catch (JSONException e) { | ||
Log.e(TAG, | ||
String.format(Locale.US, | ||
"Failed to deserialize Subscriptions list. Details: %s", | ||
e.getMessage())); | ||
} | ||
|
||
return subscriptions; | ||
} | ||
} |
Oops, something went wrong.