So, I am using elasticsearch 8.7.0. I create index 'my index':
curl --insecure --user "elastic:password" -X PUT "https://localhost:9200/myindex"
I can add JSON documents manually to elasticsearch 8.7.0 using curl
curl --insecure --user "elastic:password" -X POST "https://localhost:9200/myindex/_bulk" -H 'Content-Type: application/json' -d'{"some": "json"}'
but when I try to do it programmatically with akka-http in Scala 3 using the code below:
@main
def main(args: String*): Unit = {
val records = Seq(
"""{ "index":{ "_index" : "myindex", "_id" : "1" } }
|{"title":"title1"}""".stripMargin,
"""{ "index":{ "_index" : "myindex", "_id" : "2" } }
|{"title":"title2"}""".stripMargin,
"""{ "index":{ "_index" : "myindex", "_id" : "3" } }
|{"title":"title3"}""".stripMargin,
"""{ "index":{ "_index" : "myindex", "_id" : "4" } }
|{"title":"title4"}""".stripMargin)
implicit val system: ActorSystem = ActorSystem("elastic")
implicit val ec: ExecutionContextExecutor = system.dispatcher
val trustfulSslContext: SSLContext = {
// https://github.com/raboof/akka-http-connect-without-checking-cert/blob/0ca55a06afb83b587f592dd97358970502f7b834/Main.scala
object NoCheckX509TrustManager extends X509TrustManager {
override def checkClientTrusted(chain: Array[X509Certificate], authType: String): Unit = ()
override def checkServerTrusted(chain: Array[X509Certificate], authType: String): Unit = ()
override def getAcceptedIssuers: Array[X509Certificate] = Array[X509Certificate]()
}
val context: SSLContext = SSLContext.getInstance("TLS")
context.init(Array[KeyManager](), Array(NoCheckX509TrustManager), new SecureRandom())
context
}
Source
.fromIterator[String]( () => records.iterator )
.grouped(2)
.map(_.mkString("\n"))
.map { payload =>
val entity = HttpEntity(ContentTypes.`application/json`, payload)
val request = HttpRequest(
HttpMethods.POST,
uri = Uri("https://localhost:9200/myindex/_bulk"),
headers = Seq(headers.Authorization(BasicHttpCredentials("elastic", "password"))),
entity = entity
)
println(s"request: $request")
request
}
.via(Http().outgoingConnectionHttps(host = "localhost", port = 9200, connectionContext = ConnectionContext.httpsClient(trustfulSslContext)))
.map(response => {
println(s"response: ${response.entity.toStrict(1 second).map(_.data.utf8String)}")
response
})
.toMat(Sink.ignore)(Keep.none)
.run()
}
I get the following output
request: HttpRequest(HttpMethod(POST),https://localhost:9200/myindex/_bulk,List(Authorization),HttpEntity.Strict(application/json,137 bytes total),HttpProtocol(HTTP/1.1))
response: FulfilledFuture({"error":"no handler found for uri [https://localhost:9200/myindex/_bulk] and method [POST]"})
request: HttpRequest(HttpMethod(POST),https://localhost:9200/myindex/_bulk,List(Authorization),HttpEntity.Strict(application/json,137 bytes total),HttpProtocol(HTTP/1.1))
response: FulfilledFuture({"error":"no handler found for uri [https://localhost:9200/myindex/_bulk] and method [POST]"})
What am I missing?
Apparently, the URI can only contain the path, not the full URL.
Replacing Uri("https://localhost:9200/myindex/_bulk") by Uri("/myindex/_bulk") in the HttpRequest fixes the problem.
Additionally, the payload must end by \n so, I also had to replace .map(_.mkString("\n")) by .map(_.mkString("", "\n", "\n")) to get it to work.