FSTSerializerBeta.mm 46 KB

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