Skip to content

Commit

Permalink
Merge #53
Browse files Browse the repository at this point in the history
53: Add support to Encodable types for addDocuments function r=curquiza a=ppamorim

## Summary

- Function `addDocuments<T>` added. Now it's possible to send objects that are `Codable/Encodable` and the SDK will automatically encode the values to JSON. A custom encoder can be set by adding `encoder` in the call.

## Additions

- Fixed bug in the `ClientTests.swift` file

## Local tests

 - [X] Unit tests passed
 - [X] Integration tests passed

## Problems

Users are forced to wrap the object to `[]` when using `addDocuments` function with objects that are `Codable/Encodable`. I tried to create a wrapper function for this but Swift doesn't like the direct conversion from `T` to `[T]`.

Co-authored-by: Pedro Paulo de Amorim <[email protected]>
  • Loading branch information
bors[bot] and ppamorim authored Oct 12, 2020
2 parents aa61cdc + e1181db commit 1f138d1
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 17 deletions.
31 changes: 30 additions & 1 deletion Sources/MeiliSearch/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,36 @@ public struct MeiliSearch {
For a partial update of the document see `updateDocument`.

- parameter UID: The unique identifier for the Document's index to be found.
- parameter documents: The documents data (JSON) to be processed.
- parameter documents: The documents to be processed.
- parameter completion: The completion closure used to notify when the server
completes the update request, it returns a `Result` object that contains `Update`
value. If the request was sucessful or `Error` if a failure occured.
*/
public func addDocuments<T>(
UID: String,
documents: [T],
encoder: JSONEncoder? = nil,
primaryKey: String?,
_ completion: @escaping (Result<Update, Swift.Error>) -> Void) where T: Encodable {
self.documents.add(
UID,
documents,
encoder,
primaryKey,
completion)
}

/**
Add a list of documents as data or replace them if they already exist.

If you send an already existing document (same id) the whole existing document will
be overwritten by the new document. Fields previously in the document not present in
the new document are removed.

For a partial update of the document see `updateDocument`.

- parameter UID: The unique identifier for the Document's index to be found.
- parameter documents: The data to be processed.
- parameter completion: The completion closure used to notify when the server
completes the update request, it returns a `Result` object that contains `Update`
value. If the request was sucessful or `Error` if a failure occured.
Expand Down
7 changes: 7 additions & 0 deletions Sources/MeiliSearch/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,11 @@ struct Constants {
decoder.dateDecodingStrategy = .formatted(Formatter.iso8601)
return decoder
}()

static let customJSONEecoder: JSONEncoder = {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .formatted(Formatter.iso8601)
return encoder
}()

}
49 changes: 49 additions & 0 deletions Sources/MeiliSearch/Documents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ struct Documents {
_ document: Data,
_ primaryKey: String?,
_ completion: @escaping (Result<Update, Swift.Error>) -> Void) {

var query: String = "/indexes/\(UID)/documents"
if let primaryKey: String = primaryKey {
query += "?primaryKey=\(primaryKey)"
Expand All @@ -94,6 +95,42 @@ struct Documents {
}
}

func add<T>(
_ UID: String,
_ documents: [T],
_ encoder: JSONEncoder? = nil,
_ primaryKey: String?,
_ completion: @escaping (Result<Update, Swift.Error>) -> Void) where T: Encodable {

var query: String = "/indexes/\(UID)/documents"
if let primaryKey: String = primaryKey {
query += "?primaryKey=\(primaryKey)"
}

let data: Data!

switch encodeJSON(documents, encoder) {
case .success(let documentData):
data = documentData
case .failure(let error):
completion(.failure(error))
return
}

request.post(api: query, data) { result in

switch result {
case .success(let data):

Documents.decodeJSON(data, completion: completion)

case .failure(let error):
completion(.failure(error))
}

}
}

func update(
_ UID: String,
_ document: Data,
Expand Down Expand Up @@ -217,4 +254,16 @@ struct Documents {
}
}

private func encodeJSON<T: Encodable>(
_ documents: [T],
_ encoder: JSONEncoder?) -> Result<Data, Swift.Error> {
do {
let data: Data = try (encoder ?? Constants.customJSONEecoder)
.encode(documents)
return .success(data)
} catch {
return .failure(error)
}
}

}
78 changes: 63 additions & 15 deletions Tests/MeiliSearchIntegrationTests/DocumentsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,12 @@ class DocumentsTests: XCTestCase {
}

func testAddAndGetDocuments() {
let documents: Data = try! JSONEncoder().encode(movies)

let expectation = XCTestExpectation(description: "Add or replace Movies document")

self.client.addDocuments(
UID: self.uid,
documents: documents,
documents: movies,
primaryKey: nil
) { result in

Expand Down Expand Up @@ -112,6 +111,53 @@ class DocumentsTests: XCTestCase {

}

func testAddDataAndGetDocuments() {
let documents: Data = try! JSONEncoder().encode(movies)

let expectation = XCTestExpectation(description: "Add or replace Movies document")

self.client.addDocuments(
UID: self.uid,
documents: documents,
primaryKey: nil
) { result in

switch result {
case .success(let update):

XCTAssertEqual(Update(updateId: 0), update)

Thread.sleep(forTimeInterval: 1.0)

self.client.getDocuments(
UID: self.uid,
limit: 20
) { (result: Result<[Movie], Swift.Error>) in

switch result {
case .success(let returnedMovies):

movies.forEach { (movie: Movie) in
XCTAssertTrue(returnedMovies.contains(movie))
}

case .failure(let error):
print(error)
XCTFail()
}

expectation.fulfill()
}

case .failure(let error):
print(error)
XCTFail()
}
}
self.wait(for: [expectation], timeout: 5.0)

}

func testGetOneDocumentAndFail() {

let getExpectation = XCTestExpectation(description: "Get one document and fail")
Expand Down Expand Up @@ -194,25 +240,26 @@ class DocumentsTests: XCTestCase {

case .success(let update):


XCTAssertEqual(Update(updateId: 0), update)

Thread.sleep(forTimeInterval: 1.0)

self.client.getDocument(
UID: self.uid,
identifier: "10"
) { (result: Result<Movie, Swift.Error>) in
self.client.getDocument(
UID: self.uid,
identifier: "10"
) { (result: Result<Movie, Swift.Error>) in

switch result {
case .success(let returnedMovie):
XCTAssertEqual(movie, returnedMovie)
case .failure(let error):
print(error)
XCTFail()
}
expectation.fulfill()
switch result {
case .success(let returnedMovie):
XCTAssertEqual(movie, returnedMovie)
case .failure(let error):
print(error)
XCTFail()
}
expectation.fulfill()

}
}

case .failure(let error):
print(error)
Expand Down Expand Up @@ -439,6 +486,7 @@ class DocumentsTests: XCTestCase {

static var allTests = [
("testAddAndGetDocuments", testAddAndGetDocuments),
("testAddDataAndGetDocuments", testAddDataAndGetDocuments),
("testGetOneDocumentAndFail", testGetOneDocumentAndFail),
("testAddAndGetOneDocumentWithIntIdentifierAndSucceed", testAddAndGetOneDocumentWithIntIdentifierAndSucceed),
("testAddAndGetOneDocuments", testAddAndGetOneDocuments),
Expand Down
5 changes: 4 additions & 1 deletion Tests/MeiliSearchUnitTests/ClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ import XCTest

class ClientTests: XCTestCase {

private let session = MockURLSession()

func testValidHostURL() {
XCTAssertNotNil(try? MeiliSearch(Config(hostURL: "http://localhost:7700", apiKey: "masterKey")))
session.pushEmpty(code: 200)
XCTAssertNotNil(try? MeiliSearch(Config.default(apiKey: "masterKey", session: session)))
}

func testEmptyHostURL() {
Expand Down
46 changes: 46 additions & 0 deletions Tests/MeiliSearchUnitTests/DocumentsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,51 @@ class DocumentsTests: XCTestCase {

let uid: String = "Movies"

let movie = Movie(
id: 287947,
title: "Shazam",
overview: "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
releaseDate: Date(timeIntervalSince1970: TimeInterval(1553299200)))

let expectation = XCTestExpectation(description: "Add or replace Movies document")

self.client.addDocuments(
UID: uid,
documents: [movie],
primaryKey: "") { result in

switch result {
case .success(let update):
XCTAssertEqual(stubUpdate, update)
expectation.fulfill()
case .failure:
XCTFail("Failed to add or replace Movies document")
}

}

self.wait(for: [expectation], timeout: 1.0)

}

func testAddDataDocuments() {

//Prepare the mock server

let jsonString = """
{"updateId":0}
"""

let decoder: JSONDecoder = JSONDecoder()
let jsonData = jsonString.data(using: .utf8)!
let stubUpdate: Update = try! decoder.decode(Update.self, from: jsonData)

session.pushData(jsonString, code: 202)

// Start the test with the mocked server

let uid: String = "Movies"

let documentJsonString = """
[{
"id": 287947,
Expand Down Expand Up @@ -358,6 +403,7 @@ class DocumentsTests: XCTestCase {

static var allTests = [
("testAddDocuments", testAddDocuments),
("testAddDataDocuments", testAddDataDocuments),
("testUpdateDocuments", testUpdateDocuments),
("testGetDocument", testGetDocument),
("testGetDocuments", testGetDocuments),
Expand Down

0 comments on commit 1f138d1

Please sign in to comment.