Skip to content

Commit

Permalink
Organise store file.
Browse files Browse the repository at this point in the history
  • Loading branch information
tachyonics committed Oct 6, 2023
1 parent cf18058 commit c4e0085
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 130 deletions.
271 changes: 141 additions & 130 deletions Sources/SmokeDynamoDB/InMemoryDynamoDBCompositePrimaryKeyTableStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import DynamoDBModel

private let itemAlreadyExistsMessage = "Row already exists."

// MARK: - Transforms

internal struct InMemoryPolymorphicWriteEntryTransform: PolymorphicWriteEntryTransform {
typealias TableType = InMemoryDynamoDBCompositePrimaryKeyTableStore

Expand Down Expand Up @@ -49,6 +51,27 @@ internal struct InMemoryPolymorphicWriteEntryTransform: PolymorphicWriteEntryTra
}
}

internal struct InMemoryPolymorphicTransactionConstraintTransform: PolymorphicTransactionConstraintTransform {
typealias TableType = InMemoryDynamoDBCompositePrimaryKeyTableStore

let partitionKey: String
let sortKey: String
let rowVersion: Int

init<AttributesType: PrimaryKeyAttributes, ItemType: Codable>(_ entry: TransactionConstraintEntry<AttributesType, ItemType>,
table: TableType) throws {
switch entry {
case .required(existing: let existing):
self.partitionKey = existing.compositePrimaryKey.partitionKey
self.sortKey = existing.compositePrimaryKey.sortKey
self.rowVersion = existing.rowStatus.rowVersion
}
}
}

// MARK: - Shared implementations
// Can be used directly by `InMemoryPolymorphicTransactionConstraintTransform` or through the `InMemoryPolymorphicWriteEntryTransform`

