blob: 2aa6297aee2c3a796d15dba24d5fbde1e984d7dc [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "peridot/bin/cloud_provider_firestore/app/cloud_provider_impl.h"
#include <utility>
#include <lib/callback/scoped_callback.h>
#include <lib/fxl/logging.h>
#include <lib/fxl/strings/concatenate.h>
#include <lib/fxl/strings/string_view.h>
#include "peridot/bin/cloud_provider_firestore/app/credentials_provider_impl.h"
#include "peridot/bin/cloud_provider_firestore/app/grpc_status.h"
#include "peridot/bin/cloud_provider_firestore/firestore/encoding.h"
#include "peridot/bin/ledger/app/serialization_version.h"
#include "peridot/lib/convert/convert.h"
namespace cloud_provider_firestore {
constexpr char kSeparator[] = "/";
constexpr char kUsersCollection[] = "users";
constexpr char kVersionsCollection[] = "versions";
constexpr char kPageCollection[] = "pages";
constexpr char kNamespaceCollection[] = "namespaces";
constexpr char kExistsKey[] = "exists";
std::string GetUserPath(fxl::StringView root_path, fxl::StringView user_id) {
return fxl::Concatenate({
root_path,
kSeparator,
kUsersCollection,
kSeparator,
user_id,
});
}
std::string GetVersionPath(fxl::StringView user_path) {
return fxl::Concatenate({
user_path,
kSeparator,
kVersionsCollection,
kSeparator,
ledger::kSerializationVersion,
});
}
std::string GetNamespacePath(fxl::StringView version_path,
fxl::StringView namespace_id) {
std::string encoded_namespace_id = EncodeKey(namespace_id);
return fxl::Concatenate({version_path, kSeparator, kNamespaceCollection,
kSeparator, encoded_namespace_id});
}
std::string GetPagePath(fxl::StringView namespace_path,
fxl::StringView page_id) {
std::string encoded_page_id = EncodeKey(page_id);
return fxl::Concatenate({namespace_path, kSeparator, kPageCollection,
kSeparator, encoded_page_id});
}
CloudProviderImpl::CloudProviderImpl(
rng::Random* random, std::string user_id,
std::unique_ptr<firebase_auth::FirebaseAuth> firebase_auth,
std::unique_ptr<FirestoreService> firestore_service,
fidl::InterfaceRequest<cloud_provider::CloudProvider> request)
: random_(random),
user_id_(std::move(user_id)),
firestore_service_(std::move(firestore_service)),
binding_(this, std::move(request)),
weak_ptr_factory_(this) {
// The class shuts down when the client connection is disconnected.
binding_.set_error_handler(
[this](zx_status_t status) { ShutDownAndReportEmpty(); });
// The class also shuts down when the auth provider is disconnected.
firebase_auth->set_error_handler([this] {
FXL_LOG(ERROR) << "Lost connection to the token provider, "
<< "shutting down the cloud provider.";
ShutDownAndReportEmpty();
});
credentials_provider_ =
std::make_unique<CredentialsProviderImpl>(std::move(firebase_auth));
}
CloudProviderImpl::~CloudProviderImpl() {}
void CloudProviderImpl::ShutDownAndReportEmpty() {
if (binding_.is_bound()) {
binding_.Unbind();
}
fit::closure shut_down = [this] {
firestore_service_->ShutDown([this] {
if (on_empty_) {
on_empty_();
}
});
};
if (pending_placeholder_requests_.empty()) {
shut_down();
return;
}
pending_placeholder_requests_.set_on_empty(std::move(shut_down));
}
void CloudProviderImpl::ScopedGetCredentials(
fit::function<void(std::shared_ptr<grpc::CallCredentials>)> callback) {
credentials_provider_->GetCredentials(callback::MakeScoped(
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void CloudProviderImpl::GetDeviceSet(
fidl::InterfaceRequest<cloud_provider::DeviceSet> device_set,
GetDeviceSetCallback callback) {
const std::string user_path =
GetUserPath(firestore_service_->GetRootPath(), user_id_);
const std::string version_path = GetVersionPath(user_path);
device_sets_.emplace(version_path, credentials_provider_.get(),
firestore_service_.get(), std::move(device_set));
callback(cloud_provider::Status::OK);
// Create a placeholder document for the root of the serialization version.
CreatePlaceholderDocument(user_path, kVersionsCollection,
ledger::kSerializationVersion.ToString());
}
void CloudProviderImpl::GetPageCloud(
std::vector<uint8_t> app_id, std::vector<uint8_t> page_id,
fidl::InterfaceRequest<cloud_provider::PageCloud> page_cloud,
GetPageCloudCallback callback) {
const std::string user_path =
GetUserPath(firestore_service_->GetRootPath(), user_id_);
const std::string version_path = GetVersionPath(user_path);
const std::string app_id_str = convert::ToString(app_id);
const std::string namespace_path = GetNamespacePath(version_path, app_id_str);
const std::string page_id_str = convert::ToString(page_id);
const std::string page_path = GetPagePath(namespace_path, page_id_str);
page_clouds_.emplace(page_path, random_, credentials_provider_.get(),
firestore_service_.get(), std::move(page_cloud));
callback(cloud_provider::Status::OK);
// Create a placeholder document for the root of the serialization version.
CreatePlaceholderDocument(user_path, kVersionsCollection,
ledger::kSerializationVersion.ToString());
// Create a placeholder document for the root of the app namespace.
CreatePlaceholderDocument(version_path, kNamespaceCollection,
EncodeKey(app_id_str));
// Create a placeholder document for the root of the page.
CreatePlaceholderDocument(namespace_path, kPageCollection,
EncodeKey(page_id_str));
}
void CloudProviderImpl::CreatePlaceholderDocument(
std::string parent_document_path, std::string collection_id,
std::string document_id) {
auto request = google::firestore::v1beta1::CreateDocumentRequest();
request.set_parent(std::move(parent_document_path));
request.set_collection_id(std::move(collection_id));
request.set_document_id(std::move(document_id));
google::firestore::v1beta1::Value exists;
exists.set_boolean_value(true);
(*(request.mutable_document()->mutable_fields()))[kExistsKey] = exists;
// Create an object that tracks the request in progress, so that we don't shut
// down between requesting and receiving the credentials (see
// ShutDownAndReportEmoty()). The value |true| is not meaningful.
auto pending_request_marker = pending_placeholder_requests_.Manage(true);
ScopedGetCredentials(
[this, request = std::move(request),
pending_request_marker =
std::move(pending_request_marker)](auto call_credentials) mutable {
firestore_service_->CreateDocument(
std::move(request), std::move(call_credentials),
[](auto status, auto result) {
if (status.error_code() != grpc::OK &&
status.error_code() != grpc::ALREADY_EXISTS) {
LogGrpcRequestError(status);
}
});
});
}
} // namespace cloud_provider_firestore