Skip to content

Commit 525b7a5

Browse files
authored
Merge pull request #12 from niklhut/elasticsearch-8.4
Elasticsearch 8.4 and URL initializer
2 parents 0eb0475 + 23e58a5 commit 525b7a5

File tree

6 files changed

+157
-21
lines changed

6 files changed

+157
-21
lines changed

.github/workflows/swift.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ on:
55
branches: [ main ]
66
pull_request:
77
branches: [ main ]
8+
workflow_dispatch:
89

910
jobs:
10-
test:
11-
name: Run Tests
11+
test-v7_6:
12+
name: Run Tests for Elasticsearch 7.6
1213
runs-on: ubuntu-latest
1314
services:
1415
elasticsearch:
@@ -22,3 +23,18 @@ jobs:
2223
- name: Test
2324
run: swift test --enable-test-discovery --sanitize=thread
2425

26+
test-v8_4:
27+
name: Run Tests for Elasticsearch 8.4
28+
runs-on: ubuntu-latest
29+
services:
30+
elasticsearch:
31+
image: "docker.elastic.co/elasticsearch/elasticsearch:8.4.1"
32+
ports:
33+
- 9200:9200
34+
env:
35+
discovery.type: single-node
36+
xpack.security.enabled: false
37+
steps:
38+
- uses: actions/checkout@v3
39+
- name: Test
40+
run: swift test --enable-test-discovery --sanitize=thread

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,4 @@ If you'd like to add extra functionality, either [open an issue](https://github.
4747

4848
## Elasticsearch Version
4949

50-
The library has been tested again Elasticsearch 7.6.2, but should work for the most part against older versions.
50+
The library has been tested again Elasticsearch 8.4, but should work for the most part against older versions.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Foundation
2+
3+
extension ElasticsearchClient {
4+
public struct ValidationError: LocalizedError, Equatable {
5+
public static let invalidURLString = ValidationError(.invalidURLString)
6+
public static let missingURLScheme = ValidationError(.missingURLScheme)
7+
public static let invalidURLScheme = ValidationError(.invalidURLScheme)
8+
public static let missingURLHost = ValidationError(.missingURLHost)
9+
10+
var localizedDescription: String { self.kind.localizedDescription }
11+
12+
private let kind: Kind
13+
14+
private init(_ kind: Kind) { self.kind = kind }
15+
16+
public static func ==(lhs: ValidationError, rhs: ValidationError) -> Bool {
17+
return lhs.kind == rhs.kind
18+
}
19+
20+
private enum Kind: LocalizedError {
21+
case invalidURLString
22+
case missingURLScheme
23+
case invalidURLScheme
24+
case missingURLHost
25+
26+
var localizedDescription: String {
27+
let message: String = {
28+
switch self {
29+
case .invalidURLString: return "invalid URL string"
30+
case .missingURLScheme: return "URL scheme is missing"
31+
case .invalidURLScheme: return "invalid URL scheme, expected 'http' or 'https'"
32+
case .missingURLHost: return "missing remote hostname"
33+
}
34+
}()
35+
return "Elasticsearch connection configuration validation failed: \(message)"
36+
}
37+
}
38+
}
39+
}

Sources/ElasticsearchNIOClient/ElasticsearchClient.swift

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import Logging
66
import NIOHTTP1
77

