Skip to content

Commit

Permalink
Merge pull request #12 from niklhut/elasticsearch-8.4
Browse files Browse the repository at this point in the history
Elasticsearch 8.4 and URL initializer
  • Loading branch information
0xTim authored Oct 6, 2022
2 parents 0eb0475 + 23e58a5 commit 525b7a5
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 21 deletions.
20 changes: 18 additions & 2 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
test:
name: Run Tests
test-v7_6:
name: Run Tests for Elasticsearch 7.6
runs-on: ubuntu-latest
services:
elasticsearch:
Expand All @@ -22,3 +23,18 @@ jobs:
- name: Test
run: swift test --enable-test-discovery --sanitize=thread

test-v8_4:
name: Run Tests for Elasticsearch 8.4
runs-on: ubuntu-latest
services:
elasticsearch:
image: "docker.elastic.co/elasticsearch/elasticsearch:8.4.1"
ports:
- 9200:9200
env:
discovery.type: single-node
xpack.security.enabled: false
steps:
- uses: actions/checkout@v3
- name: Test
run: swift test --enable-test-discovery --sanitize=thread
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ If you'd like to add extra functionality, either [open an issue](https://github.

## Elasticsearch Version

The library has been tested again Elasticsearch 7.6.2, but should work for the most part against older versions.
The library has been tested again Elasticsearch 8.4, but should work for the most part against older versions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Foundation

extension ElasticsearchClient {
public struct ValidationError: LocalizedError, Equatable {
public static let invalidURLString = ValidationError(.invalidURLString)
public static let missingURLScheme = ValidationError(.missingURLScheme)
public static let invalidURLScheme = ValidationError(.invalidURLScheme)
public static let missingURLHost = ValidationError(.missingURLHost)

var localizedDescription: String { self.kind.localizedDescription }

private let kind: Kind

private init(_ kind: Kind) { self.kind = kind }

public static func ==(lhs: ValidationError, rhs: ValidationError) -> Bool {
return lhs.kind == rhs.kind
}

private enum Kind: LocalizedError {
case invalidURLString
case missingURLScheme
case invalidURLScheme
case missingURLHost

var localizedDescription: String {
let message: String = {
switch self {
case .invalidURLString: return "invalid URL string"
case .missingURLScheme: return "URL scheme is missing"
case .invalidURLScheme: return "invalid URL scheme, expected 'http' or 'https'"
case .missingURLHost: return "missing remote hostname"
}
}()
return "Elasticsearch connection configuration validation failed: \(message)"
}
}
}
}
74 changes: 60 additions & 14 deletions Sources/ElasticsearchNIOClient/ElasticsearchClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import Logging
import NIOHTTP1

