Skip to content

Commit bc8ec06

Browse files
Merge pull request #13 from NeedleInAJayStack/fix/throwingBatchFunctions
Fixes handling of throwing batch load funcs
2 parents 622f0d3 + ed096eb commit bc8ec06

File tree

4 files changed

+70
-23
lines changed

4 files changed

+70
-23
lines changed

Package.resolved

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
"repositoryURL": "https://github.com/apple/swift-nio.git",
77
"state": {
88
"branch": null,
9-
"revision": "b8368b6e09b7993896c42a6199103a73ecc1dbf9",
10-
"version": "2.0.0"
9+
"revision": "51c3fc2e4a0fcdf4a25089b288dd65b73df1b0ef",
10+
"version": "2.37.0"
1111
}
1212
}
1313
]

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ struct UserResolver {
251251

252252
class UserContext {
253253
let database = ...
254-
let userLoader = DataLoader<Int, User>() { [unowned self] keys in
254+
let userLoader = DataLoader<Int, User>() { [weak self] keys in
255+
guard let self = self else { throw ContextError }
255256
return User.query(on: self.database).filter(\.$id ~~ keys).all().map { users in
256257
keys.map { key in
257258
users.first { $0.id == key }!

Sources/DataLoader/DataLoader.swift

+31-20
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ final public class DataLoader<Key: Hashable, Value> {
2828
private var dispatchScheduled = false
2929
private let lock = Lock()
3030

31-
public init(options: DataLoaderOptions<Key, Value> = DataLoaderOptions(), batchLoadFunction: @escaping BatchLoadFunction<Key, Value>) {
31+
public init(
32+
options: DataLoaderOptions<Key, Value> = DataLoaderOptions(),
33+
batchLoadFunction: @escaping BatchLoadFunction<Key, Value>
34+
) {
3235
self.options = options
3336
self.batchLoadFunction = batchLoadFunction
3437
}
@@ -37,7 +40,7 @@ final public class DataLoader<Key: Hashable, Value> {
3740
public func load(key: Key, on eventLoopGroup: EventLoopGroup) throws -> EventLoopFuture<Value> {
3841
let cacheKey = options.cacheKeyFunction?(key) ?? key
3942

40-
return try lock.withLock {
43+
return lock.withLock {
4144
if options.cachingEnabled, let cachedFuture = cache[cacheKey] {
4245
return cachedFuture
4346
}
@@ -53,16 +56,20 @@ final public class DataLoader<Key: Hashable, Value> {
5356
dispatchScheduled = true
5457
}
5558
} else {
56-
_ = try batchLoadFunction([key]).map { results in
57-
if results.isEmpty {
58-
promise.fail(DataLoaderError.noValueForKey("Did not return value for key: \(key)"))
59-
} else {
60-
let result = results[0]
61-
switch result {
62-
case .success(let value): promise.succeed(value)
63-
case .failure(let error): promise.fail(error)
59+
do {
60+
_ = try batchLoadFunction([key]).map { results in
61+
if results.isEmpty {
62+
promise.fail(DataLoaderError.noValueForKey("Did not return value for key: \(key)"))
63+
} else {
64+
let result = results[0]
65+
switch result {
66+
case .success(let value): promise.succeed(value)
67+
case .failure(let error): promise.fail(error)
68+
}
6469
}
6570
}
71+
} catch {
72+
promise.fail(error)
6673
}
6774
}
6875

@@ -181,20 +188,24 @@ final public class DataLoader<Key: Hashable, Value> {
181188

182189
// Step through the values, resolving or rejecting each Promise in the
183190
// loaded queue.
184-
_ = try batchLoadFunction(keys).flatMapThrowing { values in
185-
if values.count != keys.count {
186-
throw DataLoaderError.typeError("The function did not return an array of the same length as the array of keys. \nKeys count: \(keys.count)\nValues count: \(values.count)")
187-
}
191+
do {
192+
_ = try batchLoadFunction(keys).flatMapThrowing { values in
193+
if values.count != keys.count {
194+
throw DataLoaderError.typeError("The function did not return an array of the same length as the array of keys. \nKeys count: \(keys.count)\nValues count: \(values.count)")
195+
}
188196

189-
for entry in batch.enumerated() {
190-
let result = values[entry.offset]
197+
for entry in batch.enumerated() {
198+
let result = values[entry.offset]
191199

192-
switch result {
193-
case .failure(let error): entry.element.promise.fail(error)
194-
case .success(let value): entry.element.promise.succeed(value)
200+
switch result {
201+
case .failure(let error): entry.element.promise.fail(error)
202+
case .success(let value): entry.element.promise.succeed(value)
203+
}
195204
}
205+
}.recover { error in
206+
self.failedExecution(batch: batch, error: error)
196207
}
197-
}.recover { error in
208+
} catch {
198209
self.failedExecution(batch: batch, error: error)
199210
}
200211
}

Tests/DataLoaderTests/DataLoaderTests.swift

+35
Original file line numberDiff line numberDiff line change
@@ -441,4 +441,39 @@ final class DataLoaderTests: XCTestCase {
441441

442442
XCTAssertNotNil(value)
443443
}
444+
445+
func testErrorResult() throws {
446+
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
447+
defer {
448+
XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully())
449+
}
450+
451+
let loaderErrorMessage = "TEST"
452+
453+
// Test throwing loader without auto-executing
454+
let throwLoader = DataLoader<Int, Int>(
455+
options: DataLoaderOptions(executionPeriod: nil)
456+
) { keys in
457+
throw DataLoaderError.typeError(loaderErrorMessage)
458+
}
459+
460+
let value = try throwLoader.load(key: 1, on: eventLoopGroup)
461+
XCTAssertNoThrow(try throwLoader.execute())
462+
XCTAssertThrowsError(
463+
try value.wait(),
464+
loaderErrorMessage
465+
)
466+
467+
// Test throwing loader with auto-executing
468+
let throwLoaderAutoExecute = DataLoader<Int, Int>(
469+
options: DataLoaderOptions()
470+
) { keys in
471+
throw DataLoaderError.typeError(loaderErrorMessage)
472+
}
473+
474+
XCTAssertThrowsError(
475+
try throwLoaderAutoExecute.load(key: 1, on: eventLoopGroup).wait(),
476+
loaderErrorMessage
477+
)
478+
}
444479
}

0 commit comments

Comments
 (0)