Skip to content

Commit c1fbc87

Browse files
cherylEnkiduncooke3yyyu-googlejscud
authored
Refactor error message passing in Pipeline stages (#15939)
Co-authored-by: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Co-authored-by: Yvonne Yu <yyyu@google.com> Co-authored-by: Jeff Scudder <jscudder@google.com>
1 parent 268250d commit c1fbc87

6 files changed

Lines changed: 85 additions & 186 deletions

File tree

Firestore/Swift/Source/ExpressionImplementation.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
// limitations under the License.
1414

1515
extension Expression {
16+
/// Returns the internal error message. It is overridden in specific expression implementations
17+
/// (like FunctionExpression) but defaults to `nil` for others.
18+
/// This design is to support pipeline conversion to expression.
19+
var errorMessage: String? {
20+
return nil
21+
}
22+
1623
func toBridge() -> ExprBridge {
1724
return (self as! BridgeWrapper).bridge
1825
}

Firestore/Swift/Source/Helper/PipelineHelper.swift

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import Foundation
16+
1517
enum Helper {
1618
enum HelperError: Error, LocalizedError {
1719
case duplicateAlias(String)
@@ -44,32 +46,57 @@ enum Helper {
4446

4547
static func selectablesToMap(selectables: [Selectable]) -> ([String: Expression], Error?) {
4648
var exprMap = [String: Expression]()
49+
var errors = [String]()
4750
for selectable in selectables {
4851
guard let value = selectable as? SelectableWrapper else {
4952
fatalError("Selectable class must conform to SelectableWrapper.")
5053
}
5154
let alias = value.alias
52-
if exprMap.keys.contains(alias) {
53-
return ([:], HelperError.duplicateAlias("Duplicate alias '\(alias)' found in selectables."))
55+
if let errorMessage = value.expr.errorMessage {
56+
errors.append(errorMessage)
57+
}
58+
if exprMap[alias] != nil {
59+
errors.append("Duplicate alias '\(alias)' found in selectables.")
5460
}
5561
exprMap[alias] = value.expr
5662
}
63+
if !errors.isEmpty {
64+
return (
65+
[:],
66+
NSError(
67+
domain: "com.google.firebase.firestore",
68+
code: 3,
69+
userInfo: [NSLocalizedDescriptionKey: errors.joined(separator: "\n")]
70+
)
71+
)
72+
}
5773
return (exprMap, nil)
5874
}
5975

6076
static func aliasedAggregatesToMap(accumulators: [AliasedAggregate])
6177
-> ([String: AggregateFunction], Error?) {
6278
var accumulatorMap = [String: AggregateFunction]()
79+
var errors = [String]()
6380
for aliasedAggregate in accumulators {
6481
let alias = aliasedAggregate.alias
65-
if accumulatorMap.keys.contains(alias) {
66-
return (
67-
[:],
68-
HelperError.duplicateAlias("Duplicate alias '\(alias)' found in accumulators.")
69-
)
82+
if let errorMessage = aliasedAggregate.aggregate.errorMessage {
83+
errors.append(errorMessage)
84+
}
85+
if accumulatorMap[alias] != nil {
86+
errors.append("Duplicate alias '\(alias)' found in accumulators.")
7087
}
7188
accumulatorMap[alias] = aliasedAggregate.aggregate
7289
}
90+
if !errors.isEmpty {
91+
return (
92+
[:],
93+
NSError(
94+
domain: "com.google.firebase.firestore",
95+
code: 3,
96+
userInfo: [NSLocalizedDescriptionKey: errors.joined(separator: "\n")]
97+
)
98+
)
99+
}
73100
return (accumulatorMap, nil)
74101
}
75102

Firestore/Swift/Source/Stages.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,17 @@ class Where: Stage {
110110

111111
let bridge: StageBridge
112112
private var condition: BooleanExpression?
113+
let errorMessage: String?
113114

114115
init(condition: BooleanExpression) {
115116
self.condition = condition
116117
bridge = WhereStageBridge(expr: condition.toBridge())
118+
errorMessage = condition.errorMessage
117119
}
118120

119121
init(bridge: WhereStageBridge) {
120122
self.bridge = bridge
123+
errorMessage = nil
121124
}
122125
}
123126

@@ -313,10 +316,12 @@ class ReplaceWith: Stage {
313316
let name: String = "replace_with"
314317
let bridge: StageBridge
315318
private var expr: Expression
319+
let errorMessage: String?
316320

317321
init(expr: Expression) {
318322
self.expr = expr
319323
bridge = ReplaceWithStageBridge(expr: expr.toBridge())
324+
errorMessage = expr.errorMessage
320325
}
321326
}
322327

@@ -346,9 +351,12 @@ class Union: Stage {
346351
let bridge: StageBridge
347352
private var other: Pipeline
348353

354+
let errorMessage: String?
355+
349356
init(other: Pipeline) {
350357
self.other = other
351358
bridge = UnionStageBridge(other: other.bridge)
359+
errorMessage = other.errorMessage
352360
}
353361
}
354362

@@ -359,12 +367,14 @@ class Unnest: Stage {
359367
private var alias: Expression
360368
private var field: Expression
361369
private var indexField: String?
370+
let errorMessage: String?
362371

363372
init(field: Selectable, indexField: String? = nil) {
364373
let seletable = field as! SelectableWrapper
365374
self.field = seletable.expr
366375
alias = Field(seletable.alias)
367376
self.indexField = indexField
377+
errorMessage = self.field.errorMessage ?? alias.errorMessage
368378

369379
bridge = UnnestStageBridge(
370380
field: self.field.toBridge(),
@@ -380,6 +390,7 @@ class RawStage: Stage {
380390
let bridge: StageBridge
381391
private var params: [Sendable]
382392
private var options: [String: Sendable]?
393+
let errorMessage: String? = nil
383394

384395
init(name: String, params: [Sendable], options: [String: Sendable]? = nil) {
385396
self.name = name

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ public class AggregateFunction: AggregateBridgeWrapper, @unchecked Sendable {
2323
let functionName: String
2424
let args: [Expression]
2525

26+
/// The error message associated with this aggregate function or its arguments, if any.
27+
var errorMessage: String? {
28+
let errors = args.compactMap { $0.errorMessage }
29+
return errors.isEmpty ? nil : errors.joined(separator: "\n")
30+
}
31+
2632
/// Creates a new `AggregateFunction`.
2733
///
2834
/// - Parameters:

Firestore/Swift/Source/SwiftAPI/Pipeline/Expressions/FunctionExpressions/FunctionExpression.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ public class FunctionExpression: Expression, BridgeWrapper, @unchecked Sendable
2525
let functionName: String
2626
let args: [Expression]
2727

28+
/// The error message associated with this expression or its arguments, if any.
29+
var errorMessage: String? {
30+
let errors = args.compactMap { $0.errorMessage }
31+
return errors.isEmpty ? nil : errors.joined(separator: "\n")
32+
}
33+
2834
/// Creates a new `FunctionExpression`.
2935
///
3036
/// - Parameters:

0 commit comments

Comments
 (0)