We are now all set to implement the REST endpoint and be able to test it with a REST Client.
Rest Handler
Rest Handler skeleton
Basically, we will extend the BaseRestHandler class. Let’s create a HelloRestAction in src/main/java/org/elasticsearch/ingest/bano/:
12345678910111213
publicclassHelloRestActionextendsBaseRestHandler{@InjectpublicHelloRestAction(Settingssettings,RestControllercontroller){super(settings);// Register your handlers here}@OverrideprotectedRestChannelConsumerprepareRequest(RestRequestrestRequest,NodeClientclient)throwsIOException{// Implement the REST logic here}}
Register endpoints
In the constructor, we can define when this class will be called:
We have to add some “hacks” to the default integration tests. Let’s create a BanoRestIT in
src/test/java/org/elasticsearch/ingest/bano/. Note that it extends AbstractITCase, which we have
have seen previously:
As we did not implemented the REST logic yet, this test will obviously fail. Let’s fix that in HelloRestAction.
First, we will send back a JSON object to the user. Elasticsearch provides XContent classes to deal
with this.
We can create a class which will represent our response:
This code is usig a lambda which builds a new Message object.
Then we create our JSON document. Basically builder.startObject() and builder.endObject() create a JSON object {}. message.toXContent(builder, restRequest) will fill this object with a JSON representation of the Message object: "message": "Hello name!".
At the end, this code will produce:
We need in prepareRequest to extract the name from the body:
123456789101112131415161718192021
@OverrideprotectedRestChannelConsumerprepareRequest(RestRequestrestRequest,NodeClientclient)throwsIOException{Stringname=restRequest.param("name");if(name==null&&restRequest.content().length()>0){// Let's try to find the name from the bodyMap<String,Object>map=XContentHelper.convertToMap(restRequest.content(),false).v2();if(map.containsKey("name")){name=(String)map.get("name");}}StringfinalName=name;returnchannel->{Messagemessage=newMessage(finalName);XContentBuilderbuilder=channel.newBuilder();builder.startObject();message.toXContent(builder,restRequest);builder.endObject();channel.sendResponse(newBytesRestResponse(RestStatus.OK,builder));};}
And we also need to register the POST request. Let’s add it in the HelloRestAction constructor by
registering now:
1
controller.registerHandler(POST,"/_hello",this);
Et voilĂ !
Calling elasticsearch client
You can also call elasticsearch client to perform some tasks and then send the result back to the caller.
Here for example, we want to call:
1
curl -XGET http://localhost:9200/_bano
And get back an array containing the list of all indices starting with .bano in the cluster:
1234567
{"bano":[".bano-17",".bano-95",".bano-29"]}
Add a test
As usual, we start adding a test:
12345678910111213141516171819202122
publicvoidtestBano()throwsException{// We first create some indicesintnumIndices=randomIntBetween(1,10);for(inti=0;i<numIndices;i++){client.performRequest("PUT","/.bano-"+i);}// We create some other indicesintnumOtherIndices=randomIntBetween(1,10);for(inti=0;i<numOtherIndices;i++){client.performRequest("PUT","/"+randomAsciiOfLengthBetween(6,10).toLowerCase(Locale.getDefault()));}client.performRequest("GET","/_cluster/health",Collections.singletonMap("wait_for_status","yellow"));Responseresponse=client.performRequest("GET","/_bano");Map<String,Object>responseMap=entityAsMap(response);assertThat(responseMap,hasKey("bano"));List<String>bano=(List<String>)responseMap.get("bano");for(inti=0;i<numIndices;i++){assertThat(bano,hasItem(".bano-"+i));}}
The test generates a random number of indices starting with .bano plus some other unrelated indices.
Let’s now create a new BanoRestAction as we did for HelloRestAction:
As you can see, we don’t block the thread here as we are using a Listener which is called once
the request in the cluster is over.
This is extremly important in the context of elasticsearch as it frees the network socket which can
hold other requests and is not blocked by the current one.
If everything goes well, buildResponse method will be called. Otherwise any exception will be thrown
and elasticsearch REST layer mechanism will send that back to the caller.
Indices is a new class as we have seen before which will help us to serialize to JSON the response:
In a coming blog post, I’ll explain how to create an ingest plugin which will helps you to transform a french postal address to geo coordinates or the other way around. We will use what we have just seen to add some administrative tasks like automatically create our datasources in elasticsearch
(download CSV files from bano website, extract, transform to JSON and index in elasticsearch).