Skip to content

Commit 7cafce4

Browse files
cherylEnkiduwu-huincooke3
authored
Pipeline tests part 1 (#14885)
Co-authored-by: wu-hui <wu.hui.github@gmail.com> Co-authored-by: Nick Cooke <36927374+ncooke3@users.noreply.github.com>
1 parent 11ae3e2 commit 7cafce4

File tree

12 files changed

+436
-78
lines changed

12 files changed

+436
-78
lines changed

Firestore/Source/API/FIRPipelineBridge.mm

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include <memory>
2222

23+
#import "Firestore/Source/API/FIRCollectionReference+Internal.h"
2324
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
2425
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
2526
#import "Firestore/Source/API/FIRFirestore+Internal.h"
@@ -39,6 +40,7 @@
3940
#include "Firestore/core/src/api/pipeline_result.h"
4041
#include "Firestore/core/src/api/pipeline_snapshot.h"
4142
#include "Firestore/core/src/api/stages.h"
43+
#include "Firestore/core/src/util/comparison.h"
4244
#include "Firestore/core/src/util/error_apple.h"
4345
#include "Firestore/core/src/util/status.h"
4446
#include "Firestore/core/src/util/string_apple.h"
@@ -57,12 +59,12 @@
5759
using firebase::firestore::api::Field;
5860
using firebase::firestore::api::FindNearestStage;
5961
using firebase::firestore::api::FunctionExpr;
60-
using firebase::firestore::api::GenericStage;
6162
using firebase::firestore::api::LimitStage;
6263
using firebase::firestore::api::MakeFIRTimestamp;
6364
using firebase::firestore::api::OffsetStage;
6465
using firebase::firestore::api::Ordering;
6566
using firebase::firestore::api::Pipeline;
67+
using firebase::firestore::api::RawStage;
6668
using firebase::firestore::api::RemoveFieldsStage;
6769
using firebase::firestore::api::ReplaceWith;
6870
using firebase::firestore::api::Sample;
@@ -73,13 +75,21 @@
7375
using firebase::firestore::api::Where;
7476
using firebase::firestore::model::FieldPath;
7577
using firebase::firestore::nanopb::SharedMessage;
78+
using firebase::firestore::util::ComparisonResult;
7679
using firebase::firestore::util::MakeCallback;
7780
using firebase::firestore::util::MakeNSString;
7881
using firebase::firestore::util::MakeString;
7982
using firebase::firestore::util::ThrowInvalidArgument;
8083

8184
NS_ASSUME_NONNULL_BEGIN
8285

86+
inline std::string EnsureLeadingSlash(const std::string &path) {
87+
if (!path.empty() && path[0] == '/') {
88+
return path;
89+
}
90+
return "/" + path;
91+
}
92+
8393
@implementation FIRExprBridge
8494
@end
8595

@@ -216,10 +226,19 @@ @implementation FIRCollectionSourceStageBridge {
216226
std::shared_ptr<CollectionSource> collection_source;
217227
}
218228

219-
- (id)initWithPath:(NSString *)path {
229+
- (id)initWithRef:(FIRCollectionReference *)ref firestore:(FIRFirestore *)db {
220230
self = [super init];
221231
if (self) {
222-
collection_source = std::make_shared<CollectionSource>(MakeString(path));
232+
if (ref.firestore.databaseID.CompareTo(db.databaseID) != ComparisonResult::Same) {
233+
ThrowInvalidArgument(
234+
"Invalid CollectionReference. The project ID (\"%s\") or the database (\"%s\") does not "
235+
"match "
236+
"the project ID (\"%s\") and database (\"%s\") of the target database of this Pipeline.",
237+
ref.firestore.databaseID.project_id(), ref.firestore.databaseID.database_id(),
238+
db.databaseID.project_id(), db.databaseID.project_id());
239+
}
240+
collection_source =
241+
std::make_shared<CollectionSource>(EnsureLeadingSlash(MakeString(ref.path)));
223242
}
224243
return self;
225244
}
@@ -270,12 +289,21 @@ @implementation FIRDocumentsSourceStageBridge {
270289
std::shared_ptr<DocumentsSource> cpp_document_source;
271290
}
272291

273-
- (id)initWithDocuments:(NSArray<NSString *> *)documents {
292+
- (id)initWithDocuments:(NSArray<FIRDocumentReference *> *)documents firestore:(FIRFirestore *)db {
274293
self = [super init];
275294
if (self) {
276295
std::vector<std::string> cpp_documents;
277-
for (NSString *doc in documents) {
278-
cpp_documents.push_back(MakeString(doc));
296+
for (FIRDocumentReference *doc in documents) {
297+
if (doc.firestore.databaseID.CompareTo(db.databaseID) != ComparisonResult::Same) {
298+
ThrowInvalidArgument("Invalid DocumentReference. The project ID (\"%s\") or the database "
299+
"(\"%s\") does not match "
300+
"the project ID (\"%s\") and database (\"%s\") of the target database "
301+
"of this Pipeline.",
302+
doc.firestore.databaseID.project_id(),
303+
doc.firestore.databaseID.database_id(), db.databaseID.project_id(),
304+
db.databaseID.project_id());
305+
}
306+
cpp_documents.push_back(EnsureLeadingSlash(MakeString(doc.path)));
279307
}
280308
cpp_document_source = std::make_shared<DocumentsSource>(std::move(cpp_documents));
281309
}
@@ -754,12 +782,12 @@ - (id)initWithField:(FIRExprBridge *)field indexField:(NSString *_Nullable)index
754782

755783
@end
756784

757-
@implementation FIRGenericStageBridge {
785+
@implementation FIRRawStageBridge {
758786
NSString *_name;
759787
NSArray<FIRExprBridge *> *_params;
760788
NSDictionary<NSString *, FIRExprBridge *> *_Nullable _options;
761789
Boolean isUserDataRead;
762-
std::shared_ptr<GenericStage> cpp_generic_stage;
790+
std::shared_ptr<RawStage> cpp_generic_stage;
763791
}
764792

765793
- (id)initWithName:(NSString *)name
@@ -787,8 +815,8 @@ - (id)initWithName:(NSString *)name
787815
cpp_options[MakeString(key)] = [_options[key] cppExprWithReader:reader];
788816
}
789817
}
790-
cpp_generic_stage = std::make_shared<GenericStage>(MakeString(_name), std::move(cpp_params),
791-
std::move(cpp_options));
818+
cpp_generic_stage = std::make_shared<RawStage>(MakeString(_name), std::move(cpp_params),
819+
std::move(cpp_options));
792820
}
793821

794822
isUserDataRead = YES;

Firestore/Source/Public/FirebaseFirestore/FIRPipelineBridge.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ NS_SWIFT_SENDABLE
7070
NS_SWIFT_NAME(CollectionSourceStageBridge)
7171
@interface FIRCollectionSourceStageBridge : FIRStageBridge
7272

73-
- (id)initWithPath:(NSString *)path;
73+
- (id)initWithRef:(FIRCollectionReference *)ref firestore:(FIRFirestore *)db;
7474

7575
@end
7676

@@ -94,7 +94,7 @@ NS_SWIFT_SENDABLE
9494
NS_SWIFT_NAME(DocumentsSourceStageBridge)
9595
@interface FIRDocumentsSourceStageBridge : FIRStageBridge
9696

97-
- (id)initWithDocuments:(NSArray<NSString *> *)documents;
97+
- (id)initWithDocuments:(NSArray<FIRDocumentReference *> *)documents firestore:(FIRFirestore *)db;
9898

9999
@end
100100

@@ -195,8 +195,8 @@ NS_SWIFT_NAME(UnnestStageBridge)
195195
@end
196196

197197
NS_SWIFT_SENDABLE
198-
NS_SWIFT_NAME(GenericStageBridge)
199-
@interface FIRGenericStageBridge : FIRStageBridge
198+
NS_SWIFT_NAME(RawStageBridge)
199+
@interface FIRRawStageBridge : FIRStageBridge
200200
- (id)initWithName:(NSString *)name
201201
params:(NSArray<FIRExprBridge *> *)params
202202
options:(NSDictionary<NSString *, FIRExprBridge *> *_Nullable)options;

Firestore/Swift/Source/SwiftAPI/Pipeline/Pipeline.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ public struct Pipeline: @unchecked Sendable {
699699
/// ```swift
700700
/// // let pipeline: Pipeline = ...
701701
/// // Example: Assuming a hypothetical backend stage "customFilterV2".
702-
/// let genericPipeline = pipeline.genericStage(
702+
/// let genericPipeline = pipeline.rawStage(
703703
/// name: "customFilterV2",
704704
/// params: [Field("userScore"), 80], // Ordered parameters.
705705
/// options: ["mode": "strict", "logLevel": 2] // Optional named parameters.
@@ -712,10 +712,10 @@ public struct Pipeline: @unchecked Sendable {
712712
/// - params: An array of ordered, `Sendable` parameters for the stage.
713713
/// - options: Optional dictionary of named, `Sendable` parameters.
714714
/// - Returns: A new `Pipeline` object with this stage appended.
715-
public func genericStage(name: String, params: [Sendable],
716-
options: [String: Sendable]? = nil) -> Pipeline {
715+
public func rawStage(name: String, params: [Sendable],
716+
options: [String: Sendable]? = nil) -> Pipeline {
717717
return Pipeline(
718-
stages: stages + [GenericStage(name: name, params: params, options: options)],
718+
stages: stages + [RawStage(name: name, params: params, options: options)],
719719
db: db
720720
)
721721
}

Firestore/Swift/Source/SwiftAPI/Pipeline/PipelineSnapshot.swift

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public struct PipelineSnapshot: Sendable {
2525
public let pipeline: Pipeline
2626

2727
/// An array of all the results in the `PipelineSnapshot`.
28-
let results_cache: [PipelineResult]
28+
public let results: [PipelineResult]
2929

3030
/// The time at which the pipeline producing this result was executed.
3131
public let executionTime: Timestamp
@@ -36,10 +36,6 @@ public struct PipelineSnapshot: Sendable {
3636
self.bridge = bridge
3737
self.pipeline = pipeline
3838
executionTime = self.bridge.execution_time
39-
results_cache = self.bridge.results.map { PipelineResult($0) }
40-
}
41-
42-
public func results() -> [PipelineResult] {
43-
return results_cache
39+
results = self.bridge.results.map { PipelineResult($0) }
4440
}
4541
}

Firestore/Swift/Source/SwiftAPI/Pipeline/PipelineSource.swift

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@ public struct PipelineSource: @unchecked Sendable {
2121
}
2222

2323
public func collection(_ path: String) -> Pipeline {
24-
let normalizedPath = path.hasPrefix("/") ? path : "/" + path
25-
return Pipeline(stages: [CollectionSource(collection: normalizedPath)], db: db)
24+
return Pipeline(stages: [CollectionSource(collection: db.collection(path), db: db)], db: db)
25+
}
26+
27+
public func collection(_ ref: CollectionReference) -> Pipeline {
28+
let collectionStage = CollectionSource(collection: ref, db: db)
29+
return Pipeline(stages: [collectionStage], db: db)
2630
}
2731

2832
public func collectionGroup(_ collectionId: String) -> Pipeline {
@@ -37,13 +41,13 @@ public struct PipelineSource: @unchecked Sendable {
3741
}
3842

3943
public func documents(_ docs: [DocumentReference]) -> Pipeline {
40-
let paths = docs.map { $0.path.hasPrefix("/") ? $0.path : "/" + $0.path }
41-
return Pipeline(stages: [DocumentsSource(paths: paths)], db: db)
44+
return Pipeline(stages: [DocumentsSource(docs: docs, db: db)], db: db)
4245
}
4346

4447
public func documents(_ paths: [String]) -> Pipeline {
45-
let normalizedPaths = paths.map { $0.hasPrefix("/") ? $0 : "/" + $0 }
46-
return Pipeline(stages: [DocumentsSource(paths: normalizedPaths)], db: db)
48+
let docs = paths.map { db.document($0) }
49+
let documentsStage = DocumentsSource(docs: docs, db: db)
50+
return Pipeline(stages: [documentsStage], db: db)
4751
}
4852

4953
public func create(from query: Query) -> Pipeline {

Firestore/Swift/Source/SwiftAPI/Stages.swift

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,13 @@ class CollectionSource: Stage {
3333
let name: String = "collection"
3434

3535
let bridge: StageBridge
36-
private var collection: String
36+
private var collection: CollectionReference
37+
private let db: Firestore
3738

38-
init(collection: String) {
39+
init(collection: CollectionReference, db: Firestore) {
3940
self.collection = collection
40-
bridge = CollectionSourceStageBridge(path: collection)
41+
self.db = db
42+
bridge = CollectionSourceStageBridge(ref: collection, firestore: db)
4143
}
4244
}
4345

@@ -70,12 +72,14 @@ class DatabaseSource: Stage {
7072
class DocumentsSource: Stage {
7173
let name: String = "documents"
7274
let bridge: StageBridge
73-
private var references: [String]
75+
private var docs: [DocumentReference]
76+
private let db: Firestore
7477

7578
// Initialize with an array of String paths
76-
init(paths: [String]) {
77-
references = paths
78-
bridge = DocumentsSourceStageBridge(documents: paths)
79+
init(docs: [DocumentReference], db: Firestore) {
80+
self.docs = docs
81+
self.db = db
82+
bridge = DocumentsSourceStageBridge(documents: docs, firestore: db)
7983
}
8084
}
8185

@@ -337,7 +341,7 @@ class Unnest: Stage {
337341
}
338342

339343
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
340-
class GenericStage: Stage {
344+
class RawStage: Stage {
341345
let name: String
342346
let bridge: StageBridge
343347
private var params: [Sendable]
@@ -349,6 +353,6 @@ class GenericStage: Stage {
349353
self.options = options
350354
let bridgeParams = params.map { Helper.sendableToExpr($0).toBridge() }
351355
let bridgeOptions = options?.mapValues { Helper.sendableToExpr($0).toBridge() }
352-
bridge = GenericStageBridge(name: name, params: bridgeParams, options: bridgeOptions)
356+
bridge = RawStageBridge(name: name, params: bridgeParams, options: bridgeOptions)
353357
}
354358
}

Firestore/Swift/Tests/Integration/PipelineApiTests.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,22 +263,22 @@ final class PipelineTests: FSTIntegrationTestCase {
263263
// ... }
264264
}
265265

266-
func testGenericStage() async throws {
266+
func testRawStage() async throws {
267267
// Assume we don't have a built-in "where" stage, the customer could still
268-
// add this stage by calling genericStage, passing the name of the stage "where",
268+
// add this stage by calling rawStage, passing the name of the stage "where",
269269
// and providing positional argument values.
270270
_ = db.pipeline().collection("books")
271-
.genericStage(name: "where",
272-
params: [Field("published").lt(1900)])
271+
.rawStage(name: "where",
272+
params: [Field("published").lt(1900)])
273273
.select("title", "author")
274274

275275
// In cases where the stage also supports named argument values, then these can be
276276
// provided with a third argument that maps the argument name to value.
277277
// Note that these named arguments are always optional in the stage definition.
278278
_ = db.pipeline().collection("books")
279-
.genericStage(name: "where",
280-
params: [Field("published").lt(1900)],
281-
options: ["someOptionalParamName": "the argument value for this param"])
279+
.rawStage(name: "where",
280+
params: [Field("published").lt(1900)],
281+
options: ["someOptionalParamName": "the argument value for this param"])
282282
.select("title", "author")
283283
}
284284

0 commit comments

Comments
 (0)