Skip to content

Commit 0d92ac5

Browse files
authored
Use a mock of the DynamoDBClientProtocol in testing. (#86)
* Use a mock of the DynamoDBClientProtocol in testing. * Cleanup documentation.
1 parent 916f152 commit 0d92ac5

12 files changed

+298
-34
lines changed

Package.resolved

Lines changed: 10 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ let package = Package(
4646
.package(url: "https://github.com/JohnSundell/CollectionConcurrencyKit", from: "0.2.0"),
4747
.package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.53.9"),
4848
.package(url: "https://github.com/apple/swift-syntax", from: "601.0.0"),
49+
.package(url: "https://github.com/tachyonics/smockable", from: "0.3.0"),
4950
],
5051
targets: [
5152
.macro(name: "DynamoDBTablesMacros", dependencies: [
@@ -59,6 +60,7 @@ let package = Package(
5960
.product(name: "Metrics", package: "swift-metrics"),
6061
.product(name: "AWSDynamoDB", package: "aws-sdk-swift"),
6162
.product(name: "CollectionConcurrencyKit", package: "CollectionConcurrencyKit"),
63+
.product(name: "Smockable", package: "smockable"),
6264
],
6365
swiftSettings: swiftSettings),
6466
.testTarget(

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+DynamoDBTableAsync.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import Foundation
2929
import Logging
3030

3131
/// DynamoDBTable conformance async functions
32-
public extension AWSDynamoDBCompositePrimaryKeyTable {
32+
public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
3333
func insertItem(_ item: TypedTTLDatabaseItem<some Any, some Any, some Any>) async throws {
3434
let putItemInput = try getInputForInsert(item)
3535

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+bulkUpdateSupport.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func getAttributes(forItem item: TypedTTLDatabaseItem<some Any, some Any, some A
6060
return attributes
6161
}
6262

63-
extension AWSDynamoDBCompositePrimaryKeyTable {
63+
extension GenericAWSDynamoDBCompositePrimaryKeyTable {
6464
func getUpdateExpression<AttributesType, ItemType, TimeToLiveAttributesType>(
6565
tableName: String,
6666
newItem: TypedTTLDatabaseItem<AttributesType, ItemType, TimeToLiveAttributesType>,

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+deleteItems.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import Logging
3232
private let maximumUpdatesPerExecuteStatement = 25
3333

3434
/// DynamoDBTable conformance updateItems function
35-
public extension AWSDynamoDBCompositePrimaryKeyTable {
35+
public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
3636
private func deleteChunkedItems(_ keys: [CompositePrimaryKey<some Any>]) async throws -> [DynamoDBClientTypes.BatchStatementResponse] {
3737
// if there are no keys, there is nothing to update
3838
guard keys.count > 0 else {

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+execute.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import Logging
3232
private let maximumKeysPerExecuteStatement = 50
3333

3434
/// DynamoDBTable conformance execute function
35-
public extension AWSDynamoDBCompositePrimaryKeyTable {
35+
public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
3636
private func getStatement(partitionKeys: [String],
3737
attributesFilter: [String]?,
3838
partitionKeyAttributeName: String,

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+getItems.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,18 @@ private let maximumKeysPerGetItemBatch = 100
3434
private let millisecondsToNanoSeconds: UInt64 = 1_000_000
3535

3636
/// DynamoDBTable conformance getItems function
37-
public extension AWSDynamoDBCompositePrimaryKeyTable {
37+
public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
3838
/**
3939
Helper type that manages the state of a getItems request.
4040

4141
As suggested here - https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html - this helper type
4242
monitors the unprocessed items returned in the response from DynamoDB and uses an exponential backoff algorithm to retry those items using
4343
the same retry configuration as the underlying DynamoDB client.
4444
*/
45-
private class GetItemsRetriable<AttributesType: PrimaryKeyAttributes, ItemType: Codable, TimeToLiveAttributesType: TimeToLiveAttributes> {
45+
private class GetItemsRetriable<AttributesType: PrimaryKeyAttributes, ItemType: Codable, TimeToLiveAttributesType: TimeToLiveAttributes, DynamoClient: DynamoDBClientProtocol> {
4646
typealias OutputType = [CompositePrimaryKey<AttributesType>: TypedTTLDatabaseItem<AttributesType, ItemType, TimeToLiveAttributesType>]
4747

48-
let dynamodb: AWSDynamoDB.DynamoDBClient
48+
let dynamodb: DynamoClient
4949
let retryConfiguration: RetryConfiguration
5050
let logger: Logging.Logger
5151

@@ -54,7 +54,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
5454
var outputItems: OutputType = [:]
5555

5656
init(initialInput: BatchGetItemInput,
57-
dynamodb: AWSDynamoDB.DynamoDBClient,
57+
dynamodb: DynamoClient,
5858
retryConfiguration: RetryConfiguration,
5959
logger: Logging.Logger)
6060
{
@@ -131,7 +131,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
131131
let maps = try await chunkedList.concurrentMap { chunk in
132132
let input = try self.getInputForBatchGetItem(forKeys: chunk)
133133

134-
let retriable = GetItemsRetriable<AttributesType, ItemType, TimeToLiveAttributesType>(
134+
let retriable = GetItemsRetriable<AttributesType, ItemType, TimeToLiveAttributesType, Client>(
135135
initialInput: input,
136136
dynamodb: self.dynamodb,
137137
retryConfiguration: self.tableConfiguration.retry,

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+polymorphicGetItems.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,18 @@ private let maximumKeysPerGetItemBatch = 100
3434
private let millisecondsToNanoSeconds: UInt64 = 1_000_000
3535

3636
/// DynamoDBTable conformance polymorphicGetItems function
37-
public extension AWSDynamoDBCompositePrimaryKeyTable {
37+
public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
3838
/**
3939
Helper type that manages the state of a polymorphicGetItems request.
4040

4141
As suggested here - https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html - this helper type
4242
monitors the unprocessed items returned in the response from DynamoDB and uses an exponential backoff algorithm to retry those items using
4343
the same retry configuration as the underlying DynamoDB client.
4444
*/
45-
private class PolymorphicGetItemsRetriable<ReturnedType: PolymorphicOperationReturnType & BatchCapableReturnType> {
45+
private class PolymorphicGetItemsRetriable<ReturnedType: PolymorphicOperationReturnType & BatchCapableReturnType, DynamoClient: DynamoDBClientProtocol> {
4646
typealias OutputType = [CompositePrimaryKey<ReturnedType.AttributesType>: ReturnedType]
4747

48-
let dynamodb: AWSDynamoDB.DynamoDBClient
48+
let dynamodb: DynamoClient
4949
let retryConfiguration: RetryConfiguration
5050
let logger: Logging.Logger
5151

@@ -54,7 +54,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
5454
var outputItems: OutputType = [:]
5555

5656
init(initialInput: BatchGetItemInput,
57-
dynamodb: AWSDynamoDB.DynamoDBClient,
57+
dynamodb: DynamoClient,
5858
retryConfiguration: RetryConfiguration,
5959
logger: Logging.Logger)
6060
{
@@ -131,7 +131,7 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
131131
let maps = try await chunkedList.concurrentMap { chunk -> [CompositePrimaryKey<ReturnedType.AttributesType>: ReturnedType] in
132132
let input = try self.getInputForBatchGetItem(forKeys: chunk)
133133

134-
let retriable = PolymorphicGetItemsRetriable<ReturnedType>(
134+
let retriable = PolymorphicGetItemsRetriable<ReturnedType, Client>(
135135
initialInput: input,
136136
dynamodb: self.dynamodb,
137137
retryConfiguration: self.tableConfiguration.retry,

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable+updateItems.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ public enum AWSDynamoDBLimits {
3838
public static let maxStatementLength = 8192
3939
}
4040

41-
private struct AWSDynamoDBPolymorphicWriteEntryTransform: PolymorphicWriteEntryTransform {
42-
typealias TableType = AWSDynamoDBCompositePrimaryKeyTable
41+
private struct AWSDynamoDBPolymorphicWriteEntryTransform<Client: DynamoDBClientProtocol>: PolymorphicWriteEntryTransform {
42+
typealias TableType = GenericAWSDynamoDBCompositePrimaryKeyTable<Client>
4343

4444
let statement: String
4545

@@ -48,8 +48,8 @@ private struct AWSDynamoDBPolymorphicWriteEntryTransform: PolymorphicWriteEntryT
4848
}
4949
}
5050

51-
private struct AWSDynamoDBPolymorphicTransactionConstraintTransform: PolymorphicTransactionConstraintTransform {
52-
typealias TableType = AWSDynamoDBCompositePrimaryKeyTable
51+
private struct AWSDynamoDBPolymorphicTransactionConstraintTransform<Client: DynamoDBClientProtocol>: PolymorphicTransactionConstraintTransform {
52+
typealias TableType = GenericAWSDynamoDBCompositePrimaryKeyTable<Client>
5353

5454
let statement: String
5555

@@ -61,7 +61,7 @@ private struct AWSDynamoDBPolymorphicTransactionConstraintTransform: Polymorphic
6161
}
6262

6363
/// DynamoDBTable conformance updateItems function
64-
public extension AWSDynamoDBCompositePrimaryKeyTable {
64+
public extension GenericAWSDynamoDBCompositePrimaryKeyTable {
6565
func validateEntry(entry: WriteEntry<some Any, some Any, some Any>) throws {
6666
let statement: String = try entryToStatement(entry)
6767

@@ -150,17 +150,17 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
150150
return nil
151151
}
152152

153-
let context = StandardPolymorphicWriteEntryContext<AWSDynamoDBPolymorphicWriteEntryTransform,
154-
AWSDynamoDBPolymorphicTransactionConstraintTransform>(table: self)
153+
let context = StandardPolymorphicWriteEntryContext<AWSDynamoDBPolymorphicWriteEntryTransform<Client>,
154+
AWSDynamoDBPolymorphicTransactionConstraintTransform<Client>>(table: self)
155155
let entryStatements = try entries.map { entry -> DynamoDBClientTypes.ParameterizedStatement in
156-
let transform: AWSDynamoDBPolymorphicWriteEntryTransform = try entry.handle(context: context)
156+
let transform: AWSDynamoDBPolymorphicWriteEntryTransform<Client> = try entry.handle(context: context)
157157
let statement = transform.statement
158158

159159
return DynamoDBClientTypes.ParameterizedStatement(statement: statement)
160160
}
161161

162162
let requiredItemsStatements = try constraints.map { entry -> DynamoDBClientTypes.ParameterizedStatement in
163-
let transform: AWSDynamoDBPolymorphicTransactionConstraintTransform = try entry.handle(context: context)
163+
let transform: AWSDynamoDBPolymorphicTransactionConstraintTransform<Client> = try entry.handle(context: context)
164164
let statement = transform.statement
165165

166166
return DynamoDBClientTypes.ParameterizedStatement(statement: statement)
@@ -427,10 +427,10 @@ public extension AWSDynamoDBCompositePrimaryKeyTable {
427427
return []
428428
}
429429

430-
let context = StandardPolymorphicWriteEntryContext<AWSDynamoDBPolymorphicWriteEntryTransform,
431-
AWSDynamoDBPolymorphicTransactionConstraintTransform>(table: self)
430+
let context = StandardPolymorphicWriteEntryContext<AWSDynamoDBPolymorphicWriteEntryTransform<Client>,
431+
AWSDynamoDBPolymorphicTransactionConstraintTransform<Client>>(table: self)
432432
let statements = try entries.map { entry -> DynamoDBClientTypes.BatchStatementRequest in
433-
let transform: AWSDynamoDBPolymorphicWriteEntryTransform = try entry.handle(context: context)
433+
let transform: AWSDynamoDBPolymorphicWriteEntryTransform<Client> = try entry.handle(context: context)
434434
let statement = transform.statement
435435

436436
return DynamoDBClientTypes.BatchStatementRequest(consistentRead: self.tableConfiguration.consistentRead, statement: statement)

Sources/DynamoDBTables/AWSDynamoDBCompositePrimaryKeyTable.swift

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,33 @@ public struct AWSDynamoDBTableConfiguration {
5555
}
5656
}
5757

58-
public struct AWSDynamoDBCompositePrimaryKeyTable: DynamoDBCompositePrimaryKeyTable {
59-
let dynamodb: AWSDynamoDB.DynamoDBClient
58+
/// A type alias for `GenericAWSDynamoDBCompositePrimaryKeyTable` specialized with the AWS DynamoDB client.
59+
///
60+
/// This provides a convenient way to use the DynamoDB table implementation with the standard AWS DynamoDB client
61+
/// without needing to specify the generic parameter explicitly.
62+
///
63+
/// ## Usage
64+
///
65+
/// Use this type alias when working with the real AWS DynamoDB service:
66+
///
67+
/// ```swift
68+
/// // Create a table using region-based initialization
69+
/// let table = try AWSDynamoDBCompositePrimaryKeyTable(
70+
/// tableName: "MyTable",
71+
/// region: "us-east-1"
72+
/// )
73+
///
74+
/// // Create a table with an existing AWS client
75+
/// let awsClient = AWSDynamoDB.DynamoDBClient(config: config)
76+
/// let table = AWSDynamoDBCompositePrimaryKeyTable(
77+
/// tableName: "MyTable",
78+
/// client: awsClient
79+
/// )
80+
/// ```
81+
public typealias AWSDynamoDBCompositePrimaryKeyTable = GenericAWSDynamoDBCompositePrimaryKeyTable<AWSDynamoDB.DynamoDBClient>
82+
83+
public struct GenericAWSDynamoDBCompositePrimaryKeyTable<Client: DynamoDBClientProtocol>: DynamoDBCompositePrimaryKeyTable {
84+
let dynamodb: Client
6085
let targetTableName: String
6186
public let tableConfiguration: AWSDynamoDBTableConfiguration
6287
public let tableMetrics: AWSDynamoDBTableMetrics
@@ -67,7 +92,7 @@ public struct AWSDynamoDBCompositePrimaryKeyTable: DynamoDBCompositePrimaryKeyTa
6792
httpClientConfiguration: ClientRuntime.HttpClientConfiguration? = nil,
6893
tableConfiguration: AWSDynamoDBTableConfiguration = .init(),
6994
tableMetrics: AWSDynamoDBTableMetrics = .init(),
70-
logger: Logging.Logger? = nil) throws
95+
logger: Logging.Logger? = nil) throws where Client == AWSDynamoDB.DynamoDBClient
7196
{
7297
self.logger = logger ?? Logging.Logger(label: "AWSDynamoDBCompositePrimaryKeyTable")
7398
let config = try DynamoDBClient.DynamoDBClientConfiguration(
@@ -83,7 +108,7 @@ public struct AWSDynamoDBCompositePrimaryKeyTable: DynamoDBCompositePrimaryKeyTa
83108
}
84109

85110
public init(tableName: String,
86-
client: AWSDynamoDB.DynamoDBClient,
111+
client: Client,
87112
tableConfiguration: AWSDynamoDBTableConfiguration = .init(),
88113
tableMetrics: AWSDynamoDBTableMetrics = .init(),
89114
logger: Logging.Logger? = nil)
@@ -98,7 +123,7 @@ public struct AWSDynamoDBCompositePrimaryKeyTable: DynamoDBCompositePrimaryKeyTa
98123
}
99124
}
100125

101-
extension AWSDynamoDBCompositePrimaryKeyTable {
126+
extension GenericAWSDynamoDBCompositePrimaryKeyTable {
102127
func getInputForInsert<AttributesType>(
103128
_ item: TypedTTLDatabaseItem<AttributesType, some Any, some Any>) throws
104129
-> AWSDynamoDB.PutItemInput

0 commit comments

Comments
 (0)