FSTUserDataConverter.mm 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Firestore/Source/API/FSTUserDataConverter.h"
  17. #include <memory>
  18. #include <string>
  19. #include <utility>
  20. #include <vector>
  21. #import "FIRGeoPoint.h"
  22. #import "FIRTimestamp.h"
  23. #import "Firestore/Source/API/FIRDocumentReference+Internal.h"
  24. #import "Firestore/Source/API/FIRFieldPath+Internal.h"
  25. #import "Firestore/Source/API/FIRFieldValue+Internal.h"
  26. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  27. #import "Firestore/Source/Model/FSTFieldValue.h"
  28. #import "Firestore/Source/Model/FSTMutation.h"
  29. #import "Firestore/Source/Util/FSTUsageValidation.h"
  30. #include "Firestore/core/src/firebase/firestore/model/database_id.h"
  31. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  32. #include "Firestore/core/src/firebase/firestore/model/field_mask.h"
  33. #include "Firestore/core/src/firebase/firestore/model/field_path.h"
  34. #include "Firestore/core/src/firebase/firestore/model/field_transform.h"
  35. #include "Firestore/core/src/firebase/firestore/model/precondition.h"
  36. #include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
  37. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  38. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  39. #include "absl/memory/memory.h"
  40. #include "absl/strings/match.h"
  41. namespace util = firebase::firestore::util;
  42. using firebase::firestore::model::ArrayTransform;
  43. using firebase::firestore::model::DatabaseId;
  44. using firebase::firestore::model::DocumentKey;
  45. using firebase::firestore::model::FieldMask;
  46. using firebase::firestore::model::FieldPath;
  47. using firebase::firestore::model::FieldTransform;
  48. using firebase::firestore::model::Precondition;
  49. using firebase::firestore::model::ServerTimestampTransform;
  50. using firebase::firestore::model::TransformOperation;
  51. NS_ASSUME_NONNULL_BEGIN
  52. static const char *RESERVED_FIELD_DESIGNATOR = "__";
  53. #pragma mark - FSTParsedSetData
  54. @implementation FSTParsedSetData {
  55. FieldMask _fieldMask;
  56. std::vector<FieldTransform> _fieldTransforms;
  57. }
  58. - (instancetype)initWithData:(FSTObjectValue *)data
  59. fieldTransforms:(std::vector<FieldTransform>)fieldTransforms {
  60. self = [super init];
  61. if (self) {
  62. _data = data;
  63. _fieldTransforms = std::move(fieldTransforms);
  64. _isPatch = NO;
  65. }
  66. return self;
  67. }
  68. - (instancetype)initWithData:(FSTObjectValue *)data
  69. fieldMask:(FieldMask)fieldMask
  70. fieldTransforms:(std::vector<FieldTransform>)fieldTransforms {
  71. self = [super init];
  72. if (self) {
  73. _data = data;
  74. _fieldMask = std::move(fieldMask);
  75. _fieldTransforms = std::move(fieldTransforms);
  76. _isPatch = YES;
  77. }
  78. return self;
  79. }
  80. - (const std::vector<FieldTransform> &)fieldTransforms {
  81. return _fieldTransforms;
  82. }
  83. - (NSArray<FSTMutation *> *)mutationsWithKey:(const DocumentKey &)key
  84. precondition:(const Precondition &)precondition {
  85. NSMutableArray<FSTMutation *> *mutations = [NSMutableArray array];
  86. if (self.isPatch) {
  87. [mutations addObject:[[FSTPatchMutation alloc] initWithKey:key
  88. fieldMask:_fieldMask
  89. value:self.data
  90. precondition:precondition]];
  91. } else {
  92. [mutations addObject:[[FSTSetMutation alloc] initWithKey:key
  93. value:self.data
  94. precondition:precondition]];
  95. }
  96. if (!self.fieldTransforms.empty()) {
  97. [mutations addObject:[[FSTTransformMutation alloc] initWithKey:key
  98. fieldTransforms:self.fieldTransforms]];
  99. }
  100. return mutations;
  101. }
  102. @end
  103. #pragma mark - FSTParsedUpdateData
  104. @implementation FSTParsedUpdateData {
  105. FieldMask _fieldMask;
  106. std::vector<FieldTransform> _fieldTransforms;
  107. }
  108. - (instancetype)initWithData:(FSTObjectValue *)data
  109. fieldMask:(FieldMask)fieldMask
  110. fieldTransforms:(std::vector<FieldTransform>)fieldTransforms {
  111. self = [super init];
  112. if (self) {
  113. _data = data;
  114. _fieldMask = std::move(fieldMask);
  115. _fieldTransforms = std::move(fieldTransforms);
  116. }
  117. return self;
  118. }
  119. - (NSArray<FSTMutation *> *)mutationsWithKey:(const DocumentKey &)key
  120. precondition:(const Precondition &)precondition {
  121. NSMutableArray<FSTMutation *> *mutations = [NSMutableArray array];
  122. [mutations addObject:[[FSTPatchMutation alloc] initWithKey:key
  123. fieldMask:self.fieldMask
  124. value:self.data
  125. precondition:precondition]];
  126. if (!self.fieldTransforms.empty()) {
  127. [mutations addObject:[[FSTTransformMutation alloc] initWithKey:key
  128. fieldTransforms:self.fieldTransforms]];
  129. }
  130. return mutations;
  131. }
  132. - (const firebase::firestore::model::FieldMask &)fieldMask {
  133. return _fieldMask;
  134. }
  135. - (const std::vector<FieldTransform> &)fieldTransforms {
  136. return _fieldTransforms;
  137. }
  138. @end
  139. /**
  140. * Represents what type of API method provided the data being parsed; useful for determining which
  141. * error conditions apply during parsing and providing better error messages.
  142. */
  143. typedef NS_ENUM(NSInteger, FSTUserDataSource) {
  144. FSTUserDataSourceSet,
  145. FSTUserDataSourceMergeSet,
  146. FSTUserDataSourceUpdate,
  147. /**
  148. * Indicates the source is a where clause, cursor bound, arrayUnion() element, etc. In particular,
  149. * this will result in [FSTParseContext isWrite] returning NO.
  150. */
  151. FSTUserDataSourceArgument,
  152. };
  153. #pragma mark - FSTParseContext
  154. /**
  155. * A "context" object passed around while parsing user data.
  156. */
  157. @interface FSTParseContext : NSObject
  158. /** Whether or not this context corresponds to an element of an array. */
  159. @property(nonatomic, assign, readonly, getter=isArrayElement) BOOL arrayElement;
  160. /**
  161. * What type of API method provided the data being parsed; useful for determining which error
  162. * conditions apply during parsing and providing better error messages.
  163. */
  164. @property(nonatomic, assign) FSTUserDataSource dataSource;
  165. - (instancetype)init NS_UNAVAILABLE;
  166. /**
  167. * Initializes a FSTParseContext with the given source and path.
  168. *
  169. * @param dataSource Indicates what kind of API method this data came from.
  170. * @param path A path within the object being parsed. This could be an empty path (in which case
  171. * the context represents the root of the data being parsed), or a nonempty path (indicating the
  172. * context represents a nested location within the data).
  173. *
  174. * TODO(b/34871131): We don't support array paths right now, so path can be nullptr to indicate
  175. * the context represents any location within an array (in which case certain features will not work
  176. * and errors will be somewhat compromised).
  177. */
  178. - (instancetype)initWithSource:(FSTUserDataSource)dataSource
  179. path:(std::unique_ptr<FieldPath>)path
  180. arrayElement:(BOOL)arrayElement
  181. fieldTransforms:(std::shared_ptr<std::vector<FieldTransform>>)fieldTransforms
  182. fieldMask:(std::shared_ptr<std::vector<FieldPath>>)fieldMask
  183. NS_DESIGNATED_INITIALIZER;
  184. // Helpers to get a FSTParseContext for a child field.
  185. - (instancetype)contextForField:(NSString *)fieldName;
  186. - (instancetype)contextForFieldPath:(const FieldPath &)fieldPath;
  187. - (instancetype)contextForArrayIndex:(NSUInteger)index;
  188. /** Returns true for the non-query parse contexts (Set, MergeSet and Update) */
  189. - (BOOL)isWrite;
  190. - (const FieldPath *)path;
  191. - (const std::vector<FieldPath> *)fieldMask;
  192. - (void)appendToFieldMaskWithFieldPath:(FieldPath)fieldPath;
  193. - (const std::vector<FieldTransform> *)fieldTransforms;
  194. - (void)appendToFieldTransformsWithFieldPath:(FieldPath)fieldPath
  195. transformOperation:
  196. (std::unique_ptr<TransformOperation>)transformOperation;
  197. @end
  198. @implementation FSTParseContext {
  199. /** The current path being parsed. */
  200. // TODO(b/34871131): path should never be nullptr, but we don't support array paths right now.
  201. std::unique_ptr<FieldPath> _path;
  202. // _fieldMask and _fieldTransforms are shared across all active context objects to accumulate the
  203. // result. For example, the result of calling any of contextForField, contextForFieldPath and
  204. // contextForArrayIndex shares the ownership of _fieldMask and _fieldTransforms.
  205. std::shared_ptr<std::vector<FieldPath>> _fieldMask;
  206. std::shared_ptr<std::vector<FieldTransform>> _fieldTransforms;
  207. }
  208. + (instancetype)contextWithSource:(FSTUserDataSource)dataSource
  209. path:(std::unique_ptr<FieldPath>)path {
  210. FSTParseContext *context =
  211. [[FSTParseContext alloc] initWithSource:dataSource
  212. path:std::move(path)
  213. arrayElement:NO
  214. fieldTransforms:std::make_shared<std::vector<FieldTransform>>()
  215. fieldMask:std::make_shared<std::vector<FieldPath>>()];
  216. [context validatePath];
  217. return context;
  218. }
  219. - (instancetype)initWithSource:(FSTUserDataSource)dataSource
  220. path:(std::unique_ptr<FieldPath>)path
  221. arrayElement:(BOOL)arrayElement
  222. fieldTransforms:(std::shared_ptr<std::vector<FieldTransform>>)fieldTransforms
  223. fieldMask:(std::shared_ptr<std::vector<FieldPath>>)fieldMask {
  224. if (self = [super init]) {
  225. _dataSource = dataSource;
  226. _path = std::move(path);
  227. _arrayElement = arrayElement;
  228. _fieldTransforms = std::move(fieldTransforms);
  229. _fieldMask = std::move(fieldMask);
  230. }
  231. return self;
  232. }
  233. - (instancetype)contextForField:(NSString *)fieldName {
  234. std::unique_ptr<FieldPath> path;
  235. if (_path) {
  236. path = absl::make_unique<FieldPath>(_path->Append(util::MakeString(fieldName)));
  237. }
  238. FSTParseContext *context = [[FSTParseContext alloc] initWithSource:self.dataSource
  239. path:std::move(path)
  240. arrayElement:NO
  241. fieldTransforms:_fieldTransforms
  242. fieldMask:_fieldMask];
  243. [context validatePathSegment:util::MakeStringView(fieldName)];
  244. return context;
  245. }
  246. - (instancetype)contextForFieldPath:(const FieldPath &)fieldPath {
  247. std::unique_ptr<FieldPath> path;
  248. if (_path) {
  249. path = absl::make_unique<FieldPath>(_path->Append(fieldPath));
  250. }
  251. FSTParseContext *context = [[FSTParseContext alloc] initWithSource:self.dataSource
  252. path:std::move(path)
  253. arrayElement:NO
  254. fieldTransforms:_fieldTransforms
  255. fieldMask:_fieldMask];
  256. [context validatePath];
  257. return context;
  258. }
  259. - (instancetype)contextForArrayIndex:(NSUInteger)index {
  260. // TODO(b/34871131): We don't support array paths right now; so make path nil.
  261. return [[FSTParseContext alloc] initWithSource:self.dataSource
  262. path:nil
  263. arrayElement:YES
  264. fieldTransforms:_fieldTransforms
  265. fieldMask:_fieldMask];
  266. }
  267. /**
  268. * Returns a string that can be appended to error messages indicating what field caused the error.
  269. */
  270. - (NSString *)fieldDescription {
  271. // TODO(b/34871131): Remove nil check once we have proper paths for fields within arrays.
  272. if (!_path || _path->empty()) {
  273. return @"";
  274. } else {
  275. return [NSString stringWithFormat:@" (found in field %s)", _path->CanonicalString().c_str()];
  276. }
  277. }
  278. - (BOOL)isWrite {
  279. switch (self.dataSource) {
  280. case FSTUserDataSourceSet: // Falls through.
  281. case FSTUserDataSourceMergeSet: // Falls through.
  282. case FSTUserDataSourceUpdate:
  283. return YES;
  284. case FSTUserDataSourceArgument:
  285. return NO;
  286. default:
  287. FSTThrowInvalidArgument(@"Unexpected case for FSTUserDataSource: %d", self.dataSource);
  288. }
  289. }
  290. - (void)validatePath {
  291. // TODO(b/34871131): Remove nil check once we have proper paths for fields within arrays.
  292. if (_path == nullptr) {
  293. return;
  294. }
  295. for (const std::string &segment : *_path) {
  296. [self validatePathSegment:segment];
  297. }
  298. }
  299. - (void)validatePathSegment:(absl::string_view)segment {
  300. absl::string_view designator{RESERVED_FIELD_DESIGNATOR};
  301. if ([self isWrite] && absl::StartsWith(segment, designator) &&
  302. absl::EndsWith(segment, designator)) {
  303. FSTThrowInvalidArgument(@"Document fields cannot begin and end with %s%@",
  304. RESERVED_FIELD_DESIGNATOR, [self fieldDescription]);
  305. }
  306. }
  307. - (const FieldPath *)path {
  308. return _path.get();
  309. }
  310. - (const std::vector<FieldPath> *)fieldMask {
  311. return _fieldMask.get();
  312. }
  313. - (void)appendToFieldMaskWithFieldPath:(FieldPath)fieldPath {
  314. _fieldMask->push_back(std::move(fieldPath));
  315. }
  316. - (const std::vector<FieldTransform> *)fieldTransforms {
  317. return _fieldTransforms.get();
  318. }
  319. - (void)appendToFieldTransformsWithFieldPath:(FieldPath)fieldPath
  320. transformOperation:
  321. (std::unique_ptr<TransformOperation>)transformOperation {
  322. _fieldTransforms->emplace_back(std::move(fieldPath), std::move(transformOperation));
  323. }
  324. @end
  325. #pragma mark - FSTDocumentKeyReference
  326. @implementation FSTDocumentKeyReference {
  327. DocumentKey _key;
  328. }
  329. - (instancetype)initWithKey:(DocumentKey)key databaseID:(const DatabaseId *)databaseID {
  330. self = [super init];
  331. if (self) {
  332. _key = std::move(key);
  333. _databaseID = databaseID;
  334. }
  335. return self;
  336. }
  337. - (const firebase::firestore::model::DocumentKey &)key {
  338. return _key;
  339. }
  340. @end
  341. #pragma mark - FSTUserDataConverter
  342. @interface FSTUserDataConverter ()
  343. // Does not own the DatabaseId instance.
  344. @property(assign, nonatomic, readonly) const DatabaseId *databaseID;
  345. @property(strong, nonatomic, readonly) FSTPreConverterBlock preConverter;
  346. @end
  347. @implementation FSTUserDataConverter
  348. - (instancetype)initWithDatabaseID:(const DatabaseId *)databaseID
  349. preConverter:(FSTPreConverterBlock)preConverter {
  350. self = [super init];
  351. if (self) {
  352. _databaseID = databaseID;
  353. _preConverter = preConverter;
  354. }
  355. return self;
  356. }
  357. - (FSTParsedSetData *)parsedMergeData:(id)input fieldMask:(nullable NSArray<id> *)fieldMask {
  358. // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
  359. // Obj-C to verify the type for us.
  360. if (![input isKindOfClass:[NSDictionary class]]) {
  361. FSTThrowInvalidArgument(@"Data to be written must be an NSDictionary.");
  362. }
  363. FSTParseContext *context =
  364. [FSTParseContext contextWithSource:FSTUserDataSourceMergeSet
  365. path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())];
  366. FSTObjectValue *updateData = (FSTObjectValue *)[self parseData:input context:context];
  367. FieldMask convertedFieldMask;
  368. std::vector<FieldTransform> convertedFieldTransform;
  369. if (fieldMask) {
  370. __block std::vector<FieldPath> fieldMaskPaths;
  371. [fieldMask enumerateObjectsUsingBlock:^(id fieldPath, NSUInteger idx, BOOL *stop) {
  372. FieldPath path;
  373. if ([fieldPath isKindOfClass:[NSString class]]) {
  374. path = [FIRFieldPath pathWithDotSeparatedString:fieldPath].internalValue;
  375. } else if ([fieldPath isKindOfClass:[FIRFieldPath class]]) {
  376. path = ((FIRFieldPath *)fieldPath).internalValue;
  377. } else {
  378. FSTThrowInvalidArgument(
  379. @"All elements in mergeFields: must be NSStrings or FIRFieldPaths.");
  380. }
  381. if ([updateData valueForPath:path] == nil) {
  382. FSTThrowInvalidArgument(
  383. @"Field '%s' is specified in your field mask but missing from your input data.",
  384. path.CanonicalString().c_str());
  385. }
  386. fieldMaskPaths.push_back(path);
  387. }];
  388. convertedFieldMask = FieldMask(fieldMaskPaths);
  389. std::copy_if(context.fieldTransforms->begin(), context.fieldTransforms->end(),
  390. std::back_inserter(convertedFieldTransform),
  391. [&](const FieldTransform &fieldTransform) {
  392. return convertedFieldMask.covers(fieldTransform.path());
  393. });
  394. } else {
  395. convertedFieldMask = FieldMask{*context.fieldMask};
  396. convertedFieldTransform = *context.fieldTransforms;
  397. }
  398. return [[FSTParsedSetData alloc] initWithData:updateData
  399. fieldMask:convertedFieldMask
  400. fieldTransforms:convertedFieldTransform];
  401. }
  402. - (FSTParsedSetData *)parsedSetData:(id)input {
  403. // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
  404. // Obj-C to verify the type for us.
  405. if (![input isKindOfClass:[NSDictionary class]]) {
  406. FSTThrowInvalidArgument(@"Data to be written must be an NSDictionary.");
  407. }
  408. FSTParseContext *context =
  409. [FSTParseContext contextWithSource:FSTUserDataSourceSet
  410. path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())];
  411. FSTObjectValue *updateData = (FSTObjectValue *)[self parseData:input context:context];
  412. return
  413. [[FSTParsedSetData alloc] initWithData:updateData fieldTransforms:*context.fieldTransforms];
  414. }
  415. - (FSTParsedUpdateData *)parsedUpdateData:(id)input {
  416. // NOTE: The public API is typed as NSDictionary but we type 'input' as 'id' since we can't trust
  417. // Obj-C to verify the type for us.
  418. if (![input isKindOfClass:[NSDictionary class]]) {
  419. FSTThrowInvalidArgument(@"Data to be written must be an NSDictionary.");
  420. }
  421. NSDictionary *dict = input;
  422. __block std::vector<FieldPath> fieldMaskPaths;
  423. __block FSTObjectValue *updateData = [FSTObjectValue objectValue];
  424. FSTParseContext *context =
  425. [FSTParseContext contextWithSource:FSTUserDataSourceUpdate
  426. path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())];
  427. [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
  428. FieldPath path;
  429. if ([key isKindOfClass:[NSString class]]) {
  430. path = [FIRFieldPath pathWithDotSeparatedString:key].internalValue;
  431. } else if ([key isKindOfClass:[FIRFieldPath class]]) {
  432. path = ((FIRFieldPath *)key).internalValue;
  433. } else {
  434. FSTThrowInvalidArgument(
  435. @"Dictionary keys in updateData: must be NSStrings or FIRFieldPaths.");
  436. }
  437. value = self.preConverter(value);
  438. if ([value isKindOfClass:[FSTDeleteFieldValue class]]) {
  439. // Add it to the field mask, but don't add anything to updateData.
  440. fieldMaskPaths.push_back(path);
  441. } else {
  442. FSTFieldValue *_Nullable parsedValue =
  443. [self parseData:value context:[context contextForFieldPath:path]];
  444. if (parsedValue) {
  445. fieldMaskPaths.push_back(path);
  446. updateData = [updateData objectBySettingValue:parsedValue forPath:path];
  447. }
  448. }
  449. }];
  450. return [[FSTParsedUpdateData alloc] initWithData:updateData
  451. fieldMask:FieldMask{fieldMaskPaths}
  452. fieldTransforms:*context.fieldTransforms];
  453. }
  454. - (FSTFieldValue *)parsedQueryValue:(id)input {
  455. FSTParseContext *context =
  456. [FSTParseContext contextWithSource:FSTUserDataSourceArgument
  457. path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())];
  458. FSTFieldValue *_Nullable parsed = [self parseData:input context:context];
  459. HARD_ASSERT(parsed, "Parsed data should not be nil.");
  460. HARD_ASSERT(context.fieldTransforms->empty(), "Field transforms should have been disallowed.");
  461. return parsed;
  462. }
  463. /**
  464. * Internal helper for parsing user data.
  465. *
  466. * @param input Data to be parsed.
  467. * @param context A context object representing the current path being parsed, the source of the
  468. * data being parsed, etc.
  469. *
  470. * @return The parsed value, or nil if the value was a FieldValue sentinel that should not be
  471. * included in the resulting parsed data.
  472. */
  473. - (nullable FSTFieldValue *)parseData:(id)input context:(FSTParseContext *)context {
  474. input = self.preConverter(input);
  475. if ([input isKindOfClass:[NSDictionary class]]) {
  476. return [self parseDictionary:(NSDictionary *)input context:context];
  477. } else if ([input isKindOfClass:[FIRFieldValue class]]) {
  478. // FieldValues usually parse into transforms (except FieldValue.delete()) in which case we
  479. // do not want to include this field in our parsed data (as doing so will overwrite the field
  480. // directly prior to the transform trying to transform it). So we don't call appendToFieldMask
  481. // and we return nil as our parsing result.
  482. [self parseSentinelFieldValue:(FIRFieldValue *)input context:context];
  483. return nil;
  484. } else {
  485. // If context.path is nil we are already inside an array and we don't support field mask paths
  486. // more granular than the top-level array.
  487. if (context.path) {
  488. [context appendToFieldMaskWithFieldPath:*context.path];
  489. }
  490. if ([input isKindOfClass:[NSArray class]]) {
  491. // TODO(b/34871131): Include the path containing the array in the error message.
  492. if (context.isArrayElement) {
  493. FSTThrowInvalidArgument(@"Nested arrays are not supported");
  494. }
  495. return [self parseArray:(NSArray *)input context:context];
  496. } else {
  497. return [self parseScalarValue:input context:context];
  498. }
  499. }
  500. }
  501. - (FSTFieldValue *)parseDictionary:(NSDictionary *)dict context:(FSTParseContext *)context {
  502. NSMutableDictionary<NSString *, FSTFieldValue *> *result =
  503. [NSMutableDictionary dictionaryWithCapacity:dict.count];
  504. [dict enumerateKeysAndObjectsUsingBlock:^(NSString *key, id value, BOOL *stop) {
  505. FSTFieldValue *_Nullable parsedValue =
  506. [self parseData:value context:[context contextForField:key]];
  507. if (parsedValue) {
  508. result[key] = parsedValue;
  509. }
  510. }];
  511. return [[FSTObjectValue alloc] initWithDictionary:result];
  512. }
  513. - (FSTFieldValue *)parseArray:(NSArray *)array context:(FSTParseContext *)context {
  514. NSMutableArray<FSTFieldValue *> *result = [NSMutableArray arrayWithCapacity:array.count];
  515. [array enumerateObjectsUsingBlock:^(id entry, NSUInteger idx, BOOL *stop) {
  516. FSTFieldValue *_Nullable parsedEntry =
  517. [self parseData:entry context:[context contextForArrayIndex:idx]];
  518. if (!parsedEntry) {
  519. // Just include nulls in the array for fields being replaced with a sentinel.
  520. parsedEntry = [FSTNullValue nullValue];
  521. }
  522. [result addObject:parsedEntry];
  523. }];
  524. return [[FSTArrayValue alloc] initWithValueNoCopy:result];
  525. }
  526. /**
  527. * "Parses" the provided FIRFieldValue, adding any necessary transforms to
  528. * context.fieldTransforms.
  529. */
  530. - (void)parseSentinelFieldValue:(FIRFieldValue *)fieldValue context:(FSTParseContext *)context {
  531. // Sentinels are only supported with writes, and not within arrays.
  532. if (![context isWrite]) {
  533. FSTThrowInvalidArgument(@"%@ can only be used with updateData() and setData()%@",
  534. fieldValue.methodName, [context fieldDescription]);
  535. }
  536. if (!context.path) {
  537. FSTThrowInvalidArgument(@"%@ is not currently supported inside arrays", fieldValue.methodName);
  538. }
  539. if ([fieldValue isKindOfClass:[FSTDeleteFieldValue class]]) {
  540. if (context.dataSource == FSTUserDataSourceMergeSet) {
  541. // No transform to add for a delete, but we need to add it to our fieldMask so it gets
  542. // deleted.
  543. [context appendToFieldMaskWithFieldPath:*context.path];
  544. } else if (context.dataSource == FSTUserDataSourceUpdate) {
  545. HARD_ASSERT(context.path->size() > 0,
  546. "FieldValue.delete() at the top level should have already been handled.");
  547. FSTThrowInvalidArgument(
  548. @"FieldValue.delete() can only appear at the top level of your "
  549. "update data%@",
  550. [context fieldDescription]);
  551. } else {
  552. // We shouldn't encounter delete sentinels for queries or non-merge setData calls.
  553. FSTThrowInvalidArgument(
  554. @"FieldValue.delete() can only be used with updateData() and setData() with "
  555. @"merge:true%@",
  556. [context fieldDescription]);
  557. }
  558. } else if ([fieldValue isKindOfClass:[FSTServerTimestampFieldValue class]]) {
  559. [context appendToFieldTransformsWithFieldPath:*context.path
  560. transformOperation:absl::make_unique<ServerTimestampTransform>(
  561. ServerTimestampTransform::Get())];
  562. } else if ([fieldValue isKindOfClass:[FSTArrayUnionFieldValue class]]) {
  563. std::vector<FSTFieldValue *> parsedElements =
  564. [self parseArrayTransformElements:((FSTArrayUnionFieldValue *)fieldValue).elements];
  565. auto array_union = absl::make_unique<ArrayTransform>(TransformOperation::Type::ArrayUnion,
  566. std::move(parsedElements));
  567. [context appendToFieldTransformsWithFieldPath:*context.path
  568. transformOperation:std::move(array_union)];
  569. } else if ([fieldValue isKindOfClass:[FSTArrayRemoveFieldValue class]]) {
  570. std::vector<FSTFieldValue *> parsedElements =
  571. [self parseArrayTransformElements:((FSTArrayRemoveFieldValue *)fieldValue).elements];
  572. auto array_remove = absl::make_unique<ArrayTransform>(TransformOperation::Type::ArrayRemove,
  573. std::move(parsedElements));
  574. [context appendToFieldTransformsWithFieldPath:*context.path
  575. transformOperation:std::move(array_remove)];
  576. } else {
  577. HARD_FAIL("Unknown FIRFieldValue type: %s", NSStringFromClass([fieldValue class]));
  578. }
  579. }
  580. /**
  581. * Helper to parse a scalar value (i.e. not an NSDictionary, NSArray, or FIRFieldValue).
  582. *
  583. * Note that it handles all NSNumber values that are encodable as int64_t or doubles
  584. * (depending on the underlying type of the NSNumber). Unsigned integer values are handled though
  585. * any value outside what is representable by int64_t (a signed 64-bit value) will throw an
  586. * exception.
  587. *
  588. * @return The parsed value.
  589. */
  590. - (FSTFieldValue *)parseScalarValue:(nullable id)input context:(FSTParseContext *)context {
  591. if (!input || [input isMemberOfClass:[NSNull class]]) {
  592. return [FSTNullValue nullValue];
  593. } else if ([input isKindOfClass:[NSNumber class]]) {
  594. // Recover the underlying type of the number, using the method described here:
  595. // http://stackoverflow.com/questions/2518761/get-type-of-nsnumber
  596. const char *cType = [input objCType];
  597. // Type Encoding values taken from
  598. // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/
  599. // Articles/ocrtTypeEncodings.html
  600. switch (cType[0]) {
  601. case 'q':
  602. return [FSTIntegerValue integerValue:[input longLongValue]];
  603. case 'i': // Falls through.
  604. case 's': // Falls through.
  605. case 'l': // Falls through.
  606. case 'I': // Falls through.
  607. case 'S':
  608. // Coerce integer values that aren't long long. Allow unsigned integer types that are
  609. // guaranteed small enough to skip a length check.
  610. return [FSTIntegerValue integerValue:[input longLongValue]];
  611. case 'L': // Falls through.
  612. case 'Q':
  613. // Unsigned integers that could be too large. Note that the 'L' (long) case is handled here
  614. // because when compiled for LP64, unsigned long is 64 bits and could overflow int64_t.
  615. {
  616. unsigned long long extended = [input unsignedLongLongValue];
  617. if (extended > LLONG_MAX) {
  618. FSTThrowInvalidArgument(@"NSNumber (%llu) is too large%@",
  619. [input unsignedLongLongValue], [context fieldDescription]);
  620. } else {
  621. return [FSTIntegerValue integerValue:(int64_t)extended];
  622. }
  623. }
  624. case 'f':
  625. return [FSTDoubleValue doubleValue:[input doubleValue]];
  626. case 'd':
  627. // Double values are already the right type, so just reuse the existing boxed double.
  628. //
  629. // Note that NSNumber already performs NaN normalization to a single shared instance
  630. // so there's no need to treat NaN specially here.
  631. return [FSTDoubleValue doubleValue:[input doubleValue]];
  632. case 'B': // Falls through.
  633. case 'c': // Falls through.
  634. case 'C':
  635. // Boolean values are weird.
  636. //
  637. // On arm64, objCType of a BOOL-valued NSNumber will be "c", even though @encode(BOOL)
  638. // returns "B". "c" is the same as @encode(signed char). Unfortunately this means that
  639. // legitimate usage of signed chars is impossible, but this should be rare.
  640. //
  641. // Additionally, for consistency, map unsigned chars to bools in the same way.
  642. return [FSTBooleanValue booleanValue:[input boolValue]];
  643. default:
  644. // All documented codes should be handled above, so this shouldn't happen.
  645. HARD_FAIL("Unknown NSNumber objCType %s on %s", cType, input);
  646. }
  647. } else if ([input isKindOfClass:[NSString class]]) {
  648. return [FSTStringValue stringValue:input];
  649. } else if ([input isKindOfClass:[NSDate class]]) {
  650. return [FSTTimestampValue timestampValue:[FIRTimestamp timestampWithDate:input]];
  651. } else if ([input isKindOfClass:[FIRTimestamp class]]) {
  652. FIRTimestamp *originalTimestamp = (FIRTimestamp *)input;
  653. FIRTimestamp *truncatedTimestamp =
  654. [FIRTimestamp timestampWithSeconds:originalTimestamp.seconds
  655. nanoseconds:originalTimestamp.nanoseconds / 1000 * 1000];
  656. return [FSTTimestampValue timestampValue:truncatedTimestamp];
  657. } else if ([input isKindOfClass:[FIRGeoPoint class]]) {
  658. return [FSTGeoPointValue geoPointValue:input];
  659. } else if ([input isKindOfClass:[NSData class]]) {
  660. return [FSTBlobValue blobValue:input];
  661. } else if ([input isKindOfClass:[FSTDocumentKeyReference class]]) {
  662. FSTDocumentKeyReference *reference = input;
  663. if (*reference.databaseID != *self.databaseID) {
  664. const DatabaseId *other = reference.databaseID;
  665. FSTThrowInvalidArgument(
  666. @"Document Reference is for database %s/%s but should be for database %s/%s%@",
  667. other->project_id().c_str(), other->database_id().c_str(),
  668. self.databaseID->project_id().c_str(), self.databaseID->database_id().c_str(),
  669. [context fieldDescription]);
  670. }
  671. return [FSTReferenceValue referenceValue:reference.key databaseID:self.databaseID];
  672. } else if ([input isKindOfClass:[FIRFieldValue class]]) {
  673. if ([input isKindOfClass:[FSTDeleteFieldValue class]]) {
  674. if (context.dataSource == FSTUserDataSourceMergeSet) {
  675. return nil;
  676. } else if (context.dataSource == FSTUserDataSourceUpdate) {
  677. HARD_ASSERT(context.path->size() > 0,
  678. "FieldValue.delete() at the top level should have already been handled.");
  679. FSTThrowInvalidArgument(
  680. @"FieldValue.delete() can only appear at the top level of your update data%@",
  681. [context fieldDescription]);
  682. } else {
  683. // We shouldn't encounter delete sentinels for queries or non-merge setData calls.
  684. FSTThrowInvalidArgument(
  685. @"FieldValue.delete() can only be used with updateData() and setData() with "
  686. @"merge: true.");
  687. }
  688. } else if ([input isKindOfClass:[FSTServerTimestampFieldValue class]]) {
  689. if (![context isWrite]) {
  690. FSTThrowInvalidArgument(
  691. @"FieldValue.serverTimestamp() can only be used with setData() and updateData().");
  692. }
  693. if (!context.path) {
  694. FSTThrowInvalidArgument(
  695. @"FieldValue.serverTimestamp() is not currently supported inside arrays%@",
  696. [context fieldDescription]);
  697. }
  698. [context appendToFieldTransformsWithFieldPath:*context.path
  699. transformOperation:absl::make_unique<ServerTimestampTransform>(
  700. ServerTimestampTransform::Get())];
  701. // Return nil so this value is omitted from the parsed result.
  702. return nil;
  703. } else {
  704. HARD_FAIL("Unknown FIRFieldValue type: %s", NSStringFromClass([input class]));
  705. }
  706. } else {
  707. FSTThrowInvalidArgument(@"Unsupported type: %@%@", NSStringFromClass([input class]),
  708. [context fieldDescription]);
  709. }
  710. }
  711. - (std::vector<FSTFieldValue *>)parseArrayTransformElements:(NSArray<id> *)elements {
  712. std::vector<FSTFieldValue *> results;
  713. for (NSUInteger i = 0; i < elements.count; i++) {
  714. id element = elements[i];
  715. // Although array transforms are used with writes, the actual elements being unioned or removed
  716. // are not considered writes since they cannot contain any FieldValue sentinels, etc.
  717. FSTParseContext *context =
  718. [FSTParseContext contextWithSource:FSTUserDataSourceArgument
  719. path:absl::make_unique<FieldPath>(FieldPath::EmptyPath())];
  720. FSTFieldValue *parsedElement =
  721. [self parseData:element context:[context contextForArrayIndex:i]];
  722. HARD_ASSERT(parsedElement && context.fieldTransforms->size() == 0,
  723. "Failed to properly parse array transform element: %s", element);
  724. results.push_back(parsedElement);
  725. }
  726. return results;
  727. }
  728. @end
  729. NS_ASSUME_NONNULL_END