88
public struct ElasticsearchClient {
9-
9+
10+
public static let defaultPort = 9200
11+
public static let allowedUrlSchemes = ["http", "https"]
12+
1013
let requester: ElasticsearchRequester
1114
let eventLoop: EventLoop
1215
let logger: Logger
@@ -17,25 +20,68 @@ public struct ElasticsearchClient {
1720
let password: String?
1821
let jsonEncoder: JSONEncoder
1922
let jsonDecoder: JSONDecoder
23+
24+
public init(httpClient: HTTPClient, eventLoop: EventLoop, logger: Logger, url string: String, username: String? = nil, password: String? = nil, jsonEncoder: JSONEncoder = JSONEncoder(), jsonDecoder: JSONDecoder = JSONDecoder()) throws {
25+
guard let url = URL(string: string) else { throw ValidationError.invalidURLString }
26+
try self.init(
27+
httpClient: httpClient,
28+
eventLoop: eventLoop,
29+
logger: logger,
30+
url: url,
31+
username: username,
32+
password: password,
33+
jsonEncoder: jsonEncoder,
34+
jsonDecoder: jsonDecoder
35+
)
36+
}
2037

21-
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()) {
22-
self.eventLoop = eventLoop
23-
self.logger = logger
24-
self.scheme = scheme
25-
self.host = host
26-
self.port = port
27-
self.username = username
28-
self.password = password
29-
self.jsonEncoder = jsonEncoder
30-
self.jsonDecoder = jsonDecoder
31-
self.requester = HTTPClientElasticsearchRequester(eventLoop: eventLoop, logger: logger, client: httpClient)
38+
public init(httpClient: HTTPClient, eventLoop: EventLoop, logger: Logger, url: URL, username: String? = nil, password: String? = nil, jsonEncoder: JSONEncoder = JSONEncoder(), jsonDecoder: JSONDecoder = JSONDecoder()) throws {
39+
guard
40+
let scheme = url.scheme,
41+
!scheme.isEmpty
42+
else { throw ValidationError.missingURLScheme }
43+
guard Self.allowedUrlSchemes.contains(scheme) else { throw ValidationError.invalidURLScheme }
44+
guard let host = url.host, !host.isEmpty else { throw ValidationError.missingURLHost }
45+
46+
try self.init(
47+
requester: HTTPClientElasticsearchRequester(eventLoop: eventLoop, logger: logger, client: httpClient),
48+
eventLoop: eventLoop,
49+
logger: logger,
50+
scheme: scheme,
51+
host: host,
52+
port: url.port,
53+
username: username,
54+
password: password,
55+
jsonEncoder: jsonEncoder,
56+
jsonDecoder: jsonDecoder
57+
)
58+
}
59+
60+
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 {
61+
try self.init(
62+
requester: HTTPClientElasticsearchRequester(eventLoop: eventLoop, logger: logger, client: httpClient),
63+
eventLoop: eventLoop,
64+
logger: logger,
65+
scheme: scheme,
66+
host: host,
67+
port: port,
68+
username: username,
69+
password: password,
70+
jsonEncoder: jsonEncoder,
71+
jsonDecoder: jsonDecoder
72+
)
3273
}
3374

34-
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()) {
75+
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 {
3576
self.requester = requester
3677
self.eventLoop = eventLoop
3778
self.logger = logger
38-
self.scheme = scheme
79+
if let scheme = scheme {
80+
guard Self.allowedUrlSchemes.contains(scheme) else { throw ValidationError.invalidURLScheme }
81+
self.scheme = scheme
82+
} else {
83+
self.scheme = Self.allowedUrlSchemes.first!
84+
}
3985
self.host = host
4086
self.port = port
4187
self.username = username

Sources/ElasticsearchNIOClient/Models/ESBulkResponse.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ public struct ESBulkResponseItem: Codable {
1515

1616
public struct ESBulkResponseItemAction: Codable {
1717
public let index: String
18-
public let type: String
1918
public let id: String
2019
public let version: Int?
2120
public let result: String?
@@ -25,7 +24,6 @@ public struct ESBulkResponseItemAction: Codable {
2524

2625
enum CodingKeys: String, CodingKey {
2726
case index = "_index"
28-
case type = "_type"
2927
case id = "_id"
3028
case version = "_version"
3129
case result

Tests/ElasticsearchNIOClientTests/ElasticsearchNIOClientTests.swift

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ class ElasticSearchIntegrationTests: XCTestCase {
1717
eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
1818
let logger = Logger(label: "io.brokenhands.swift-soto-elasticsearch.test")
1919
httpClient = HTTPClient(eventLoopGroupProvider: .shared(eventLoopGroup))
20-
client = ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "http", host: "localhost", port: 9200)
21-
_ = try client.deleteIndex("_all").wait()
20+
client = try! ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "http", host: "localhost", port: 9200)
21+
if try client.checkIndexExists(indexName).wait() {
22+
_ = try client.deleteIndex(indexName).wait()
23+
}
2224
}
2325

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

2931
// MARK: - Tests
32+
func testURLSetup() throws {
33+
let logger = Logger(label: "io.brokenhands.swift-soto-elasticsearch.test")
34+
35+
let invalidURLString = ""
36+
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: invalidURLString)) { error in
37+
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .invalidURLString)
38+
}
39+
40+
let urlWithoutScheme = URL(string: "://localhost:9200")!
41+
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: urlWithoutScheme)) { error in
42+
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .missingURLScheme)
43+
}
44+
45+
let urlWithIncorrectScheme = URL(string: "localhost:9200")!
46+
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: urlWithIncorrectScheme)) { error in
47+
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .invalidURLScheme)
48+
}
49+
50+
let urlWithoutHost = URL(string: "http://:9200")!
51+
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: urlWithoutHost)) { error in
52+
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .missingURLHost)
53+
}
54+
55+
let correctURL = URL(string: "http://localhost:9200")!
56+
XCTAssertNoThrow(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, url: correctURL))
57+
58+
XCTAssertThrowsError(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "incorrectScheme", host: "localhost", port: 9200)) { error in
59+
XCTAssertEqual(error as! ElasticsearchClient.ValidationError, .invalidURLScheme)
60+
}
61+
62+
XCTAssertNoThrow(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "http", host: "localhost", port: 9200))
63+
64+
XCTAssertNoThrow(try ElasticsearchClient(httpClient: httpClient, eventLoop: eventLoopGroup.next(), logger: logger, scheme: "https", host: "localhost", port: 9200))
65+
}
66+
3067
func testSearchingItems() throws {
3168
try setupItems()
3269

0 commit comments

Comments
 (0)