Skip to content

fix: when transactions enabled throw errors when needed #295

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ coverage:
status:
patch:
default:
target: 74
target: auto
changes: false
project:
default:
Expand Down
2 changes: 1 addition & 1 deletion ParseSwift.playground/Sources/Common.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public func initializeParse() {
clientKey: "clientKey",
masterKey: "masterKey",
serverURL: URL(string: "http://localhost:1337/1")!,
useTransactionsInternally: false)
useTransactions: false)
}

public func initializeParseCustomObjectId() {
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/Objects/ParseInstallation+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public extension Sequence where Element: ParseInstallation {
the transactions can fail.
*/
func saveAll(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
isIgnoreCustomObjectIdConfig: Bool = false,
options: API.Options = []) async throws -> [(Result<Self.Element, ParseError>)] {
try await withCheckedThrowingContinuation { continuation in
Expand Down Expand Up @@ -138,7 +138,7 @@ public extension Sequence where Element: ParseInstallation {
the transactions can fail.
*/
func deleteAll(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = []) async throws -> [(Result<Void, ParseError>)] {
try await withCheckedThrowingContinuation { continuation in
self.deleteAll(batchLimit: limit,
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/Objects/ParseInstallation+combine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public extension Sequence where Element: ParseInstallation {
the transactions can fail.
*/
func saveAllPublisher(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
isIgnoreCustomObjectIdConfig: Bool = false,
options: API.Options = []) -> Future<[(Result<Self.Element, ParseError>)], ParseError> {
Future { promise in
Expand All @@ -126,7 +126,7 @@ public extension Sequence where Element: ParseInstallation {
the transactions can fail.
*/
func deleteAllPublisher(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = []) -> Future<[(Result<Void, ParseError>)], ParseError> {
Future { promise in
self.deleteAll(batchLimit: limit,
Expand Down
44 changes: 15 additions & 29 deletions Sources/ParseSwift/Objects/ParseInstallation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ public extension Sequence where Element: ParseInstallation {
desires a different policy, it should be inserted in `options`.
*/
func saveAll(batchLimit limit: Int? = nil, // swiftlint:disable:this function_body_length
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
isIgnoreCustomObjectIdConfig: Bool = false,
options: API.Options = []) throws -> [(Result<Self.Element, ParseError>)] {
var options = options
Expand Down Expand Up @@ -799,12 +799,8 @@ public extension Sequence where Element: ParseInstallation {
let commands = try map {
try $0.saveCommand(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig)
}
let batchLimit: Int!
if transaction {
batchLimit = commands.count
} else {
batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
}
let batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
try canSendTransactions(transaction, objectCount: commands.count, batchLimit: batchLimit)
let batches = BatchUtils.splitArray(commands, valuesPerSegment: batchLimit)
try batches.forEach {
let currentBatch = try API.Command<Self.Element, Self.Element>
Expand Down Expand Up @@ -850,7 +846,7 @@ public extension Sequence where Element: ParseInstallation {
*/
func saveAll( // swiftlint:disable:this function_body_length cyclomatic_complexity
batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
isIgnoreCustomObjectIdConfig: Bool = false,
options: API.Options = [],
callbackQueue: DispatchQueue = .main,
Expand All @@ -874,7 +870,9 @@ public extension Sequence where Element: ParseInstallation {
let group = DispatchGroup()
group.enter()
installation
.ensureDeepSave(options: options) { (savedChildObjects, savedChildFiles, parseError) -> Void in
.ensureDeepSave(options: options,
// swiftlint:disable:next line_length
isShouldReturnIfChildObjectsFound: true) { (savedChildObjects, savedChildFiles, parseError) -> Void in
//If an error occurs, everything should be skipped
if parseError != nil {
error = parseError
Expand Down Expand Up @@ -917,12 +915,8 @@ public extension Sequence where Element: ParseInstallation {
let commands = try map {
try $0.saveCommand(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig)
}
let batchLimit: Int!
if transaction {
batchLimit = commands.count
} else {
batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
}
let batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
try canSendTransactions(transaction, objectCount: commands.count, batchLimit: batchLimit)
let batches = BatchUtils.splitArray(commands, valuesPerSegment: batchLimit)
var completed = 0
for batch in batches {
Expand Down Expand Up @@ -1093,18 +1087,14 @@ public extension Sequence where Element: ParseInstallation {
desires a different policy, it should be inserted in `options`.
*/
func deleteAll(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = []) throws -> [(Result<Void, ParseError>)] {
var options = options
options.insert(.cachePolicy(.reloadIgnoringLocalCacheData))
var returnBatch = [(Result<Void, ParseError>)]()
let commands = try map { try $0.deleteCommand() }
let batchLimit: Int!
if transaction {
batchLimit = commands.count
} else {
batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
}
let batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
try canSendTransactions(transaction, objectCount: commands.count, batchLimit: batchLimit)
let batches = BatchUtils.splitArray(commands, valuesPerSegment: batchLimit)
try batches.forEach {
let currentBatch = try API.Command<Self.Element, (Result<Void, ParseError>)>
Expand Down Expand Up @@ -1146,7 +1136,7 @@ public extension Sequence where Element: ParseInstallation {
*/
func deleteAll(
batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = [],
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<[(Result<Void, ParseError>)], ParseError>) -> Void
Expand All @@ -1156,12 +1146,8 @@ public extension Sequence where Element: ParseInstallation {
do {
var returnBatch = [(Result<Void, ParseError>)]()
let commands = try map({ try $0.deleteCommand() })
let batchLimit: Int!
if transaction {
batchLimit = commands.count
} else {
batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
}
let batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
try canSendTransactions(transaction, objectCount: commands.count, batchLimit: batchLimit)
let batches = BatchUtils.splitArray(commands, valuesPerSegment: batchLimit)
var completed = 0
for batch in batches {
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/Objects/ParseObject+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public extension Sequence where Element: ParseObject {
the transactions can fail.
*/
func saveAll(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
isIgnoreCustomObjectIdConfig: Bool = false,
options: API.Options = []) async throws -> [(Result<Self.Element, ParseError>)] {
try await withCheckedThrowingContinuation { continuation in
Expand Down Expand Up @@ -137,7 +137,7 @@ public extension Sequence where Element: ParseObject {
the transactions can fail.
*/
func deleteAll(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = []) async throws -> [(Result<Void, ParseError>)] {
try await withCheckedThrowingContinuation { continuation in
self.deleteAll(batchLimit: limit,
Expand Down
4 changes: 2 additions & 2 deletions Sources/ParseSwift/Objects/ParseObject+combine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public extension Sequence where Element: ParseObject {
client-side checks are disabled. Developers are responsible for handling such cases.
*/
func saveAllPublisher(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
isIgnoreCustomObjectIdConfig: Bool = false,
options: API.Options = []) -> Future<[(Result<Self.Element, ParseError>)], ParseError> {
Future { promise in
Expand All @@ -137,7 +137,7 @@ public extension Sequence where Element: ParseObject {
the transactions can fail.
*/
func deleteAllPublisher(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = []) -> Future<[(Result<Void, ParseError>)], ParseError> {
Future { promise in
self.deleteAll(batchLimit: limit,
Expand Down
78 changes: 46 additions & 32 deletions Sources/ParseSwift/Objects/ParseObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,22 @@ public extension ParseObject {
// MARK: Batch Support
public extension Sequence where Element: ParseObject {

internal func canSendTransactions(_ isUsingTransactions: Bool,
objectCount: Int,
batchLimit: Int) throws {
if isUsingTransactions {
if objectCount > batchLimit {
let error = ParseError(code: .unknownError,
message: """
The amount of objects (\(objectCount)) can't exceed the batch size(\(batchLimit)).
Either decrease the amount of objects, increase the batch size, or disable
transactions for this call.
""")
throw error
}
}
}

/**
Saves a collection of objects *synchronously* all at once and throws an error if necessary.
- parameter batchLimit: The maximum number of objects to send in each batch. If the items to be batched.
Expand Down Expand Up @@ -113,7 +129,7 @@ public extension Sequence where Element: ParseObject {
desires a different policy, it should be inserted in `options`.
*/
func saveAll(batchLimit limit: Int? = nil, // swiftlint:disable:this function_body_length
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
isIgnoreCustomObjectIdConfig: Bool = false,
options: API.Options = []) throws -> [(Result<Self.Element, ParseError>)] {
var options = options
Expand All @@ -126,7 +142,9 @@ public extension Sequence where Element: ParseObject {
for object in objects {
let group = DispatchGroup()
group.enter()
object.ensureDeepSave(options: options) { (savedChildObjects, savedChildFiles, parseError) -> Void in
object.ensureDeepSave(options: options,
// swiftlint:disable:next line_length
isShouldReturnIfChildObjectsFound: true) { (savedChildObjects, savedChildFiles, parseError) -> Void in
//If an error occurs, everything should be skipped
if parseError != nil {
error = parseError
Expand Down Expand Up @@ -163,12 +181,8 @@ public extension Sequence where Element: ParseObject {

var returnBatch = [(Result<Self.Element, ParseError>)]()
let commands = try map { try $0.saveCommand(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig) }
let batchLimit: Int!
if transaction {
batchLimit = commands.count
} else {
batchLimit = limit ?? ParseConstants.batchLimit
}
let batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
try canSendTransactions(transaction, objectCount: commands.count, batchLimit: batchLimit)
let batches = BatchUtils.splitArray(commands, valuesPerSegment: batchLimit)
try batches.forEach {
let currentBatch = try API.Command<Self.Element, Self.Element>
Expand Down Expand Up @@ -212,7 +226,7 @@ public extension Sequence where Element: ParseObject {
*/
func saveAll( // swiftlint:disable:this function_body_length cyclomatic_complexity
batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
isIgnoreCustomObjectIdConfig: Bool = false,
options: API.Options = [],
callbackQueue: DispatchQueue = .main,
Expand All @@ -236,7 +250,9 @@ public extension Sequence where Element: ParseObject {
for object in objects {
let group = DispatchGroup()
group.enter()
object.ensureDeepSave(options: options) { (savedChildObjects, savedChildFiles, parseError) -> Void in
object.ensureDeepSave(options: options,
// swiftlint:disable:next line_length
isShouldReturnIfChildObjectsFound: true) { (savedChildObjects, savedChildFiles, parseError) -> Void in
//If an error occurs, everything should be skipped
if parseError != nil {
error = parseError
Expand Down Expand Up @@ -279,12 +295,8 @@ public extension Sequence where Element: ParseObject {
let commands = try map {
try $0.saveCommand(isIgnoreCustomObjectIdConfig: isIgnoreCustomObjectIdConfig)
}
let batchLimit: Int!
if transaction {
batchLimit = commands.count
} else {
batchLimit = limit ?? ParseConstants.batchLimit
}
let batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
try canSendTransactions(transaction, objectCount: commands.count, batchLimit: batchLimit)
let batches = BatchUtils.splitArray(commands, valuesPerSegment: batchLimit)
var completed = 0
for batch in batches {
Expand Down Expand Up @@ -447,18 +459,14 @@ public extension Sequence where Element: ParseObject {
desires a different policy, it should be inserted in `options`.
*/
func deleteAll(batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = []) throws -> [(Result<Void, ParseError>)] {
var options = options
options.insert(.cachePolicy(.reloadIgnoringLocalCacheData))
var returnBatch = [(Result<Void, ParseError>)]()
let commands = try map { try $0.deleteCommand() }
let batchLimit: Int!
if transaction {
batchLimit = commands.count
} else {
batchLimit = limit ?? ParseConstants.batchLimit
}
let batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
try canSendTransactions(transaction, objectCount: commands.count, batchLimit: batchLimit)
let batches = BatchUtils.splitArray(commands, valuesPerSegment: batchLimit)
try batches.forEach {
let currentBatch = try API.Command<Self.Element, (Result<Void, ParseError>)>
Expand Down Expand Up @@ -497,7 +505,7 @@ public extension Sequence where Element: ParseObject {
*/
func deleteAll(
batchLimit limit: Int? = nil,
transaction: Bool = false,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = [],
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<[(Result<Void, ParseError>)], ParseError>) -> Void
Expand All @@ -507,12 +515,8 @@ public extension Sequence where Element: ParseObject {
options.insert(.cachePolicy(.reloadIgnoringLocalCacheData))
var returnBatch = [(Result<Void, ParseError>)]()
let commands = try map({ try $0.deleteCommand() })
let batchLimit: Int!
if transaction {
batchLimit = commands.count
} else {
batchLimit = limit ?? ParseConstants.batchLimit
}
let batchLimit = limit != nil ? limit! : ParseConstants.batchLimit
try canSendTransactions(transaction, objectCount: commands.count, batchLimit: batchLimit)
let batches = BatchUtils.splitArray(commands, valuesPerSegment: batchLimit)
var completed = 0
for batch in batches {
Expand Down Expand Up @@ -755,6 +759,7 @@ extension ParseObject {

// swiftlint:disable:next function_body_length
internal func ensureDeepSave(options: API.Options = [],
isShouldReturnIfChildObjectsFound: Bool = false,
completion: @escaping ([String: PointerType],
[UUID: ParseFile], ParseError?) -> Void) {
let uuid = UUID()
Expand All @@ -779,7 +784,16 @@ extension ParseObject {
filesSavedBeforeThisOne: nil)

var waitingToBeSaved = object.unsavedChildren

if isShouldReturnIfChildObjectsFound && waitingToBeSaved.count > 0 {
let error = ParseError(code: .unknownError,
message: """
When using transactions, all child ParseObjects have to originally
be saved to the Parse Server. Either save all child objects first
or disable transactions for this call.
""")
completion([String: PointerType](), [UUID: ParseFile](), error)
return
}
while waitingToBeSaved.count > 0 {
var savableObjects = [ParseType]()
var savableFiles = [ParseFile]()
Expand Down Expand Up @@ -848,7 +862,7 @@ extension ParseObject {
// MARK: Savable Encodable Version
internal extension ParseType {
func saveAll(objects: [ParseType],
transaction: Bool = ParseSwift.configuration.useTransactionsInternally,
transaction: Bool = ParseSwift.configuration.useTransactions,
options: API.Options = []) throws -> [(Result<PointerType, ParseError>)] {
try API.NonParseBodyCommand<AnyCodable, PointerType>
.batch(objects: objects,
Expand Down
Loading