FSTSerializerBeta.m 42 KB

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