blob: 3080571bd99fb770f3f88670496b3b7e18d125ad [file] [log] [blame]
// Copyright 2022 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.
#include "components/commerce/core/subscriptions/subscriptions_manager.h"
#include "components/commerce/core/commerce_feature_list.h"
#include "components/commerce/core/subscriptions/commerce_subscription.h"
#include "components/commerce/core/subscriptions/subscriptions_server_proxy.h"
#include "components/commerce/core/subscriptions/subscriptions_storage.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include <queue>
#include <string>
namespace commerce {
SubscriptionsManager::SubscriptionsManager(
signin::IdentityManager* identity_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: SubscriptionsManager(identity_manager,
std::make_unique<SubscriptionsServerProxy>(
identity_manager,
std::move(url_loader_factory)),
std::make_unique<SubscriptionsStorage>()) {}
SubscriptionsManager::SubscriptionsManager(
signin::IdentityManager* identity_manager,
std::unique_ptr<SubscriptionsServerProxy> server_proxy,
std::unique_ptr<SubscriptionsStorage> storage)
: server_proxy_(std::move(server_proxy)),
storage_(std::move(storage)),
weak_ptr_factory_(this) {
// Avoid duplicate server calls on android. Remove this after we integrate
// android implementation to shopping service.
#if !BUILDFLAG(IS_ANDROID)
InitSubscriptions();
scoped_identity_manager_observation_.Observe(identity_manager);
#endif // !BUILDFLAG(IS_ANDROID)
}
SubscriptionsManager::~SubscriptionsManager() = default;
SubscriptionsManager::Request::Request(SubscriptionType type,
AsyncOperation operation,
base::OnceCallback<void(bool)> callback)
: type(type), operation(operation), callback(std::move(callback)) {
CHECK(operation == AsyncOperation::kInit);
}
SubscriptionsManager::Request::Request(
SubscriptionType type,
AsyncOperation operation,
std::unique_ptr<std::vector<CommerceSubscription>> subscriptions,
base::OnceCallback<void(bool)> callback)
: type(type),
operation(operation),
subscriptions(std::move(subscriptions)),
callback(std::move(callback)) {
CHECK(operation == AsyncOperation::kSubscribe ||
operation == AsyncOperation::kUnsubscribe);
}
SubscriptionsManager::Request::Request(Request&&) = default;
SubscriptionsManager::Request::~Request() = default;
void SubscriptionsManager::Subscribe(
std::unique_ptr<std::vector<CommerceSubscription>> subscriptions,
base::OnceCallback<void(bool)> callback) {
SubscriptionType type = (*subscriptions)[0].type;
pending_requests_.push(
Request(type, AsyncOperation::kSubscribe, std::move(subscriptions),
base::BindOnce(
[](base::WeakPtr<SubscriptionsManager> manager,
base::OnceCallback<void(bool)> callback, bool result) {
std::move(callback).Run(result);
manager->OnRequestCompletion();
},
weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
CheckAndProcessRequest();
}
void SubscriptionsManager::Unsubscribe(
std::unique_ptr<std::vector<CommerceSubscription>> subscriptions,
base::OnceCallback<void(bool)> callback) {
SubscriptionType type = (*subscriptions)[0].type;
pending_requests_.push(
Request(type, AsyncOperation::kUnsubscribe, std::move(subscriptions),
base::BindOnce(
[](base::WeakPtr<SubscriptionsManager> manager,
base::OnceCallback<void(bool)> callback, bool result) {
std::move(callback).Run(result);
manager->OnRequestCompletion();
},
weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
CheckAndProcessRequest();
}
void SubscriptionsManager::InitSubscriptions() {
init_succeeded_ = false;
storage_->DeleteAll();
if (base::FeatureList::IsEnabled(commerce::kShoppingList)) {
pending_requests_.push(Request(
SubscriptionType::kPriceTrack, AsyncOperation::kInit,
base::BindOnce(
[](base::WeakPtr<SubscriptionsManager> manager, bool result) {
manager->init_succeeded_ = result;
manager->OnRequestCompletion();
},
weak_ptr_factory_.GetWeakPtr())));
}
CheckAndProcessRequest();
}
void SubscriptionsManager::CheckAndProcessRequest() {
if (has_request_running_ || pending_requests_.empty())
return;
// If there is no request running, we can start processing next request in the
// queue.
has_request_running_ = true;
Request request = std::move(pending_requests_.front());
pending_requests_.pop();
CHECK(request.type != SubscriptionType::kTypeUnspecified);
switch (request.operation) {
case AsyncOperation::kInit:
ProcessInitRequest(std::move(request));
break;
case AsyncOperation::kSubscribe:
ProcessSubscribeRequest(std::move(request));
break;
case AsyncOperation::kUnsubscribe:
ProcessUnsubscribeRequest(std::move(request));
break;
}
}
void SubscriptionsManager::OnRequestCompletion() {
has_request_running_ = false;
CheckAndProcessRequest();
}
void SubscriptionsManager::ProcessInitRequest(Request request) {
GetRemoteSubscriptionsAndUpdateStorage(request.type,
std::move(request.callback));
};
void SubscriptionsManager::ProcessSubscribeRequest(Request request) {
if (!init_succeeded_) {
std::move(request.callback).Run(false);
return;
}
storage_->GetUniqueNonExistingSubscriptions(
std::move(request.subscriptions),
base::BindOnce(
[](base::WeakPtr<SubscriptionsManager> manager, SubscriptionType type,
base::OnceCallback<void(bool)> callback,
std::unique_ptr<std::vector<CommerceSubscription>>
unique_subscriptions) {
manager->server_proxy_->Create(
std::move(unique_subscriptions),
base::BindOnce(
&SubscriptionsManager::HandleManageSubscriptionsResponse,
manager, type, std::move(callback)));
},
weak_ptr_factory_.GetWeakPtr(), request.type,
std::move(request.callback)));
}
void SubscriptionsManager::ProcessUnsubscribeRequest(Request request) {
if (!init_succeeded_) {
std::move(request.callback).Run(false);
return;
}
storage_->GetUniqueExistingSubscriptions(
std::move(request.subscriptions),
base::BindOnce(
[](base::WeakPtr<SubscriptionsManager> manager, SubscriptionType type,
base::OnceCallback<void(bool)> callback,
std::unique_ptr<std::vector<CommerceSubscription>>
unique_subscriptions) {
manager->server_proxy_->Delete(
std::move(unique_subscriptions),
base::BindOnce(
&SubscriptionsManager::HandleManageSubscriptionsResponse,
manager, type, std::move(callback)));
},
weak_ptr_factory_.GetWeakPtr(), request.type,
std::move(request.callback)));
}
void SubscriptionsManager::GetRemoteSubscriptionsAndUpdateStorage(
SubscriptionType type,
base::OnceCallback<void(bool)> callback) {
server_proxy_->Get(type, base::BindOnce(&SubscriptionsStorage::UpdateStorage,
base::Unretained(storage_.get()),
type, std::move(callback)));
}
void SubscriptionsManager::HandleManageSubscriptionsResponse(
SubscriptionType type,
base::OnceCallback<void(bool)> callback,
bool succeeded) {
if (!succeeded) {
VLOG(1) << "Fail to create or delete subscriptions on server";
std::move(callback).Run(false);
} else {
GetRemoteSubscriptionsAndUpdateStorage(type, std::move(callback));
}
}
void SubscriptionsManager::OnPrimaryAccountChanged(
const signin::PrimaryAccountChangeEvent& event_details) {
InitSubscriptions();
}
bool SubscriptionsManager::GetInitSucceededForTesting() {
return init_succeeded_;
}
void SubscriptionsManager::SetHasRequestRunningForTesting(
bool has_request_running) {
has_request_running_ = has_request_running;
}
bool SubscriptionsManager::HasPendingRequestsForTesting() {
return !pending_requests_.empty();
}
} // namespace commerce