Skip to content

Commit 6a0fc97

Browse files
author
Clément Le Provost
committed
Merge branch 'feat/createIfNotExists'
2 parents f874c93 + 3d97543 commit 6a0fc97

File tree

4 files changed

+142
-2
lines changed

4 files changed

+142
-2
lines changed

Source/Error.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ public enum StatusCode: Int {
9999
/// Operation unauthorized with the provided credentials.
100100
case forbidden = 403
101101

102+
/// The targeted resource does not exist.
103+
case notFound = 404
104+
102105
/// The HTTP method used in the request is not supported for the targeted endpoint.
103106
///
104107
/// + Note: Should never occur when using this library.

Source/Index.swift

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,18 +213,38 @@ import Foundation
213213
/// - parameter completionHandler: Completion handler to be notified of the request's outcome.
214214
/// - returns: A cancellable operation.
215215
///
216+
/// + Note: If the object does not exist, it will be created. You can avoid automatic creation of the object by
217+
/// passing `false` to `createIfNotExists` in `partialUpdateObject(_:withID:createIfNotExists:completionHandler:)`.
218+
///
216219
@objc
217220
@discardableResult public func partialUpdateObject(_ partialObject: JSONObject, withID objectID: String, completionHandler: CompletionHandler? = nil) -> Operation {
218221
let path = "1/indexes/\(urlEncodedName)/\(objectID.urlEncodedPathComponent())/partial"
219222
return client.performHTTPQuery(path: path, method: .POST, body: partialObject, hostnames: client.writeHosts, completionHandler: completionHandler)
220223
}
221224

225+
/// Partially update an object.
226+
///
227+
/// - parameter partialObject: New values/operations for the object.
228+
/// - parameter objectID: Identifier of object to be updated.
229+
/// - parameter createIfNotExists: Whether an update on a nonexistent object ID should create the object.
230+
/// - parameter completionHandler: Completion handler to be notified of the request's outcome.
231+
/// - returns: A cancellable operation.
232+
///
233+
@objc
234+
@discardableResult public func partialUpdateObject(_ partialObject: JSONObject, withID objectID: String, createIfNotExists: Bool, completionHandler: CompletionHandler? = nil) -> Operation {
235+
let path = "1/indexes/\(urlEncodedName)/\(objectID.urlEncodedPathComponent())/partial?createIfNotExists=\(String(createIfNotExists).urlEncodedQueryParam())"
236+
return client.performHTTPQuery(path: path, method: .POST, body: partialObject, hostnames: client.writeHosts, completionHandler: completionHandler)
237+
}
238+
222239
/// Partially update several objects.
223240
///
224241
/// - parameter objects: New values/operations for the objects. Each object must contain an `objectID` attribute.
225242
/// - parameter completionHandler: Completion handler to be notified of the request's outcome.
226243
/// - returns: A cancellable operation.
227244
///
245+
/// + Note: If an object does not exist, it will be created. You can avoid automatic creation of objects by
246+
/// passing `false` to `createIfNotExists` in `partialUpdateObjects(_:createIfNotExists:completionHandler:)`.
247+
///
228248
@objc
229249
@discardableResult public func partialUpdateObjects(_ objects: [JSONObject], completionHandler: CompletionHandler? = nil) -> Operation {
230250
let path = "1/indexes/\(urlEncodedName)/batch"
@@ -242,7 +262,32 @@ import Foundation
242262

243263
return client.performHTTPQuery(path: path, method: .POST, body: request, hostnames: client.writeHosts, completionHandler: completionHandler)
244264
}
245-
265+
266+
/// Partially update several objects.
267+
///
268+
/// - parameter objects: New values/operations for the objects. Each object must contain an `objectID` attribute.
269+
/// - parameter createIfNotExists: Whether an update on a nonexistent object ID should create the object.
270+
/// - parameter completionHandler: Completion handler to be notified of the request's outcome.
271+
/// - returns: A cancellable operation.
272+
///
273+
@objc
274+
@discardableResult public func partialUpdateObjects(_ objects: [JSONObject], createIfNotExists: Bool, completionHandler: CompletionHandler? = nil) -> Operation {
275+
let path = "1/indexes/\(urlEncodedName)/batch"
276+
let action = createIfNotExists ? "partialUpdateObject" : "partialUpdateObjectNoCreate"
277+
var requests = [Any]()
278+
requests.reserveCapacity(objects.count)
279+
for object in objects {
280+
requests.append([
281+
"action": action,
282+
"objectID": object["objectID"] as! String,
283+
"body": object
284+
])
285+
}
286+
let request = ["requests": requests]
287+
288+
return client.performHTTPQuery(path: path, method: .POST, body: request, hostnames: client.writeHosts, completionHandler: completionHandler)
289+
}
290+
246291
/// Update an object.
247292
///
248293
/// - parameter object: New version of the object to update. Must contain an `objectID` attribute.

Tests/IndexTests.swift

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,46 @@ class IndexTests: OnlineTestCase {
376376

377377
self.waitForExpectations(timeout: expectationTimeout, handler: nil)
378378
}
379+
380+
func testPartialUpdateObjectCreateIfNotExists() {
381+
let expectation = self.expectation(description: "testPartialUpdateObject")
382+
let objectID = "unknown"
383+
384+
// Partial update with `createIfNotExists=false` should not create object.
385+
self.index.partialUpdateObject(["city": "Los Angeles"], withID: objectID, createIfNotExists: false) { (content, error) -> Void in
386+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
387+
self.index.waitTask(withID: content!["taskID"] as! Int) { (content, error) -> Void in
388+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
389+
self.index.getObject(withID: objectID) { (content, error) -> Void in
390+
guard let error = error else {
391+
XCTFail("The object should not have been created")
392+
expectation.fulfill()
393+
return
394+
}
395+
XCTAssertEqual(StatusCode.notFound.rawValue, (error as NSError).code)
396+
397+
// Partial update with `createIfNotExists=true` should create object.
398+
self.index.partialUpdateObject(["city": "Los Angeles"], withID: objectID, createIfNotExists: true) { (content, error) -> Void in
399+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
400+
self.index.waitTask(withID: content!["taskID"] as! Int) { (content, error) -> Void in
401+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
402+
self.index.getObject(withID: objectID) { (content, error) -> Void in
403+
guard error == nil else {
404+
XCTFail("The object should have been created (\(error!.localizedDescription))")
405+
expectation.fulfill()
406+
return
407+
}
408+
XCTAssertEqual(content?["objectID"] as? String, objectID)
409+
XCTAssertEqual(content?["city"] as? String, "Los Angeles")
410+
expectation.fulfill()
411+
}
412+
}
413+
}
414+
}
415+
}
416+
}
417+
self.waitForExpectations(timeout: expectationTimeout, handler: nil)
418+
}
379419

380420
func testPartialUpdateObjects() {
381421
let expectation = self.expectation(description: "testPartialUpdateObjects")
@@ -425,7 +465,53 @@ class IndexTests: OnlineTestCase {
425465

426466
self.waitForExpectations(timeout: expectationTimeout, handler: nil)
427467
}
428-
468+
469+
func testPartialUpdateObjectsCreateIfNotExists() {
470+
let expectation = self.expectation(description: #function)
471+
let objectUpdates: [JSONObject] = [
472+
["city": "Paris", "objectID": "a/go/?à"],
473+
["city": "Strasbourg", "objectID": "a/go/?à$"]
474+
]
475+
// Partial updates with `createIfNotExists=false` should not create objects.
476+
self.index.partialUpdateObjects(objectUpdates, createIfNotExists: false) { (content, error) -> Void in
477+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
478+
self.index.waitTask(withID: content!["taskID"] as! Int) { (content, error) -> Void in
479+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
480+
self.index.getObjects(withIDs: ["a/go/?à", "a/go/?à$"]) { (content, error) -> Void in
481+
// NOTE: Multiple get does not return an error, but simply returns `null` for missing objects.
482+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
483+
guard let results = content?["results"] as? [Any] else { XCTFail("Invalid results"); expectation.fulfill(); return }
484+
XCTAssertEqual(results.count, 2)
485+
for i in 0..<2 {
486+
XCTAssert(results[i] is NSNull)
487+
}
488+
489+
// Partial updates with `createIfNotExists=true` should create objects.
490+
self.index.partialUpdateObjects(objectUpdates, createIfNotExists: true) { (content, error) -> Void in
491+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
492+
self.index.waitTask(withID: content!["taskID"] as! Int) { (content, error) -> Void in
493+
guard error == nil else { XCTFail(error!.localizedDescription); expectation.fulfill(); return }
494+
self.index.getObjects(withIDs: ["a/go/?à", "a/go/?à$"]) { (content, error) -> Void in
495+
guard error == nil else {
496+
XCTFail("Objects should have been created (\(error!.localizedDescription))")
497+
expectation.fulfill()
498+
return
499+
}
500+
guard let results = content?["results"] as? [JSONObject] else { XCTFail("Invalid results"); expectation.fulfill(); return }
501+
XCTAssertEqual(results.count, 2)
502+
for i in 0..<2 {
503+
XCTAssertEqual(results[i]["objectID"] as? String, objectUpdates[i]["objectID"] as? String)
504+
}
505+
expectation.fulfill()
506+
}
507+
}
508+
}
509+
}
510+
}
511+
}
512+
self.waitForExpectations(timeout: expectationTimeout, handler: nil)
513+
}
514+
429515
func testSaveObject() {
430516
let expectation = self.expectation(description: "testSaveObject")
431517
let object: JSONObject = ["city": "New York", "initial": "NY", "objectID": "a/go/?à"]

Tests/ObjectiveCBridging.m

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,15 @@ - (void)testIndex {
225225
[index partialUpdateObject:DUMMY_JSON withID:@"snoopy" completionHandler:^(NSDictionary<NSString*,id>* content, NSError* error) {
226226
// Do nothing.
227227
}];
228+
[index partialUpdateObject:DUMMY_JSON withID:@"snoopy" createIfNotExists:NO completionHandler:^(NSDictionary<NSString*,id>* content, NSError* error) {
229+
// Do nothing.
230+
}];
228231
[index partialUpdateObjects:@[ DUMMY_JSON ] completionHandler:^(NSDictionary<NSString*,id>* content, NSError* error) {
229232
// Do nothing.
230233
}];
234+
[index partialUpdateObjects:@[ DUMMY_JSON ] createIfNotExists:NO completionHandler:^(NSDictionary<NSString*,id>* content, NSError* error) {
235+
// Do nothing.
236+
}];
231237
[index search:[Query new] completionHandler:^(NSDictionary<NSString*,id>* content, NSError* error) {
232238
// Do nothing.
233239
}];

0 commit comments

Comments
 (0)