public struct ElasticsearchClient {


public static let defaultPort = 9200
public static let allowedUrlSchemes = ["http", "https"]

let requester: ElasticsearchRequester
let eventLoop: EventLoop
let logger: Logger
Expand All @@ -17,25 +20,68 @@ public struct ElasticsearchClient {
let password: String?
let jsonEncoder: JSONEncoder
let jsonDecoder: JSONDecoder

public init(httpClient: HTTPClient, eventLoop: EventLoop, logger: Logger, url string: String, username: String? = nil, password: String? = nil, jsonEncoder: JSONEncoder = JSONEncoder(), jsonDecoder: JSONDecoder = JSONDecoder()) throws {
guard let url = URL(string: string) else { throw ValidationError.invalidURLString }
try self.init(
httpClient: httpClient,
eventLoop: eventLoop,
logger: logger,
url: url,
username: username,
password: password,
jsonEncoder: jsonEncoder,
jsonDecoder: jsonDecoder
)
}

public init(httpClient: HTTPClient, eventLoop: EventLoop, logger: Logger, scheme: String = "http", host: String, port: Int? = 9200, username: String? = nil, password: String? = nil, jsonEncoder: JSONEncoder = JSONEncoder(), jsonDecoder: JSONDecoder = JSONDecoder()) {
self.eventLoop = eventLoop
self.logger = logger
self.scheme = scheme
self.host = host
self.port = port
self.username = username
self.password = password
self.jsonEncoder = jsonEncoder
self.jsonDecoder = jsonDecoder
self.requester = HTTPClientElasticsearchRequester(eventLoop: eventLoop, logger: logger, client: httpClient)
public init(httpClient: HTTPClient, eventLoop: EventLoop, logger: Logger, url: URL, username: String? = nil, password: String? = nil, jsonEncoder: JSONEncoder = JSONEncoder(), jsonDecoder: JSONDecoder = JSONDecoder()) throws {
guard
let scheme = url.scheme,
!scheme.isEmpty
else { throw ValidationError.missingURLScheme }
guard Self.allowedUrlSchemes.contains(scheme) else { throw ValidationError.invalidURLScheme }
guard let host = url.host, !host.isEmpty else { throw ValidationError.missingURLHost }

try self.init(
requester: HTTPClientElasticsearchRequester(eventLoop: eventLoop, logger: logger, client: httpClient),
eventLoop: eventLoop,
logger: logger,
scheme: scheme,
host: host,
port: url.port,
username: username,
password: password,
jsonEncoder: jsonEncoder,
jsonDecoder: jsonDecoder
)
}

public init(httpClient: HTTPClient, eventLoop: EventLoop, logger: Logger, scheme: String? = nil, host: String, port: Int? = defaultPort, username: String? = nil, password: String? = nil, jsonEncoder: JSONEncoder = JSONEncoder(), jsonDecoder: JSONDecoder = JSONDecoder()) throws {
try self.init(
requester: HTTPClientElasticsearchRequester(eventLoop: eventLoop, logger: logger, client: httpClient),
eventLoop: eventLoop,
logger: logger,
scheme: scheme,
host: host,
port: port,
username: username,
password: password,
jsonEncoder: jsonEncoder,
jsonDecoder: jsonDecoder
)
}

public init(requester: ElasticsearchRequester, eventLoop: EventLoop, logger: Logger, scheme: String = "http", host: String, port: Int? = 9200, username: String? = nil, password: String? = nil, jsonEncoder: JSONEncoder = JSONEncoder(), jsonDecoder: JSONDecoder = JSONDecoder()) {
public init(requester: ElasticsearchRequester, eventLoop: EventLoop, logger: Logger, scheme: String? = nil, host: String, port: Int? = defaultPort, username: String? = nil, password: String? = nil, jsonEncoder: JSONEncoder = JSONEncoder(), jsonDecoder: JSONDecoder = JSONDecoder()) throws {
self.requester = requester
self.eventLoop = eventLoop
self.logger = logger
self.scheme = scheme
if let scheme = scheme {
guard Self.allowedUrlSchemes.contains(scheme) else { throw ValidationError.invalidURLScheme }
self.scheme = scheme
} else {
self.scheme = Self.allowedUrlSchemes.first!
}
self.host = host
self.port = port
self.username = username
Expand Down
2 changes: 0 additions & 2 deletions Sources/ElasticsearchNIOClient/Models/ESBulkResponse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public struct ESBulkResponseItem: Codable {

public struct ESBulkResponseItemAction: Codable {
public let index: String
public let type: String
public let id: String
public let version: Int?
public let result: String?
Expand All @@ -25,7 +24,6 @@ public struct ESBulkResponseItemAction: Codable {

enum CodingKeys: String, CodingKey {
case index = "_index"
case type = "_type"
case id = "_id"
case version = "_version"
case result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ class ElasticSearchIntegrationTests: XCTestCase {
eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let logger = Logger(label: "io.brokenhands.swift-soto-elasticsearch.test")
httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup))
client = ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "http", host: "localhost", port: 9200)
_ = try client.deleteIndex("_all").wait()
client = try! ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "http", host: "localhost", port: 9200)
if try client.checkIndexExists(indexName).wait() {
_ = try client.deleteIndex(indexName).wait()
}
}

override func tearDownWithError() throws {
Expand All @@ -27,6 +29,41 @@ class ElasticSearchIntegrationTests: XCTestCase {
}

// MARK: - Tests
func testURLSetup() throws {
let logger = Logger(label: "io.brokenhands.swift-soto-elasticsearch.test")

let invalidURLString = ""
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: invalidURLString)) { error in
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .invalidURLString)
}

let urlWithoutScheme = URL(string: "://localhost:9200")!
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: urlWithoutScheme)) { error in
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .missingURLScheme)
}

let urlWithIncorrectScheme = URL(string: "localhost:9200")!
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: urlWithIncorrectScheme)) { error in
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .invalidURLScheme)
}

let urlWithoutHost = URL(string: "http://:9200")!
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: urlWithoutHost)) { error in
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .missingURLHost)
}

let correctURL = URL(string: "http://localhost:9200")!
XCTAssertNoThrow(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: correctURL))

XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "incorrectScheme", host: "localhost", port: 9200)) { error in
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .invalidURLScheme)
}

XCTAssertNoThrow(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "http", host: "localhost", port: 9200))

XCTAssertNoThrow(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "https", host: "localhost", port: 9200))
}

func testSearchingItems() throws {
try setupItems()

Expand Down

0 comments on commit 525b7a5

Please sign in to comment.