FSTUserDataWriter.mm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Copyright 2021 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "Firestore/Source/API/FSTUserDataWriter.h"
  15. #import <Foundation/Foundation.h>
  16. #include <string>
  17. #include <utility>
  18. #include "Firestore/Protos/nanopb/google/firestore/v1/document.nanopb.h"
  19. #include "Firestore/Source/API/FIRDocumentReference+Internal.h"
  20. #include "Firestore/Source/API/FIRFieldValue+Internal.h"
  21. #include "Firestore/Source/API/converters.h"
  22. #include "Firestore/Source/Public/FirebaseFirestore/FIRBSONBinaryData.h"
  23. #include "Firestore/Source/Public/FirebaseFirestore/FIRBSONObjectId.h"
  24. #include "Firestore/Source/Public/FirebaseFirestore/FIRBSONTimestamp.h"
  25. #include "Firestore/Source/Public/FirebaseFirestore/FIRDecimal128Value.h"
  26. #include "Firestore/Source/Public/FirebaseFirestore/FIRInt32Value.h"
  27. #include "Firestore/Source/Public/FirebaseFirestore/FIRMaxKey.h"
  28. #include "Firestore/Source/Public/FirebaseFirestore/FIRMinKey.h"
  29. #include "Firestore/Source/Public/FirebaseFirestore/FIRRegexValue.h"
  30. #include "Firestore/core/include/firebase/firestore/geo_point.h"
  31. #include "Firestore/core/include/firebase/firestore/timestamp.h"
  32. #include "Firestore/core/src/api/firestore.h"
  33. #include "Firestore/core/src/model/database_id.h"
  34. #include "Firestore/core/src/model/document_key.h"
  35. #include "Firestore/core/src/model/server_timestamp_util.h"
  36. #include "Firestore/core/src/model/value_util.h"
  37. #include "Firestore/core/src/nanopb/nanopb_util.h"
  38. #include "Firestore/core/src/util/hard_assert.h"
  39. #include "Firestore/core/src/util/log.h"
  40. #include "Firestore/core/src/util/string_apple.h"
  41. @class FIRTimestamp;
  42. namespace api = firebase::firestore::api;
  43. namespace model = firebase::firestore::model;
  44. namespace nanopb = firebase::firestore::nanopb;
  45. using api::MakeFIRDocumentReference;
  46. using api::MakeFIRGeoPoint;
  47. using api::MakeFIRTimestamp;
  48. using firebase::firestore::GeoPoint;
  49. using firebase::firestore::google_firestore_v1_ArrayValue;
  50. using firebase::firestore::google_firestore_v1_MapValue;
  51. using firebase::firestore::google_firestore_v1_Value;
  52. using firebase::firestore::google_protobuf_Timestamp;
  53. using firebase::firestore::model::kRawBsonTimestampTypeIncrementFieldValue;
  54. using firebase::firestore::model::kRawBsonTimestampTypeSecondsFieldValue;
  55. using firebase::firestore::model::kRawDecimal128TypeFieldValue;
  56. using firebase::firestore::model::kRawInt32TypeFieldValue;
  57. using firebase::firestore::model::kRawRegexTypeOptionsFieldValue;
  58. using firebase::firestore::model::kRawRegexTypePatternFieldValue;
  59. using firebase::firestore::model::kRawVectorValueFieldKey;
  60. using firebase::firestore::util::MakeNSString;
  61. using model::DatabaseId;
  62. using model::DocumentKey;
  63. using model::GetLocalWriteTime;
  64. using model::GetPreviousValue;
  65. using model::GetTypeOrder;
  66. using model::TypeOrder;
  67. using nanopb::MakeBytesArray;
  68. using nanopb::MakeByteString;
  69. using nanopb::MakeNSData;
  70. using nanopb::MakeString;
  71. using nanopb::MakeStringView;
  72. NS_ASSUME_NONNULL_BEGIN
  73. @implementation FSTUserDataWriter {
  74. std::shared_ptr<api::Firestore> _firestore;
  75. FIRServerTimestampBehavior _serverTimestampBehavior;
  76. }
  77. - (instancetype)initWithFirestore:(std::shared_ptr<api::Firestore>)firestore
  78. serverTimestampBehavior:(FIRServerTimestampBehavior)serverTimestampBehavior {
  79. self = [super init];
  80. if (self) {
  81. _firestore = std::move(firestore);
  82. _serverTimestampBehavior = serverTimestampBehavior;
  83. }
  84. return self;
  85. }
  86. - (id)convertedValue:(const google_firestore_v1_Value &)value {
  87. switch (GetTypeOrder(value)) {
  88. case TypeOrder::kMap:
  89. return [self convertedObject:value.map_value];
  90. case TypeOrder::kArray:
  91. return [self convertedArray:value.array_value];
  92. case TypeOrder::kReference:
  93. return [self convertedReference:value];
  94. case TypeOrder::kTimestamp:
  95. return [self convertedTimestamp:value.timestamp_value];
  96. case TypeOrder::kServerTimestamp:
  97. return [self convertedServerTimestamp:value];
  98. case TypeOrder::kNull:
  99. return [NSNull null];
  100. case TypeOrder::kBoolean:
  101. return value.boolean_value ? @YES : @NO;
  102. case TypeOrder::kNumber:
  103. if (value.which_value_type == google_firestore_v1_Value_map_value_tag) {
  104. absl::string_view key = MakeStringView(value.map_value.fields[0].key);
  105. if (key.compare(absl::string_view(kRawInt32TypeFieldValue)) == 0) {
  106. return [self convertedInt32:value.map_value];
  107. } else if (key.compare(absl::string_view(kRawDecimal128TypeFieldValue)) == 0) {
  108. return [self convertedDecimal128Value:value.map_value];
  109. }
  110. }
  111. return value.which_value_type == google_firestore_v1_Value_integer_value_tag
  112. ? @(value.integer_value)
  113. : @(value.double_value);
  114. case TypeOrder::kString:
  115. return MakeNSString(MakeStringView(value.string_value));
  116. case TypeOrder::kBlob:
  117. return MakeNSData(value.bytes_value);
  118. case TypeOrder::kGeoPoint:
  119. return MakeFIRGeoPoint(
  120. GeoPoint(value.geo_point_value.latitude, value.geo_point_value.longitude));
  121. case TypeOrder::kMinKey:
  122. return [FIRMinKey shared];
  123. case TypeOrder::kMaxKey:
  124. return [FIRMaxKey shared];
  125. case TypeOrder::kRegex:
  126. return [self convertedRegex:value.map_value];
  127. case TypeOrder::kBsonObjectId:
  128. return [self convertedBsonObjectId:value.map_value];
  129. case TypeOrder::kBsonTimestamp:
  130. return [self convertedBsonTimestamp:value.map_value];
  131. case TypeOrder::kBsonBinaryData:
  132. return [self convertedBsonBinaryData:value.map_value];
  133. case TypeOrder::kVector:
  134. return [self convertedVector:value.map_value];
  135. case TypeOrder::kInternalMaxValue:
  136. // It is not possible for users to construct a kInternalMaxValue manually.
  137. break;
  138. }
  139. UNREACHABLE();
  140. }
  141. - (NSDictionary<NSString *, id> *)convertedObject:(const google_firestore_v1_MapValue &)mapValue {
  142. NSMutableDictionary *result = [NSMutableDictionary dictionary];
  143. for (pb_size_t i = 0; i < mapValue.fields_count; ++i) {
  144. absl::string_view key = MakeStringView(mapValue.fields[i].key);
  145. const google_firestore_v1_Value &value = mapValue.fields[i].value;
  146. result[MakeNSString(key)] = [self convertedValue:value];
  147. }
  148. return result;
  149. }
  150. - (FIRVectorValue *)convertedVector:(const google_firestore_v1_MapValue &)mapValue {
  151. for (pb_size_t i = 0; i < mapValue.fields_count; ++i) {
  152. absl::string_view key = MakeStringView(mapValue.fields[i].key);
  153. const google_firestore_v1_Value &value = mapValue.fields[i].value;
  154. if ((key.compare(absl::string_view(kRawVectorValueFieldKey)) == 0) &&
  155. value.which_value_type == google_firestore_v1_Value_array_value_tag) {
  156. return [FIRFieldValue vectorWithArray:[self convertedArray:value.array_value]];
  157. }
  158. }
  159. return [FIRFieldValue vectorWithArray:@[]];
  160. }
  161. - (FIRRegexValue *)convertedRegex:(const google_firestore_v1_MapValue &)mapValue {
  162. NSString *pattern = @"";
  163. NSString *options = @"";
  164. if (mapValue.fields_count == 1) {
  165. const google_firestore_v1_Value &innerValue = mapValue.fields[0].value;
  166. if (innerValue.which_value_type == google_firestore_v1_Value_map_value_tag) {
  167. for (pb_size_t i = 0; i < innerValue.map_value.fields_count; ++i) {
  168. absl::string_view key = MakeStringView(innerValue.map_value.fields[i].key);
  169. const google_firestore_v1_Value &value = innerValue.map_value.fields[i].value;
  170. if ((key.compare(absl::string_view(kRawRegexTypePatternFieldValue)) == 0) &&
  171. value.which_value_type == google_firestore_v1_Value_string_value_tag) {
  172. pattern = MakeNSString(MakeStringView(value.string_value));
  173. }
  174. if ((key.compare(absl::string_view(kRawRegexTypeOptionsFieldValue)) == 0) &&
  175. value.which_value_type == google_firestore_v1_Value_string_value_tag) {
  176. options = MakeNSString(MakeStringView(value.string_value));
  177. }
  178. }
  179. }
  180. }
  181. return [[FIRRegexValue alloc] initWithPattern:pattern options:options];
  182. }
  183. - (FIRInt32Value *)convertedInt32:(const google_firestore_v1_MapValue &)mapValue {
  184. int32_t value = 0;
  185. if (mapValue.fields_count == 1) {
  186. value = static_cast<int32_t>(mapValue.fields[0].value.integer_value);
  187. }
  188. return [[FIRInt32Value alloc] initWithValue:value];
  189. }
  190. - (FIRDecimal128Value *)convertedDecimal128Value:(const google_firestore_v1_MapValue &)mapValue {
  191. NSString *decimalString = @"";
  192. if (mapValue.fields_count == 1) {
  193. const google_firestore_v1_Value &decimalValue = mapValue.fields[0].value;
  194. if (decimalValue.which_value_type == google_firestore_v1_Value_string_value_tag) {
  195. decimalString = MakeNSString(MakeStringView(decimalValue.string_value));
  196. }
  197. }
  198. return [[FIRDecimal128Value alloc] initWithValue:decimalString];
  199. }
  200. - (FIRBSONObjectId *)convertedBsonObjectId:(const google_firestore_v1_MapValue &)mapValue {
  201. NSString *oid = @"";
  202. if (mapValue.fields_count == 1) {
  203. const google_firestore_v1_Value &oidValue = mapValue.fields[0].value;
  204. if (oidValue.which_value_type == google_firestore_v1_Value_string_value_tag) {
  205. oid = MakeNSString(MakeStringView(oidValue.string_value));
  206. }
  207. }
  208. return [[FIRBSONObjectId alloc] initWithValue:oid];
  209. }
  210. - (FIRBSONTimestamp *)convertedBsonTimestamp:(const google_firestore_v1_MapValue &)mapValue {
  211. uint32_t seconds = 0;
  212. uint32_t increment = 0;
  213. if (mapValue.fields_count == 1) {
  214. const google_firestore_v1_Value &innerValue = mapValue.fields[0].value;
  215. if (innerValue.which_value_type == google_firestore_v1_Value_map_value_tag) {
  216. for (pb_size_t i = 0; i < innerValue.map_value.fields_count; ++i) {
  217. absl::string_view key = MakeStringView(innerValue.map_value.fields[i].key);
  218. const google_firestore_v1_Value &value = innerValue.map_value.fields[i].value;
  219. if ((key.compare(absl::string_view(kRawBsonTimestampTypeSecondsFieldValue)) == 0) &&
  220. value.which_value_type == google_firestore_v1_Value_integer_value_tag) {
  221. // The value from the server is guaranteed to fit in a 32-bit unsigned integer.
  222. seconds = static_cast<uint32_t>(value.integer_value);
  223. }
  224. if ((key.compare(absl::string_view(kRawBsonTimestampTypeIncrementFieldValue)) == 0) &&
  225. value.which_value_type == google_firestore_v1_Value_integer_value_tag) {
  226. // The value from the server is guaranteed to fit in a 32-bit unsigned integer.
  227. increment = static_cast<uint32_t>(value.integer_value);
  228. }
  229. }
  230. }
  231. }
  232. return [[FIRBSONTimestamp alloc] initWithSeconds:seconds increment:increment];
  233. }
  234. - (FIRBSONBinaryData *)convertedBsonBinaryData:(const google_firestore_v1_MapValue &)mapValue {
  235. uint8_t subtype = 0;
  236. NSData *data = [[NSData alloc] init];
  237. if (mapValue.fields_count == 1) {
  238. const google_firestore_v1_Value &dataValue = mapValue.fields[0].value;
  239. if (dataValue.which_value_type == google_firestore_v1_Value_bytes_value_tag) {
  240. NSData *concatData = MakeNSData(dataValue.bytes_value);
  241. if (concatData.length > 0) {
  242. uint8_t buffer[1];
  243. [concatData getBytes:buffer length:1];
  244. subtype = buffer[0];
  245. }
  246. if (concatData.length > 1) {
  247. data = [concatData subdataWithRange:NSMakeRange(1, concatData.length - 1)];
  248. }
  249. }
  250. }
  251. return [[FIRBSONBinaryData alloc] initWithSubtype:subtype data:data];
  252. }
  253. - (NSArray<id> *)convertedArray:(const google_firestore_v1_ArrayValue &)arrayValue {
  254. NSMutableArray *result = [NSMutableArray arrayWithCapacity:arrayValue.values_count];
  255. for (pb_size_t i = 0; i < arrayValue.values_count; ++i) {
  256. [result addObject:[self convertedValue:arrayValue.values[i]]];
  257. }
  258. return result;
  259. }
  260. - (id)convertedServerTimestamp:(const google_firestore_v1_Value &)serverTimestampValue {
  261. switch (_serverTimestampBehavior) {
  262. case FIRServerTimestampBehavior::FIRServerTimestampBehaviorNone:
  263. return [NSNull null];
  264. case FIRServerTimestampBehavior::FIRServerTimestampBehaviorEstimate:
  265. return [self convertedTimestamp:GetLocalWriteTime(serverTimestampValue)];
  266. case FIRServerTimestampBehavior::FIRServerTimestampBehaviorPrevious: {
  267. auto previous_value = GetPreviousValue(serverTimestampValue);
  268. return previous_value ? [self convertedValue:*previous_value] : [NSNull null];
  269. }
  270. }
  271. UNREACHABLE();
  272. }
  273. - (FIRTimestamp *)convertedTimestamp:(const google_protobuf_Timestamp &)value {
  274. return MakeFIRTimestamp(firebase::Timestamp{value.seconds, value.nanos});
  275. }
  276. - (FIRDocumentReference *)convertedReference:(const google_firestore_v1_Value &)value {
  277. std::string ref = MakeString(value.reference_value);
  278. DatabaseId databaseID = DatabaseId::FromName(ref);
  279. DocumentKey key = DocumentKey::FromName(ref);
  280. if (databaseID != _firestore->database_id()) {
  281. LOG_WARN("Document reference is for a different database (%s/%s) which "
  282. "is not supported. It will be treated as a reference within the current database "
  283. "(%s/%s) instead.",
  284. databaseID.project_id(), databaseID.database_id(), databaseID.project_id(),
  285. databaseID.database_id());
  286. }
  287. return MakeFIRDocumentReference(key, _firestore);
  288. }
  289. @end
  290. NS_ASSUME_NONNULL_END