Skip to content

Commit

Permalink
feat(swift): remaining helpers (#2758)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fluf22 committed Feb 22, 2024
1 parent 32773c5 commit fc4be59
Show file tree
Hide file tree
Showing 6 changed files with 592 additions and 73 deletions.
11 changes: 9 additions & 2 deletions templates/swift/Package.mustache
Expand Up @@ -22,15 +22,22 @@ extraTargetDependencies.append(contentsOf: [
.product(name: "Logging", package: "swift-log")
])

targets.append(contentsOf: [
targets.append(
.target(
name: "Core",
dependencies: [
.product(name: "Gzip", package: "GzipSwift")
] + extraTargetDependencies,
path: "Sources/Core"
)
])
)

products.append(
.library(
name: "Core",
targets: ["Core"]
)
)

[{{#packageList}}
"{{#lambda.client-to-name}}{{.}}{{/lambda.client-to-name}}"{{^-last}},{{/-last}}{{/packageList}}
Expand Down
159 changes: 159 additions & 0 deletions templates/swift/api.mustache
Expand Up @@ -323,6 +323,165 @@ typealias Client = {{classname}}
)
)
}

/// Allows to aggregate all the hits returned by the API calls
/// - parameter indexName: The name of the index to browse
/// - parameter browseParams: The browse request parameters
/// - parameter validate: A closure that validates the response
/// - parameter aggregator: A closure that aggregates the response
/// - parameter requestOptions: The request options
/// - returns: BrowseResponse
@discardableResult
open func browseObjects(
indexName: String,
browseParams: BrowseParamsObject,
validate: (BrowseResponse) -> Bool = { response in
response.cursor == nil
},
aggregator: @escaping (BrowseResponse) -> Void,
requestOptions: RequestOptions? = nil
) async throws -> BrowseResponse {
return try await createIterable(
execute: { previousResponse in
var updatedBrowseParams = browseParams
if let previousResponse {
updatedBrowseParams.cursor = previousResponse.cursor
}

return try await self.browse(
indexName: indexName,
browseParams: .browseParamsObject(updatedBrowseParams),
requestOptions: requestOptions
)
},
validate: validate,
aggregator: aggregator
)
}

/// Allows to aggregate all the rules returned by the API calls
/// - parameter indexName: The name of the index to browse
/// - parameter searchRulesParams: The search rules request parameters
/// - parameter validate: A closure that validates the response
/// - parameter aggregator: A closure that aggregates the response
/// - parameter requestOptions: The request options
/// - returns: SearchRulesResponse
@discardableResult
open func browseRules(
indexName: String,
searchRulesParams: SearchRulesParams,
validate: ((SearchRulesResponse) -> Bool)? = nil,
aggregator: @escaping (SearchRulesResponse) -> Void,
requestOptions: RequestOptions? = nil
) async throws -> SearchRulesResponse {
let hitsPerPage = searchRulesParams.hitsPerPage ?? 1000
return try await createIterable(
execute: { previousResponse in
var updatedSearchRulesParams = searchRulesParams
updatedSearchRulesParams.hitsPerPage = hitsPerPage
if let previousResponse {
updatedSearchRulesParams.page = previousResponse.page + 1
}
if updatedSearchRulesParams.page == nil {
updatedSearchRulesParams.page = 0
}

return try await self.searchRules(
indexName: indexName,
searchRulesParams: updatedSearchRulesParams,
requestOptions: requestOptions
)
},
validate: validate ?? { response in
response.nbHits < hitsPerPage
},
aggregator: aggregator
)
}

/// Allows to aggregate all the synonyms returned by the API calls
/// - parameter indexName: The name of the index to browse
/// - parameter searchSynonymsParams: The search synonyms request parameters
/// - parameter validate: A closure that validates the response
/// - parameter aggregator: A closure that aggregates the response
/// - parameter requestOptions: The request options
/// - returns: SearchSynonymsResponse
@discardableResult
open func browseSynonyms(
indexName: String,
searchSynonymsParams: SearchSynonymsParams,
validate: ((SearchSynonymsResponse) -> Bool)? = nil,
aggregator: @escaping (SearchSynonymsResponse) -> Void,
requestOptions: RequestOptions? = nil
) async throws -> SearchSynonymsResponse {
let hitsPerPage = searchSynonymsParams.hitsPerPage ?? 1000
var updatedSearchSynonymsParams = searchSynonymsParams
if updatedSearchSynonymsParams.page == nil {
updatedSearchSynonymsParams.page = 0
}

return try await createIterable(
execute: { previousResponse in
updatedSearchSynonymsParams.hitsPerPage = hitsPerPage
defer {
updatedSearchSynonymsParams.page! += 1
}

return try await self.searchSynonyms(
indexName: indexName,
searchSynonymsParams: updatedSearchSynonymsParams,
requestOptions: requestOptions
)
},
validate: validate ?? { response in
response.nbHits < hitsPerPage
},
aggregator: aggregator
)
}

/// Helper: calls the `search` method but with certainty that we will only request Algolia records (hits) and not
/// facets.
/// Disclaimer: We don't assert that the parameters you pass to this method only contains `hits` requests to prevent
/// impacting search performances, this helper is purely for typing purposes.
open func searchForHits(
searchMethodParams: SearchMethodParams,
requestOptions: RequestOptions? = nil
) async throws -> [SearchResponse] {
try await self.search(searchMethodParams: searchMethodParams, requestOptions: requestOptions).results
.reduce(into: [SearchResponse]()) { acc, cur in
switch cur {
case let .searchResponse(searchResponse):
acc.append(searchResponse)
case .searchForFacetValuesResponse(_):
break
}
}
}

/// Helper: calls the `search` method but with certainty that we will only request Algolia facets and not records
/// (hits).
/// Disclaimer: We don't assert that the parameters you pass to this method only contains `facets` requests to
/// prevent impacting search performances, this helper is purely for typing purposes.
open func searchForFacets(
searchMethodParams: SearchMethodParams,
requestOptions: RequestOptions? = nil
) async throws -> [SearchForFacetValuesResponse] {
try await self.search(searchMethodParams: searchMethodParams, requestOptions: requestOptions).results
.reduce(into: [SearchForFacetValuesResponse]()) { acc, cur in
switch cur {
case .searchResponse(_):
break
case let .searchForFacetValuesResponse(searchForFacet):
acc.append(searchForFacet)
}
}
}
{{/isSearchClient}}
}
{{/operations}}
15 changes: 11 additions & 4 deletions templates/swift/tests/Package.mustache
Expand Up @@ -4,13 +4,16 @@ import PackageDescription

let libraries: [Target.Dependency] = [{{#packageList}}
.product(
name: "{{.}}",
package: "algoliasearch-client-swift"
name: "{{.}}",
package: "algoliasearch-client-swift"
),{{/packageList}}
]

let package = Package(
name: "AlgoliaSearchClientTests",
platforms: [
.macOS(.v10_15)
],
dependencies: [
.package(url: "https://github.com/Flight-School/AnyCodable", from: "0.6.7"),
.package(url: "https://github.com/swiftpackages/DotEnv.git", from: "3.0.0"),
Expand All @@ -20,7 +23,9 @@ let package = Package(
.target(
name: "Utils",
dependencies: [
.product(name: "AnyCodable", package: "AnyCodable")
.product(name: "AnyCodable", package: "AnyCodable"),
.product(name: "Core", package: "algoliasearch-client-swift"),
.product(name: "Search", package: "algoliasearch-client-swift")
],
path: "Tests/Utils"
),
Expand All @@ -45,7 +50,9 @@ let package = Package(
.product(name: "AnyCodable", package: "AnyCodable"),
.product(name: "DotEnv", package: "DotEnv"),
.target(name: "Utils"),
] + libraries
.product(name: "Core", package: "algoliasearch-client-swift"),
.product(name: "Search", package: "algoliasearch-client-swift")
]
)
]
)
123 changes: 123 additions & 0 deletions tests/output/swift/Tests/Utils/MockSearchClient.swift
@@ -0,0 +1,123 @@
//
// MockSearchClient.swift
//
//
// Created by Algolia on 21/02/2024.
//

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
@testable import Core
@testable import Search

public class MockSearchClient<T>: SearchClient {
var loop = 0
var responses: [T] = []

public func setResponses(_ elements: [T]) {
self.responses = elements
}

override public func setSettings(
indexName _: String,
indexSettings _: IndexSettings,
forwardToReplicas _: Bool? = nil,
requestOptions _: RequestOptions? = nil
) async throws -> UpdatedAtResponse {
UpdatedAtResponse(
taskID: 12345,
updatedAt: "2024-02-20T10:10:00Z"
)
}

override public func addApiKey(
apiKey _: ApiKey,
requestOptions _: RequestOptions? = nil
) async throws -> AddApiKeyResponse {
AddApiKeyResponse(
key: "created-api-key",
createdAt: "2024-02-20T10:10:00Z"
)
}

override public func deleteApiKey(
key _: String,
requestOptions _: RequestOptions? = nil
) async throws -> DeleteApiKeyResponse {
DeleteApiKeyResponse(deletedAt: "2024-02-20T10:10:00Z")
}

override public func updateApiKey(
key: String,
apiKey _: ApiKey,
requestOptions _: RequestOptions? = nil
) async throws -> UpdateApiKeyResponse {
UpdateApiKeyResponse(key: key, updatedAt: "2024-02-20T10:10:00Z")
}

override public func getTask(
indexName _: String,
taskID _: Int64,
requestOptions _: RequestOptions? = nil
) async throws -> GetTaskResponse {
defer {
loop += 1
}
return self.responses[self.loop] as! GetTaskResponse
}

override public func getApiKeyWithHTTPInfo(
key _: String,
requestOptions _: RequestOptions? = nil
) async throws -> Response<GetApiKeyResponse> {
defer {
loop += 1
}
return self.responses[self.loop] as! Response<GetApiKeyResponse>
}

override public func browse(
indexName _: String,
browseParams _: BrowseParams? = nil,
requestOptions _: RequestOptions? = nil
) async throws -> BrowseResponse {
defer {
loop += 1
}
return self.responses[self.loop] as! BrowseResponse
}

override public func searchRules(
indexName _: String,
searchRulesParams _: SearchRulesParams? = nil,
requestOptions _: RequestOptions? = nil
) async throws -> SearchRulesResponse {
defer {
loop += 1
}
return self.responses[self.loop] as! SearchRulesResponse
}

override public func searchSynonyms(
indexName _: String,
searchSynonymsParams _: SearchSynonymsParams? = nil,
requestOptions _: RequestOptions? = nil
) async throws -> SearchSynonymsResponse {
defer {
loop += 1
}
return self.responses[self.loop] as! SearchSynonymsResponse
}

override public func search(
searchMethodParams _: SearchMethodParams,
requestOptions _: RequestOptions? = nil
) async throws -> SearchResponses {
defer {
loop += 1
}
return self.responses[self.loop] as! SearchResponses
}
}

0 comments on commit fc4be59

Please sign in to comment.