Skip to content

Commit

Permalink
Merge Bunch of secondary features #9
Browse files Browse the repository at this point in the history
  • Loading branch information
programVeins authored Dec 30, 2021
2 parents 5805d86 + b503ffe commit 6025944
Show file tree
Hide file tree
Showing 18 changed files with 942 additions and 68 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import PackageDescription
let package = Package(
name: "Typesense",
platforms: [
.iOS(.v15), .macOS(.v12)
.iOS(.v13), .macOS(.v12)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,16 @@ sh get-models.sh
```

The generated Models (inside the Models directory) are to be used inside the Models directory of the source code as well. Models need to be generated as and when the [Typesense-Api-Spec](https://github.com/typesense/typesense-api-spec) is updated.

## TODO: Features

- Curation API
- Multisearch
- Dealing with Dirty Data
- Scoped Search Key

## TODO: Testing

- Geosearch
- Auto schema detection
- Importing a .jsonl file
62 changes: 62 additions & 0 deletions Sources/Typesense/Alias.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Foundation

public struct Alias {
var apiCall: ApiCall
let RESOURCEPATH = "aliases"
var config: Configuration

public init(config: Configuration) {
apiCall = ApiCall(config: config)
self.config = config
}

public func upsert(name: String, collection: CollectionAliasSchema) async throws -> (CollectionAlias?, URLResponse?) {
let schemaData: Data?

schemaData = try encoder.encode(collection)

if let validSchema = schemaData {
let (data, response) = try await apiCall.put(endPoint: "\(RESOURCEPATH)/\(name)", body: validSchema)
if let result = data {
let alias = try decoder.decode(CollectionAlias.self, from: result)
return (alias, response)
}
}
return (nil, nil)
}

public func retrieve(name: String) async throws -> (CollectionAlias?, URLResponse?) {
let (data, response) = try await apiCall.get(endPoint: "\(RESOURCEPATH)/\(name)")
if let result = data {
if let notExists = try? decoder.decode(ApiResponse.self, from: result) {
throw ResponseError.aliasNotFound(desc: "Alias \(notExists.message)")
}
let alias = try decoder.decode(CollectionAlias.self, from: result)
return (alias, response)
}
return (nil, nil)
}

public func retrieve() async throws -> (CollectionAliasesResponse?, URLResponse?) {
let (data, response) = try await apiCall.get(endPoint: "\(RESOURCEPATH)")
if let result = data {
let aliases = try decoder.decode(CollectionAliasesResponse.self, from: result)
return (aliases, response)
}
return (nil, nil)
}

public func delete(name: String) async throws -> (CollectionAlias?, URLResponse?) {
let (data, response) = try await apiCall.delete(endPoint: "\(RESOURCEPATH)/\(name)")
if let result = data {
if let notExists = try? decoder.decode(ApiResponse.self, from: result) {
throw ResponseError.aliasNotFound(desc: "Alias \(notExists.message)")
}
let alias = try decoder.decode(CollectionAlias.self, from: result)
return (alias, response)
}
return (nil, nil)
}


}
62 changes: 62 additions & 0 deletions Sources/Typesense/ApiKeys.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import Foundation

public struct ApiKeys {
var apiCall: ApiCall
let RESOURCEPATH = "keys"

public init(config: Configuration) {
apiCall = ApiCall(config: config)
}

public func create(_ keySchema: ApiKeySchema) async throws -> (ApiKey?, URLResponse?) {
var schemaData: Data? = nil

schemaData = try encoder.encode(keySchema)

if let validSchema = schemaData {
let (data, response) = try await apiCall.post(endPoint: "\(RESOURCEPATH)", body: validSchema)
if let result = data {
let keyResponse = try decoder.decode(ApiKey.self, from: result)
return (keyResponse, response)
}
}

return (nil, nil)
}

public func retrieve(id: Int) async throws -> (ApiKey?, URLResponse?) {

let (data, response) = try await apiCall.get(endPoint: "\(RESOURCEPATH)/\(id)")
if let result = data {
if let notFound = try? decoder.decode(ApiResponse.self, from: result) {
throw ResponseError.apiKeyNotFound(desc: notFound.message)
}
let keyResponse = try decoder.decode(ApiKey.self, from: result)
return (keyResponse, response)
}

return (nil, nil)
}

public func retrieve() async throws -> (ApiKeysResponse?, URLResponse?) {

let (data, response) = try await apiCall.get(endPoint: "\(RESOURCEPATH)")
if let result = data {
let keyResponse = try decoder.decode(ApiKeysResponse.self, from: result)
return (keyResponse, response)
}

return (nil, nil)
}

public func delete(id: Int) async throws -> (Data?, URLResponse?) {

let (data, response) = try await apiCall.delete(endPoint: "\(RESOURCEPATH)/\(id)")
if let result = data {
if let notFound = try? decoder.decode(ApiResponse.self, from: result) {
throw ResponseError.apiKeyNotFound(desc: notFound.message)
}
}
return (data, response)
}
}
12 changes: 12 additions & 0 deletions Sources/Typesense/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,16 @@ public struct Client {
public func collection(name: String) -> Collection {
return Collection(config: self.configuration, collectionName: name)
}

public func keys() -> ApiKeys {
return ApiKeys(config: self.configuration)
}

public func aliases() -> Alias {
return Alias(config: self.configuration)
}

public func operations() -> Operations {
return Operations(config: self.configuration)
}
}
4 changes: 4 additions & 0 deletions Sources/Typesense/Collection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@ public struct Collection {
}
return (nil, response)
}

public func synonyms() -> Synonyms {
return Synonyms(config: self.config, collectionName: self.collectionName)
}
}
24 changes: 24 additions & 0 deletions Sources/Typesense/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,40 @@ public struct Document {

public func delete() async throws -> (Data?, URLResponse?) {
let (data, response) = try await apiCall.delete(endPoint: "\(RESOURCEPATH)/\(self.id)")
if let result = data {
if let responseErr = try? decoder.decode(ApiResponse.self, from: result) {
if (responseErr.message == "Not Found") {
throw ResponseError.invalidCollection(desc: "Collection \(self.collectionName) \(responseErr.message)")
}
throw ResponseError.documentDoesNotExist(desc: responseErr.message)
}
}
return (data, response)
}

public func retrieve() async throws -> (Data?, URLResponse?) {
let (data, response) = try await apiCall.get(endPoint: "\(RESOURCEPATH)/\(self.id)")
if let result = data {
if let responseErr = try? decoder.decode(ApiResponse.self, from: result) {
if (responseErr.message == "Not Found") {
throw ResponseError.invalidCollection(desc: "Collection \(self.collectionName) \(responseErr.message)")
}
throw ResponseError.documentDoesNotExist(desc: responseErr.message)
}
}
return (data, response)
}

public func update(newDocument: Data) async throws -> (Data?, URLResponse?) {
let (data, response) = try await apiCall.patch(endPoint: "\(RESOURCEPATH)/\(self.id)", body: newDocument)
if let result = data {
if let responseErr = try? decoder.decode(ApiResponse.self, from: result) {
if (responseErr.message == "Not Found") {
throw ResponseError.invalidCollection(desc: "Collection \(self.collectionName) \(responseErr.message)")
}
throw ResponseError.documentDoesNotExist(desc: responseErr.message)
}
}
return (data, response)
}

Expand Down
36 changes: 35 additions & 1 deletion Sources/Typesense/Documents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation

public struct Documents {
var apiCall: ApiCall
var collectionName: String?
var collectionName: String
let RESOURCEPATH: String

public init(config: Configuration, collectionName: String) {
Expand All @@ -13,13 +13,42 @@ public struct Documents {

public func create(document: Data) async throws -> (Data?, URLResponse?) {
let (data, response) = try await apiCall.post(endPoint: RESOURCEPATH, body: document)
if let result = data {
if let responseErr = try? decoder.decode(ApiResponse.self, from: result) {
if (responseErr.message == "Not Found") {
throw ResponseError.invalidCollection(desc: "Collection \(self.collectionName) \(responseErr.message)")
}
throw ResponseError.documentAlreadyExists(desc: responseErr.message)
}
}
return (data, response)
}

public func upsert(document: Data) async throws -> (Data?, URLResponse?) {
let upsertAction = URLQueryItem(name: "action", value: "upsert")
let (data, response) = try await apiCall.post(endPoint: RESOURCEPATH, body: document, queryParameters: [upsertAction])
if let result = data {
if let responseErr = try? decoder.decode(ApiResponse.self, from: result) {
if (responseErr.message == "Not Found") {
throw ResponseError.invalidCollection(desc: "Collection \(self.collectionName) \(responseErr.message)")
}
throw ResponseError.documentAlreadyExists(desc: responseErr.message)
}
}
return (data, response)
}

public func delete(filter: String, batchSize: Int? = nil) async throws -> (Data?, URLResponse?) {
var deleteQueryParams: [URLQueryItem] =
[
URLQueryItem(name: "filter_by", value: filter)
]
if let givenBatchSize = batchSize {
deleteQueryParams.append(URLQueryItem(name: "batch_size", value: String(givenBatchSize)))
}
let (data, response) = try await apiCall.delete(endPoint: "\(RESOURCEPATH)", queryParameters: deleteQueryParams)
return (data, response)

}

public func search<T>(_ searchParameters: SearchParameters, for: T.Type) async throws -> (SearchResult<T>?, URLResponse?) {
Expand Down Expand Up @@ -190,4 +219,9 @@ public struct Documents {
let (data, response) = try await apiCall.post(endPoint: "\(RESOURCEPATH)/import", body: documents, queryParameters: [importAction])
return (data, response)
}

public func export() async throws -> (Data?, URLResponse?) {
let (data, response) = try await apiCall.get(endPoint: "\(RESOURCEPATH)/export")
return (data, response)
}
}
5 changes: 5 additions & 0 deletions Sources/Typesense/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ public enum DataError: Error {
public enum ResponseError: Error {
case collectionAlreadyExists(desc: String)
case collectionDoesNotExist(desc: String)
case documentAlreadyExists(desc: String)
case documentDoesNotExist(desc: String)
case invalidCollection(desc: String)
case apiKeyNotFound(desc: String)
case aliasNotFound(desc: String)
}
6 changes: 3 additions & 3 deletions Sources/Typesense/Models/ApiKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ public struct ApiKey: Codable {
public var collections: [String]
public var expiresAt: Int64?
public var _id: Int64
public var value: String
public var valuePrefix: String
public var value: String?
public var valuePrefix: String?

public init(_description: String? = nil, actions: [String], collections: [String], expiresAt: Int64? = nil, _id: Int64, value: String, valuePrefix: String) {
public init(_description: String? = nil, actions: [String], collections: [String], expiresAt: Int64? = nil, _id: Int64, value: String? = nil, valuePrefix: String? = nil) {
self._description = _description
self.actions = actions
self.collections = collections
Expand Down
61 changes: 61 additions & 0 deletions Sources/Typesense/Operations.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Foundation

public struct Operations {
var apiCall: ApiCall
var RESOURCEPATH = "operations"

public init(config: Configuration) {
apiCall = ApiCall(config: config)
}

public func getHealth() async throws -> (HealthStatus?, URLResponse?) {
let (data, response) = try await apiCall.get(endPoint: "health")
if let result = data {
let health = try decoder.decode(HealthStatus.self, from: result)
return (health, response)
}
return (nil, nil)
}

public func getStats() async throws -> (Data?, URLResponse?) {
let (data, response) = try await apiCall.get(endPoint: "stats.json")
return (data, response)
}

public func getMetrics() async throws -> (Data?, URLResponse?) {
let (data, response) = try await apiCall.get(endPoint: "metrics.json")
return (data, response)
}

public func vote() async throws -> (SuccessStatus?, URLResponse?) {
let (data, response) = try await apiCall.post(endPoint: "\(RESOURCEPATH)/vote", body: Data())
if let result = data {
let success = try decoder.decode(SuccessStatus.self, from: result)
return (success, response)
}
return (nil, nil)
}

public func snapshot(path: String? = "/tmp/typesense-data-snapshot") async throws -> (SuccessStatus?, URLResponse?) {
let snapshotQueryParam = URLQueryItem(name: "snapshot_path", value: path)
let (data, response) = try await apiCall.post(endPoint: "\(RESOURCEPATH)/snapshot", body: Data(), queryParameters: [snapshotQueryParam])
if let result = data {
let success = try decoder.decode(SuccessStatus.self, from: result)
return (success, response)
}
return (nil, nil)
}

public func toggleSlowRequestLog(seconds: Float) async throws -> (SuccessStatus?, URLResponse?) {
let durationInMs = seconds * 1000
let slowReq = SlowRequest(durationInMs)
let slowReqData = try encoder.encode(slowReq)
let (data, response) = try await apiCall.post(endPoint: "/config", body: slowReqData)
if let result = data {
let success = try decoder.decode(SuccessStatus.self, from: result)
return (success, response)
}
return (nil, nil)
}

}
12 changes: 12 additions & 0 deletions Sources/Typesense/Shared/Coders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ public enum ActionModes: String {
case upsert = "upsert"
case update = "update"
}

public struct SlowRequest: Codable {
public var logSlowRequestsTimeMs: Float?

public init(_ timeInMS: Float? = nil) {
self.logSlowRequestsTimeMs = timeInMS
}

public enum CodingKeys: String, CodingKey {
case logSlowRequestsTimeMs = "log-slow-requests-time-ms"
}
}
Loading

0 comments on commit 6025944

Please sign in to comment.