blob: 3837c6d2fd0319233d280f607729fa55edc8fa50 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/data_sharing/internal/collaboration_group_sync_bridge.h"
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/notimplemented.h"
#include "base/sequence_checker.h"
#include "components/sync/base/model_type.h"
#include "components/sync/model/in_memory_metadata_change_list.h"
#include "components/sync/model/model_type_sync_bridge.h"
#include "components/sync/model/mutable_data_batch.h"
#include "components/sync/protocol/collaboration_group_specifics.pb.h"
#include "components/sync/protocol/entity_data.h"
#include "components/sync/protocol/entity_specifics.pb.h"
namespace data_sharing {
namespace {
std::unique_ptr<syncer::EntityData> SpecificsToEntityData(
const sync_pb::CollaborationGroupSpecifics& specifics) {
auto entity_data = std::make_unique<syncer::EntityData>();
*entity_data->specifics.mutable_collaboration_group() = specifics;
entity_data->name = specifics.collaboration_id();
return entity_data;
}
} // namespace
CollaborationGroupSyncBridge::CollaborationGroupSyncBridge(
std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
syncer::OnceModelTypeStoreFactory store_factory)
: syncer::ModelTypeSyncBridge(std::move(change_processor)) {
std::move(store_factory)
.Run(
syncer::COLLABORATION_GROUP,
base::BindOnce(&CollaborationGroupSyncBridge::OnModelTypeStoreCreated,
weak_ptr_factory_.GetWeakPtr()));
}
CollaborationGroupSyncBridge::~CollaborationGroupSyncBridge() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
std::unique_ptr<syncer::MetadataChangeList>
CollaborationGroupSyncBridge::CreateMetadataChangeList() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return std::make_unique<syncer::InMemoryMetadataChangeList>();
}
std::optional<syncer::ModelError>
CollaborationGroupSyncBridge::MergeFullSyncData(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_change_list) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(ids_to_specifics_.empty());
// This is a read-only data type, meaning that no data originates locally,
// hence there is nothing to merge.
return ApplyIncrementalSyncChanges(std::move(metadata_change_list),
std::move(entity_change_list));
}
std::optional<syncer::ModelError>
CollaborationGroupSyncBridge::ApplyIncrementalSyncChanges(
std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
syncer::EntityChangeList entity_changes) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::unique_ptr<syncer::ModelTypeStore::WriteBatch> batch =
model_type_store_->CreateWriteBatch();
for (const std::unique_ptr<syncer::EntityChange>& change : entity_changes) {
const std::string& collaboration_id = change->storage_key();
switch (change->type()) {
case syncer::EntityChange::ACTION_ADD:
case syncer::EntityChange::ACTION_UPDATE: {
const sync_pb::EntitySpecifics& entity_specifics =
change->data().specifics;
// Guaranteed by ClientTagBasedModelTypeProcessor, based on
// IsEntityDataValid().
CHECK(entity_specifics.has_collaboration_group());
const sync_pb::CollaborationGroupSpecifics collaboration_specifics =
entity_specifics.collaboration_group();
ids_to_specifics_[collaboration_id] = collaboration_specifics;
batch->WriteData(collaboration_id,
collaboration_specifics.SerializeAsString());
break;
}
case syncer::EntityChange::ACTION_DELETE:
ids_to_specifics_.erase(collaboration_id);
batch->DeleteData(collaboration_id);
break;
}
}
batch->TakeMetadataChangesFrom(std::move(metadata_change_list));
model_type_store_->CommitWriteBatch(
std::move(batch),
base::BindOnce(&CollaborationGroupSyncBridge::OnModelTypeStoreCommit,
weak_ptr_factory_.GetWeakPtr()));
// TODO(crbug.com/301389859): introduce observing mechanism and notify
// observers here.
return std::nullopt;
}
void CollaborationGroupSyncBridge::GetData(StorageKeyList storage_keys,
DataCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(crbug.com/301389859): GetData() actually not used for read-only
// datatypes and could be NOTREACHED(). However, it is not reflected in the
// name/documentation. Rename to something like GetDataForCommit() and replace
// NOTIMPLEMENTED() with NOTREACHED().
NOTIMPLEMENTED();
}
void CollaborationGroupSyncBridge::GetAllDataForDebugging(
DataCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto batch = std::make_unique<syncer::MutableDataBatch>();
for (const auto& [id, specifics] : ids_to_specifics_) {
batch->Put(id, SpecificsToEntityData(specifics));
}
std::move(callback).Run(std::move(batch));
}
std::string CollaborationGroupSyncBridge::GetClientTag(
const syncer::EntityData& entity_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return GetStorageKey(entity_data);
}
std::string CollaborationGroupSyncBridge::GetStorageKey(
const syncer::EntityData& entity_data) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(entity_data.specifics.has_collaboration_group());
return entity_data.specifics.collaboration_group().collaboration_id();
}
void CollaborationGroupSyncBridge::ApplyDisableSyncChanges(
std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ids_to_specifics_.clear();
model_type_store_->DeleteAllDataAndMetadata(base::DoNothing());
weak_ptr_factory_.InvalidateWeakPtrs();
}
bool CollaborationGroupSyncBridge::IsEntityDataValid(
const syncer::EntityData& entity_data) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return !entity_data.specifics.collaboration_group()
.collaboration_id()
.empty();
}
void CollaborationGroupSyncBridge::OnModelTypeStoreCreated(
const std::optional<syncer::ModelError>& error,
std::unique_ptr<syncer::ModelTypeStore> store) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error) {
change_processor()->ReportError(*error);
return;
}
model_type_store_ = std::move(store);
model_type_store_->ReadAllData(
base::BindOnce(&CollaborationGroupSyncBridge::OnReadAllData,
weak_ptr_factory_.GetWeakPtr()));
}
void CollaborationGroupSyncBridge::OnReadAllData(
const std::optional<syncer::ModelError>& error,
std::unique_ptr<syncer::ModelTypeStore::RecordList> record_list) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error) {
change_processor()->ReportError(*error);
return;
}
for (const auto& record : *record_list) {
sync_pb::CollaborationGroupSpecifics specifics;
if (!specifics.ParseFromString(record.value)) {
change_processor()->ReportError(
{FROM_HERE, "Failed to deserialize database record as specifics."});
return;
}
ids_to_specifics_[specifics.collaboration_id()] = std::move(specifics);
}
// TODO(crbug.com/301389859): introduce observing mechanism and notify
// observers here.
model_type_store_->ReadAllMetadata(
base::BindOnce(&CollaborationGroupSyncBridge::OnReadAllMetadata,
weak_ptr_factory_.GetWeakPtr()));
}
void CollaborationGroupSyncBridge::OnReadAllMetadata(
const std::optional<syncer::ModelError>& error,
std::unique_ptr<syncer::MetadataBatch> metadata_batch) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error) {
change_processor()->ReportError(*error);
return;
}
change_processor()->ModelReadyToSync(std::move(metadata_batch));
}
void CollaborationGroupSyncBridge::OnModelTypeStoreCommit(
const std::optional<syncer::ModelError>& error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (error) {
change_processor()->ReportError(*error);
}
}
} // namespace data_sharing