Commit 3fda733e authored by Rushan Suleymanov's avatar Rushan Suleymanov Committed by Commit Bot

[Sync] Add callbacks for sharing messages.

Extend the SharingMessageBridge interface with callbacks on each commit
event (successful or failed).

Bug: 1034930
Change-Id: Ibf90895eaf914edead685791944c48b30be12026
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2013285
Commit-Queue: Rushan Suleymanov <rushans@google.com>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Reviewed-by: default avatarAlex Chau <alexchau@chromium.org>
Reviewed-by: default avatarvitaliii <vitaliii@chromium.org>
Cr-Commit-Position: refs/heads/master@{#734479}
parent 58f2c7f2
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "base/callback.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
#include "components/sync/protocol/sharing_message_specifics.pb.h" #include "components/sync/protocol/sharing_message_specifics.pb.h"
...@@ -18,13 +19,17 @@ class ModelTypeControllerDelegate; ...@@ -18,13 +19,17 @@ class ModelTypeControllerDelegate;
// Class to provide an interface to send sharing messages using Sync. // Class to provide an interface to send sharing messages using Sync.
class SharingMessageBridge : public KeyedService { class SharingMessageBridge : public KeyedService {
public: public:
// TODO(crbug.com/1034930): take callbacks once commit error propagation back using CommitFinishedCallback =
// to the bridge is implemented. base::OnceCallback<void(const sync_pb::SharingMessageCommitError&)>;
// Sends Sharing Message to Sync server. |on_commit_callback| will be called
// when commit attempt finishes (either successfully or unsuccessfully).
// TODO(crbug.com/1034932): take each parameter separately and construct // TODO(crbug.com/1034932): take each parameter separately and construct
// specifics inside. Currently this method updates given |specifics| and // specifics inside. Currently this method updates given |specifics| and
// fills in |message_id| field. // fills in |message_id| field.
virtual void SendSharingMessage( virtual void SendSharingMessage(
std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) = 0; std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics,
CommitFinishedCallback on_commit_callback) = 0;
// Returns the delegate for the controller, i.e. sync integration point. // Returns the delegate for the controller, i.e. sync integration point.
virtual base::WeakPtr<syncer::ModelTypeControllerDelegate> virtual base::WeakPtr<syncer::ModelTypeControllerDelegate>
......
...@@ -11,10 +11,18 @@ ...@@ -11,10 +11,18 @@
namespace { namespace {
syncer::ClientTagHash GetClientTagHashFromStorageKey(
const std::string& storage_key) {
return syncer::ClientTagHash::FromUnhashed(syncer::SHARING_MESSAGE,
storage_key);
}
std::unique_ptr<syncer::EntityData> MoveToEntityData( std::unique_ptr<syncer::EntityData> MoveToEntityData(
std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) { std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) {
auto entity_data = std::make_unique<syncer::EntityData>(); auto entity_data = std::make_unique<syncer::EntityData>();
entity_data->name = specifics->message_id(); entity_data->name = specifics->message_id();
entity_data->client_tag_hash =
GetClientTagHashFromStorageKey(specifics->message_id());
entity_data->specifics.set_allocated_sharing_message(specifics.release()); entity_data->specifics.set_allocated_sharing_message(specifics.release());
return entity_data; return entity_data;
} }
...@@ -33,7 +41,8 @@ SharingMessageBridgeImpl::SharingMessageBridgeImpl( ...@@ -33,7 +41,8 @@ SharingMessageBridgeImpl::SharingMessageBridgeImpl(
SharingMessageBridgeImpl::~SharingMessageBridgeImpl() = default; SharingMessageBridgeImpl::~SharingMessageBridgeImpl() = default;
void SharingMessageBridgeImpl::SendSharingMessage( void SharingMessageBridgeImpl::SendSharingMessage(
std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) { std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics,
CommitFinishedCallback on_commit_callback) {
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list = std::unique_ptr<syncer::MetadataChangeList> metadata_change_list =
CreateMetadataChangeList(); CreateMetadataChangeList();
// Fill in the internal message id with unique generated identifier. // Fill in the internal message id with unique generated identifier.
...@@ -41,6 +50,10 @@ void SharingMessageBridgeImpl::SendSharingMessage( ...@@ -41,6 +50,10 @@ void SharingMessageBridgeImpl::SendSharingMessage(
specifics->set_message_id(message_id); specifics->set_message_id(message_id);
std::unique_ptr<syncer::EntityData> entity_data = std::unique_ptr<syncer::EntityData> entity_data =
MoveToEntityData(std::move(specifics)); MoveToEntityData(std::move(specifics));
const auto result =
commit_callbacks_.emplace(GetClientTagHashFromStorageKey(message_id),
std::move(on_commit_callback));
DCHECK(result.second);
change_processor()->Put(message_id, std::move(entity_data), change_processor()->Put(message_id, std::move(entity_data),
metadata_change_list.get()); metadata_change_list.get());
} }
...@@ -71,9 +84,17 @@ base::Optional<syncer::ModelError> SharingMessageBridgeImpl::MergeSyncData( ...@@ -71,9 +84,17 @@ base::Optional<syncer::ModelError> SharingMessageBridgeImpl::MergeSyncData(
base::Optional<syncer::ModelError> SharingMessageBridgeImpl::ApplySyncChanges( base::Optional<syncer::ModelError> SharingMessageBridgeImpl::ApplySyncChanges(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_data) { syncer::EntityChangeList entity_changes) {
// This data type is commit only and does not store any data in persistent sync_pb::SharingMessageCommitError no_error_message;
// storage. We can ignore any data coming from the server. no_error_message.set_error_code(sync_pb::SharingMessageCommitError::NONE);
for (const std::unique_ptr<syncer::EntityChange>& change : entity_changes) {
// For commit-only data type we expect only |ACTION_DELETE| changes.
DCHECK_EQ(syncer::EntityChange::ACTION_DELETE, change->type());
const syncer::ClientTagHash client_tag_hash =
GetClientTagHashFromStorageKey(change->storage_key());
ProcessCommitResponse(client_tag_hash, no_error_message);
}
return {}; return {};
} }
...@@ -98,3 +119,27 @@ std::string SharingMessageBridgeImpl::GetStorageKey( ...@@ -98,3 +119,27 @@ std::string SharingMessageBridgeImpl::GetStorageKey(
DCHECK(entity_data.specifics.has_sharing_message()); DCHECK(entity_data.specifics.has_sharing_message());
return entity_data.specifics.sharing_message().message_id(); return entity_data.specifics.sharing_message().message_id();
} }
void SharingMessageBridgeImpl::OnCommitAttemptErrors(
const syncer::FailedCommitResponseDataList& error_response_list) {
for (const syncer::FailedCommitResponseData& response : error_response_list) {
// TODO(rushans): untrack entity in change processor on error. We cannot
// untrack it by only client tag hash and there is no storage key in
// response data.
ProcessCommitResponse(response.client_tag_hash,
response.sharing_message_error);
}
}
void SharingMessageBridgeImpl::ProcessCommitResponse(
const syncer::ClientTagHash& client_tag_hash,
const sync_pb::SharingMessageCommitError& commit_error_message) {
const auto iter = commit_callbacks_.find(client_tag_hash);
if (iter == commit_callbacks_.end()) {
// TODO(crbug.com/1034930): mark as NOTREACHABLE() when the entity will be
// untracked on commit errors.
return;
}
std::move(iter->second).Run(commit_error_message);
commit_callbacks_.erase(iter);
}
...@@ -25,7 +25,8 @@ class SharingMessageBridgeImpl : public SharingMessageBridge, ...@@ -25,7 +25,8 @@ class SharingMessageBridgeImpl : public SharingMessageBridge,
// SharingMessageBridge implementation. // SharingMessageBridge implementation.
void SendSharingMessage( void SendSharingMessage(
std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics) override; std::unique_ptr<sync_pb::SharingMessageSpecifics> specifics,
CommitFinishedCallback on_commit_callback) override;
base::WeakPtr<syncer::ModelTypeControllerDelegate> GetControllerDelegate() base::WeakPtr<syncer::ModelTypeControllerDelegate> GetControllerDelegate()
override; override;
...@@ -42,6 +43,21 @@ class SharingMessageBridgeImpl : public SharingMessageBridge, ...@@ -42,6 +43,21 @@ class SharingMessageBridgeImpl : public SharingMessageBridge,
void GetAllDataForDebugging(DataCallback callback) override; void GetAllDataForDebugging(DataCallback callback) override;
std::string GetClientTag(const syncer::EntityData& entity_data) override; std::string GetClientTag(const syncer::EntityData& entity_data) override;
std::string GetStorageKey(const syncer::EntityData& entity_data) override; std::string GetStorageKey(const syncer::EntityData& entity_data) override;
void OnCommitAttemptErrors(
const syncer::FailedCommitResponseDataList& error_response_list) override;
size_t GetCallbacksCountForTesting() const {
return commit_callbacks_.size();
}
private:
// Sends commit outcome via callback for |client_tag_hash| and removes it from
// callbacks mapping.
void ProcessCommitResponse(
const syncer::ClientTagHash& client_tag_hash,
const sync_pb::SharingMessageCommitError& commit_error);
std::map<syncer::ClientTagHash, CommitFinishedCallback> commit_callbacks_;
}; };
#endif // CHROME_BROWSER_SHARING_SHARING_MESSAGE_BRIDGE_IMPL_H_ #endif // CHROME_BROWSER_SHARING_SHARING_MESSAGE_BRIDGE_IMPL_H_
...@@ -4,9 +4,12 @@ ...@@ -4,9 +4,12 @@
#include "chrome/browser/sharing/sharing_message_bridge_impl.h" #include "chrome/browser/sharing/sharing_message_bridge_impl.h"
#include "base/bind_helpers.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "components/sync/model/metadata_batch.h" #include "components/sync/model/metadata_batch.h"
#include "components/sync/model/metadata_change_list.h"
#include "components/sync/model/mock_model_type_change_processor.h" #include "components/sync/model/mock_model_type_change_processor.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -56,13 +59,15 @@ TEST_F(SharingMessageBridgeTest, ShouldWriteMessagesToProcessor) { ...@@ -56,13 +59,15 @@ TEST_F(SharingMessageBridgeTest, ShouldWriteMessagesToProcessor) {
syncer::EntityData entity_data; syncer::EntityData entity_data;
EXPECT_CALL(*processor(), Put(_, _, _)) EXPECT_CALL(*processor(), Put(_, _, _))
.WillRepeatedly(SaveArgPointeeMove<1>(&entity_data)); .WillRepeatedly(SaveArgPointeeMove<1>(&entity_data));
bridge()->SendSharingMessage(CreateSpecifics("test_payload")); bridge()->SendSharingMessage(CreateSpecifics("test_payload"),
base::DoNothing());
EXPECT_TRUE(entity_data.specifics.has_sharing_message()); EXPECT_TRUE(entity_data.specifics.has_sharing_message());
EXPECT_EQ(entity_data.specifics.sharing_message().payload(), "test_payload"); EXPECT_EQ(entity_data.specifics.sharing_message().payload(), "test_payload");
entity_data.specifics.Clear(); entity_data.specifics.Clear();
bridge()->SendSharingMessage(CreateSpecifics("another_payload")); bridge()->SendSharingMessage(CreateSpecifics("another_payload"),
base::DoNothing());
EXPECT_TRUE(entity_data.specifics.has_sharing_message()); EXPECT_TRUE(entity_data.specifics.has_sharing_message());
EXPECT_EQ(entity_data.specifics.sharing_message().payload(), EXPECT_EQ(entity_data.specifics.sharing_message().payload(),
...@@ -76,12 +81,69 @@ TEST_F(SharingMessageBridgeTest, ShouldGenerateUniqueStorageKey) { ...@@ -76,12 +81,69 @@ TEST_F(SharingMessageBridgeTest, ShouldGenerateUniqueStorageKey) {
EXPECT_CALL(*processor(), Put(_, _, _)) EXPECT_CALL(*processor(), Put(_, _, _))
.WillOnce(SaveArg<0>(&first_storage_key)) .WillOnce(SaveArg<0>(&first_storage_key))
.WillOnce(SaveArg<0>(&second_storage_key)); .WillOnce(SaveArg<0>(&second_storage_key));
bridge()->SendSharingMessage(CreateSpecifics("payload")); bridge()->SendSharingMessage(CreateSpecifics("payload"), base::DoNothing());
bridge()->SendSharingMessage(CreateSpecifics("another_payload")); bridge()->SendSharingMessage(CreateSpecifics("another_payload"),
base::DoNothing());
EXPECT_FALSE(first_storage_key.empty()); EXPECT_FALSE(first_storage_key.empty());
EXPECT_FALSE(second_storage_key.empty()); EXPECT_FALSE(second_storage_key.empty());
EXPECT_NE(first_storage_key, second_storage_key); EXPECT_NE(first_storage_key, second_storage_key);
} }
TEST_F(SharingMessageBridgeTest, ShouldInvokeCallbackOnSuccess) {
std::string storage_key;
EXPECT_CALL(*processor(), Put(_, _, _)).WillOnce(SaveArg<0>(&storage_key));
base::MockCallback<SharingMessageBridge::CommitFinishedCallback> callback;
sync_pb::SharingMessageCommitError commit_error;
EXPECT_CALL(callback, Run).WillOnce(SaveArg<0>(&commit_error));
bridge()->SendSharingMessage(CreateSpecifics("payload"), callback.Get());
// The callback should be called only after committing data.
EXPECT_FALSE(commit_error.has_error_code());
// Mark data as committed.
syncer::EntityChangeList change_list;
change_list.push_back(syncer::EntityChange::CreateDelete(storage_key));
bridge()->ApplySyncChanges(nullptr, std::move(change_list));
EXPECT_TRUE(commit_error.has_error_code());
EXPECT_EQ(commit_error.error_code(),
sync_pb::SharingMessageCommitError::NONE);
EXPECT_EQ(bridge()->GetCallbacksCountForTesting(), 0u);
}
TEST_F(SharingMessageBridgeTest, ShouldInvokeCallbackOnFailure) {
syncer::EntityData entity_data;
EXPECT_CALL(*processor(), Put(_, _, _))
.WillRepeatedly(SaveArgPointeeMove<1>(&entity_data));
base::MockCallback<SharingMessageBridge::CommitFinishedCallback> callback;
sync_pb::SharingMessageCommitError commit_error;
EXPECT_CALL(callback, Run).WillOnce(SaveArg<0>(&commit_error));
bridge()->SendSharingMessage(CreateSpecifics("payload"), callback.Get());
EXPECT_FALSE(entity_data.client_tag_hash.value().empty());
// The callback should be called only after committing data.
EXPECT_FALSE(commit_error.has_error_code());
syncer::FailedCommitResponseDataList response_list;
{
syncer::FailedCommitResponseData response;
response.client_tag_hash = entity_data.client_tag_hash;
response.sharing_message_error.set_error_code(
sync_pb::SharingMessageCommitError::PERMISSION_DENIED);
response_list.push_back(std::move(response));
}
bridge()->OnCommitAttemptErrors(response_list);
EXPECT_TRUE(commit_error.has_error_code());
EXPECT_EQ(commit_error.error_code(),
sync_pb::SharingMessageCommitError::PERMISSION_DENIED);
EXPECT_EQ(bridge()->GetCallbacksCountForTesting(), 0u);
}
} // namespace } // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment