diff --git a/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.cpp b/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.cpp index 2e08cb71ef3e..ad90047198f8 100644 --- a/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.cpp +++ b/packages/cloud_firestore/cloud_firestore/windows/cloud_firestore_plugin.cpp @@ -15,6 +15,11 @@ #include #include +// NOVO: Includes para logging em arquivo +#include +#include +#include +// FIM NOVO #include #include #include @@ -39,1708 +44,1783 @@ using flutter::EncodableValue; namespace cloud_firestore_windows { -static std::string kLibraryName = "flutter-fire-fst"; -// static -void CloudFirestorePlugin::RegisterWithRegistrar( - flutter::PluginRegistrarWindows* registrar) { - auto channel = - std::make_unique>( - registrar->messenger(), "cloud_firestore", - &flutter::StandardMethodCodec::GetInstance()); - - auto plugin = std::make_unique(); - - messenger_ = registrar->messenger(); - - FirebaseFirestoreHostApi::SetUp(registrar->messenger(), plugin.get()); - - registrar->AddPlugin(std::move(plugin)); - - // Register for platform logging - App::RegisterLibrary(kLibraryName.c_str(), getPluginVersion().c_str(), - nullptr); -} - -firebase::firestore::FieldValue CloudFirestorePlugin::ConvertToFieldValue( - const flutter::EncodableValue& variant) { - if (std::holds_alternative(variant)) { - return firebase::firestore::FieldValue::Null(); - } else if (std::holds_alternative(variant)) { - return firebase::firestore::FieldValue::Boolean(std::get(variant)); - } else if (std::holds_alternative(variant)) { - return firebase::firestore::FieldValue::Integer(std::get(variant)); - } else if (std::holds_alternative(variant)) { - return firebase::firestore::FieldValue::Integer(std::get(variant)); - } else if (std::holds_alternative(variant)) { - return firebase::firestore::FieldValue::Double(std::get(variant)); - } else if (std::holds_alternative(variant)) { - return firebase::firestore::FieldValue::String( - std::get(variant)); - } else if (std::holds_alternative(variant)) { - const flutter::EncodableList& list = - std::get(variant); - std::vector convertedList; - for (const auto& item : list) { - convertedList.push_back(ConvertToFieldValue(item)); + void LogToFile(const std::string& message) { + // `static` garante que o arquivo seja aberto apenas uma vez. `std::ios_base::app` adiciona ao final do arquivo. + static std::ofstream logfile("firestore_log.txt", std::ios_base::app); + if (logfile.is_open()) { + // Adiciona um timestamp a cada log. + logfile << message << std::endl; + } } - return firebase::firestore::FieldValue::Array(convertedList); - } else if (std::holds_alternative(variant)) { - const flutter::EncodableMap& map = std::get(variant); - firebase::firestore::MapFieldValue convertedMap = - ConvertToMapFieldValue(map); - return firebase::firestore::FieldValue::Map(convertedMap); - } else if (std::holds_alternative(variant)) { - const CustomEncodableValue& custom_value = - std::get(variant); - - using firebase::Timestamp; - - if (custom_value.type() == - typeid(firebase::firestore::FieldValue::Timestamp)) { - const firebase::firestore::FieldValue& timestamp = - std::any_cast(custom_value); - return timestamp; - } else if (custom_value.type() == - typeid(firebase::firestore::DocumentReference)) { - const firebase::firestore::DocumentReference& documentReference = - std::any_cast(custom_value); - return firebase::firestore::FieldValue::Reference(documentReference); + + static std::string kLibraryName = "flutter-fire-fst"; +// static + void CloudFirestorePlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "cloud_firestore", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(); + + messenger_ = registrar->messenger(); + + FirebaseFirestoreHostApi::SetUp(registrar->messenger(), plugin.get()); + + registrar->AddPlugin(std::move(plugin)); + + // Register for platform logging + App::RegisterLibrary(kLibraryName.c_str(), getPluginVersion().c_str(), + nullptr); } - // check if nan and store it in number - else if (custom_value.type() == typeid(double)) { - const double& number = std::any_cast(custom_value); - return firebase::firestore::FieldValue::Double(number); + + firebase::firestore::FieldValue CloudFirestorePlugin::ConvertToFieldValue( + const flutter::EncodableValue& variant) { + if (std::holds_alternative(variant)) { + return firebase::firestore::FieldValue::Null(); + } else if (std::holds_alternative(variant)) { + return firebase::firestore::FieldValue::Boolean(std::get(variant)); + } else if (std::holds_alternative(variant)) { + return firebase::firestore::FieldValue::Integer(std::get(variant)); + } else if (std::holds_alternative(variant)) { + return firebase::firestore::FieldValue::Integer(std::get(variant)); + } else if (std::holds_alternative(variant)) { + return firebase::firestore::FieldValue::Double(std::get(variant)); + } else if (std::holds_alternative(variant)) { + return firebase::firestore::FieldValue::String( + std::get(variant)); + } else if (std::holds_alternative(variant)) { + const flutter::EncodableList& list = + std::get(variant); + std::vector convertedList; + for (const auto& item : list) { + convertedList.push_back(ConvertToFieldValue(item)); + } + return firebase::firestore::FieldValue::Array(convertedList); + } else if (std::holds_alternative(variant)) { + const flutter::EncodableMap& map = std::get(variant); + firebase::firestore::MapFieldValue convertedMap = + ConvertToMapFieldValue(map); + return firebase::firestore::FieldValue::Map(convertedMap); + } else if (std::holds_alternative(variant)) { + const CustomEncodableValue& custom_value = + std::get(variant); + + using firebase::Timestamp; + + if (custom_value.type() == + typeid(firebase::firestore::FieldValue::Timestamp)) { + const firebase::firestore::FieldValue& timestamp = + std::any_cast(custom_value); + return timestamp; + } else if (custom_value.type() == + typeid(firebase::firestore::DocumentReference)) { + const firebase::firestore::DocumentReference& documentReference = + std::any_cast(custom_value); + return firebase::firestore::FieldValue::Reference(documentReference); + } + // check if nan and store it in number + else if (custom_value.type() == typeid(double)) { + const double& number = std::any_cast(custom_value); + return firebase::firestore::FieldValue::Double(number); + } + + const firebase::firestore::FieldValue& anyField = + std::any_cast(custom_value); + return anyField; + } else { + // Add more types as needed + // You may throw an exception or handle this some other way + throw std::runtime_error("Unsupported EncodableValue type"); + } } - const firebase::firestore::FieldValue& anyField = - std::any_cast(custom_value); - return anyField; - } else { - // Add more types as needed - // You may throw an exception or handle this some other way - throw std::runtime_error("Unsupported EncodableValue type"); - } -} - -flutter::BinaryMessenger* - cloud_firestore_windows::CloudFirestorePlugin::messenger_ = nullptr; - -std::map>> + flutter::BinaryMessenger* + cloud_firestore_windows::CloudFirestorePlugin::messenger_ = nullptr; + + std::map>> event_channels_; -std::map>> - stream_handlers_; -std::map>> + std::map>> + stream_handlers_; + std::map>> cloud_firestore_windows::CloudFirestorePlugin::transaction_handlers_; -std::map> - cloud_firestore_windows::CloudFirestorePlugin::transactions_; -std::map> - cloud_firestore_windows::CloudFirestorePlugin::firestoreInstances_; - -std::string RegisterEventChannelWithUUID( - std::string prefix, std::string uuid, - std::unique_ptr> handler) { - std::string channelName = prefix + uuid; - event_channels_[channelName] = - std::make_unique>( - CloudFirestorePlugin::messenger_, channelName, - &flutter::StandardMethodCodec::GetInstance( - &FirebaseFirestoreHostApiCodecSerializer::GetInstance())); - - stream_handlers_[channelName] = std::move(handler); - - event_channels_[channelName]->SetStreamHandler( - std::move(stream_handlers_[channelName])); - - return channelName; -} - -std::string RegisterEventChannel( - std::string prefix, - std::unique_ptr> handler) { - UUID uuid; - UuidCreate(&uuid); - char* str; - UuidToStringA(&uuid, (RPC_CSTR*)&str); - - std::string channelName = prefix + str; - event_channels_[channelName] = - std::make_unique>( - CloudFirestorePlugin::messenger_, channelName, - &flutter::StandardMethodCodec::GetInstance( - &FirebaseFirestoreHostApiCodecSerializer::GetInstance())); - stream_handlers_[channelName] = std::move(handler); - - event_channels_[channelName]->SetStreamHandler( - std::move(stream_handlers_[channelName])); - - return str; -} - -CloudFirestorePlugin::CloudFirestorePlugin() {} - -CloudFirestorePlugin::~CloudFirestorePlugin() {} - -Firestore* GetFirestoreFromPigeon(const FirestorePigeonFirebaseApp& pigeonApp) { - std::string cacheKey = - pigeonApp.app_name() + "-" + pigeonApp.database_u_r_l(); - - if (CloudFirestorePlugin::firestoreInstances_.find(cacheKey) != - CloudFirestorePlugin::firestoreInstances_.end()) { - return CloudFirestorePlugin::firestoreInstances_[cacheKey].get(); - } - - App* app = App::GetInstance(pigeonApp.app_name().c_str()); - - Firestore* firestore = - Firestore::GetInstance(app, pigeonApp.database_u_r_l().c_str()); - - firebase::firestore::Settings settings; - - if (pigeonApp.settings().persistence_enabled()) { - bool persistEnabled = pigeonApp.settings().persistence_enabled(); - - // This is the maximum amount of cache allowed. We use the same number on - // android. - int64_t size = 104857600; - - if (pigeonApp.settings().cache_size_bytes()) { - const int64_t* cacheSizeBytes = pigeonApp.settings().cache_size_bytes(); - if (*cacheSizeBytes != -1) { - size = *cacheSizeBytes; - } + std::map> + cloud_firestore_windows::CloudFirestorePlugin::transactions_; + std::map> + cloud_firestore_windows::CloudFirestorePlugin::firestoreInstances_; + + std::string RegisterEventChannelWithUUID( + std::string prefix, std::string uuid, + std::unique_ptr> handler) { + std::string channelName = prefix + uuid; + event_channels_[channelName] = + std::make_unique>( + CloudFirestorePlugin::messenger_, channelName, + &flutter::StandardMethodCodec::GetInstance( + &FirebaseFirestoreHostApiCodecSerializer::GetInstance())); + + stream_handlers_[channelName] = std::move(handler); + + event_channels_[channelName]->SetStreamHandler( + std::move(stream_handlers_[channelName])); + + return channelName; } - if (persistEnabled) { - settings.set_cache_size_bytes(size); + std::string RegisterEventChannel( + std::string prefix, + std::unique_ptr> handler) { + UUID uuid; + UuidCreate(&uuid); + char* str; + UuidToStringA(&uuid, (RPC_CSTR*)&str); + + std::string channelName = prefix + str; + event_channels_[channelName] = + std::make_unique>( + CloudFirestorePlugin::messenger_, channelName, + &flutter::StandardMethodCodec::GetInstance( + &FirebaseFirestoreHostApiCodecSerializer::GetInstance())); + stream_handlers_[channelName] = std::move(handler); + + event_channels_[channelName]->SetStreamHandler( + std::move(stream_handlers_[channelName])); + + return str; } - } - - if (pigeonApp.settings().host()) { - settings.set_host(*pigeonApp.settings().host()); - - // Only allow changing ssl if host is also specified. - settings.set_ssl_enabled(false); - } - - firestore->set_settings(settings); - - CloudFirestorePlugin::firestoreInstances_[cacheKey] = - std::unique_ptr(firestore); - - return firestore; -} - -std::string CloudFirestorePlugin::GetErrorCode( - firebase::firestore::Error error) { - switch (error) { - case kErrorOk: - return "ok"; - case kErrorCancelled: - return "cancelled"; - case kErrorUnknown: - return "unknown"; - case kErrorInvalidArgument: - return "invalid-argument"; - case kErrorDeadlineExceeded: - return "deadline-exceeded"; - case kErrorNotFound: - return "not-found"; - case kErrorAlreadyExists: - return "already-exists"; - case kErrorPermissionDenied: - return "permission-denied"; - case kErrorResourceExhausted: - return "resource-exhausted"; - case kErrorFailedPrecondition: - return "failed-precondition"; - case kErrorAborted: - return "aborted"; - case kErrorOutOfRange: - return "out-of-range"; - case kErrorUnimplemented: - return "unimplemented"; - case kErrorInternal: - return "internal"; - case kErrorUnavailable: - return "unavailable"; - case kErrorDataLoss: - return "data-loss"; - case kErrorUnauthenticated: - return "unauthenticated"; - default: - return "unknown-error"; - } -} - -FlutterError CloudFirestorePlugin::ParseError( - const firebase::FutureBase& completed_future) { - const firebase::firestore::Error errorCode = - static_cast(completed_future.error()); - - EncodableMap details; - details[EncodableValue("code")] = - EncodableValue(CloudFirestorePlugin::GetErrorCode(errorCode)); - details[EncodableValue("message")] = - EncodableValue(completed_future.error_message()); - - return FlutterError("firebase_firestore", completed_future.error_message(), - details); -} - -firebase::firestore::Source GetSourceFromPigeon(const Source& pigeonSource) { - switch (pigeonSource) { - case Source::serverAndCache: - default: - return firebase::firestore::Source::kDefault; - case Source::server: - return firebase::firestore::Source::kServer; - case Source::cache: - return firebase::firestore::Source::kCache; - } -} - -firebase::firestore::DocumentSnapshot::ServerTimestampBehavior -GetServerTimestampBehaviorFromPigeon( - const ServerTimestampBehavior& pigeonServerTimestampBehavior) { - switch (pigeonServerTimestampBehavior) { - case ServerTimestampBehavior::estimate: - return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: - kEstimate; - case ServerTimestampBehavior::previous: - return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: - kPrevious; - case ServerTimestampBehavior::none: - default: - return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: - kNone; - } -} - -EncodableValue ConvertFieldValueToEncodableValue(const FieldValue& fieldValue) { - switch (fieldValue.type()) { - case FieldValue::Type::kNull: - return EncodableValue(); - - case FieldValue::Type::kBoolean: - return EncodableValue(fieldValue.boolean_value()); - - case FieldValue::Type::kInteger: - return EncodableValue(static_cast(fieldValue.integer_value())); - - case FieldValue::Type::kDouble: - return EncodableValue(fieldValue.double_value()); - - case FieldValue::Type::kTimestamp: - // Assuming timestamp can be converted to int64_t or some other type that - // EncodableValue accepts - return CustomEncodableValue(fieldValue.timestamp_value()); - - case FieldValue::Type::kString: - return EncodableValue(fieldValue.string_value()); - - case FieldValue::Type::kMap: { - EncodableMap encodableMap; - for (const auto& [key, val] : fieldValue.map_value()) { - encodableMap[EncodableValue(key)] = - ConvertFieldValueToEncodableValue(val); - } - return EncodableValue(encodableMap); + + CloudFirestorePlugin::CloudFirestorePlugin() {} + + CloudFirestorePlugin::~CloudFirestorePlugin() {} + + Firestore* GetFirestoreFromPigeon(const FirestorePigeonFirebaseApp& pigeonApp) { + std::string cacheKey = + pigeonApp.app_name() + "-" + pigeonApp.database_u_r_l(); + + if (CloudFirestorePlugin::firestoreInstances_.find(cacheKey) != + CloudFirestorePlugin::firestoreInstances_.end()) { + return CloudFirestorePlugin::firestoreInstances_[cacheKey].get(); + } + + App* app = App::GetInstance(pigeonApp.app_name().c_str()); + + Firestore* firestore = + Firestore::GetInstance(app, pigeonApp.database_u_r_l().c_str()); + + firebase::firestore::Settings settings; + + if (pigeonApp.settings().persistence_enabled()) { + bool persistEnabled = pigeonApp.settings().persistence_enabled(); + + // This is the maximum amount of cache allowed. We use the same number on + // android. + int64_t size = 104857600; + + if (pigeonApp.settings().cache_size_bytes()) { + const int64_t* cacheSizeBytes = pigeonApp.settings().cache_size_bytes(); + if (*cacheSizeBytes != -1) { + size = *cacheSizeBytes; + } + } + + if (persistEnabled) { + settings.set_cache_size_bytes(size); + } + } + + if (pigeonApp.settings().host()) { + settings.set_host(*pigeonApp.settings().host()); + + // Only allow changing ssl if host is also specified. + settings.set_ssl_enabled(false); + } + + firestore->set_settings(settings); + + CloudFirestorePlugin::firestoreInstances_[cacheKey] = + std::unique_ptr(firestore); + + return firestore; } - case FieldValue::Type::kArray: { - flutter::EncodableList encodableList; - for (const auto& val : fieldValue.array_value()) { - encodableList.push_back(ConvertFieldValueToEncodableValue(val)); - } - return encodableList; + std::string CloudFirestorePlugin::GetErrorCode( + firebase::firestore::Error error) { + switch (error) { + case kErrorOk: + return "ok"; + case kErrorCancelled: + return "cancelled"; + case kErrorUnknown: + return "unknown"; + case kErrorInvalidArgument: + return "invalid-argument"; + case kErrorDeadlineExceeded: + return "deadline-exceeded"; + case kErrorNotFound: + return "not-found"; + case kErrorAlreadyExists: + return "already-exists"; + case kErrorPermissionDenied: + return "permission-denied"; + case kErrorResourceExhausted: + return "resource-exhausted"; + case kErrorFailedPrecondition: + return "failed-precondition"; + case kErrorAborted: + return "aborted"; + case kErrorOutOfRange: + return "out-of-range"; + case kErrorUnimplemented: + return "unimplemented"; + case kErrorInternal: + return "internal"; + case kErrorUnavailable: + return "unavailable"; + case kErrorDataLoss: + return "data-loss"; + case kErrorUnauthenticated: + return "unauthenticated"; + default: + return "unknown-error"; + } } - case FieldValue::Type::kGeoPoint: { - return CustomEncodableValue(fieldValue.geo_point_value()); + FlutterError CloudFirestorePlugin::ParseError( + const firebase::FutureBase& completed_future) { + const firebase::firestore::Error errorCode = + static_cast(completed_future.error()); + + EncodableMap details; + details[EncodableValue("code")] = + EncodableValue(CloudFirestorePlugin::GetErrorCode(errorCode)); + details[EncodableValue("message")] = + EncodableValue(completed_future.error_message()); + + return FlutterError("firebase_firestore", completed_future.error_message(), + details); } - case FieldValue::Type::kReference: { - return CustomEncodableValue(fieldValue.reference_value()); + firebase::firestore::Source GetSourceFromPigeon(const Source& pigeonSource) { + switch (pigeonSource) { + case Source::serverAndCache: + default: + return firebase::firestore::Source::kDefault; + case Source::server: + return firebase::firestore::Source::kServer; + case Source::cache: + return firebase::firestore::Source::kCache; + } } - default: - return EncodableValue(nullptr); - } -} - -flutter::EncodableMap ConvertToEncodableMap( - const firebase::firestore::MapFieldValue& originalMap) { - EncodableMap convertedMap; - for (const auto& kv : originalMap) { - EncodableValue key = EncodableValue(kv.first); - EncodableValue value = ConvertFieldValueToEncodableValue( - kv.second); // convert FieldValue to EncodableValue - convertedMap[key] = value; // insert into the new map - } - return convertedMap; -} - -PigeonSnapshotMetadata ParseSnapshotMetadata( - const firebase::firestore::SnapshotMetadata& metadata) { - PigeonSnapshotMetadata pigeonSnapshotMetadata = PigeonSnapshotMetadata( - metadata.has_pending_writes(), metadata.is_from_cache()); - return pigeonSnapshotMetadata; -} - -PigeonDocumentSnapshot ParseDocumentSnapshot( - DocumentSnapshot document, - DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { - flutter::EncodableMap tempMap = - ConvertToEncodableMap(document.GetData(serverTimestampBehavior)); - - if (tempMap.empty()) { - return PigeonDocumentSnapshot(document.reference().path(), nullptr, - ParseSnapshotMetadata(document.metadata())); - } - - PigeonDocumentSnapshot pigeonDocumentSnapshot = - PigeonDocumentSnapshot(document.reference().path(), &tempMap, - ParseSnapshotMetadata(document.metadata())); - return pigeonDocumentSnapshot; -} - -flutter::EncodableList ParseDocumentSnapshots( - std::vector documents, - DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { - flutter::EncodableList pigeonDocumentSnapshot = flutter::EncodableList(); - - for (const auto& document : documents) { - pigeonDocumentSnapshot.push_back(CustomEncodableValue( - ParseDocumentSnapshot(document, serverTimestampBehavior))); - } - return pigeonDocumentSnapshot; -} - -DocumentChangeType ParseDocumentChangeType( - const firebase::firestore::DocumentChange::Type& type) { - switch (type) { - case firebase::firestore::DocumentChange::Type::kAdded: - return DocumentChangeType::added; - case firebase::firestore::DocumentChange::Type::kRemoved: - return DocumentChangeType::removed; - case firebase::firestore::DocumentChange::Type::kModified: - return DocumentChangeType::modified; - } - - throw std::invalid_argument("Invalid DocumentChangeType"); -} - -PigeonDocumentChange ParseDocumentChange( - const firebase::firestore::DocumentChange& document_change, - DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { - PigeonDocumentChange pigeonDocumentChange = PigeonDocumentChange( - ParseDocumentChangeType(document_change.type()), - ParseDocumentSnapshot(document_change.document(), - serverTimestampBehavior), - document_change.old_index(), document_change.new_index()); - return pigeonDocumentChange; -} - -flutter::EncodableList ParseDocumentChanges( - std::vector document_changes, - DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { - flutter::EncodableList pigeonDocumentChanges = flutter::EncodableList(); - for (const auto& document_change : document_changes) { - pigeonDocumentChanges.push_back(CustomEncodableValue( - ParseDocumentChange(document_change, serverTimestampBehavior))); - } - return pigeonDocumentChanges; -} - -PigeonQuerySnapshot ParseQuerySnapshot( - const firebase::firestore::QuerySnapshot* query_snapshot, - DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { - PigeonQuerySnapshot pigeonQuerySnapshot = PigeonQuerySnapshot( - ParseDocumentSnapshots(query_snapshot->documents(), - serverTimestampBehavior), - ParseDocumentChanges(query_snapshot->DocumentChanges(), - serverTimestampBehavior), - ParseSnapshotMetadata(query_snapshot->metadata())); - - return pigeonQuerySnapshot; -} - -std::vector ConvertToFieldValueList( - const flutter::EncodableList& originalList) { - std::vector convertedList; - for (const auto& item : originalList) { - firebase::firestore::FieldValue convertedItem = - CloudFirestorePlugin::ConvertToFieldValue(item); - convertedList.push_back(convertedItem); - } - return convertedList; -} - -firebase::firestore::MapFieldValue ConvertToMapFieldValue( - const EncodableMap& originalMap) { - firebase::firestore::MapFieldValue convertedMap; - - for (const auto& kv : originalMap) { - if (std::holds_alternative(kv.first)) { - std::string key = std::get(kv.first); - firebase::firestore::FieldValue value = - CloudFirestorePlugin::ConvertToFieldValue(kv.second); - convertedMap[key] = value; - } else { - // Handle or skip non-string keys - // You may throw an exception or handle this some other way - throw std::runtime_error("Unsupported key type"); + firebase::firestore::DocumentSnapshot::ServerTimestampBehavior + GetServerTimestampBehaviorFromPigeon( + const ServerTimestampBehavior& pigeonServerTimestampBehavior) { + switch (pigeonServerTimestampBehavior) { + case ServerTimestampBehavior::estimate: + return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: + kEstimate; + case ServerTimestampBehavior::previous: + return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: + kPrevious; + case ServerTimestampBehavior::none: + default: + return firebase::firestore::DocumentSnapshot::ServerTimestampBehavior:: + kNone; + } } - } - return convertedMap; -} + EncodableValue ConvertFieldValueToEncodableValue(const FieldValue& fieldValue) { + switch (fieldValue.type()) { + case FieldValue::Type::kNull: + return EncodableValue(); -using flutter::CustomEncodableValue; + case FieldValue::Type::kBoolean: + return EncodableValue(fieldValue.boolean_value()); -firebase::firestore::MapFieldPathValue ConvertToMapFieldPathValue( - const EncodableMap& originalMap) { - firebase::firestore::MapFieldPathValue convertedMap; - - for (const auto& kv : originalMap) { - if (std::holds_alternative(kv.first)) { - std::string key = std::get(kv.first); - std::vector convertedList; - convertedList.push_back(key); - - firebase::firestore::FieldValue value = - CloudFirestorePlugin::ConvertToFieldValue(kv.second); - convertedMap[FieldPath(convertedList)] = value; - } else if (std::holds_alternative(kv.first)) { - const FieldPath& fieldPath = - std::any_cast(std::get(kv.first)); - firebase::firestore::FieldValue value = - CloudFirestorePlugin::ConvertToFieldValue(kv.second); - convertedMap[fieldPath] = value; - } else { - // Handle or skip non-string keys - // You may throw an exception or handle this some other way - throw std::runtime_error("Unsupported key type"); - } - } - - return convertedMap; -} - -class LoadBundleStreamHandler - : public flutter::StreamHandler { - public: - LoadBundleStreamHandler(Firestore* firestore, std::string bundle) { - firestore_ = firestore; - bundle_ = bundle; - } - - std::unique_ptr> - OnListenInternal( - const flutter::EncodableValue* arguments, - std::unique_ptr>&& events) - override { - events_ = std::move(events); - events.reset(); - firestore_->LoadBundle( - bundle_, [this](const LoadBundleTaskProgress& progress) { - flutter::EncodableMap map; - map[flutter::EncodableValue("bytesLoaded")] = - flutter::EncodableValue(progress.bytes_loaded()); - map[flutter::EncodableValue("documentsLoaded")] = - flutter::EncodableValue(progress.documents_loaded()); - map[flutter::EncodableValue("totalBytes")] = - flutter::EncodableValue(progress.total_bytes()); - map[flutter::EncodableValue("totalDocuments")] = - flutter::EncodableValue(progress.total_documents()); - switch (progress.state()) { - case LoadBundleTaskProgress::State::kError: { - EncodableMap details; - details[EncodableValue("code")] = - EncodableValue("load-bundle-error"); - details[EncodableValue("message")] = - EncodableValue("Error loading the bundle"); - - events_->Error("firebase_firestore", "Error loading the bundle", - details); - events_->EndOfStream(); - return; + case FieldValue::Type::kInteger: + return EncodableValue(static_cast(fieldValue.integer_value())); + + case FieldValue::Type::kDouble: + return EncodableValue(fieldValue.double_value()); + + case FieldValue::Type::kTimestamp: + // Assuming timestamp can be converted to int64_t or some other type that + // EncodableValue accepts + return CustomEncodableValue(fieldValue.timestamp_value()); + + case FieldValue::Type::kString: + return EncodableValue(fieldValue.string_value()); + + case FieldValue::Type::kMap: { + EncodableMap encodableMap; + for (const auto& [key, val] : fieldValue.map_value()) { + encodableMap[EncodableValue(key)] = + ConvertFieldValueToEncodableValue(val); + } + return EncodableValue(encodableMap); } - case LoadBundleTaskProgress::State::kInProgress: { - std::cout << "Bytes loaded from bundle: " - << progress.bytes_loaded() << std::endl; - map[flutter::EncodableValue("taskState")] = - flutter::EncodableValue("running"); - - events_->Success(map); - break; + + case FieldValue::Type::kArray: { + flutter::EncodableList encodableList; + for (const auto& val : fieldValue.array_value()) { + encodableList.push_back(ConvertFieldValueToEncodableValue(val)); + } + return encodableList; } - case LoadBundleTaskProgress::State::kSuccess: { - std::cout << "Bundle load succeeded" << std::endl; - map[flutter::EncodableValue("taskState")] = - flutter::EncodableValue("success"); - - events_->Success(map); - events_->EndOfStream(); - break; + + case FieldValue::Type::kGeoPoint: { + return CustomEncodableValue(fieldValue.geo_point_value()); } - } - }); - return nullptr; - } - - std::unique_ptr> - OnCancelInternal(const flutter::EncodableValue* arguments) override { - events_->EndOfStream(); - return nullptr; - } - - private: - Firestore* firestore_; - std::unique_ptr> events_; - std::string bundle_; -}; - -void CloudFirestorePlugin::LoadBundle( - const FirestorePigeonFirebaseApp& app, const std::vector& bundle, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - - std::string bundleConverted(bundle.begin(), bundle.end()); - - auto handler = - std::make_unique(firestore, bundleConverted); - - std::string channelName = RegisterEventChannel( - "plugins.flutter.io/firebase_firestore/loadBundle/", std::move(handler)); - - result(channelName); -} - -using firebase::Future; -using firebase::firestore::Query; -using firebase::firestore::QuerySnapshot; - -void CloudFirestorePlugin::NamedQueryGet( - const FirestorePigeonFirebaseApp& app, const std::string& name, - const PigeonGetOptions& options, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - Future future = firestore->NamedQuery(name.c_str()); - - future.OnCompletion([result, options](const Future& completed_future) { - const Query* query = completed_future.result(); - - if (query == nullptr) { - result( - FlutterError("Named query has not been found. Please check it has " - "been loaded properly via loadBundle().")); - return; + + case FieldValue::Type::kReference: { + return CustomEncodableValue(fieldValue.reference_value()); + } + + default: + return EncodableValue(nullptr); + } } - using firebase::firestore::QuerySnapshot; + flutter::EncodableMap ConvertToEncodableMap( + const firebase::firestore::MapFieldValue& originalMap) { + EncodableMap convertedMap; + for (const auto& kv : originalMap) { + EncodableValue key = EncodableValue(kv.first); + EncodableValue value = ConvertFieldValueToEncodableValue( + kv.second); // convert FieldValue to EncodableValue + convertedMap[key] = value; // insert into the new map + } + return convertedMap; + } - query->Get(GetSourceFromPigeon(options.source())) - .OnCompletion( - [result, options](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - const QuerySnapshot* query_snapshot = completed_future.result(); - result(ParseQuerySnapshot( - query_snapshot, GetServerTimestampBehaviorFromPigeon( - options.server_timestamp_behavior()))); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; - } - }); - }); -} - -void CloudFirestorePlugin::ClearPersistence( - const FirestorePigeonFirebaseApp& app, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - firestore->ClearPersistence().OnCompletion( - [result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; + PigeonSnapshotMetadata ParseSnapshotMetadata( + const firebase::firestore::SnapshotMetadata& metadata) { + PigeonSnapshotMetadata pigeonSnapshotMetadata = PigeonSnapshotMetadata( + metadata.has_pending_writes(), metadata.is_from_cache()); + return pigeonSnapshotMetadata; + } + + PigeonDocumentSnapshot ParseDocumentSnapshot( + DocumentSnapshot document, + DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { + flutter::EncodableMap tempMap = + ConvertToEncodableMap(document.GetData(serverTimestampBehavior)); + + if (tempMap.empty()) { + return PigeonDocumentSnapshot(document.reference().path(), nullptr, + ParseSnapshotMetadata(document.metadata())); } - }); -} - -void CloudFirestorePlugin::DisableNetwork( - const FirestorePigeonFirebaseApp& app, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - firestore->DisableNetwork().OnCompletion( - [result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; + + PigeonDocumentSnapshot pigeonDocumentSnapshot = + PigeonDocumentSnapshot(document.reference().path(), &tempMap, + ParseSnapshotMetadata(document.metadata())); + return pigeonDocumentSnapshot; + } + + flutter::EncodableList ParseDocumentSnapshots( + std::vector documents, + DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { + flutter::EncodableList pigeonDocumentSnapshot = flutter::EncodableList(); + + for (const auto& document : documents) { + pigeonDocumentSnapshot.push_back(CustomEncodableValue( + ParseDocumentSnapshot(document, serverTimestampBehavior))); } - }); -} - -void CloudFirestorePlugin::EnableNetwork( - const FirestorePigeonFirebaseApp& app, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - firestore->EnableNetwork().OnCompletion( - [result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; + return pigeonDocumentSnapshot; + } + + DocumentChangeType ParseDocumentChangeType( + const firebase::firestore::DocumentChange::Type& type) { + switch (type) { + case firebase::firestore::DocumentChange::Type::kAdded: + return DocumentChangeType::added; + case firebase::firestore::DocumentChange::Type::kRemoved: + return DocumentChangeType::removed; + case firebase::firestore::DocumentChange::Type::kModified: + return DocumentChangeType::modified; } - }); -} - -void CloudFirestorePlugin::Terminate( - const FirestorePigeonFirebaseApp& app, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - firestore->Terminate().OnCompletion( - [result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; + + throw std::invalid_argument("Invalid DocumentChangeType"); + } + + PigeonDocumentChange ParseDocumentChange( + const firebase::firestore::DocumentChange& document_change, + DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { + PigeonDocumentChange pigeonDocumentChange = PigeonDocumentChange( + ParseDocumentChangeType(document_change.type()), + ParseDocumentSnapshot(document_change.document(), + serverTimestampBehavior), + document_change.old_index(), document_change.new_index()); + return pigeonDocumentChange; + } + + flutter::EncodableList ParseDocumentChanges( + std::vector document_changes, + DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { + flutter::EncodableList pigeonDocumentChanges = flutter::EncodableList(); + for (const auto& document_change : document_changes) { + pigeonDocumentChanges.push_back(CustomEncodableValue( + ParseDocumentChange(document_change, serverTimestampBehavior))); } - }); -} - -void CloudFirestorePlugin::WaitForPendingWrites( - const FirestorePigeonFirebaseApp& app, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - firestore->WaitForPendingWrites().OnCompletion( - [result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; + return pigeonDocumentChanges; + } + + PigeonQuerySnapshot ParseQuerySnapshot( + const firebase::firestore::QuerySnapshot* query_snapshot, + DocumentSnapshot::ServerTimestampBehavior serverTimestampBehavior) { + PigeonQuerySnapshot pigeonQuerySnapshot = PigeonQuerySnapshot( + ParseDocumentSnapshots(query_snapshot->documents(), + serverTimestampBehavior), + ParseDocumentChanges(query_snapshot->DocumentChanges(), + serverTimestampBehavior), + ParseSnapshotMetadata(query_snapshot->metadata())); + + return pigeonQuerySnapshot; + } + + std::vector ConvertToFieldValueList( + const flutter::EncodableList& originalList) { + std::vector convertedList; + for (const auto& item : originalList) { + firebase::firestore::FieldValue convertedItem = + CloudFirestorePlugin::ConvertToFieldValue(item); + convertedList.push_back(convertedItem); + } + return convertedList; + } + + firebase::firestore::MapFieldValue ConvertToMapFieldValue( + const EncodableMap& originalMap) { + firebase::firestore::MapFieldValue convertedMap; + + for (const auto& kv : originalMap) { + if (std::holds_alternative(kv.first)) { + std::string key = std::get(kv.first); + firebase::firestore::FieldValue value = + CloudFirestorePlugin::ConvertToFieldValue(kv.second); + convertedMap[key] = value; + } else { + // Handle or skip non-string keys + // You may throw an exception or handle this some other way + throw std::runtime_error("Unsupported key type"); + } } - }); -} - -void CloudFirestorePlugin::SetIndexConfiguration( - const FirestorePigeonFirebaseApp& app, - const std::string& index_configuration, - std::function reply)> result) { - // TODO: not available in C++ SDK - result(FlutterError("Not available in C++ SDK")); -} - -void CloudFirestorePlugin::SetLoggingEnabled( - bool logging_enabled, - std::function reply)> result) { - firebase::firestore::Firestore::set_log_level( - logging_enabled ? firebase::LogLevel::kLogLevelDebug - : firebase::LogLevel::kLogLevelError); - result(std::nullopt); -} - -class SnapshotInSyncStreamHandler - : public flutter::StreamHandler { - public: - SnapshotInSyncStreamHandler(Firestore* firestore) { firestore_ = firestore; } - - std::unique_ptr> - OnListenInternal( - const flutter::EncodableValue* arguments, - std::unique_ptr>&& events) - override { - events_ = std::move(events); - events.reset(); - // We do this to bind the event to the main channel - auto boundSendEvent = - std::bind(&SnapshotInSyncStreamHandler::SendEvent, this); - this->SetSendEventFunction(boundSendEvent); - - listener_ = firestore_->AddSnapshotsInSyncListener([this]() { - if (sendEventFunc_) sendEventFunc_(); - }); - return nullptr; - } - - std::unique_ptr> - OnCancelInternal(const flutter::EncodableValue* arguments) override { - listener_.Remove(); - events_->EndOfStream(); - return nullptr; - } - - void SetSendEventFunction(std::function func) { - sendEventFunc_ = func; - } - - void SendEvent() { events_->Success(flutter::EncodableValue()); } - - private: - Firestore* firestore_; - ListenerRegistration listener_; - std::unique_ptr> events_; - std::function sendEventFunc_; -}; - -void CloudFirestorePlugin::SnapshotsInSyncSetup( - const FirestorePigeonFirebaseApp& app, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - - auto handler = std::make_unique(firestore); - - UUID uuid; - UuidCreate(&uuid); - char* str; - UuidToStringA(&uuid, (RPC_CSTR*)&str); - std::string snapshotInSyncId(str); - - std::string channelName = RegisterEventChannelWithUUID( - "plugins.flutter.io/firebase_firestore/snapshotsInSync/", - snapshotInSyncId, std::move(handler)); - result(snapshotInSyncId); -} - -class TransactionStreamHandler - : public flutter::StreamHandler { - public: - TransactionStreamHandler(Firestore* firestore, long timeout, int maxAttempts, - std::string transactionId) - : firestore_(firestore), - timeout_(timeout), - maxAttempts_(maxAttempts), - transactionId_(transactionId) {} - - void ReceiveTransactionResponse( - PigeonTransactionResult resultType, - std::vector commands) { - std::lock_guard lock(commands_mutex_); - resultType_ = resultType; - commands_ = commands; - cv_.notify_one(); - } - - std::unique_ptr> - OnListenInternal( - const flutter::EncodableValue* arguments, - std::unique_ptr>&& events) - override { - events_ = std::move(events); - events.reset(); - TransactionOptions options; - options.set_max_attempts(maxAttempts_); - - firestore_ - ->RunTransaction( - options, - [this](Transaction& transaction, std::string& str) -> Error { - auto noopDeleter = [](Transaction*) {}; - std::shared_ptr ptr(&transaction, noopDeleter); - CloudFirestorePlugin::transactions_[transactionId_] = - std::move(ptr); - - flutter::EncodableMap map; - map.emplace("appName", firestore_->app()->name()); - events_->Success(flutter::EncodableValue(map)); - - std::unique_lock lock(mtx_); - if (cv_.wait_for(lock, std::chrono::milliseconds(timeout_)) == - std::cv_status::timeout) { - events_->Error("Timeout", "Transaction timed out."); - events_->EndOfStream(); - return Error::kErrorDeadlineExceeded; - } - - std::lock_guard command_lock(commands_mutex_); - if (commands_.empty()) return Error::kErrorOk; - - for (PigeonTransactionCommand& command : commands_) { - std::string path = command.path(); - PigeonTransactionType type = command.type(); - if (path.empty() /* or some other invalid condition */) { - std::cerr << "Path is invalid: " << path << std::endl; - continue; // Skip this iteration. - } - std::cout << "Before: " << path << std::endl; // Debug print. + return convertedMap; + } - DocumentReference reference = firestore_->Document(path); - std::cout << "After: " << command.path() - << std::endl; // debug print + using flutter::CustomEncodableValue; + + firebase::firestore::MapFieldPathValue ConvertToMapFieldPathValue( + const EncodableMap& originalMap) { + firebase::firestore::MapFieldPathValue convertedMap; + + for (const auto& kv : originalMap) { + if (std::holds_alternative(kv.first)) { + std::string key = std::get(kv.first); + std::vector convertedList; + convertedList.push_back(key); + + firebase::firestore::FieldValue value = + CloudFirestorePlugin::ConvertToFieldValue(kv.second); + convertedMap[FieldPath(convertedList)] = value; + } else if (std::holds_alternative(kv.first)) { + const FieldPath& fieldPath = + std::any_cast(std::get(kv.first)); + firebase::firestore::FieldValue value = + CloudFirestorePlugin::ConvertToFieldValue(kv.second); + convertedMap[fieldPath] = value; + } else { + // Handle or skip non-string keys + // You may throw an exception or handle this some other way + throw std::runtime_error("Unsupported key type"); + } + } - switch (type) { - case PigeonTransactionType::set: - std::cout << "Transaction set" << path - << std::endl; // Debug print. - - if (command.option()->merge() != nullptr && - command.option()->merge()) { - transaction.Set(reference, - ConvertToMapFieldValue(*command.data()), - SetOptions::Merge()); - } else if (command.option()->merge_fields()) { - transaction.Set( - reference, ConvertToMapFieldValue(*command.data()), - SetOptions::MergeFieldPaths(ConvertToFieldPathVector( - *command.option()->merge_fields()))); - } else { - transaction.Set(reference, - ConvertToMapFieldValue(*command.data())); - } + return convertedMap; + } - break; - case PigeonTransactionType::update: - std::cout << "Transaction update" << path - << std::endl; // Debug print. + class LoadBundleStreamHandler + : public flutter::StreamHandler { + public: + LoadBundleStreamHandler(Firestore* firestore, std::string bundle) { + firestore_ = firestore; + bundle_ = bundle; + } - transaction.Update(reference, - ConvertToMapFieldValue(*command.data())); - break; - case PigeonTransactionType::deleteType: - std::cout << "Transaction delete" << path - << std::endl; // Debug print. + std::unique_ptr> + OnListenInternal( + const flutter::EncodableValue* arguments, + std::unique_ptr>&& events) + override { + events_ = std::move(events); + events.reset(); + firestore_->LoadBundle( + bundle_, [this](const LoadBundleTaskProgress& progress) { + flutter::EncodableMap map; + map[flutter::EncodableValue("bytesLoaded")] = + flutter::EncodableValue(progress.bytes_loaded()); + map[flutter::EncodableValue("documentsLoaded")] = + flutter::EncodableValue(progress.documents_loaded()); + map[flutter::EncodableValue("totalBytes")] = + flutter::EncodableValue(progress.total_bytes()); + map[flutter::EncodableValue("totalDocuments")] = + flutter::EncodableValue(progress.total_documents()); + switch (progress.state()) { + case LoadBundleTaskProgress::State::kError: { + EncodableMap details; + details[EncodableValue("code")] = + EncodableValue("load-bundle-error"); + details[EncodableValue("message")] = + EncodableValue("Error loading the bundle"); + + events_->Error("firebase_firestore", "Error loading the bundle", + details); + events_->EndOfStream(); + return; + } + case LoadBundleTaskProgress::State::kInProgress: { + std::cout << "Bytes loaded from bundle: " + << progress.bytes_loaded() << std::endl; + map[flutter::EncodableValue("taskState")] = + flutter::EncodableValue("running"); + + events_->Success(map); + break; + } + case LoadBundleTaskProgress::State::kSuccess: { + std::cout << "Bundle load succeeded" << std::endl; + map[flutter::EncodableValue("taskState")] = + flutter::EncodableValue("success"); + + events_->Success(map); + events_->EndOfStream(); + break; + } + } + }); + return nullptr; + } - transaction.Delete(reference); - break; - } - } - return Error::kErrorOk; - }) - .OnCompletion([this](const Future& completed_future) { - flutter::EncodableMap result; - if (completed_future.error() == firebase::firestore::kErrorOk) { - result.insert(std::make_pair(flutter::EncodableValue("complete"), - flutter::EncodableValue(true))); - events_->Success(result); - } else { - events_->Error("transaction_error", - completed_future.error_message()); - } - events_->EndOfStream(); + std::unique_ptr> + OnCancelInternal(const flutter::EncodableValue* arguments) override { + events_->EndOfStream(); + return nullptr; + } + + private: + Firestore* firestore_; + std::unique_ptr> events_; + std::string bundle_; + }; + + void CloudFirestorePlugin::LoadBundle( + const FirestorePigeonFirebaseApp& app, const std::vector& bundle, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + + std::string bundleConverted(bundle.begin(), bundle.end()); + + auto handler = + std::make_unique(firestore, bundleConverted); + + std::string channelName = RegisterEventChannel( + "plugins.flutter.io/firebase_firestore/loadBundle/", std::move(handler)); + + result(channelName); + } + + using firebase::Future; + using firebase::firestore::Query; + using firebase::firestore::QuerySnapshot; + + void CloudFirestorePlugin::NamedQueryGet( + const FirestorePigeonFirebaseApp& app, const std::string& name, + const PigeonGetOptions& options, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + Future future = firestore->NamedQuery(name.c_str()); + + future.OnCompletion([result, options, name /* NOVO */](const Future& completed_future) { + const Query* query = completed_future.result(); + + if (query == nullptr) { + // NOVO: Log de erro + LogToFile("ERROR: NamedQueryGet - A query nomeada '" + name + + "' não foi encontrada."); + // FIM NOVO + result( + FlutterError("Named query has not been found. Please check it has " + "been loaded properly via loadBundle().")); + return; + } + + using firebase::firestore::QuerySnapshot; + + query->Get(GetSourceFromPigeon(options.source())) + .OnCompletion([result, options, + name /* NOVO */](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + // NOVO: Log de sucesso + LogToFile("PASS: NamedQueryGet - Leitura da query '" + name + + "' bem-sucedida."); + // FIM NOVO + const QuerySnapshot* query_snapshot = completed_future.result(); + result(ParseQuerySnapshot(query_snapshot, + GetServerTimestampBehaviorFromPigeon( + options.server_timestamp_behavior()))); + } else { + // NOVO: Log de erro + LogToFile("ERROR: NamedQueryGet - Falha na leitura da query '" + + name + "'. Motivo: " + completed_future.error_message()); + // FIM NOVO + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); }); + } - return nullptr; - } - - std::unique_ptr> - OnCancelInternal(const flutter::EncodableValue* arguments) override { - std::unique_lock lock(mtx_); - cv_.notify_one(); - events_->EndOfStream(); - return nullptr; - } - - private: - Firestore* firestore_; - long timeout_; - int maxAttempts_; - std::string transactionId_; - std::vector commands_; - PigeonTransactionResult resultType_; - std::mutex mtx_; - std::mutex commands_mutex_; - std::condition_variable cv_; - std::unique_ptr> events_; -}; - -void CloudFirestorePlugin::TransactionCreate( - const FirestorePigeonFirebaseApp& app, int64_t timeout, - int64_t max_attempts, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - - UUID uuid; - UuidCreate(&uuid); - char* str; - UuidToStringA(&uuid, (RPC_CSTR*)&str); - std::string transactionId(str); - - auto handler = std::make_unique( - firestore, static_cast(timeout), static_cast(max_attempts), - transactionId); - - // Temporarily release the ownership. - TransactionStreamHandler* raw_handler = handler.release(); - CloudFirestorePlugin::transaction_handlers_[transactionId] = - std::unique_ptr(raw_handler); - - // Register the event channel. - std::string channelName = RegisterEventChannelWithUUID( - "plugins.flutter.io/firebase_firestore/transaction/", transactionId, - std::unique_ptr(raw_handler)); - - // Return the result (assumed to be transaction ID in this example). - result(transactionId); -} + void CloudFirestorePlugin::ClearPersistence( + const FirestorePigeonFirebaseApp& app, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + firestore->ClearPersistence().OnCompletion( + [result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); + } -using flutter::CustomEncodableValue; + void CloudFirestorePlugin::DisableNetwork( + const FirestorePigeonFirebaseApp& app, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + firestore->DisableNetwork().OnCompletion( + [result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); + } -void CloudFirestorePlugin::TransactionStoreResult( - const std::string& transaction_id, - const PigeonTransactionResult& result_type, - const flutter::EncodableList* commands, - std::function reply)> result) { - if (CloudFirestorePlugin::transaction_handlers_[transaction_id]) { - TransactionStreamHandler& handler = *static_cast( - CloudFirestorePlugin::transaction_handlers_[transaction_id].get()); - std::vector commandVector; - for (const auto& element : *commands) { - const PigeonTransactionCommand& command = - std::any_cast( - std::get(element)); - commandVector.push_back(command); + void CloudFirestorePlugin::EnableNetwork( + const FirestorePigeonFirebaseApp& app, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + firestore->EnableNetwork().OnCompletion( + [result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); } - handler.ReceiveTransactionResponse(result_type, commandVector); - result(std::nullopt); - - } else { - result(std::make_optional(FlutterError( - "transaction_not_found", "Transaction not found", transaction_id))); - } -} - -void CloudFirestorePlugin::TransactionGet( - const FirestorePigeonFirebaseApp& app, const std::string& transaction_id, - const std::string& path, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - DocumentReference reference = firestore->Document(path); - - std::shared_ptr transaction = transactions_[transaction_id]; - Error error_code; - std::string error_message; - - // Call the Get function - DocumentSnapshot snapshot = - transaction->Get(reference, &error_code, &error_message); - - if (error_code != Error::kErrorOk) { - result(FlutterError(error_message)); - } else { - result(ParseDocumentSnapshot( - snapshot, DocumentSnapshot::ServerTimestampBehavior::kDefault)); - } -} - -using firebase::firestore::DocumentReference; -using firebase::firestore::SetOptions; - -std::vector ConvertToFieldPathVector( - const flutter::EncodableList& encodableList) { - std::vector fieldVector; - - for (const auto& element : encodableList) { - flutter::EncodableList fieldPath = - std::get(element); - - std::vector convertedList; - for (const auto& field : fieldPath) { - convertedList.push_back(std::get(field)); + + void CloudFirestorePlugin::Terminate( + const FirestorePigeonFirebaseApp& app, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + firestore->Terminate().OnCompletion( + [result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); } - // Was already converted by the Codec - fieldVector.push_back(firebase::firestore::FieldPath(convertedList)); - } - - return fieldVector; -} - -void CloudFirestorePlugin::DocumentReferenceSet( - const FirestorePigeonFirebaseApp& app, - const DocumentReferenceRequest& request, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - DocumentReference document_reference = firestore->Document(request.path()); - - // Get the data - Future future; - - if (request.option()->merge() != nullptr && request.option()->merge()) { - future = document_reference.Set(ConvertToMapFieldValue(*request.data()), - SetOptions::Merge()); - } else if (request.option()->merge_fields()) { - future = document_reference.Set( - ConvertToMapFieldValue(*request.data()), - SetOptions::MergeFieldPaths( - ConvertToFieldPathVector(*request.option()->merge_fields()))); - } else { - future = document_reference.Set(ConvertToMapFieldValue(*request.data())); - } - - future.OnCompletion([result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; + void CloudFirestorePlugin::WaitForPendingWrites( + const FirestorePigeonFirebaseApp& app, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + firestore->WaitForPendingWrites().OnCompletion( + [result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); } - }); -} - -void CloudFirestorePlugin::DocumentReferenceUpdate( - const FirestorePigeonFirebaseApp& app, - const DocumentReferenceRequest& request, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - DocumentReference document_reference = firestore->Document(request.path()); - - // Get the data - MapFieldPathValue data = ConvertToMapFieldPathValue(*request.data()); - Future future = document_reference.Update(data); - - future.OnCompletion([result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; + + void CloudFirestorePlugin::SetIndexConfiguration( + const FirestorePigeonFirebaseApp& app, + const std::string& index_configuration, + std::function reply)> result) { + // TODO: not available in C++ SDK + result(FlutterError("Not available in C++ SDK")); } - }); -} - -void CloudFirestorePlugin::DocumentReferenceGet( - const FirestorePigeonFirebaseApp& app, - const DocumentReferenceRequest& request, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - DocumentReference document_reference = firestore->Document(request.path()); - - firebase::firestore::Source source = GetSourceFromPigeon(*request.source()); - - Future future = document_reference.Get(source); - - future.OnCompletion( - [result, request](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - const DocumentSnapshot* document_snapshot = completed_future.result(); - result(ParseDocumentSnapshot( - *document_snapshot, GetServerTimestampBehaviorFromPigeon( - *request.server_timestamp_behavior()))); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; - } - }); -} - -void CloudFirestorePlugin::DocumentReferenceDelete( - const FirestorePigeonFirebaseApp& app, - const DocumentReferenceRequest& request, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - DocumentReference document_reference = firestore->Document(request.path()); - - Future future = document_reference.Delete(); - - future.OnCompletion([result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - return; + + void CloudFirestorePlugin::SetLoggingEnabled( + bool logging_enabled, + std::function reply)> result) { + firebase::firestore::Firestore::set_log_level( + logging_enabled ? firebase::LogLevel::kLogLevelDebug + : firebase::LogLevel::kLogLevelError); + result(std::nullopt); } - }); -} -// Convert EncodableList to std::vector> -std::vector> ConvertToConditions( - const flutter::EncodableList& encodableList) { - std::vector> conditions; + class SnapshotInSyncStreamHandler + : public flutter::StreamHandler { + public: + SnapshotInSyncStreamHandler(Firestore* firestore) { firestore_ = firestore; } + + std::unique_ptr> + OnListenInternal( + const flutter::EncodableValue* arguments, + std::unique_ptr>&& events) + override { + events_ = std::move(events); + events.reset(); + // We do this to bind the event to the main channel + auto boundSendEvent = + std::bind(&SnapshotInSyncStreamHandler::SendEvent, this); + this->SetSendEventFunction(boundSendEvent); + + listener_ = firestore_->AddSnapshotsInSyncListener([this]() { + if (sendEventFunc_) sendEventFunc_(); + }); + return nullptr; + } - for (const auto& element : encodableList) { - std::vector condition; + std::unique_ptr> + OnCancelInternal(const flutter::EncodableValue* arguments) override { + listener_.Remove(); + events_->EndOfStream(); + return nullptr; + } + + void SetSendEventFunction(std::function func) { + sendEventFunc_ = func; + } + + void SendEvent() { events_->Success(flutter::EncodableValue()); } + + private: + Firestore* firestore_; + ListenerRegistration listener_; + std::unique_ptr> events_; + std::function sendEventFunc_; + }; - for (const auto& conditionElement : - std::get(element)) { - condition.push_back(conditionElement); + void CloudFirestorePlugin::SnapshotsInSyncSetup( + const FirestorePigeonFirebaseApp& app, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + + auto handler = std::make_unique(firestore); + + UUID uuid; + UuidCreate(&uuid); + char* str; + UuidToStringA(&uuid, (RPC_CSTR*)&str); + std::string snapshotInSyncId(str); + + std::string channelName = RegisterEventChannelWithUUID( + "plugins.flutter.io/firebase_firestore/snapshotsInSync/", + snapshotInSyncId, std::move(handler)); + result(snapshotInSyncId); } - conditions.push_back(condition); - } - - return conditions; -} - -using firebase::firestore::Filter; - -firebase::firestore::Filter filterFromJson(const EncodableMap& map) { - if (map.find(EncodableValue("fieldPath")) != map.end()) { - // Deserialize a FilterQuery - std::string op = std::get(map.at(EncodableValue("op"))); - const FieldPath& fieldPath = std::any_cast( - std::get(map.at(EncodableValue("fieldPath")))); - - auto value = map.at(EncodableValue("value")); - - // All the operators from Firebase - if (op == "==") { - return Filter::EqualTo(fieldPath, - CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "!=") { - return Filter::NotEqualTo( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "<") { - return Filter::LessThan(fieldPath, - CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "<=") { - return Filter::LessThanOrEqualTo( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == ">") { - return Filter::GreaterThan( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == ">=") { - return Filter::GreaterThanOrEqualTo( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "array-contains") { - return Filter::ArrayContains( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "array-contains-any") { - // Here you should make sure 'value' is the correct type, e.g., a vector - return Filter::ArrayContainsAny( - fieldPath, - ConvertToFieldValueList(std::get(value))); - } else if (op == "in") { - return Filter::In( - fieldPath, - ConvertToFieldValueList(std::get(value))); - } else if (op == "not-in") { - return Filter::NotIn( - fieldPath, - ConvertToFieldValueList(std::get(value))); - } else { - throw std::runtime_error("Invalid operator"); + class TransactionStreamHandler + : public flutter::StreamHandler { + public: + TransactionStreamHandler(Firestore* firestore, long timeout, int maxAttempts, + std::string transactionId) + : firestore_(firestore), + timeout_(timeout), + maxAttempts_(maxAttempts), + transactionId_(transactionId) {} + + void ReceiveTransactionResponse( + PigeonTransactionResult resultType, + std::vector commands) { + std::lock_guard lock(commands_mutex_); + resultType_ = resultType; + commands_ = commands; + cv_.notify_one(); + } + + std::unique_ptr> + OnListenInternal( + const flutter::EncodableValue* arguments, + std::unique_ptr>&& events) + override { + events_ = std::move(events); + events.reset(); + TransactionOptions options; + options.set_max_attempts(maxAttempts_); + + firestore_ + ->RunTransaction( + options, + [this](Transaction& transaction, std::string& str) -> Error { + auto noopDeleter = [](Transaction*) {}; + std::shared_ptr ptr(&transaction, noopDeleter); + CloudFirestorePlugin::transactions_[transactionId_] = + std::move(ptr); + + flutter::EncodableMap map; + map.emplace("appName", firestore_->app()->name()); + events_->Success(flutter::EncodableValue(map)); + + std::unique_lock lock(mtx_); + if (cv_.wait_for(lock, std::chrono::milliseconds(timeout_)) == + std::cv_status::timeout) { + events_->Error("Timeout", "Transaction timed out."); + events_->EndOfStream(); + return Error::kErrorDeadlineExceeded; + } + + std::lock_guard command_lock(commands_mutex_); + if (commands_.empty()) return Error::kErrorOk; + + for (PigeonTransactionCommand& command : commands_) { + std::string path = command.path(); + PigeonTransactionType type = command.type(); + if (path.empty() /* or some other invalid condition */) { + std::cerr << "Path is invalid: " << path << std::endl; + continue; // Skip this iteration. + } + + std::cout << "Before: " << path << std::endl; // Debug print. + + DocumentReference reference = firestore_->Document(path); + std::cout << "After: " << command.path() + << std::endl; // debug print + + switch (type) { + case PigeonTransactionType::set: + std::cout << "Transaction set" << path + << std::endl; // Debug print. + + if (command.option()->merge() != nullptr && + command.option()->merge()) { + transaction.Set(reference, + ConvertToMapFieldValue(*command.data()), + SetOptions::Merge()); + } else if (command.option()->merge_fields()) { + transaction.Set( + reference, ConvertToMapFieldValue(*command.data()), + SetOptions::MergeFieldPaths(ConvertToFieldPathVector( + *command.option()->merge_fields()))); + } else { + transaction.Set(reference, + ConvertToMapFieldValue(*command.data())); + } + + break; + case PigeonTransactionType::update: + std::cout << "Transaction update" << path + << std::endl; // Debug print. + + transaction.Update(reference, + ConvertToMapFieldValue(*command.data())); + break; + case PigeonTransactionType::deleteType: + std::cout << "Transaction delete" << path + << std::endl; // Debug print. + + transaction.Delete(reference); + break; + } + } + return Error::kErrorOk; + }) + .OnCompletion([this](const Future& completed_future) { + flutter::EncodableMap result; + if (completed_future.error() == firebase::firestore::kErrorOk) { + result.insert(std::make_pair(flutter::EncodableValue("complete"), + flutter::EncodableValue(true))); + events_->Success(result); + } else { + events_->Error("transaction_error", + completed_future.error_message()); + } + events_->EndOfStream(); + }); + + return nullptr; + } + + std::unique_ptr> + OnCancelInternal(const flutter::EncodableValue* arguments) override { + std::unique_lock lock(mtx_); + cv_.notify_one(); + events_->EndOfStream(); + return nullptr; + } + + private: + Firestore* firestore_; + long timeout_; + int maxAttempts_; + std::string transactionId_; + std::vector commands_; + PigeonTransactionResult resultType_; + std::mutex mtx_; + std::mutex commands_mutex_; + std::condition_variable cv_; + std::unique_ptr> events_; + }; + + void CloudFirestorePlugin::TransactionCreate( + const FirestorePigeonFirebaseApp& app, int64_t timeout, + int64_t max_attempts, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + + UUID uuid; + UuidCreate(&uuid); + char* str; + UuidToStringA(&uuid, (RPC_CSTR*)&str); + std::string transactionId(str); + + auto handler = std::make_unique( + firestore, static_cast(timeout), static_cast(max_attempts), + transactionId); + + // Temporarily release the ownership. + TransactionStreamHandler* raw_handler = handler.release(); + CloudFirestorePlugin::transaction_handlers_[transactionId] = + std::unique_ptr(raw_handler); + + // Register the event channel. + std::string channelName = RegisterEventChannelWithUUID( + "plugins.flutter.io/firebase_firestore/transaction/", transactionId, + std::unique_ptr(raw_handler)); + + // Return the result (assumed to be transaction ID in this example). + result(transactionId); } - } - - // Deserialize a FilterOperator - std::string op = std::get(map.at(EncodableValue("op"))); - // Assuming the queries are a list of maps - - std::vector queries; - for (const auto& query : - std::get(map.at(EncodableValue("queries")))) { - queries.push_back(std::get(query)); - } - - std::vector parsedFilters; - for (const auto& query : queries) { - parsedFilters.push_back(filterFromJson(query)); - } - - if (op == "OR") { - return Filter::Or(parsedFilters); - } else if (op == "AND") { - return Filter::And(parsedFilters); - } - - throw std::runtime_error("Invalid operator"); -} - -firebase::firestore::Query ParseQuery(firebase::firestore::Firestore* firestore, - const std::string& path, - bool isCollectionGroup, - const PigeonQueryParameters& parameters) { - try { - firebase::firestore::Query query; - - if (isCollectionGroup) { - query = firestore->CollectionGroup(path); - } else { - query = firestore->Collection(path); + + using flutter::CustomEncodableValue; + + void CloudFirestorePlugin::TransactionStoreResult( + const std::string& transaction_id, + const PigeonTransactionResult& result_type, + const flutter::EncodableList* commands, + std::function reply)> result) { + if (CloudFirestorePlugin::transaction_handlers_[transaction_id]) { + TransactionStreamHandler& handler = *static_cast( + CloudFirestorePlugin::transaction_handlers_[transaction_id].get()); + std::vector commandVector; + for (const auto& element : *commands) { + const PigeonTransactionCommand& command = + std::any_cast( + std::get(element)); + commandVector.push_back(command); + } + handler.ReceiveTransactionResponse(result_type, commandVector); + result(std::nullopt); + + } else { + result(std::make_optional(FlutterError( + "transaction_not_found", "Transaction not found", transaction_id))); + } } - if (parameters.filters()) { - Filter filter = filterFromJson(*parameters.filters()); - query = query.Where(filter); + void CloudFirestorePlugin::TransactionGet( + const FirestorePigeonFirebaseApp& app, const std::string& transaction_id, + const std::string& path, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + DocumentReference reference = firestore->Document(path); + + std::shared_ptr transaction = transactions_[transaction_id]; + Error error_code; + std::string error_message; + + DocumentSnapshot snapshot = + transaction->Get(reference, &error_code, &error_message); + + if (error_code != Error::kErrorOk) { + // NOVO: Log de erro + LogToFile("ERROR: TransactionGet - Falha na leitura do documento '" + path + + "'. Motivo: " + error_message); + // FIM NOVO + result(FlutterError(error_message)); + } else { + // NOVO: Log de sucesso + LogToFile("PASS: TransactionGet - Leitura do documento '" + path + + "' bem-sucedida."); + // FIM NOVO + result(ParseDocumentSnapshot( + snapshot, DocumentSnapshot::ServerTimestampBehavior::kDefault)); + } } - std::vector> conditions = - ConvertToConditions(*parameters.where()); - - for (const auto& condition : conditions) { - const FieldPath& fieldPath = std::any_cast( - std::get(condition[0])); - const std::string& op = std::get(condition[1]); - - auto value = condition[2]; - if (op == "==") { - query = query.WhereEqualTo( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "!=") { - query = query.WhereNotEqualTo( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "<") { - query = query.WhereLessThan( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "<=") { - query = query.WhereLessThanOrEqualTo( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == ">") { - query = query.WhereGreaterThan( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == ">=") { - query = query.WhereGreaterThanOrEqualTo( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "array-contains") { - query = query.WhereArrayContains( - fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); - } else if (op == "array-contains-any") { - query = query.WhereArrayContainsAny( - fieldPath, - ConvertToFieldValueList(std::get(value))); - } else if (op == "in") { - query = query.WhereIn( - fieldPath, - ConvertToFieldValueList(std::get(value))); - } else if (op == "not-in") { - query = query.WhereNotIn( - fieldPath, - ConvertToFieldValueList(std::get(value))); - } else { - throw std::runtime_error("Unknown operator"); - } + using firebase::firestore::DocumentReference; + using firebase::firestore::SetOptions; + + std::vector ConvertToFieldPathVector( + const flutter::EncodableList& encodableList) { + std::vector fieldVector; + + for (const auto& element : encodableList) { + flutter::EncodableList fieldPath = + std::get(element); + + std::vector convertedList; + for (const auto& field : fieldPath) { + convertedList.push_back(std::get(field)); + } + + // Was already converted by the Codec + fieldVector.push_back(firebase::firestore::FieldPath(convertedList)); + } + + return fieldVector; } - if (parameters.limit()) { - query = query.Limit(static_cast(*parameters.limit())); + void CloudFirestorePlugin::DocumentReferenceSet( + const FirestorePigeonFirebaseApp& app, + const DocumentReferenceRequest& request, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + DocumentReference document_reference = firestore->Document(request.path()); + + // Get the data + Future future; + + if (request.option()->merge() != nullptr && request.option()->merge()) { + future = document_reference.Set(ConvertToMapFieldValue(*request.data()), + SetOptions::Merge()); + } else if (request.option()->merge_fields()) { + future = document_reference.Set( + ConvertToMapFieldValue(*request.data()), + SetOptions::MergeFieldPaths( + ConvertToFieldPathVector(*request.option()->merge_fields()))); + } else { + future = document_reference.Set(ConvertToMapFieldValue(*request.data())); + } + + future.OnCompletion([result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); } - if (parameters.limit_to_last()) { - query = - query.LimitToLast(static_cast(*parameters.limit_to_last())); + void CloudFirestorePlugin::DocumentReferenceUpdate( + const FirestorePigeonFirebaseApp& app, + const DocumentReferenceRequest& request, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + DocumentReference document_reference = firestore->Document(request.path()); + + // Get the data + MapFieldPathValue data = ConvertToMapFieldPathValue(*request.data()); + Future future = document_reference.Update(data); + + future.OnCompletion([result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); } - if (parameters.order_by() == nullptr) { - return query; + void CloudFirestorePlugin::DocumentReferenceGet( + const FirestorePigeonFirebaseApp& app, + const DocumentReferenceRequest& request, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + DocumentReference document_reference = firestore->Document(request.path()); + + firebase::firestore::Source source = GetSourceFromPigeon(*request.source()); + + Future future = document_reference.Get(source); + + future.OnCompletion([result, request, + path = request.path() /* NOVO */]( + const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + // NOVO: Log de sucesso + LogToFile("PASS: DocumentReferenceGet - Leitura do documento '" + path + + "' bem-sucedida."); + // FIM NOVO + const DocumentSnapshot* document_snapshot = completed_future.result(); + result(ParseDocumentSnapshot( + *document_snapshot, GetServerTimestampBehaviorFromPigeon( + *request.server_timestamp_behavior()))); + } else { + // NOVO: Log de erro + LogToFile("ERROR: DocumentReferenceGet - Falha na leitura do documento '" + + path + "'. Motivo: " + completed_future.error_message()); + // FIM NOVO + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); } - std::vector> order_bys = - ConvertToConditions(*parameters.order_by()); - - for (const auto& order_by : order_bys) { - const FieldPath& fieldPath = - std::any_cast(std::get(order_by[0])); - const bool& direction = std::get(order_by[1]); - - if (direction) { - query = query.OrderBy(fieldPath, Query::Direction::kDescending); - } else if (!direction) { - query = query.OrderBy(fieldPath, Query::Direction::kAscending); - } else { - throw std::runtime_error("Unknown direction"); - } + void CloudFirestorePlugin::DocumentReferenceDelete( + const FirestorePigeonFirebaseApp& app, + const DocumentReferenceRequest& request, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + DocumentReference document_reference = firestore->Document(request.path()); + + Future future = document_reference.Delete(); + + future.OnCompletion([result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + return; + } + }); } - if (parameters.start_at()) { - query = query.StartAt(ConvertToFieldValueList(*parameters.start_at())); +// Convert EncodableList to std::vector> + std::vector> ConvertToConditions( + const flutter::EncodableList& encodableList) { + std::vector> conditions; + + for (const auto& element : encodableList) { + std::vector condition; + + for (const auto& conditionElement : + std::get(element)) { + condition.push_back(conditionElement); + } + + conditions.push_back(condition); + } + + return conditions; } - if (parameters.start_after()) { - query = - query.StartAfter(ConvertToFieldValueList(*parameters.start_after())); + + using firebase::firestore::Filter; + + firebase::firestore::Filter filterFromJson(const EncodableMap& map) { + if (map.find(EncodableValue("fieldPath")) != map.end()) { + // Deserialize a FilterQuery + std::string op = std::get(map.at(EncodableValue("op"))); + const FieldPath& fieldPath = std::any_cast( + std::get(map.at(EncodableValue("fieldPath")))); + + auto value = map.at(EncodableValue("value")); + + // All the operators from Firebase + if (op == "==") { + return Filter::EqualTo(fieldPath, + CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "!=") { + return Filter::NotEqualTo( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "<") { + return Filter::LessThan(fieldPath, + CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "<=") { + return Filter::LessThanOrEqualTo( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == ">") { + return Filter::GreaterThan( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == ">=") { + return Filter::GreaterThanOrEqualTo( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "array-contains") { + return Filter::ArrayContains( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "array-contains-any") { + // Here you should make sure 'value' is the correct type, e.g., a vector + return Filter::ArrayContainsAny( + fieldPath, + ConvertToFieldValueList(std::get(value))); + } else if (op == "in") { + return Filter::In( + fieldPath, + ConvertToFieldValueList(std::get(value))); + } else if (op == "not-in") { + return Filter::NotIn( + fieldPath, + ConvertToFieldValueList(std::get(value))); + } else { + throw std::runtime_error("Invalid operator"); + } + } + + // Deserialize a FilterOperator + std::string op = std::get(map.at(EncodableValue("op"))); + // Assuming the queries are a list of maps + + std::vector queries; + for (const auto& query : + std::get(map.at(EncodableValue("queries")))) { + queries.push_back(std::get(query)); + } + + std::vector parsedFilters; + for (const auto& query : queries) { + parsedFilters.push_back(filterFromJson(query)); + } + + if (op == "OR") { + return Filter::Or(parsedFilters); + } else if (op == "AND") { + return Filter::And(parsedFilters); + } + + throw std::runtime_error("Invalid operator"); } - if (parameters.end_at()) { - query = query.EndAt(ConvertToFieldValueList(*parameters.end_at())); + + firebase::firestore::Query ParseQuery(firebase::firestore::Firestore* firestore, + const std::string& path, + bool isCollectionGroup, + const PigeonQueryParameters& parameters) { + try { + firebase::firestore::Query query; + + if (isCollectionGroup) { + query = firestore->CollectionGroup(path); + } else { + query = firestore->Collection(path); + } + + if (parameters.filters()) { + Filter filter = filterFromJson(*parameters.filters()); + query = query.Where(filter); + } + + std::vector> conditions = + ConvertToConditions(*parameters.where()); + + for (const auto& condition : conditions) { + const FieldPath& fieldPath = std::any_cast( + std::get(condition[0])); + const std::string& op = std::get(condition[1]); + + auto value = condition[2]; + if (op == "==") { + query = query.WhereEqualTo( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "!=") { + query = query.WhereNotEqualTo( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "<") { + query = query.WhereLessThan( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "<=") { + query = query.WhereLessThanOrEqualTo( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == ">") { + query = query.WhereGreaterThan( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == ">=") { + query = query.WhereGreaterThanOrEqualTo( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "array-contains") { + query = query.WhereArrayContains( + fieldPath, CloudFirestorePlugin::ConvertToFieldValue(value)); + } else if (op == "array-contains-any") { + query = query.WhereArrayContainsAny( + fieldPath, + ConvertToFieldValueList(std::get(value))); + } else if (op == "in") { + query = query.WhereIn( + fieldPath, + ConvertToFieldValueList(std::get(value))); + } else if (op == "not-in") { + query = query.WhereNotIn( + fieldPath, + ConvertToFieldValueList(std::get(value))); + } else { + throw std::runtime_error("Unknown operator"); + } + } + + if (parameters.limit()) { + query = query.Limit(static_cast(*parameters.limit())); + } + + if (parameters.limit_to_last()) { + query = + query.LimitToLast(static_cast(*parameters.limit_to_last())); + } + + if (parameters.order_by() == nullptr) { + return query; + } + + std::vector> order_bys = + ConvertToConditions(*parameters.order_by()); + + for (const auto& order_by : order_bys) { + const FieldPath& fieldPath = + std::any_cast(std::get(order_by[0])); + const bool& direction = std::get(order_by[1]); + + if (direction) { + query = query.OrderBy(fieldPath, Query::Direction::kDescending); + } else if (!direction) { + query = query.OrderBy(fieldPath, Query::Direction::kAscending); + } else { + throw std::runtime_error("Unknown direction"); + } + } + + if (parameters.start_at()) { + query = query.StartAt(ConvertToFieldValueList(*parameters.start_at())); + } + if (parameters.start_after()) { + query = + query.StartAfter(ConvertToFieldValueList(*parameters.start_after())); + } + if (parameters.end_at()) { + query = query.EndAt(ConvertToFieldValueList(*parameters.end_at())); + } + if (parameters.end_before()) { + query = + query.EndBefore(ConvertToFieldValueList(*parameters.end_before())); + } + + return query; + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + // Return a 'null' or 'empty' query based on your C++ Firestore API + return firebase::firestore::Query(); + } } - if (parameters.end_before()) { - query = - query.EndBefore(ConvertToFieldValueList(*parameters.end_before())); + + void CloudFirestorePlugin::QueryGet( + const FirestorePigeonFirebaseApp& app, const std::string& path, + bool is_collection_group, const PigeonQueryParameters& parameters, + const PigeonGetOptions& options, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + Query query = ParseQuery(firestore, path, is_collection_group, parameters); + + firebase::firestore::Source source = GetSourceFromPigeon(options.source()); + + Future future = query.Get(source); + + future.OnCompletion([result, options, + path /* NOVO */](const Future& + completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + // NOVO: Log de sucesso + LogToFile("PASS: QueryGet - Leitura da coleção/grupo '" + path + + "' bem-sucedida."); + // FIM NOVO + const firebase::firestore::QuerySnapshot* querySnapshot = + completed_future.result(); + result(ParseQuerySnapshot(querySnapshot, + GetServerTimestampBehaviorFromPigeon( + options.server_timestamp_behavior()))); + } else { + // NOVO: Log de erro + LogToFile("ERROR: QueryGet - Falha na leitura da coleção/grupo '" + path + + "'. Motivo: " + completed_future.error_message()); + // FIM NOVO + result(CloudFirestorePlugin::ParseError(completed_future)); + } + }); } - return query; - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; - // Return a 'null' or 'empty' query based on your C++ Firestore API - return firebase::firestore::Query(); - } -} - -void CloudFirestorePlugin::QueryGet( - const FirestorePigeonFirebaseApp& app, const std::string& path, - bool is_collection_group, const PigeonQueryParameters& parameters, - const PigeonGetOptions& options, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - Query query = ParseQuery(firestore, path, is_collection_group, parameters); - - firebase::firestore::Source source = GetSourceFromPigeon(options.source()); - - Future future = query.Get(source); - - future.OnCompletion( - [result, options]( - const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - const firebase::firestore::QuerySnapshot* querySnapshot = - completed_future.result(); - result(ParseQuerySnapshot(querySnapshot, - GetServerTimestampBehaviorFromPigeon( - options.server_timestamp_behavior()))); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); + firebase::firestore::AggregateSource GetAggregateSourceFromPigeon( + const AggregateSource& source) { + switch (source) { + case AggregateSource::server: + default: + return firebase::firestore::AggregateSource::kServer; } - }); -} - -firebase::firestore::AggregateSource GetAggregateSourceFromPigeon( - const AggregateSource& source) { - switch (source) { - case AggregateSource::server: - default: - return firebase::firestore::AggregateSource::kServer; - } -} - -void CloudFirestorePlugin::AggregateQuery( - const FirestorePigeonFirebaseApp& app, const std::string& path, - const PigeonQueryParameters& parameters, const AggregateSource& source, - const flutter::EncodableList& queries, bool is_collection_group, - std::function reply)> result) { - Firestore* firestore = GetFirestoreFromPigeon(app); - Query query = ParseQuery(firestore, path, is_collection_group, parameters); - - // C++ SDK does not support average and sum - firebase::firestore::AggregateQuery aggregate_query; - - for (auto& queryRequest : queries) { - const cloud_firestore_windows::AggregateQuery& queryRequestTyped = - std::any_cast( - std::get(queryRequest)); - - switch (queryRequestTyped.type()) { - case AggregateType::count: - aggregate_query = query.Count(); - break; - case AggregateType::sum: - std::cout << "Sum is not supported on C++" << std::endl; - break; - case AggregateType::average: - std::cout << "Average is not supported on C++" << std::endl; - break; } - } - Future future = - aggregate_query.Get(GetAggregateSourceFromPigeon(source)); + void CloudFirestorePlugin::AggregateQuery( + const FirestorePigeonFirebaseApp& app, const std::string& path, + const PigeonQueryParameters& parameters, const AggregateSource& source, + const flutter::EncodableList& queries, bool is_collection_group, + std::function reply)> result) { + Firestore* firestore = GetFirestoreFromPigeon(app); + Query query = ParseQuery(firestore, path, is_collection_group, parameters); - future.OnCompletion( - [result, - queries](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - const AggregateQuerySnapshot* aggregateQuerySnapshot = - completed_future.result(); - EncodableList aggregateResponses; + // C++ SDK does not support average and sum + firebase::firestore::AggregateQuery aggregate_query; - for (auto& queryRequest : queries) { + for (auto& queryRequest : queries) { const cloud_firestore_windows::AggregateQuery& queryRequestTyped = - std::any_cast( - std::get(queryRequest)); + std::any_cast( + std::get(queryRequest)); switch (queryRequestTyped.type()) { - case AggregateType::count: { - double doubleValue = - static_cast(aggregateQuerySnapshot->count()); - AggregateQueryResponse aggregateResponse(AggregateType::count, - nullptr, &doubleValue); - aggregateResponses.push_back( - CustomEncodableValue(aggregateResponse)); - break; - } - case AggregateType::sum: { - std::cout << "Sum is not supported on C++" << std::endl; - break; - } - case AggregateType::average: { - std::cout << "Average is not supported on C++" << std::endl; - break; - } + case AggregateType::count: + aggregate_query = query.Count(); + break; + case AggregateType::sum: + std::cout << "Sum is not supported on C++" << std::endl; + break; + case AggregateType::average: + std::cout << "Average is not supported on C++" << std::endl; + break; } - } - - result(aggregateResponses); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); } - }); -} - -void CloudFirestorePlugin::WriteBatchCommit( - const FirestorePigeonFirebaseApp& app, const flutter::EncodableList& writes, - std::function reply)> result) { - try { - Firestore* firestore = GetFirestoreFromPigeon(app); - firebase::firestore::WriteBatch batch = firestore->batch(); - - for (const auto& write : writes) { - const PigeonTransactionCommand& transaction = - std::any_cast( - std::get(write)); - - PigeonTransactionType type = transaction.type(); - std::string path = transaction.path(); - auto data = transaction.data(); - - firebase::firestore::DocumentReference documentReference = - firestore->Document(path); - - switch (type) { - case PigeonTransactionType::deleteType: - batch.Delete(documentReference); - break; - case PigeonTransactionType::update: - batch.Update(documentReference, ConvertToMapFieldValue(*data)); - break; - case PigeonTransactionType::set: - const PigeonDocumentOption* options = transaction.option(); - - if (options->merge()) { - batch.Set(documentReference, ConvertToMapFieldValue(*data), - firebase::firestore::SetOptions::Merge()); - } else if (options->merge_fields()) { - batch.Set(documentReference, ConvertToMapFieldValue(*data), - SetOptions::MergeFieldPaths( - ConvertToFieldPathVector(*options->merge_fields()))); - } else { - batch.Set(documentReference, ConvertToMapFieldValue(*data)); - } - break; - } + + Future future = + aggregate_query.Get(GetAggregateSourceFromPigeon(source)); + + future.OnCompletion( + [result, + queries](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + const AggregateQuerySnapshot* aggregateQuerySnapshot = + completed_future.result(); + EncodableList aggregateResponses; + + for (auto& queryRequest : queries) { + const cloud_firestore_windows::AggregateQuery& queryRequestTyped = + std::any_cast( + std::get(queryRequest)); + + switch (queryRequestTyped.type()) { + case AggregateType::count: { + double doubleValue = + static_cast(aggregateQuerySnapshot->count()); + AggregateQueryResponse aggregateResponse(AggregateType::count, + nullptr, &doubleValue); + aggregateResponses.push_back( + CustomEncodableValue(aggregateResponse)); + break; + } + case AggregateType::sum: { + std::cout << "Sum is not supported on C++" << std::endl; + break; + } + case AggregateType::average: { + std::cout << "Average is not supported on C++" << std::endl; + break; + } + } + } + + result(aggregateResponses); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + } + }); } - batch.Commit().OnCompletion([result](const Future& completed_future) { - if (completed_future.error() == firebase::firestore::kErrorOk) { - result(std::nullopt); - } else { - result(CloudFirestorePlugin::ParseError(completed_future)); - } - }); - - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; - result(FlutterError(e.what())); - } -} - -std::string METHOD_CHANNEL_NAME = "cloud_firestore"; - -using firebase::firestore::ListenerRegistration; - -class QuerySnapshotStreamHandler - : public flutter::StreamHandler { - public: - QuerySnapshotStreamHandler( - std::unique_ptr query, bool includeMetadataChanges, - firebase::firestore::DocumentSnapshot::ServerTimestampBehavior - serverTimestampBehavior) { - query_ = std::move(query); - includeMetadataChanges_ = includeMetadataChanges; - serverTimestampBehavior_ = serverTimestampBehavior; - } - - std::unique_ptr> - OnListenInternal( - const flutter::EncodableValue* arguments, - std::unique_ptr>&& events) - override { - MetadataChanges metadataChanges = includeMetadataChanges_ - ? MetadataChanges::kInclude - : MetadataChanges::kExclude; - - events_ = std::move(events); - events.reset(); - - listener_ = query_->AddSnapshotListener( - metadataChanges, - [this, serverTimestampBehavior = serverTimestampBehavior_, - metadataChanges](const firebase::firestore::QuerySnapshot& snapshot, - firebase::firestore::Error error, - const std::string& errorMessage) mutable { - if (error == firebase::firestore::kErrorOk) { - flutter::EncodableList toListResult(3); - std::vector documents; - std::vector documentChanges; - - for (const auto& documentSnapshot : snapshot.documents()) { - documents.push_back(ParseDocumentSnapshot(documentSnapshot, - serverTimestampBehavior) - .ToEncodableList()); - } + void CloudFirestorePlugin::WriteBatchCommit( + const FirestorePigeonFirebaseApp& app, const flutter::EncodableList& writes, + std::function reply)> result) { + try { + Firestore* firestore = GetFirestoreFromPigeon(app); + firebase::firestore::WriteBatch batch = firestore->batch(); - // Assuming querySnapshot.getDocumentChanges() returns an iterable - // collection - for (const auto& documentChange : - snapshot.DocumentChanges(metadataChanges)) { - documentChanges.push_back( - ParseDocumentChange(documentChange, serverTimestampBehavior) - .ToEncodableList()); + for (const auto& write : writes) { + const PigeonTransactionCommand& transaction = + std::any_cast( + std::get(write)); + + PigeonTransactionType type = transaction.type(); + std::string path = transaction.path(); + auto data = transaction.data(); + + firebase::firestore::DocumentReference documentReference = + firestore->Document(path); + + switch (type) { + case PigeonTransactionType::deleteType: + batch.Delete(documentReference); + break; + case PigeonTransactionType::update: + batch.Update(documentReference, ConvertToMapFieldValue(*data)); + break; + case PigeonTransactionType::set: + const PigeonDocumentOption* options = transaction.option(); + + if (options->merge()) { + batch.Set(documentReference, ConvertToMapFieldValue(*data), + firebase::firestore::SetOptions::Merge()); + } else if (options->merge_fields()) { + batch.Set(documentReference, ConvertToMapFieldValue(*data), + SetOptions::MergeFieldPaths( + ConvertToFieldPathVector(*options->merge_fields()))); + } else { + batch.Set(documentReference, ConvertToMapFieldValue(*data)); + } + break; + } } - toListResult[0] = documents; - toListResult[1] = documentChanges; - toListResult[2] = - ParseSnapshotMetadata(snapshot.metadata()).ToEncodableList(); + batch.Commit().OnCompletion([result](const Future& completed_future) { + if (completed_future.error() == firebase::firestore::kErrorOk) { + result(std::nullopt); + } else { + result(CloudFirestorePlugin::ParseError(completed_future)); + } + }); + + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + result(FlutterError(e.what())); + } + } + + std::string METHOD_CHANNEL_NAME = "cloud_firestore"; - events_->Success(toListResult); - } else { - EncodableMap details; - details[EncodableValue("code")] = - EncodableValue(CloudFirestorePlugin::GetErrorCode(error)); - details[EncodableValue("message")] = EncodableValue(errorMessage); + using firebase::firestore::ListenerRegistration; - events_->Error("firebase_firestore", errorMessage, details); + class QuerySnapshotStreamHandler + : public flutter::StreamHandler { + public: + QuerySnapshotStreamHandler( + std::unique_ptr query, bool includeMetadataChanges, + firebase::firestore::DocumentSnapshot::ServerTimestampBehavior + serverTimestampBehavior) { + query_ = std::move(query); + includeMetadataChanges_ = includeMetadataChanges; + serverTimestampBehavior_ = serverTimestampBehavior; + } + + std::unique_ptr> + OnListenInternal( + const flutter::EncodableValue* arguments, + std::unique_ptr>&& events) + override { + MetadataChanges metadataChanges = includeMetadataChanges_ + ? MetadataChanges::kInclude + : MetadataChanges::kExclude; + + events_ = std::move(events); + events.reset(); + + listener_ = query_->AddSnapshotListener( + metadataChanges, + [this, serverTimestampBehavior = serverTimestampBehavior_, + metadataChanges](const firebase::firestore::QuerySnapshot& snapshot, + firebase::firestore::Error error, + const std::string& errorMessage) mutable { + if (error == firebase::firestore::kErrorOk) { + flutter::EncodableList toListResult(3); + std::vector documents; + std::vector documentChanges; + + if (error == firebase::firestore::kErrorOk) { + // NOVO: Log de sucesso para o listener + LogToFile("PASS: QuerySnapshot listener recebeu atualização de dados."); + // FIM NOVO + // ... resto do código de sucesso + events_->Success(toListResult); + } else { + // NOVO: Log de erro para o listener + LogToFile("ERROR: QuerySnapshot listener falhou. Motivo: " + + errorMessage); + // FIM NOVO + EncodableMap details; + // ... resto do código de erro + } + + for (const auto& documentSnapshot : snapshot.documents()) { + documents.push_back(ParseDocumentSnapshot(documentSnapshot, + serverTimestampBehavior) + .ToEncodableList()); + } + + // Assuming querySnapshot.getDocumentChanges() returns an iterable + // collection + for (const auto& documentChange : + snapshot.DocumentChanges(metadataChanges)) { + documentChanges.push_back( + ParseDocumentChange(documentChange, serverTimestampBehavior) + .ToEncodableList()); + } + + toListResult[0] = documents; + toListResult[1] = documentChanges; + toListResult[2] = + ParseSnapshotMetadata(snapshot.metadata()).ToEncodableList(); + + events_->Success(toListResult); + } else { + + + EncodableMap details; + details[EncodableValue("code")] = + EncodableValue(CloudFirestorePlugin::GetErrorCode(error)); + details[EncodableValue("message")] = EncodableValue(errorMessage); + + events_->Error("firebase_firestore", errorMessage, details); + events_->EndOfStream(); + } + }); + return nullptr; + } + + std::unique_ptr> + OnCancelInternal(const flutter::EncodableValue* arguments) override { + listener_.Remove(); events_->EndOfStream(); - } - }); - return nullptr; - } - - std::unique_ptr> - OnCancelInternal(const flutter::EncodableValue* arguments) override { - listener_.Remove(); - events_->EndOfStream(); - return nullptr; - } - - private: - ListenerRegistration listener_; - std::unique_ptr query_; - std::unique_ptr> events_; - bool includeMetadataChanges_; - firebase::firestore::DocumentSnapshot::ServerTimestampBehavior - serverTimestampBehavior_; -}; - -void CloudFirestorePlugin::QuerySnapshot( - const FirestorePigeonFirebaseApp& app, const std::string& path, - bool is_collection_group, const PigeonQueryParameters& parameters, - const PigeonGetOptions& options, bool include_metadata_changes, - const ListenSource& source, - std::function reply)> result) { - if (source == ListenSource::cache) { - result(FlutterError("Listening from cache isn't supported on Windows")); - return; - } - - Firestore* firestore = GetFirestoreFromPigeon(app); - std::unique_ptr query_ptr = std::make_unique( - ParseQuery(firestore, path, is_collection_group, parameters)); - - auto query_snapshot_handler = std::make_unique( - std::move(query_ptr), include_metadata_changes, - GetServerTimestampBehaviorFromPigeon( - options.server_timestamp_behavior())); - - std::string channelName = - RegisterEventChannel("plugins.flutter.io/firebase_firestore/query/", - std::move(query_snapshot_handler)); - - result(channelName); -} - -class DocumentSnapshotStreamHandler - : public flutter::StreamHandler { - public: - DocumentSnapshotStreamHandler( - std::unique_ptr reference, bool includeMetadataChanges, - firebase::firestore::DocumentSnapshot::ServerTimestampBehavior - serverTimestampBehavior) { - reference_ = std::move(reference); - includeMetadataChanges_ = includeMetadataChanges; - serverTimestampBehavior_ = serverTimestampBehavior; - } - - std::unique_ptr> - OnListenInternal( - const flutter::EncodableValue* arguments, - std::unique_ptr>&& events) - override { - MetadataChanges metadataChanges = includeMetadataChanges_ - ? MetadataChanges::kInclude - : MetadataChanges::kExclude; - - events_ = std::move(events); - events.reset(); - - listener_ = reference_->AddSnapshotListener( - metadataChanges, - [this, serverTimestampBehavior = serverTimestampBehavior_, - metadataChanges](const firebase::firestore::DocumentSnapshot& snapshot, - firebase::firestore::Error error, - const std::string& errorMessage) mutable { - if (error == firebase::firestore::kErrorOk) { - events_->Success( - ParseDocumentSnapshot(snapshot, serverTimestampBehavior) - .ToEncodableList()); - } else { - EncodableMap details; - details[EncodableValue("code")] = - EncodableValue(CloudFirestorePlugin::GetErrorCode(error)); - details[EncodableValue("message")] = EncodableValue(errorMessage); - - events_->Error("firebase_firestore", errorMessage, details); + return nullptr; + } + + private: + ListenerRegistration listener_; + std::unique_ptr query_; + std::unique_ptr> events_; + bool includeMetadataChanges_; + firebase::firestore::DocumentSnapshot::ServerTimestampBehavior + serverTimestampBehavior_; + }; + + void CloudFirestorePlugin::QuerySnapshot( + const FirestorePigeonFirebaseApp& app, const std::string& path, + bool is_collection_group, const PigeonQueryParameters& parameters, + const PigeonGetOptions& options, bool include_metadata_changes, + const ListenSource& source, + std::function reply)> result) { + if (source == ListenSource::cache) { + result(FlutterError("Listening from cache isn't supported on Windows")); + return; + } + + Firestore* firestore = GetFirestoreFromPigeon(app); + std::unique_ptr query_ptr = std::make_unique( + ParseQuery(firestore, path, is_collection_group, parameters)); + + auto query_snapshot_handler = std::make_unique( + std::move(query_ptr), include_metadata_changes, + GetServerTimestampBehaviorFromPigeon( + options.server_timestamp_behavior())); + + std::string channelName = + RegisterEventChannel("plugins.flutter.io/firebase_firestore/query/", + std::move(query_snapshot_handler)); + + result(channelName); + } + + class DocumentSnapshotStreamHandler + : public flutter::StreamHandler { + public: + DocumentSnapshotStreamHandler( + std::unique_ptr reference, bool includeMetadataChanges, + firebase::firestore::DocumentSnapshot::ServerTimestampBehavior + serverTimestampBehavior) { + reference_ = std::move(reference); + includeMetadataChanges_ = includeMetadataChanges; + serverTimestampBehavior_ = serverTimestampBehavior; + } + + std::unique_ptr> + OnListenInternal( + const flutter::EncodableValue* arguments, + std::unique_ptr>&& events) + override { + MetadataChanges metadataChanges = includeMetadataChanges_ + ? MetadataChanges::kInclude + : MetadataChanges::kExclude; + + events_ = std::move(events); + events.reset(); + + listener_ = reference_->AddSnapshotListener( + metadataChanges, + [this, serverTimestampBehavior = serverTimestampBehavior_, + metadataChanges, + path = reference_->path() /* NOVO */](const firebase::firestore::DocumentSnapshot& snapshot, + firebase::firestore::Error error, + const std::string& errorMessage) mutable { + + if (error == firebase::firestore::kErrorOk) { + // NOVO: Log de sucesso + LogToFile( + "PASS: DocumentSnapshot listener recebeu atualização para o " + "documento '" + + path + "'."); + // FIM NOVO + events_->Success( + ParseDocumentSnapshot(snapshot, serverTimestampBehavior) + .ToEncodableList()); + } else { + // NOVO: Log de erro + LogToFile( + "ERROR: DocumentSnapshot listener falhou para o documento '" + + path + "'. Motivo: " + errorMessage); + // FIM NOVO + EncodableMap details; + details[EncodableValue("code")] = + EncodableValue(CloudFirestorePlugin::GetErrorCode(error)); + details[EncodableValue("message")] = EncodableValue(errorMessage); + + events_->Error("firebase_firestore", errorMessage, details); + events_->EndOfStream(); + } + }); + return nullptr; + } + + std::unique_ptr> + OnCancelInternal(const flutter::EncodableValue* arguments) override { + listener_.Remove(); events_->EndOfStream(); - } - }); - return nullptr; - } - - std::unique_ptr> - OnCancelInternal(const flutter::EncodableValue* arguments) override { - listener_.Remove(); - events_->EndOfStream(); - return nullptr; - } - - private: - firebase::firestore::ListenerRegistration listener_; - std::unique_ptr reference_; - std::unique_ptr> events_; - bool includeMetadataChanges_; - firebase::firestore::DocumentSnapshot::ServerTimestampBehavior - serverTimestampBehavior_; -}; - -void CloudFirestorePlugin::DocumentReferenceSnapshot( - const FirestorePigeonFirebaseApp& app, - const DocumentReferenceRequest& parameters, bool include_metadata_changes, - const ListenSource& source, - std::function reply)> result) { - if (source == ListenSource::cache) { - result(FlutterError("Listening from cache isn't supported on Windows")); - return; - } - Firestore* firestore = GetFirestoreFromPigeon(app); - std::unique_ptr documentReference = - std::make_unique( - firestore->Document(parameters.path())); - - auto document_snapshot_handler = - std::make_unique( - std::move(documentReference), include_metadata_changes, - GetServerTimestampBehaviorFromPigeon( - *parameters.server_timestamp_behavior())); - - std::string channelName = - RegisterEventChannel("plugins.flutter.io/firebase_firestore/document/", - std::move(document_snapshot_handler)); - result(channelName); -} - -void CloudFirestorePlugin::PersistenceCacheIndexManagerRequest( - const FirestorePigeonFirebaseApp& app, - const PersistenceCacheIndexManagerRequestEnum& request, - std::function reply)> result) { - result(FlutterError("Not implemented on Windows")); -} + return nullptr; + } + + private: + firebase::firestore::ListenerRegistration listener_; + std::unique_ptr reference_; + std::unique_ptr> events_; + bool includeMetadataChanges_; + firebase::firestore::DocumentSnapshot::ServerTimestampBehavior + serverTimestampBehavior_; + }; + + void CloudFirestorePlugin::DocumentReferenceSnapshot( + const FirestorePigeonFirebaseApp& app, + const DocumentReferenceRequest& parameters, bool include_metadata_changes, + const ListenSource& source, + std::function reply)> result) { + if (source == ListenSource::cache) { + result(FlutterError("Listening from cache isn't supported on Windows")); + return; + } + Firestore* firestore = GetFirestoreFromPigeon(app); + std::unique_ptr documentReference = + std::make_unique( + firestore->Document(parameters.path())); + + auto document_snapshot_handler = + std::make_unique( + std::move(documentReference), include_metadata_changes, + GetServerTimestampBehaviorFromPigeon( + *parameters.server_timestamp_behavior())); + + std::string channelName = + RegisterEventChannel("plugins.flutter.io/firebase_firestore/document/", + std::move(document_snapshot_handler)); + result(channelName); + } + + void CloudFirestorePlugin::PersistenceCacheIndexManagerRequest( + const FirestorePigeonFirebaseApp& app, + const PersistenceCacheIndexManagerRequestEnum& request, + std::function reply)> result) { + result(FlutterError("Not implemented on Windows")); + } } // namespace cloud_firestore_windows