FSTSerializerBeta.mm 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  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/Remote/FSTSerializerBeta.h"
  17. #include <inttypes.h>
  18. #import <GRPCClient/GRPCCall.h>
  19. #import "Firestore/Protos/objc/google/firestore/v1beta1/Common.pbobjc.h"
  20. #import "Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h"
  21. #import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbobjc.h"
  22. #import "Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.h"
  23. #import "Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.h"
  24. #import "Firestore/Protos/objc/google/rpc/Status.pbobjc.h"
  25. #import "Firestore/Protos/objc/google/type/Latlng.pbobjc.h"
  26. #import "FIRFirestoreErrors.h"
  27. #import "FIRGeoPoint.h"
  28. #import "Firestore/Source/Core/FSTQuery.h"
  29. #import "Firestore/Source/Core/FSTSnapshotVersion.h"
  30. #import "Firestore/Source/Core/FSTTimestamp.h"
  31. #import "Firestore/Source/Local/FSTQueryData.h"
  32. #import "Firestore/Source/Model/FSTDocument.h"
  33. #import "Firestore/Source/Model/FSTDocumentKey.h"
  34. #import "Firestore/Source/Model/FSTFieldValue.h"
  35. #import "Firestore/Source/Model/FSTMutation.h"
  36. #import "Firestore/Source/Model/FSTMutationBatch.h"
  37. #import "Firestore/Source/Model/FSTPath.h"
  38. #import "Firestore/Source/Remote/FSTExistenceFilter.h"
  39. #import "Firestore/Source/Remote/FSTWatchChange.h"
  40. #import "Firestore/Source/Util/FSTAssert.h"
  41. #include "Firestore/core/src/firebase/firestore/model/database_id.h"
  42. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  43. namespace util = firebase::firestore::util;
  44. using firebase::firestore::model::DatabaseId;
  45. NS_ASSUME_NONNULL_BEGIN
  46. @interface FSTSerializerBeta ()
  47. // Does not own this DatabaseId.
  48. @property(nonatomic, assign, readonly) const DatabaseId *databaseID;
  49. @end
  50. @implementation FSTSerializerBeta
  51. - (instancetype)initWithDatabaseID:(const DatabaseId *)databaseID {
  52. self = [super init];
  53. if (self) {
  54. _databaseID = databaseID;
  55. }
  56. return self;
  57. }
  58. #pragma mark - FSTSnapshotVersion <=> GPBTimestamp
  59. - (GPBTimestamp *)encodedTimestamp:(FSTTimestamp *)timestamp {
  60. GPBTimestamp *result = [GPBTimestamp message];
  61. result.seconds = timestamp.seconds;
  62. result.nanos = timestamp.nanos;
  63. return result;
  64. }
  65. - (FSTTimestamp *)decodedTimestamp:(GPBTimestamp *)timestamp {
  66. return [[FSTTimestamp alloc] initWithSeconds:timestamp.seconds nanos:timestamp.nanos];
  67. }
  68. - (GPBTimestamp *)encodedVersion:(FSTSnapshotVersion *)version {
  69. return [self encodedTimestamp:version.timestamp];
  70. }
  71. - (FSTSnapshotVersion *)decodedVersion:(GPBTimestamp *)version {
  72. return [FSTSnapshotVersion versionWithTimestamp:[self decodedTimestamp:version]];
  73. }
  74. #pragma mark - FIRGeoPoint <=> GTPLatLng
  75. - (GTPLatLng *)encodedGeoPoint:(FIRGeoPoint *)geoPoint {
  76. GTPLatLng *latLng = [GTPLatLng message];
  77. latLng.latitude = geoPoint.latitude;
  78. latLng.longitude = geoPoint.longitude;
  79. return latLng;
  80. }
  81. - (FIRGeoPoint *)decodedGeoPoint:(GTPLatLng *)latLng {
  82. return [[FIRGeoPoint alloc] initWithLatitude:latLng.latitude longitude:latLng.longitude];
  83. }
  84. #pragma mark - FSTDocumentKey <=> Key proto
  85. - (NSString *)encodedDocumentKey:(FSTDocumentKey *)key {
  86. return [self encodedResourcePathForDatabaseID:self.databaseID path:key.path];
  87. }
  88. - (FSTDocumentKey *)decodedDocumentKey:(NSString *)name {
  89. FSTResourcePath *path = [self decodedResourcePathWithDatabaseID:name];
  90. FSTAssert([[path segmentAtIndex:1]
  91. isEqualToString:util::WrapNSStringNoCopy(self.databaseID->project_id())],
  92. @"Tried to deserialize key from different project.");
  93. FSTAssert([[path segmentAtIndex:3]
  94. isEqualToString:util::WrapNSStringNoCopy(self.databaseID->database_id())],
  95. @"Tried to deserialize key from different datbase.");
  96. return [FSTDocumentKey keyWithPath:[self localResourcePathForQualifiedResourcePath:path]];
  97. }
  98. - (NSString *)encodedResourcePathForDatabaseID:(const DatabaseId *)databaseID
  99. path:(FSTResourcePath *)path {
  100. return [[[[self encodedResourcePathForDatabaseID:databaseID] pathByAppendingSegment:@"documents"]
  101. pathByAppendingPath:path] canonicalString];
  102. }
  103. - (FSTResourcePath *)decodedResourcePathWithDatabaseID:(NSString *)name {
  104. FSTResourcePath *path = [FSTResourcePath pathWithString:name];
  105. FSTAssert([self validQualifiedResourcePath:path], @"Tried to deserialize invalid key %@", path);
  106. return path;
  107. }
  108. - (NSString *)encodedQueryPath:(FSTResourcePath *)path {
  109. if (path.length == 0) {
  110. // If the path is empty, the backend requires we leave off the /documents at the end.
  111. return [self encodedDatabaseID];
  112. }
  113. return [self encodedResourcePathForDatabaseID:self.databaseID path:path];
  114. }
  115. - (FSTResourcePath *)decodedQueryPath:(NSString *)name {
  116. FSTResourcePath *resource = [self decodedResourcePathWithDatabaseID:name];
  117. if (resource.length == 4) {
  118. return [FSTResourcePath pathWithSegments:@[]];
  119. } else {
  120. return [self localResourcePathForQualifiedResourcePath:resource];
  121. }
  122. }
  123. - (FSTResourcePath *)encodedResourcePathForDatabaseID:(const DatabaseId *)databaseID {
  124. return [FSTResourcePath pathWithSegments:@[
  125. @"projects", util::WrapNSStringNoCopy(databaseID->project_id()), @"databases",
  126. util::WrapNSStringNoCopy(databaseID->database_id())
  127. ]];
  128. }
  129. - (FSTResourcePath *)localResourcePathForQualifiedResourcePath:(FSTResourcePath *)resourceName {
  130. FSTAssert(
  131. resourceName.length > 4 && [[resourceName segmentAtIndex:4] isEqualToString:@"documents"],
  132. @"Tried to deserialize invalid key %@", resourceName);
  133. return [resourceName pathByRemovingFirstSegments:5];
  134. }
  135. - (BOOL)validQualifiedResourcePath:(FSTResourcePath *)path {
  136. return path.length >= 4 && [[path segmentAtIndex:0] isEqualToString:@"projects"] &&
  137. [[path segmentAtIndex:2] isEqualToString:@"databases"];
  138. }
  139. - (NSString *)encodedDatabaseID {
  140. return [[self encodedResourcePathForDatabaseID:self.databaseID] canonicalString];
  141. }
  142. #pragma mark - FSTFieldValue <=> Value proto
  143. - (GCFSValue *)encodedFieldValue:(FSTFieldValue *)fieldValue {
  144. Class fieldClass = [fieldValue class];
  145. if (fieldClass == [FSTNullValue class]) {
  146. return [self encodedNull];
  147. } else if (fieldClass == [FSTBooleanValue class]) {
  148. return [self encodedBool:[[fieldValue value] boolValue]];
  149. } else if (fieldClass == [FSTIntegerValue class]) {
  150. return [self encodedInteger:[[fieldValue value] longLongValue]];
  151. } else if (fieldClass == [FSTDoubleValue class]) {
  152. return [self encodedDouble:[[fieldValue value] doubleValue]];
  153. } else if (fieldClass == [FSTStringValue class]) {
  154. return [self encodedString:[fieldValue value]];
  155. } else if (fieldClass == [FSTTimestampValue class]) {
  156. return [self encodedTimestampValue:((FSTTimestampValue *)fieldValue).internalValue];
  157. } else if (fieldClass == [FSTGeoPointValue class]) {
  158. return [self encodedGeoPointValue:[fieldValue value]];
  159. } else if (fieldClass == [FSTBlobValue class]) {
  160. return [self encodedBlobValue:[fieldValue value]];
  161. } else if (fieldClass == [FSTReferenceValue class]) {
  162. FSTReferenceValue *ref = (FSTReferenceValue *)fieldValue;
  163. return [self encodedReferenceValueForDatabaseID:[ref databaseID] key:[ref value]];
  164. } else if (fieldClass == [FSTObjectValue class]) {
  165. GCFSValue *result = [GCFSValue message];
  166. result.mapValue = [self encodedMapValue:(FSTObjectValue *)fieldValue];
  167. return result;
  168. } else if (fieldClass == [FSTArrayValue class]) {
  169. GCFSValue *result = [GCFSValue message];
  170. result.arrayValue = [self encodedArrayValue:(FSTArrayValue *)fieldValue];
  171. return result;
  172. } else {
  173. FSTFail(@"Unhandled type %@ on %@", NSStringFromClass([fieldValue class]), fieldValue);
  174. }
  175. }
  176. - (FSTFieldValue *)decodedFieldValue:(GCFSValue *)valueProto {
  177. switch (valueProto.valueTypeOneOfCase) {
  178. case GCFSValue_ValueType_OneOfCase_NullValue:
  179. return [FSTNullValue nullValue];
  180. case GCFSValue_ValueType_OneOfCase_BooleanValue:
  181. return [FSTBooleanValue booleanValue:valueProto.booleanValue];
  182. case GCFSValue_ValueType_OneOfCase_IntegerValue:
  183. return [FSTIntegerValue integerValue:valueProto.integerValue];
  184. case GCFSValue_ValueType_OneOfCase_DoubleValue:
  185. return [FSTDoubleValue doubleValue:valueProto.doubleValue];
  186. case GCFSValue_ValueType_OneOfCase_StringValue:
  187. return [FSTStringValue stringValue:valueProto.stringValue];
  188. case GCFSValue_ValueType_OneOfCase_TimestampValue:
  189. return [FSTTimestampValue timestampValue:[self decodedTimestamp:valueProto.timestampValue]];
  190. case GCFSValue_ValueType_OneOfCase_GeoPointValue:
  191. return [FSTGeoPointValue geoPointValue:[self decodedGeoPoint:valueProto.geoPointValue]];
  192. case GCFSValue_ValueType_OneOfCase_BytesValue:
  193. return [FSTBlobValue blobValue:valueProto.bytesValue];
  194. case GCFSValue_ValueType_OneOfCase_ReferenceValue:
  195. return [self decodedReferenceValue:valueProto.referenceValue];
  196. case GCFSValue_ValueType_OneOfCase_ArrayValue:
  197. return [self decodedArrayValue:valueProto.arrayValue];
  198. case GCFSValue_ValueType_OneOfCase_MapValue:
  199. return [self decodedMapValue:valueProto.mapValue];
  200. default:
  201. FSTFail(@"Unhandled type %d on %@", valueProto.valueTypeOneOfCase, valueProto);
  202. }
  203. }
  204. - (GCFSValue *)encodedNull {
  205. GCFSValue *result = [GCFSValue message];
  206. result.nullValue = GPBNullValue_NullValue;
  207. return result;
  208. }
  209. - (GCFSValue *)encodedBool:(BOOL)value {
  210. GCFSValue *result = [GCFSValue message];
  211. result.booleanValue = value;
  212. return result;
  213. }
  214. - (GCFSValue *)encodedDouble:(double)value {
  215. GCFSValue *result = [GCFSValue message];
  216. result.doubleValue = value;
  217. return result;
  218. }
  219. - (GCFSValue *)encodedInteger:(int64_t)value {
  220. GCFSValue *result = [GCFSValue message];
  221. result.integerValue = value;
  222. return result;
  223. }
  224. - (GCFSValue *)encodedString:(NSString *)value {
  225. GCFSValue *result = [GCFSValue message];
  226. result.stringValue = value;
  227. return result;
  228. }
  229. - (GCFSValue *)encodedTimestampValue:(FSTTimestamp *)value {
  230. GCFSValue *result = [GCFSValue message];
  231. result.timestampValue = [self encodedTimestamp:value];
  232. return result;
  233. }
  234. - (GCFSValue *)encodedGeoPointValue:(FIRGeoPoint *)value {
  235. GCFSValue *result = [GCFSValue message];
  236. result.geoPointValue = [self encodedGeoPoint:value];
  237. return result;
  238. }
  239. - (GCFSValue *)encodedBlobValue:(NSData *)value {
  240. GCFSValue *result = [GCFSValue message];
  241. result.bytesValue = value;
  242. return result;
  243. }
  244. - (GCFSValue *)encodedReferenceValueForDatabaseID:(const DatabaseId *)databaseID
  245. key:(FSTDocumentKey *)key {
  246. FSTAssert(*databaseID == *self.databaseID, @"Database %@:%@ cannot encode reference from %@:%@",
  247. util::WrapNSStringNoCopy(self.databaseID->project_id()),
  248. util::WrapNSStringNoCopy(self.databaseID->database_id()),
  249. util::WrapNSStringNoCopy(databaseID->project_id()),
  250. util::WrapNSStringNoCopy(databaseID->database_id()));
  251. GCFSValue *result = [GCFSValue message];
  252. result.referenceValue = [self encodedResourcePathForDatabaseID:databaseID path:key.path];
  253. return result;
  254. }
  255. - (FSTReferenceValue *)decodedReferenceValue:(NSString *)resourceName {
  256. FSTResourcePath *path = [self decodedResourcePathWithDatabaseID:resourceName];
  257. NSString *project = [path segmentAtIndex:1];
  258. NSString *database = [path segmentAtIndex:3];
  259. FSTDocumentKey *key =
  260. [FSTDocumentKey keyWithPath:[self localResourcePathForQualifiedResourcePath:path]];
  261. const DatabaseId database_id(util::MakeStringView(project), util::MakeStringView(database));
  262. FSTAssert(database_id == *self.databaseID, @"Database %@:%@ cannot encode reference from %@:%@",
  263. util::WrapNSStringNoCopy(self.databaseID->project_id()),
  264. util::WrapNSStringNoCopy(self.databaseID->database_id()),
  265. util::WrapNSStringNoCopy(database_id.project_id()),
  266. util::WrapNSStringNoCopy(database_id.database_id()));
  267. return [FSTReferenceValue referenceValue:key databaseID:self.databaseID];
  268. }
  269. - (GCFSArrayValue *)encodedArrayValue:(FSTArrayValue *)arrayValue {
  270. GCFSArrayValue *proto = [GCFSArrayValue message];
  271. NSMutableArray<GCFSValue *> *protoContents = [proto valuesArray];
  272. [[arrayValue internalValue]
  273. enumerateObjectsUsingBlock:^(FSTFieldValue *value, NSUInteger idx, BOOL *stop) {
  274. GCFSValue *converted = [self encodedFieldValue:value];
  275. [protoContents addObject:converted];
  276. }];
  277. return proto;
  278. }
  279. - (FSTArrayValue *)decodedArrayValue:(GCFSArrayValue *)arrayValue {
  280. NSMutableArray<FSTFieldValue *> *contents =
  281. [NSMutableArray arrayWithCapacity:arrayValue.valuesArray_Count];
  282. [arrayValue.valuesArray
  283. enumerateObjectsUsingBlock:^(GCFSValue *value, NSUInteger idx, BOOL *stop) {
  284. [contents addObject:[self decodedFieldValue:value]];
  285. }];
  286. return [[FSTArrayValue alloc] initWithValueNoCopy:contents];
  287. }
  288. - (GCFSMapValue *)encodedMapValue:(FSTObjectValue *)value {
  289. GCFSMapValue *result = [GCFSMapValue message];
  290. result.fields = [self encodedFields:value];
  291. return result;
  292. }
  293. - (FSTObjectValue *)decodedMapValue:(GCFSMapValue *)map {
  294. return [self decodedFields:map.fields];
  295. }
  296. /**
  297. * Encodes an FSTObjectValue into a dictionary.
  298. * @return a new dictionary that can be assigned to a field in another proto.
  299. */
  300. - (NSMutableDictionary<NSString *, GCFSValue *> *)encodedFields:(FSTObjectValue *)value {
  301. FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *fields = value.internalValue;
  302. NSMutableDictionary<NSString *, GCFSValue *> *result = [NSMutableDictionary dictionary];
  303. [fields enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *obj, BOOL *stop) {
  304. GCFSValue *converted = [self encodedFieldValue:obj];
  305. result[key] = converted;
  306. }];
  307. return result;
  308. }
  309. - (FSTObjectValue *)decodedFields:(NSDictionary<NSString *, GCFSValue *> *)fields {
  310. __block FSTObjectValue *result = [FSTObjectValue objectValue];
  311. [fields enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, GCFSValue *_Nonnull obj,
  312. BOOL *_Nonnull stop) {
  313. FSTFieldPath *path = [FSTFieldPath pathWithSegments:@[ key ]];
  314. FSTFieldValue *value = [self decodedFieldValue:obj];
  315. result = [result objectBySettingValue:value forPath:path];
  316. }];
  317. return result;
  318. }
  319. #pragma mark - FSTObjectValue <=> Document proto
  320. - (GCFSDocument *)encodedDocumentWithFields:(FSTObjectValue *)objectValue
  321. key:(FSTDocumentKey *)key {
  322. GCFSDocument *proto = [GCFSDocument message];
  323. proto.name = [self encodedDocumentKey:key];
  324. proto.fields = [self encodedFields:objectValue];
  325. return proto;
  326. }
  327. #pragma mark - FSTMaybeDocument <= BatchGetDocumentsResponse proto
  328. - (FSTMaybeDocument *)decodedMaybeDocumentFromBatch:(GCFSBatchGetDocumentsResponse *)response {
  329. switch (response.resultOneOfCase) {
  330. case GCFSBatchGetDocumentsResponse_Result_OneOfCase_Found:
  331. return [self decodedFoundDocument:response];
  332. case GCFSBatchGetDocumentsResponse_Result_OneOfCase_Missing:
  333. return [self decodedDeletedDocument:response];
  334. default:
  335. FSTFail(@"Unknown document type: %@", response);
  336. }
  337. }
  338. - (FSTDocument *)decodedFoundDocument:(GCFSBatchGetDocumentsResponse *)response {
  339. FSTAssert(!!response.found, @"Tried to deserialize a found document from a deleted document.");
  340. FSTDocumentKey *key = [self decodedDocumentKey:response.found.name];
  341. FSTObjectValue *value = [self decodedFields:response.found.fields];
  342. FSTSnapshotVersion *version = [self decodedVersion:response.found.updateTime];
  343. FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
  344. @"Got a document response with no snapshot version");
  345. return [FSTDocument documentWithData:value key:key version:version hasLocalMutations:NO];
  346. }
  347. - (FSTDeletedDocument *)decodedDeletedDocument:(GCFSBatchGetDocumentsResponse *)response {
  348. FSTAssert(!!response.missing, @"Tried to deserialize a deleted document from a found document.");
  349. FSTDocumentKey *key = [self decodedDocumentKey:response.missing];
  350. FSTSnapshotVersion *version = [self decodedVersion:response.readTime];
  351. FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
  352. @"Got a no document response with no snapshot version");
  353. return [FSTDeletedDocument documentWithKey:key version:version];
  354. }
  355. #pragma mark - FSTMutation => GCFSWrite proto
  356. - (GCFSWrite *)encodedMutation:(FSTMutation *)mutation {
  357. GCFSWrite *proto = [GCFSWrite message];
  358. Class mutationClass = [mutation class];
  359. if (mutationClass == [FSTSetMutation class]) {
  360. FSTSetMutation *set = (FSTSetMutation *)mutation;
  361. proto.update = [self encodedDocumentWithFields:set.value key:set.key];
  362. } else if (mutationClass == [FSTPatchMutation class]) {
  363. FSTPatchMutation *patch = (FSTPatchMutation *)mutation;
  364. proto.update = [self encodedDocumentWithFields:patch.value key:patch.key];
  365. proto.updateMask = [self encodedFieldMask:patch.fieldMask];
  366. } else if (mutationClass == [FSTTransformMutation class]) {
  367. FSTTransformMutation *transform = (FSTTransformMutation *)mutation;
  368. proto.transform = [GCFSDocumentTransform message];
  369. proto.transform.document = [self encodedDocumentKey:transform.key];
  370. proto.transform.fieldTransformsArray = [self encodedFieldTransforms:transform.fieldTransforms];
  371. // NOTE: We set a precondition of exists: true as a safety-check, since we always combine
  372. // FSTTransformMutations with an FSTSetMutation or FSTPatchMutation which (if successful) should
  373. // end up with an existing document.
  374. proto.currentDocument.exists = YES;
  375. } else if (mutationClass == [FSTDeleteMutation class]) {
  376. FSTDeleteMutation *deleteMutation = (FSTDeleteMutation *)mutation;
  377. proto.delete_p = [self encodedDocumentKey:deleteMutation.key];
  378. } else {
  379. FSTFail(@"Unknown mutation type %@", NSStringFromClass(mutationClass));
  380. }
  381. if (!mutation.precondition.isNone) {
  382. proto.currentDocument = [self encodedPrecondition:mutation.precondition];
  383. }
  384. return proto;
  385. }
  386. - (FSTMutation *)decodedMutation:(GCFSWrite *)mutation {
  387. FSTPrecondition *precondition = [mutation hasCurrentDocument]
  388. ? [self decodedPrecondition:mutation.currentDocument]
  389. : [FSTPrecondition none];
  390. switch (mutation.operationOneOfCase) {
  391. case GCFSWrite_Operation_OneOfCase_Update:
  392. if (mutation.hasUpdateMask) {
  393. return [[FSTPatchMutation alloc] initWithKey:[self decodedDocumentKey:mutation.update.name]
  394. fieldMask:[self decodedFieldMask:mutation.updateMask]
  395. value:[self decodedFields:mutation.update.fields]
  396. precondition:precondition];
  397. } else {
  398. return [[FSTSetMutation alloc] initWithKey:[self decodedDocumentKey:mutation.update.name]
  399. value:[self decodedFields:mutation.update.fields]
  400. precondition:precondition];
  401. }
  402. case GCFSWrite_Operation_OneOfCase_Delete_p:
  403. return [[FSTDeleteMutation alloc] initWithKey:[self decodedDocumentKey:mutation.delete_p]
  404. precondition:precondition];
  405. case GCFSWrite_Operation_OneOfCase_Transform: {
  406. FSTPreconditionExists exists = precondition.exists;
  407. FSTAssert(exists == FSTPreconditionExistsYes,
  408. @"Transforms must have precondition \"exists == true\"");
  409. return [[FSTTransformMutation alloc]
  410. initWithKey:[self decodedDocumentKey:mutation.transform.document]
  411. fieldTransforms:[self decodedFieldTransforms:mutation.transform.fieldTransformsArray]];
  412. }
  413. default:
  414. // Note that insert is intentionally unhandled, since we don't ever deal in them.
  415. FSTFail(@"Unknown mutation operation: %d", mutation.operationOneOfCase);
  416. }
  417. }
  418. - (GCFSPrecondition *)encodedPrecondition:(FSTPrecondition *)precondition {
  419. FSTAssert(!precondition.isNone, @"Can't serialize an empty precondition");
  420. GCFSPrecondition *message = [GCFSPrecondition message];
  421. if (precondition.updateTime) {
  422. message.updateTime = [self encodedVersion:precondition.updateTime];
  423. } else if (precondition.exists != FSTPreconditionExistsNotSet) {
  424. message.exists = precondition.exists == FSTPreconditionExistsYes;
  425. } else {
  426. FSTFail(@"Unknown precondition: %@", precondition);
  427. }
  428. return message;
  429. }
  430. - (FSTPrecondition *)decodedPrecondition:(GCFSPrecondition *)precondition {
  431. switch (precondition.conditionTypeOneOfCase) {
  432. case GCFSPrecondition_ConditionType_OneOfCase_GPBUnsetOneOfCase:
  433. return [FSTPrecondition none];
  434. case GCFSPrecondition_ConditionType_OneOfCase_Exists:
  435. return [FSTPrecondition preconditionWithExists:precondition.exists];
  436. case GCFSPrecondition_ConditionType_OneOfCase_UpdateTime:
  437. return [FSTPrecondition
  438. preconditionWithUpdateTime:[self decodedVersion:precondition.updateTime]];
  439. default:
  440. FSTFail(@"Unrecognized Precondition one-of case %@", precondition);
  441. }
  442. }
  443. - (GCFSDocumentMask *)encodedFieldMask:(FSTFieldMask *)fieldMask {
  444. GCFSDocumentMask *mask = [GCFSDocumentMask message];
  445. for (FSTFieldPath *field in fieldMask.fields) {
  446. [mask.fieldPathsArray addObject:field.canonicalString];
  447. }
  448. return mask;
  449. }
  450. - (FSTFieldMask *)decodedFieldMask:(GCFSDocumentMask *)fieldMask {
  451. NSMutableArray<FSTFieldPath *> *fields =
  452. [NSMutableArray arrayWithCapacity:fieldMask.fieldPathsArray_Count];
  453. for (NSString *path in fieldMask.fieldPathsArray) {
  454. [fields addObject:[FSTFieldPath pathWithServerFormat:path]];
  455. }
  456. return [[FSTFieldMask alloc] initWithFields:fields];
  457. }
  458. - (NSMutableArray<GCFSDocumentTransform_FieldTransform *> *)encodedFieldTransforms:
  459. (NSArray<FSTFieldTransform *> *)fieldTransforms {
  460. NSMutableArray *protos = [NSMutableArray array];
  461. for (FSTFieldTransform *fieldTransform in fieldTransforms) {
  462. FSTAssert([fieldTransform.transform isKindOfClass:[FSTServerTimestampTransform class]],
  463. @"Unknown transform: %@", fieldTransform.transform);
  464. GCFSDocumentTransform_FieldTransform *proto = [GCFSDocumentTransform_FieldTransform message];
  465. proto.fieldPath = fieldTransform.path.canonicalString;
  466. proto.setToServerValue = GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime;
  467. [protos addObject:proto];
  468. }
  469. return protos;
  470. }
  471. - (NSArray<FSTFieldTransform *> *)decodedFieldTransforms:
  472. (NSArray<GCFSDocumentTransform_FieldTransform *> *)protos {
  473. NSMutableArray<FSTFieldTransform *> *fieldTransforms = [NSMutableArray array];
  474. for (GCFSDocumentTransform_FieldTransform *proto in protos) {
  475. FSTAssert(
  476. proto.setToServerValue == GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime,
  477. @"Unknown transform setToServerValue: %d", proto.setToServerValue);
  478. [fieldTransforms
  479. addObject:[[FSTFieldTransform alloc]
  480. initWithPath:[FSTFieldPath pathWithServerFormat:proto.fieldPath]
  481. transform:[FSTServerTimestampTransform serverTimestampTransform]]];
  482. }
  483. return fieldTransforms;
  484. }
  485. #pragma mark - FSTMutationResult <= GCFSWriteResult proto
  486. - (FSTMutationResult *)decodedMutationResult:(GCFSWriteResult *)mutation {
  487. // NOTE: Deletes don't have an updateTime.
  488. FSTSnapshotVersion *_Nullable version =
  489. mutation.updateTime ? [self decodedVersion:mutation.updateTime] : nil;
  490. NSMutableArray *_Nullable transformResults = nil;
  491. if (mutation.transformResultsArray.count > 0) {
  492. transformResults = [NSMutableArray array];
  493. for (GCFSValue *result in mutation.transformResultsArray) {
  494. [transformResults addObject:[self decodedFieldValue:result]];
  495. }
  496. }
  497. return [[FSTMutationResult alloc] initWithVersion:version transformResults:transformResults];
  498. }
  499. #pragma mark - FSTQueryData => GCFSTarget proto
  500. - (nullable NSMutableDictionary<NSString *, NSString *> *)encodedListenRequestLabelsForQueryData:
  501. (FSTQueryData *)queryData {
  502. NSString *value = [self encodedLabelForPurpose:queryData.purpose];
  503. if (!value) {
  504. return nil;
  505. }
  506. NSMutableDictionary<NSString *, NSString *> *result =
  507. [NSMutableDictionary dictionaryWithCapacity:1];
  508. [result setObject:value forKey:@"goog-listen-tags"];
  509. return result;
  510. }
  511. - (nullable NSString *)encodedLabelForPurpose:(FSTQueryPurpose)purpose {
  512. switch (purpose) {
  513. case FSTQueryPurposeListen:
  514. return nil;
  515. case FSTQueryPurposeExistenceFilterMismatch:
  516. return @"existence-filter-mismatch";
  517. case FSTQueryPurposeLimboResolution:
  518. return @"limbo-document";
  519. default:
  520. FSTFail(@"Unrecognized query purpose: %lu", (unsigned long)purpose);
  521. }
  522. }
  523. - (GCFSTarget *)encodedTarget:(FSTQueryData *)queryData {
  524. GCFSTarget *result = [GCFSTarget message];
  525. FSTQuery *query = queryData.query;
  526. if ([query isDocumentQuery]) {
  527. result.documents = [self encodedDocumentsTarget:query];
  528. } else {
  529. result.query = [self encodedQueryTarget:query];
  530. }
  531. result.targetId = queryData.targetID;
  532. if (queryData.resumeToken.length > 0) {
  533. result.resumeToken = queryData.resumeToken;
  534. }
  535. return result;
  536. }
  537. - (GCFSTarget_DocumentsTarget *)encodedDocumentsTarget:(FSTQuery *)query {
  538. GCFSTarget_DocumentsTarget *result = [GCFSTarget_DocumentsTarget message];
  539. NSMutableArray<NSString *> *docs = result.documentsArray;
  540. [docs addObject:[self encodedQueryPath:query.path]];
  541. return result;
  542. }
  543. - (FSTQuery *)decodedQueryFromDocumentsTarget:(GCFSTarget_DocumentsTarget *)target {
  544. NSArray<NSString *> *documents = target.documentsArray;
  545. FSTAssert(documents.count == 1, @"DocumentsTarget contained other than 1 document %lu",
  546. (unsigned long)documents.count);
  547. NSString *name = documents[0];
  548. return [FSTQuery queryWithPath:[self decodedQueryPath:name]];
  549. }
  550. - (GCFSTarget_QueryTarget *)encodedQueryTarget:(FSTQuery *)query {
  551. // Dissect the path into parent, collectionId, and optional key filter.
  552. GCFSTarget_QueryTarget *queryTarget = [GCFSTarget_QueryTarget message];
  553. if (query.path.length == 0) {
  554. queryTarget.parent = [self encodedQueryPath:query.path];
  555. } else {
  556. FSTResourcePath *path = query.path;
  557. FSTAssert(path.length % 2 != 0, @"Document queries with filters are not supported.");
  558. queryTarget.parent = [self encodedQueryPath:[path pathByRemovingLastSegment]];
  559. GCFSStructuredQuery_CollectionSelector *from = [GCFSStructuredQuery_CollectionSelector message];
  560. from.collectionId = path.lastSegment;
  561. [queryTarget.structuredQuery.fromArray addObject:from];
  562. }
  563. // Encode the filters.
  564. GCFSStructuredQuery_Filter *_Nullable where = [self encodedFilters:query.filters];
  565. if (where) {
  566. queryTarget.structuredQuery.where = where;
  567. }
  568. NSArray<GCFSStructuredQuery_Order *> *orders = [self encodedSortOrders:query.sortOrders];
  569. if (orders.count) {
  570. [queryTarget.structuredQuery.orderByArray addObjectsFromArray:orders];
  571. }
  572. if (query.limit != NSNotFound) {
  573. queryTarget.structuredQuery.limit.value = (int32_t)query.limit;
  574. }
  575. if (query.startAt) {
  576. queryTarget.structuredQuery.startAt = [self encodedBound:query.startAt];
  577. }
  578. if (query.endAt) {
  579. queryTarget.structuredQuery.endAt = [self encodedBound:query.endAt];
  580. }
  581. return queryTarget;
  582. }
  583. - (FSTQuery *)decodedQueryFromQueryTarget:(GCFSTarget_QueryTarget *)target {
  584. FSTResourcePath *path = [self decodedQueryPath:target.parent];
  585. GCFSStructuredQuery *query = target.structuredQuery;
  586. NSUInteger fromCount = query.fromArray_Count;
  587. if (fromCount > 0) {
  588. FSTAssert(fromCount == 1,
  589. @"StructuredQuery.from with more than one collection is not supported.");
  590. GCFSStructuredQuery_CollectionSelector *from = query.fromArray[0];
  591. path = [path pathByAppendingSegment:from.collectionId];
  592. }
  593. NSArray<id<FSTFilter>> *filterBy;
  594. if (query.hasWhere) {
  595. filterBy = [self decodedFilters:query.where];
  596. } else {
  597. filterBy = @[];
  598. }
  599. NSArray<FSTSortOrder *> *orderBy;
  600. if (query.orderByArray_Count > 0) {
  601. orderBy = [self decodedSortOrders:query.orderByArray];
  602. } else {
  603. orderBy = @[];
  604. }
  605. NSInteger limit = NSNotFound;
  606. if (query.hasLimit) {
  607. limit = query.limit.value;
  608. }
  609. FSTBound *_Nullable startAt;
  610. if (query.hasStartAt) {
  611. startAt = [self decodedBound:query.startAt];
  612. }
  613. FSTBound *_Nullable endAt;
  614. if (query.hasEndAt) {
  615. endAt = [self decodedBound:query.endAt];
  616. }
  617. return [[FSTQuery alloc] initWithPath:path
  618. filterBy:filterBy
  619. orderBy:orderBy
  620. limit:limit
  621. startAt:startAt
  622. endAt:endAt];
  623. }
  624. #pragma mark Filters
  625. - (GCFSStructuredQuery_Filter *_Nullable)encodedFilters:(NSArray<id<FSTFilter>> *)filters {
  626. if (filters.count == 0) {
  627. return nil;
  628. }
  629. NSMutableArray<GCFSStructuredQuery_Filter *> *protos = [NSMutableArray array];
  630. for (id<FSTFilter> filter in filters) {
  631. if ([filter isKindOfClass:[FSTRelationFilter class]]) {
  632. [protos addObject:[self encodedRelationFilter:filter]];
  633. } else {
  634. [protos addObject:[self encodedUnaryFilter:filter]];
  635. }
  636. }
  637. if (protos.count == 1) {
  638. // Special case: no existing filters and we only need to add one filter. This can be made the
  639. // single root filter without a composite filter.
  640. return protos[0];
  641. }
  642. GCFSStructuredQuery_Filter *composite = [GCFSStructuredQuery_Filter message];
  643. composite.compositeFilter.op = GCFSStructuredQuery_CompositeFilter_Operator_And;
  644. composite.compositeFilter.filtersArray = protos;
  645. return composite;
  646. }
  647. - (NSArray<id<FSTFilter>> *)decodedFilters:(GCFSStructuredQuery_Filter *)proto {
  648. NSMutableArray<id<FSTFilter>> *result = [NSMutableArray array];
  649. NSArray<GCFSStructuredQuery_Filter *> *filters;
  650. if (proto.filterTypeOneOfCase ==
  651. GCFSStructuredQuery_Filter_FilterType_OneOfCase_CompositeFilter) {
  652. FSTAssert(proto.compositeFilter.op == GCFSStructuredQuery_CompositeFilter_Operator_And,
  653. @"Only AND-type composite filters are supported, got %d", proto.compositeFilter.op);
  654. filters = proto.compositeFilter.filtersArray;
  655. } else {
  656. filters = @[ proto ];
  657. }
  658. for (GCFSStructuredQuery_Filter *filter in filters) {
  659. switch (filter.filterTypeOneOfCase) {
  660. case GCFSStructuredQuery_Filter_FilterType_OneOfCase_CompositeFilter:
  661. FSTFail(@"Nested composite filters are not supported");
  662. case GCFSStructuredQuery_Filter_FilterType_OneOfCase_FieldFilter:
  663. [result addObject:[self decodedRelationFilter:filter.fieldFilter]];
  664. break;
  665. case GCFSStructuredQuery_Filter_FilterType_OneOfCase_UnaryFilter:
  666. [result addObject:[self decodedUnaryFilter:filter.unaryFilter]];
  667. break;
  668. default:
  669. FSTFail(@"Unrecognized Filter.filterType %d", filter.filterTypeOneOfCase);
  670. }
  671. }
  672. return result;
  673. }
  674. - (GCFSStructuredQuery_Filter *)encodedRelationFilter:(FSTRelationFilter *)filter {
  675. GCFSStructuredQuery_Filter *proto = [GCFSStructuredQuery_Filter message];
  676. GCFSStructuredQuery_FieldFilter *fieldFilter = proto.fieldFilter;
  677. fieldFilter.field = [self encodedFieldPath:filter.field];
  678. fieldFilter.op = [self encodedRelationFilterOperator:filter.filterOperator];
  679. fieldFilter.value = [self encodedFieldValue:filter.value];
  680. return proto;
  681. }
  682. - (FSTRelationFilter *)decodedRelationFilter:(GCFSStructuredQuery_FieldFilter *)proto {
  683. FSTFieldPath *fieldPath = [FSTFieldPath pathWithServerFormat:proto.field.fieldPath];
  684. FSTRelationFilterOperator filterOperator = [self decodedRelationFilterOperator:proto.op];
  685. FSTFieldValue *value = [self decodedFieldValue:proto.value];
  686. return [FSTRelationFilter filterWithField:fieldPath filterOperator:filterOperator value:value];
  687. }
  688. - (GCFSStructuredQuery_Filter *)encodedUnaryFilter:(id<FSTFilter>)filter {
  689. GCFSStructuredQuery_Filter *proto = [GCFSStructuredQuery_Filter message];
  690. proto.unaryFilter.field = [self encodedFieldPath:filter.field];
  691. if ([filter isKindOfClass:[FSTNanFilter class]]) {
  692. proto.unaryFilter.op = GCFSStructuredQuery_UnaryFilter_Operator_IsNan;
  693. } else if ([filter isKindOfClass:[FSTNullFilter class]]) {
  694. proto.unaryFilter.op = GCFSStructuredQuery_UnaryFilter_Operator_IsNull;
  695. } else {
  696. FSTFail(@"Unrecognized filter: %@", filter);
  697. }
  698. return proto;
  699. }
  700. - (id<FSTFilter>)decodedUnaryFilter:(GCFSStructuredQuery_UnaryFilter *)proto {
  701. FSTFieldPath *field = [FSTFieldPath pathWithServerFormat:proto.field.fieldPath];
  702. switch (proto.op) {
  703. case GCFSStructuredQuery_UnaryFilter_Operator_IsNan:
  704. return [[FSTNanFilter alloc] initWithField:field];
  705. case GCFSStructuredQuery_UnaryFilter_Operator_IsNull:
  706. return [[FSTNullFilter alloc] initWithField:field];
  707. default:
  708. FSTFail(@"Unrecognized UnaryFilter.operator %d", proto.op);
  709. }
  710. }
  711. - (GCFSStructuredQuery_FieldReference *)encodedFieldPath:(FSTFieldPath *)fieldPath {
  712. GCFSStructuredQuery_FieldReference *ref = [GCFSStructuredQuery_FieldReference message];
  713. ref.fieldPath = fieldPath.canonicalString;
  714. return ref;
  715. }
  716. - (GCFSStructuredQuery_FieldFilter_Operator)encodedRelationFilterOperator:
  717. (FSTRelationFilterOperator)filterOperator {
  718. switch (filterOperator) {
  719. case FSTRelationFilterOperatorLessThan:
  720. return GCFSStructuredQuery_FieldFilter_Operator_LessThan;
  721. case FSTRelationFilterOperatorLessThanOrEqual:
  722. return GCFSStructuredQuery_FieldFilter_Operator_LessThanOrEqual;
  723. case FSTRelationFilterOperatorEqual:
  724. return GCFSStructuredQuery_FieldFilter_Operator_Equal;
  725. case FSTRelationFilterOperatorGreaterThanOrEqual:
  726. return GCFSStructuredQuery_FieldFilter_Operator_GreaterThanOrEqual;
  727. case FSTRelationFilterOperatorGreaterThan:
  728. return GCFSStructuredQuery_FieldFilter_Operator_GreaterThan;
  729. default:
  730. FSTFail(@"Unhandled FSTRelationFilterOperator: %ld", (long)filterOperator);
  731. }
  732. }
  733. - (FSTRelationFilterOperator)decodedRelationFilterOperator:
  734. (GCFSStructuredQuery_FieldFilter_Operator)filterOperator {
  735. switch (filterOperator) {
  736. case GCFSStructuredQuery_FieldFilter_Operator_LessThan:
  737. return FSTRelationFilterOperatorLessThan;
  738. case GCFSStructuredQuery_FieldFilter_Operator_LessThanOrEqual:
  739. return FSTRelationFilterOperatorLessThanOrEqual;
  740. case GCFSStructuredQuery_FieldFilter_Operator_Equal:
  741. return FSTRelationFilterOperatorEqual;
  742. case GCFSStructuredQuery_FieldFilter_Operator_GreaterThanOrEqual:
  743. return FSTRelationFilterOperatorGreaterThanOrEqual;
  744. case GCFSStructuredQuery_FieldFilter_Operator_GreaterThan:
  745. return FSTRelationFilterOperatorGreaterThan;
  746. default:
  747. FSTFail(@"Unhandled FieldFilter.operator: %d", filterOperator);
  748. }
  749. }
  750. #pragma mark Property Orders
  751. - (NSArray<GCFSStructuredQuery_Order *> *)encodedSortOrders:(NSArray<FSTSortOrder *> *)orders {
  752. NSMutableArray<GCFSStructuredQuery_Order *> *protos = [NSMutableArray array];
  753. for (FSTSortOrder *order in orders) {
  754. [protos addObject:[self encodedSortOrder:order]];
  755. }
  756. return protos;
  757. }
  758. - (NSArray<FSTSortOrder *> *)decodedSortOrders:(NSArray<GCFSStructuredQuery_Order *> *)protos {
  759. NSMutableArray<FSTSortOrder *> *result = [NSMutableArray arrayWithCapacity:protos.count];
  760. for (GCFSStructuredQuery_Order *orderProto in protos) {
  761. [result addObject:[self decodedSortOrder:orderProto]];
  762. }
  763. return result;
  764. }
  765. - (GCFSStructuredQuery_Order *)encodedSortOrder:(FSTSortOrder *)sortOrder {
  766. GCFSStructuredQuery_Order *proto = [GCFSStructuredQuery_Order message];
  767. proto.field = [self encodedFieldPath:sortOrder.field];
  768. if (sortOrder.ascending) {
  769. proto.direction = GCFSStructuredQuery_Direction_Ascending;
  770. } else {
  771. proto.direction = GCFSStructuredQuery_Direction_Descending;
  772. }
  773. return proto;
  774. }
  775. - (FSTSortOrder *)decodedSortOrder:(GCFSStructuredQuery_Order *)proto {
  776. FSTFieldPath *fieldPath = [FSTFieldPath pathWithServerFormat:proto.field.fieldPath];
  777. BOOL ascending;
  778. switch (proto.direction) {
  779. case GCFSStructuredQuery_Direction_Ascending:
  780. ascending = YES;
  781. break;
  782. case GCFSStructuredQuery_Direction_Descending:
  783. ascending = NO;
  784. break;
  785. default:
  786. FSTFail(@"Unrecognized GCFSStructuredQuery_Direction %d", proto.direction);
  787. }
  788. return [FSTSortOrder sortOrderWithFieldPath:fieldPath ascending:ascending];
  789. }
  790. #pragma mark - Bounds/Cursors
  791. - (GCFSCursor *)encodedBound:(FSTBound *)bound {
  792. GCFSCursor *proto = [GCFSCursor message];
  793. proto.before = bound.isBefore;
  794. for (FSTFieldValue *fieldValue in bound.position) {
  795. GCFSValue *value = [self encodedFieldValue:fieldValue];
  796. [proto.valuesArray addObject:value];
  797. }
  798. return proto;
  799. }
  800. - (FSTBound *)decodedBound:(GCFSCursor *)proto {
  801. NSMutableArray<FSTFieldValue *> *indexComponents = [NSMutableArray array];
  802. for (GCFSValue *valueProto in proto.valuesArray) {
  803. FSTFieldValue *value = [self decodedFieldValue:valueProto];
  804. [indexComponents addObject:value];
  805. }
  806. return [FSTBound boundWithPosition:indexComponents isBefore:proto.before];
  807. }
  808. #pragma mark - FSTWatchChange <= GCFSListenResponse proto
  809. - (FSTWatchChange *)decodedWatchChange:(GCFSListenResponse *)watchChange {
  810. switch (watchChange.responseTypeOneOfCase) {
  811. case GCFSListenResponse_ResponseType_OneOfCase_TargetChange:
  812. return [self decodedTargetChangeFromWatchChange:watchChange.targetChange];
  813. case GCFSListenResponse_ResponseType_OneOfCase_DocumentChange:
  814. return [self decodedDocumentChange:watchChange.documentChange];
  815. case GCFSListenResponse_ResponseType_OneOfCase_DocumentDelete:
  816. return [self decodedDocumentDelete:watchChange.documentDelete];
  817. case GCFSListenResponse_ResponseType_OneOfCase_DocumentRemove:
  818. return [self decodedDocumentRemove:watchChange.documentRemove];
  819. case GCFSListenResponse_ResponseType_OneOfCase_Filter:
  820. return [self decodedExistenceFilterWatchChange:watchChange.filter];
  821. default:
  822. FSTFail(@"Unknown WatchChange.changeType %" PRId32, watchChange.responseTypeOneOfCase);
  823. }
  824. }
  825. - (FSTSnapshotVersion *)versionFromListenResponse:(GCFSListenResponse *)watchChange {
  826. // We have only reached a consistent snapshot for the entire stream if there is a read_time set
  827. // and it applies to all targets (i.e. the list of targets is empty). The backend is guaranteed to
  828. // send such responses.
  829. if (watchChange.responseTypeOneOfCase != GCFSListenResponse_ResponseType_OneOfCase_TargetChange) {
  830. return [FSTSnapshotVersion noVersion];
  831. }
  832. if (watchChange.targetChange.targetIdsArray.count != 0) {
  833. return [FSTSnapshotVersion noVersion];
  834. }
  835. return [self decodedVersion:watchChange.targetChange.readTime];
  836. }
  837. - (FSTWatchTargetChange *)decodedTargetChangeFromWatchChange:(GCFSTargetChange *)change {
  838. FSTWatchTargetChangeState state = [self decodedWatchTargetChangeState:change.targetChangeType];
  839. NSMutableArray<NSNumber *> *targetIDs =
  840. [NSMutableArray arrayWithCapacity:change.targetIdsArray_Count];
  841. [change.targetIdsArray enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
  842. [targetIDs addObject:@(value)];
  843. }];
  844. NSError *cause = nil;
  845. if (change.hasCause) {
  846. cause = [NSError errorWithDomain:FIRFirestoreErrorDomain
  847. code:change.cause.code
  848. userInfo:@{NSLocalizedDescriptionKey : change.cause.message}];
  849. }
  850. return [[FSTWatchTargetChange alloc] initWithState:state
  851. targetIDs:targetIDs
  852. resumeToken:change.resumeToken
  853. cause:cause];
  854. }
  855. - (FSTWatchTargetChangeState)decodedWatchTargetChangeState:
  856. (GCFSTargetChange_TargetChangeType)state {
  857. switch (state) {
  858. case GCFSTargetChange_TargetChangeType_NoChange:
  859. return FSTWatchTargetChangeStateNoChange;
  860. case GCFSTargetChange_TargetChangeType_Add:
  861. return FSTWatchTargetChangeStateAdded;
  862. case GCFSTargetChange_TargetChangeType_Remove:
  863. return FSTWatchTargetChangeStateRemoved;
  864. case GCFSTargetChange_TargetChangeType_Current:
  865. return FSTWatchTargetChangeStateCurrent;
  866. case GCFSTargetChange_TargetChangeType_Reset:
  867. return FSTWatchTargetChangeStateReset;
  868. default:
  869. FSTFail(@"Unexpected TargetChange.state: %" PRId32, state);
  870. }
  871. }
  872. - (NSArray<NSNumber *> *)decodedIntegerArray:(GPBInt32Array *)values {
  873. NSMutableArray<NSNumber *> *result = [NSMutableArray arrayWithCapacity:values.count];
  874. [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
  875. [result addObject:@(value)];
  876. }];
  877. return result;
  878. }
  879. - (FSTDocumentWatchChange *)decodedDocumentChange:(GCFSDocumentChange *)change {
  880. FSTObjectValue *value = [self decodedFields:change.document.fields];
  881. FSTDocumentKey *key = [self decodedDocumentKey:change.document.name];
  882. FSTSnapshotVersion *version = [self decodedVersion:change.document.updateTime];
  883. FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
  884. @"Got a document change with no snapshot version");
  885. FSTMaybeDocument *document =
  886. [FSTDocument documentWithData:value key:key version:version hasLocalMutations:NO];
  887. NSArray<NSNumber *> *updatedTargetIds = [self decodedIntegerArray:change.targetIdsArray];
  888. NSArray<NSNumber *> *removedTargetIds = [self decodedIntegerArray:change.removedTargetIdsArray];
  889. return [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:updatedTargetIds
  890. removedTargetIDs:removedTargetIds
  891. documentKey:document.key
  892. document:document];
  893. }
  894. - (FSTDocumentWatchChange *)decodedDocumentDelete:(GCFSDocumentDelete *)change {
  895. FSTDocumentKey *key = [self decodedDocumentKey:change.document];
  896. // Note that version might be unset in which case we use [FSTSnapshotVersion noVersion]
  897. FSTSnapshotVersion *version = [self decodedVersion:change.readTime];
  898. FSTMaybeDocument *document = [FSTDeletedDocument documentWithKey:key version:version];
  899. NSArray<NSNumber *> *removedTargetIds = [self decodedIntegerArray:change.removedTargetIdsArray];
  900. return [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[]
  901. removedTargetIDs:removedTargetIds
  902. documentKey:document.key
  903. document:document];
  904. }
  905. - (FSTDocumentWatchChange *)decodedDocumentRemove:(GCFSDocumentRemove *)change {
  906. FSTDocumentKey *key = [self decodedDocumentKey:change.document];
  907. NSArray<NSNumber *> *removedTargetIds = [self decodedIntegerArray:change.removedTargetIdsArray];
  908. return [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[]
  909. removedTargetIDs:removedTargetIds
  910. documentKey:key
  911. document:nil];
  912. }
  913. - (FSTExistenceFilterWatchChange *)decodedExistenceFilterWatchChange:(GCFSExistenceFilter *)filter {
  914. // TODO(dimond): implement existence filter parsing
  915. FSTExistenceFilter *existenceFilter = [FSTExistenceFilter filterWithCount:filter.count];
  916. FSTTargetID targetID = filter.targetId;
  917. return [FSTExistenceFilterWatchChange changeWithFilter:existenceFilter targetID:targetID];
  918. }
  919. @end
  920. NS_ASSUME_NONNULL_END