chromium/components/desks_storage/core/desk_sync_bridge.h

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_DESKS_STORAGE_CORE_DESK_SYNC_BRIDGE_H_
#define COMPONENTS_DESKS_STORAGE_CORE_DESK_SYNC_BRIDGE_H_

#include <stddef.h>

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/uuid.h"
#include "components/account_id/account_id.h"
#include "components/desks_storage/core/desk_model.h"
#include "components/sync/base/data_type.h"
#include "components/sync/model/data_type_store.h"
#include "components/sync/model/data_type_sync_bridge.h"

namespace syncer {
class DataTypeLocalChangeProcessor;
}  // namespace syncer

namespace ash {
class DeskTemplate;
enum class DeskTemplateType;
}  // namespace ash

namespace desks_storage {

// A Sync-backed persistence layer for Workspace Desk.
class DeskSyncBridge : public syncer::DataTypeSyncBridge, public DeskModel {
 public:
  DeskSyncBridge(
      std::unique_ptr<syncer::DataTypeLocalChangeProcessor> change_processor,
      syncer::OnceDataTypeStoreFactory create_store_callback,
      const AccountId& account_id);
  DeskSyncBridge(const DeskSyncBridge&) = delete;
  DeskSyncBridge& operator=(const DeskSyncBridge&) = delete;
  ~DeskSyncBridge() override;

  // syncer::DataTypeSyncBridge overrides.
  std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
      override;
  std::optional<syncer::ModelError> MergeFullSyncData(
      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
      syncer::EntityChangeList entity_data) override;
  std::optional<syncer::ModelError> ApplyIncrementalSyncChanges(
      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
      syncer::EntityChangeList entity_changes) override;
  std::unique_ptr<syncer::DataBatch> GetDataForCommit(
      StorageKeyList storage_keys) override;
  std::unique_ptr<syncer::DataBatch> GetAllDataForDebugging() override;
  std::string GetClientTag(const syncer::EntityData& entity_data) override;
  std::string GetStorageKey(const syncer::EntityData& entity_data) override;

  // DeskModel overrides.
  DeskModel::GetAllEntriesResult GetAllEntries() override;
  DeskModel::GetEntryByUuidResult GetEntryByUUID(
      const base::Uuid& uuid) override;

  void AddOrUpdateEntry(std::unique_ptr<ash::DeskTemplate> new_entry,
                        AddOrUpdateEntryCallback callback) override;
  void DeleteEntry(const base::Uuid& uuid,
                   DeleteEntryCallback callback) override;
  void DeleteAllEntries(DeleteEntryCallback callback) override;
  size_t GetEntryCount() const override;
  size_t GetSaveAndRecallDeskEntryCount() const override;
  size_t GetDeskTemplateEntryCount() const override;
  size_t GetMaxSaveAndRecallDeskEntryCount() const override;
  size_t GetMaxDeskTemplateEntryCount() const override;
  std::set<base::Uuid> GetAllEntryUuids() const override;
  bool IsReady() const override;
  // Whether this sync bridge is syncing local data to sync. This sync bridge
  // still allows user to save desk templates locally when users disable syncing
  // for Workspace Desk data type.
  bool IsSyncing() const override;

  ash::DeskTemplate* FindOtherEntryWithName(
      const std::u16string& name,
      ash::DeskTemplateType type,
      const base::Uuid& uuid) const override;

  std::string GetCacheGuid() override;

  // Other helper methods.
  bool HasUuid(const base::Uuid& uuid) const;
  const ash::DeskTemplate* GetUserEntryByUUID(const base::Uuid& uuid) const;

 private:
  friend class DeskModelWrapper;

  using DeskEntries =
      base::flat_map<base::Uuid, std::unique_ptr<ash::DeskTemplate>>;

  DeskModel::DeleteEntryStatus DeleteAllEntriesSync();

  // Notify all observers that the model is loaded;
  void NotifyDeskModelLoaded();

  // Notify all observers of any `new_entries` when they are added/updated
  // via sync.
  void NotifyRemoteDeskTemplateAddedOrUpdated(
      const std::vector<raw_ptr<const ash::DeskTemplate, VectorExperimental>>&
          new_entries);

  // Notify all observers when the entries with `uuids` have been removed via
  // sync or disabling sync locally.
  void NotifyRemoteDeskTemplateDeleted(const std::vector<base::Uuid>& uuids);

  // Methods used as callbacks given to DataTypeStore.
  void OnStoreCreated(const std::optional<syncer::ModelError>& error,
                      std::unique_ptr<syncer::DataTypeStore> store);
  void OnReadAllData(std::unique_ptr<DeskEntries> initial_entries,
                     const std::optional<syncer::ModelError>& error);
  void OnReadAllMetadata(const std::optional<syncer::ModelError>& error,
                         std::unique_ptr<syncer::MetadataBatch> metadata_batch);
  void OnCommit(const std::optional<syncer::ModelError>& error);

  // Persists changes in sync store.
  void Commit(std::unique_ptr<syncer::DataTypeStore::WriteBatch> batch);

  // Uploads data that only exists locally to Sync during MergeFullSyncData().
  void UploadLocalOnlyData(syncer::MetadataChangeList* metadata_change_list,
                           const syncer::EntityChangeList& entity_data);

  // Returns true if `templates_` contains a desk template with `name`.
  bool HasUserTemplateWithName(const std::u16string& name);

  // `desk_template_entries_` is keyed by UUIDs.
  DeskEntries desk_template_entries_;

  // Whether local data and metadata have finished loading and this sync bridge
  // is ready to be accessed.
  bool is_ready_;

  // In charge of actually persisting changes to disk, or loading previous
  // data.
  std::unique_ptr<syncer::DataTypeStore> store_;

  // Account ID of the user this class will sync data for.
  const AccountId account_id_;

  base::WeakPtrFactory<DeskSyncBridge> weak_ptr_factory_{this};
};

}  // namespace desks_storage

#endif  // COMPONENTS_DESKS_STORAGE_CORE_DESK_SYNC_BRIDGE_H_