private func updateItem<AttributesType, ItemType>(newItem: TypedDatabaseItem<AttributesType, ItemType>,
existingItem: TypedDatabaseItem<AttributesType, ItemType>,
store: inout [String: [String: PolymorphicOperationReturnTypeConvertable]]) throws {
Expand Down Expand Up @@ -145,23 +168,7 @@ private func deleteItem<ItemType: DatabaseItem>(existingItem: ItemType,
store[existingItem.compositePrimaryKey.partitionKey] = updatedPartition
}

internal struct InMemoryPolymorphicTransactionConstraintTransform: PolymorphicTransactionConstraintTransform {
typealias TableType = InMemoryDynamoDBCompositePrimaryKeyTableStore

let partitionKey: String
let sortKey: String
let rowVersion: Int

init<AttributesType: PrimaryKeyAttributes, ItemType: Codable>(_ entry: TransactionConstraintEntry<AttributesType, ItemType>,
table: TableType) throws {
switch entry {
case .required(existing: let existing):
self.partitionKey = existing.compositePrimaryKey.partitionKey
self.sortKey = existing.compositePrimaryKey.sortKey
self.rowVersion = existing.rowStatus.rowVersion
}
}
}
// MARK: - Store implementation

internal actor InMemoryDynamoDBCompositePrimaryKeyTableStore {
typealias StoreType = [String: [String: PolymorphicOperationReturnTypeConvertable]]
Expand Down Expand Up @@ -207,89 +214,6 @@ internal actor InMemoryDynamoDBCompositePrimaryKeyTableStore {
try SmokeDynamoDB.updateItem(newItem: newItem, existingItem: existingItem, store: &self.store)
}

private func handleConstraints<TransactionConstraintEntryType: PolymorphicTransactionConstraintEntry>(
constraints: [TransactionConstraintEntryType], isTransaction: Bool,
context: StandardPolymorphicWriteEntryContext<InMemoryPolymorphicWriteEntryTransform,
InMemoryPolymorphicTransactionConstraintTransform>)
-> SmokeDynamoDBError? {
let errors = constraints.compactMap { entry -> SmokeDynamoDBError? in
let transform: InMemoryPolymorphicTransactionConstraintTransform
do {
transform = try entry.handle(context: context)
} catch {
return SmokeDynamoDBError.unexpectedError(cause: error)
}

guard let partition = store[transform.partitionKey],
let item = partition[transform.sortKey],
item.rowStatus.rowVersion == transform.rowVersion else {
if isTransaction {
return SmokeDynamoDBError.transactionConditionalCheckFailed(partitionKey: transform.partitionKey,
sortKey: transform.sortKey,
message: "Item doesn't exist or doesn't have correct version")
} else {
return SmokeDynamoDBError.conditionalCheckFailed(partitionKey: transform.partitionKey,
sortKey: transform.sortKey,
message: "Item doesn't exist or doesn't have correct version")
}
}

return nil
}

if !errors.isEmpty {
return SmokeDynamoDBError.transactionCanceled(reasons: errors)
}

return nil
}

private func handleEntries<WriteEntryType: PolymorphicWriteEntry>(
entries: [WriteEntryType], isTransaction: Bool,
context: StandardPolymorphicWriteEntryContext<InMemoryPolymorphicWriteEntryTransform,
InMemoryPolymorphicTransactionConstraintTransform>)
-> SmokeDynamoDBError? {
let writeErrors = entries.compactMap { entry -> SmokeDynamoDBError? in
let transform: InMemoryPolymorphicWriteEntryTransform
do {
transform = try entry.handle(context: context)
} catch {
return SmokeDynamoDBError.unexpectedError(cause: error)
}

do {
try transform.operation(&self.store)
} catch let error {
if let typedError = error as? SmokeDynamoDBError {
if case .conditionalCheckFailed(let partitionKey, let sortKey, let message) = typedError, isTransaction {
if message == itemAlreadyExistsMessage {
return .duplicateItem(partitionKey: partitionKey, sortKey: sortKey, message: message)
} else {
return .transactionConditionalCheckFailed(partitionKey: partitionKey,
sortKey: sortKey, message: message)
}
}
return typedError
}

// return unexpected error
return SmokeDynamoDBError.unexpectedError(cause: error)
}

return nil
}

if writeErrors.count > 0 {
if isTransaction {
return SmokeDynamoDBError.transactionCanceled(reasons: writeErrors)
} else {
return SmokeDynamoDBError.batchErrorsReturned(errorCount: writeErrors.count, messageMap: [:])
}
}

return nil
}

func bulkWrite<WriteEntryType: PolymorphicWriteEntry,
TransactionConstraintEntryType: PolymorphicTransactionConstraintEntry>(
_ entries: [WriteEntryType], constraints: [TransactionConstraintEntryType],
Expand Down Expand Up @@ -536,36 +460,6 @@ internal actor InMemoryDynamoDBCompositePrimaryKeyTableStore {

return items
}

internal func convertToQueryableType<ReturnedType: PolymorphicOperationReturnType>(input: PolymorphicOperationReturnTypeConvertable) throws -> ReturnedType {
let storedRowTypeName = input.rowTypeIdentifier

var queryableTypeProviders: [String: PolymorphicOperationReturnOption<ReturnedType.AttributesType, ReturnedType>] = [:]
ReturnedType.types.forEach { (type, provider) in
queryableTypeProviders[getTypeRowIdentifier(type: type)] = provider
}

if let provider = queryableTypeProviders[storedRowTypeName] {
return try provider.getReturnType(input: input)
} else {
// throw an exception, we don't know what this type is
throw SmokeDynamoDBError.unexpectedType(provided: storedRowTypeName)
}
}

func query<ReturnedType: PolymorphicOperationReturnType>(forPartitionKey partitionKey: String,
sortKeyCondition: AttributeCondition?,
limit: Int?,
exclusiveStartKey: String?,
consistentRead: Bool) throws
-> (items: [ReturnedType], lastEvaluatedKey: String?) {
return try query(forPartitionKey: partitionKey,
sortKeyCondition: sortKeyCondition,
limit: limit,
scanIndexForward: true,
exclusiveStartKey: exclusiveStartKey,
consistentRead: consistentRead)
}

func query<ReturnedType: PolymorphicOperationReturnType>(forPartitionKey partitionKey: String,
sortKeyCondition: AttributeCondition?,
Expand Down Expand Up @@ -611,3 +505,120 @@ internal actor InMemoryDynamoDBCompositePrimaryKeyTableStore {
return (Array(items[startIndex..<endIndex]), lastEvaluatedKey)
}
}

// MARK: - Internal helper functions

extension InMemoryDynamoDBCompositePrimaryKeyTableStore {
func handleConstraints<TransactionConstraintEntryType: PolymorphicTransactionConstraintEntry>(
constraints: [TransactionConstraintEntryType], isTransaction: Bool,
context: StandardPolymorphicWriteEntryContext<InMemoryPolymorphicWriteEntryTransform,
InMemoryPolymorphicTransactionConstraintTransform>)
-> SmokeDynamoDBError? {
let errors = constraints.compactMap { entry -> SmokeDynamoDBError? in
let transform: InMemoryPolymorphicTransactionConstraintTransform
do {
transform = try entry.handle(context: context)
} catch {
return SmokeDynamoDBError.unexpectedError(cause: error)
}

guard let partition = store[transform.partitionKey],
let item = partition[transform.sortKey],
item.rowStatus.rowVersion == transform.rowVersion else {
if isTransaction {
return SmokeDynamoDBError.transactionConditionalCheckFailed(partitionKey: transform.partitionKey,
sortKey: transform.sortKey,
message: "Item doesn't exist or doesn't have correct version")
} else {
return SmokeDynamoDBError.conditionalCheckFailed(partitionKey: transform.partitionKey,
sortKey: transform.sortKey,
message: "Item doesn't exist or doesn't have correct version")
}
}

return nil
}

if !errors.isEmpty {
return SmokeDynamoDBError.transactionCanceled(reasons: errors)
}

return nil
}

func handleEntries<WriteEntryType: PolymorphicWriteEntry>(
entries: [WriteEntryType], isTransaction: Bool,
context: StandardPolymorphicWriteEntryContext<InMemoryPolymorphicWriteEntryTransform,
InMemoryPolymorphicTransactionConstraintTransform>)
-> SmokeDynamoDBError? {
let writeErrors = entries.compactMap { entry -> SmokeDynamoDBError? in
let transform: InMemoryPolymorphicWriteEntryTransform
do {
transform = try entry.handle(context: context)
} catch {
return SmokeDynamoDBError.unexpectedError(cause: error)
}

do {
try transform.operation(&self.store)
} catch let error {
if let typedError = error as? SmokeDynamoDBError {
if case .conditionalCheckFailed(let partitionKey, let sortKey, let message) = typedError, isTransaction {
if message == itemAlreadyExistsMessage {
return .duplicateItem(partitionKey: partitionKey, sortKey: sortKey, message: message)
} else {
return .transactionConditionalCheckFailed(partitionKey: partitionKey,
sortKey: sortKey, message: message)
}
}
return typedError
}

// return unexpected error
return SmokeDynamoDBError.unexpectedError(cause: error)
}

return nil
}

if writeErrors.count > 0 {
if isTransaction {
return SmokeDynamoDBError.transactionCanceled(reasons: writeErrors)
} else {
return SmokeDynamoDBError.batchErrorsReturned(errorCount: writeErrors.count, messageMap: [:])
}
}

return nil
}

func convertToQueryableType<ReturnedType: PolymorphicOperationReturnType>(input: PolymorphicOperationReturnTypeConvertable) throws -> ReturnedType {
let storedRowTypeName = input.rowTypeIdentifier

var queryableTypeProviders: [String: PolymorphicOperationReturnOption<ReturnedType.AttributesType, ReturnedType>] = [:]
ReturnedType.types.forEach { (type, provider) in
queryableTypeProviders[getTypeRowIdentifier(type: type)] = provider
}

if let provider = queryableTypeProviders[storedRowTypeName] {
return try provider.getReturnType(input: input)
} else {
// throw an exception, we don't know what this type is
throw SmokeDynamoDBError.unexpectedType(provided: storedRowTypeName)
}
}

func query<ReturnedType: PolymorphicOperationReturnType>(forPartitionKey partitionKey: String,
sortKeyCondition: AttributeCondition?,
limit: Int?,
exclusiveStartKey: String?,
consistentRead: Bool) throws
-> (items: [ReturnedType], lastEvaluatedKey: String?) {
return try query(forPartitionKey: partitionKey,
sortKeyCondition: sortKeyCondition,
limit: limit,
scanIndexForward: true,
exclusiveStartKey: exclusiveStartKey,
consistentRead: consistentRead)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import Foundation
import SmokeHTTPClient
import DynamoDBModel

// MARK: - Store implementation

internal actor InMemoryDynamoDBCompositePrimaryKeysProjectionStore {
public var keys: [Any] = []

Expand Down

0 comments on commit c4e0085

Please sign in to comment.