Indexed scripts: make sure headers are handed over to internal requests and streamline versioning support

The get, put and delete indexed script apis map to get, index and delete api and internally create those corresponding requests. We need to make sure that the original headers are handed over to the new request by passing the original request in the constructor when creating the new one.

Also streamlined the support for version and version_type in the REST layer since the parameters were not consistently parsed and set to the internal java API requests.

Modified the REST delete template and delete script actions to make use of a client instead of using the `ScriptService` directly.

Closes #7569
This commit is contained in:
javanna 2014-09-03 16:50:27 +02:00 committed by Luca Cavanna
parent 221eafab59
commit a857798e1c
19 changed files with 356 additions and 291 deletions

View File

@ -16,6 +16,17 @@
"description" : "Script language",
"required" : true
}
},
"params" : {
"version": {
"type": "number",
"description": "Explicit version number for concurrency control"
},
"version_type": {
"type": "enum",
"options": ["internal", "external", "external_gte", "force"],
"description": "Specific version type"
}
}
},
"body": null

View File

@ -12,6 +12,17 @@
}
}
},
"params" : {
"version": {
"type": "number",
"description": "Explicit version number for concurrency control"
},
"version_type": {
"type": "enum",
"options": ["internal", "external", "external_gte", "force"],
"description": "Specific version type"
}
},
"body": null
}
}

View File

@ -16,6 +16,17 @@
"description" : "Script language",
"required" : true
}
},
"params" : {
"version": {
"type": "number",
"description": "Explicit version number for concurrency control"
},
"version_type": {
"type": "enum",
"options": ["internal", "external", "external_gte", "force"],
"description": "Specific version type"
}
}
},
"body": null

View File

@ -11,11 +11,19 @@
"description" : "Template ID",
"required" : true
}
},
"params" : {
"version": {
"type": "number",
"description": "Explicit version number for concurrency control"
},
"version_type": {
"type": "enum",
"options": ["internal", "external", "external_gte", "force"],
"description": "Specific version type"
}
}
},
"body": {
"description" : "The document",
"required" : false
}
"body": null
}
}

View File

@ -16,6 +16,23 @@
"description" : "Script language",
"required" : true
}
},
"params" : {
"op_type": {
"type" : "enum",
"options" : ["index", "create"],
"default" : "index",
"description" : "Explicit operation type"
},
"version": {
"type": "number",
"description": "Explicit version number for concurrency control"
},
"version_type": {
"type": "enum",
"options": ["internal", "external", "external_gte", "force"],
"description": "Specific version type"
}
}
},
"body": {

View File

@ -13,6 +13,23 @@
}
}
},
"params" : {
"op_type": {
"type" : "enum",
"options" : ["index", "create"],
"default" : "index",
"description" : "Explicit operation type"
},
"version": {
"type": "number",
"description": "Explicit version number for concurrency control"
},
"version_type": {
"type": "enum",
"options": ["internal", "external", "external_gte", "force"],
"description": "Specific version type"
}
},
"body": {
"description" : "The document",
"required" : true

View File

@ -0,0 +1,112 @@
---
"External version":
- do:
put_script:
lang: groovy
id: 1
body: { "script": "_score * doc[\"myParent.weight\"].value" }
version_type: external
version: 0
- match: { _version: 0 }
- do:
put_script:
lang: groovy
id: 1
body: { "script": "_score * doc[\"myParent.weight\"].value" }
version_type: external
version: 5
- match: { _version: 5 }
- do:
catch: conflict
get_script:
lang: groovy
id: 1
version: 3
- do:
catch: conflict
delete_script:
lang: groovy
id: 1
version: 3
- do:
get_script:
lang: groovy
id: 1
version: 5
version_type: external
- is_true: script
- do:
get_script:
lang: groovy
id: 1
version: 5
version_type: external_gte
- is_true: script
- do:
catch: conflict
get_script:
lang: groovy
id: 1
version: 10
version_type: external_gte
- do:
catch: conflict
delete_script:
lang: groovy
id: 1
version: 3
version_type: external
- do:
get_script:
lang: groovy
id: 1
version: 5
version_type: force
- is_true: script
- do:
get_script:
lang: groovy
id: 1
version: 10
version_type: force
- is_true: script
- do:
catch: conflict
put_script:
lang: groovy
id: 1
body: { "script": "_score * doc[\"myParent.weight\"].value" }
version_type: external
version: 5
- do:
catch: conflict
put_script:
lang: groovy
id: 1
body: { "script": "_score * doc[\"myParent.weight\"].value" }
version_type: external
version: 0
- do:
put_script:
lang: groovy
id: 1
body: { "script": "_score * doc[\"myParent.weight\"].value" }
version_type: external
version: 6
- match: { _version: 6}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.action.delete;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.support.replication.ShardReplicationOperationRequest;
import org.elasticsearch.common.Nullable;
@ -86,6 +87,14 @@ public class DeleteRequest extends ShardReplicationOperationRequest<DeleteReques
public DeleteRequest() {
}
/**
* Creates a delete request caused by some other request, which is provided as an
* argument so that its headers and context can be copied to the new request
*/
public DeleteRequest(ActionRequest request) {
super(request);
}
@Override
public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = super.validate();

View File

@ -21,6 +21,7 @@ package org.elasticsearch.action.index;
import com.google.common.base.Charsets;
import org.elasticsearch.*;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.RoutingMissingException;
import org.elasticsearch.action.TimestampParsingException;
@ -151,6 +152,14 @@ public class IndexRequest extends ShardReplicationOperationRequest<IndexRequest>
public IndexRequest() {
}
/**
* Creates an index request caused by some other request, which is provided as an
* argument so that its headers and context can be copied to the new request
*/
public IndexRequest(ActionRequest request) {
super(request);
}
/**
* Constructs a new index request against the specific index. The {@link #type(String)}
* {@link #source(byte[])} must be set.

View File

@ -20,11 +20,10 @@
package org.elasticsearch.action.indexedscripts.delete;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.DelegatingActionListener;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.ScriptService;
@ -37,14 +36,12 @@ import org.elasticsearch.transport.TransportService;
public class TransportDeleteIndexedScriptAction extends HandledTransportAction<DeleteIndexedScriptRequest, DeleteIndexedScriptResponse> {
private final ScriptService scriptService;
private final Client client;
@Inject
public TransportDeleteIndexedScriptAction(Settings settings, ThreadPool threadPool, ScriptService scriptService,
Client client, TransportService transportService, ActionFilters actionFilters) {
TransportService transportService, ActionFilters actionFilters) {
super(settings, DeleteIndexedScriptAction.NAME, threadPool, transportService, actionFilters);
this.scriptService = scriptService;
this.client = client;
}
@Override
@ -54,10 +51,10 @@ public class TransportDeleteIndexedScriptAction extends HandledTransportAction<D
@Override
protected void doExecute(final DeleteIndexedScriptRequest request, final ActionListener<DeleteIndexedScriptResponse> listener) {
scriptService.deleteScriptFromIndex(client, request.scriptLang(), request.id(), request.version(), new DelegatingActionListener<DeleteResponse, DeleteIndexedScriptResponse>(listener) {
scriptService.deleteScriptFromIndex(request, new DelegatingActionListener<DeleteResponse, DeleteIndexedScriptResponse>(listener) {
@Override
public DeleteIndexedScriptResponse getDelegatedFromInstigator(DeleteResponse deleteResponse){
return new DeleteIndexedScriptResponse(deleteResponse.getIndex(),deleteResponse.getType(),deleteResponse.getType(),deleteResponse.getVersion(), deleteResponse.isFound());
return new DeleteIndexedScriptResponse(deleteResponse.getIndex(), deleteResponse.getType(), deleteResponse.getId(), deleteResponse.getVersion(), deleteResponse.isFound());
}
});
}

View File

@ -23,7 +23,6 @@ import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.ScriptService;
@ -36,14 +35,12 @@ import org.elasticsearch.transport.TransportService;
public class TransportGetIndexedScriptAction extends HandledTransportAction<GetIndexedScriptRequest, GetIndexedScriptResponse> {
private final ScriptService scriptService;
private final Client client;
@Inject
public TransportGetIndexedScriptAction(Settings settings, ThreadPool threadPool, ScriptService scriptService,
TransportService transportService, Client client, ActionFilters actionFilters) {
TransportService transportService, ActionFilters actionFilters) {
super(settings, GetIndexedScriptAction.NAME, threadPool,transportService, actionFilters);
this.scriptService = scriptService;
this.client = client;
}
@Override
@ -53,7 +50,7 @@ public class TransportGetIndexedScriptAction extends HandledTransportAction<GetI
@Override
public void doExecute(GetIndexedScriptRequest request, ActionListener<GetIndexedScriptResponse> listener){
GetResponse scriptResponse = scriptService.queryScriptIndex(client, request.scriptLang(), request.id(), request.version(), request.versionType());
GetResponse scriptResponse = scriptService.queryScriptIndex(request);
listener.onResponse(new GetIndexedScriptResponse(scriptResponse));
}
}

View File

@ -24,7 +24,6 @@ import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.DelegatingActionListener;
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.ScriptService;
@ -37,14 +36,11 @@ import org.elasticsearch.transport.TransportService;
public class TransportPutIndexedScriptAction extends HandledTransportAction<PutIndexedScriptRequest, PutIndexedScriptResponse> {
private final ScriptService scriptService;
private final Client client;
@Inject
public TransportPutIndexedScriptAction(Settings settings, ThreadPool threadPool,
ScriptService scriptService, Client client,
TransportService transportService, ActionFilters actionFilters) {
ScriptService scriptService, TransportService transportService, ActionFilters actionFilters) {
super(settings, PutIndexedScriptAction.NAME, threadPool, transportService, actionFilters);
this.client = client;
this.scriptService = scriptService;
}
@ -55,12 +51,11 @@ public class TransportPutIndexedScriptAction extends HandledTransportAction<PutI
@Override
protected void doExecute(final PutIndexedScriptRequest request, final ActionListener<PutIndexedScriptResponse> listener) {
scriptService.putScriptToIndex(client, request.safeSource(), request.scriptLang(), request.id(), null, request.opType().toString(), request.version(), request.versionType(), new DelegatingActionListener<IndexResponse,PutIndexedScriptResponse>(listener) {
scriptService.putScriptToIndex(request, new DelegatingActionListener<IndexResponse,PutIndexedScriptResponse>(listener) {
@Override
public PutIndexedScriptResponse getDelegatedFromInstigator(IndexResponse indexResponse){
return new PutIndexedScriptResponse(indexResponse.getType(),indexResponse.getId(),indexResponse.getVersion(),indexResponse.isCreated());
}
});
}
}

View File

@ -18,17 +18,16 @@
*/
package org.elasticsearch.rest.action.script;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptRequest;
import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.uid.Versions;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestBuilderListener;
import org.elasticsearch.script.ScriptService;
import static org.elasticsearch.rest.RestRequest.Method.DELETE;
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
@ -36,24 +35,28 @@ import static org.elasticsearch.rest.RestStatus.OK;
public class RestDeleteIndexedScriptAction extends BaseRestHandler {
private ScriptService scriptService;
@Inject
public RestDeleteIndexedScriptAction(Settings settings, Client client,
ScriptService scriptService, RestController controller) {
public RestDeleteIndexedScriptAction(Settings settings, Client client, RestController controller) {
super(settings, client);
controller.registerHandler(DELETE, "/_scripts/{lang}/{id}", this);
this.scriptService = scriptService;
}
protected RestDeleteIndexedScriptAction(Settings settings, Client client) {
super(settings, client);
}
protected String getScriptLang(RestRequest request) {
return request.param("lang");
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, Client client) {
final String id = request.param("id");
final long version = request.paramAsLong("version", Versions.MATCH_ANY);
scriptService.deleteScriptFromIndex(client,request.param("lang"), id, version, new RestBuilderListener<DeleteResponse>(channel) {
DeleteIndexedScriptRequest deleteIndexedScriptRequest = new DeleteIndexedScriptRequest(getScriptLang(request), request.param("id"));
deleteIndexedScriptRequest.version(request.paramAsLong("version", deleteIndexedScriptRequest.version()));
deleteIndexedScriptRequest.versionType(VersionType.fromString(request.param("version_type"), deleteIndexedScriptRequest.versionType()));
client.deleteIndexedScript(deleteIndexedScriptRequest, new RestBuilderListener<DeleteIndexedScriptResponse>(channel) {
@Override
public RestResponse buildResponse(DeleteResponse result, XContentBuilder builder) throws Exception {
public RestResponse buildResponse(DeleteIndexedScriptResponse result, XContentBuilder builder) throws Exception {
builder.startObject()
.field(Fields.FOUND, result.isFound())
.field(Fields._INDEX, result.getIndex())
@ -77,6 +80,4 @@ public class RestDeleteIndexedScriptAction extends BaseRestHandler {
static final XContentBuilderString _ID = new XContentBuilderString("_id");
static final XContentBuilderString _VERSION = new XContentBuilderString("_version");
}
}

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestResponseListener;
@ -47,11 +48,24 @@ public class RestGetIndexedScriptAction extends BaseRestHandler {
controller.registerHandler(GET, "/_scripts/{lang}/{id}", this);
}
protected RestGetIndexedScriptAction(Settings settings, Client client) {
super(settings, client);
}
protected String getScriptLang(RestRequest request) {
return request.param("lang");
}
protected String getScriptFieldName() {
return "script";
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, Client client) {
final GetIndexedScriptRequest getRequest = new GetIndexedScriptRequest(request.param("lang"), request.param("id"));
RestResponseListener<GetIndexedScriptResponse> responseListener = new RestResponseListener<GetIndexedScriptResponse>(channel) {
final GetIndexedScriptRequest getRequest = new GetIndexedScriptRequest(getScriptLang(request), request.param("id"));
getRequest.version(request.paramAsLong("version", getRequest.version()));
getRequest.versionType(VersionType.fromString(request.param("version_type"), getRequest.versionType()));
client.getIndexedScript(getRequest, new RestResponseListener<GetIndexedScriptResponse>(channel) {
@Override
public RestResponse buildResponse(GetIndexedScriptResponse response) throws Exception {
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
@ -61,7 +75,7 @@ public class RestGetIndexedScriptAction extends BaseRestHandler {
try{
String script = response.getScript();
builder.startObject();
builder.field("script",script);
builder.field(getScriptFieldName(), script);
builder.endObject();
return new BytesRestResponse(OK, builder);
} catch( IOException|ClassCastException e ){
@ -69,7 +83,6 @@ public class RestGetIndexedScriptAction extends BaseRestHandler {
}
}
}
};
client.getIndexedScript(getRequest, responseListener);
});
}
}

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestBuilderListener;
@ -52,6 +53,10 @@ public class RestPutIndexedScriptAction extends BaseRestHandler {
controller.registerHandler(POST, "/_scripts/{lang}/{id}/_create", new CreateHandler(settings, client));
}
protected RestPutIndexedScriptAction(Settings settings, Client client) {
super(settings, client);
}
final class CreateHandler extends BaseRestHandler {
protected CreateHandler(Settings settings, final Client client) {
super(settings, client);
@ -64,12 +69,15 @@ public class RestPutIndexedScriptAction extends BaseRestHandler {
}
}
protected String getScriptLang(RestRequest request) {
return request.param("lang");
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, Client client) {
PutIndexedScriptRequest putRequest = new PutIndexedScriptRequest(request.param("lang"), request.param("id"));
putRequest.listenerThreaded(false);
PutIndexedScriptRequest putRequest = new PutIndexedScriptRequest(getScriptLang(request), request.param("id")).listenerThreaded(false);
putRequest.version(request.paramAsLong("version", putRequest.version()));
putRequest.versionType(VersionType.fromString(request.param("version_type"), putRequest.versionType()));
putRequest.source(request.content(), request.contentUnsafe());
String sOpType = request.param("op_type");
if (sOpType != null) {
@ -109,5 +117,4 @@ public class RestPutIndexedScriptAction extends BaseRestHandler {
static final XContentBuilderString _ID = new XContentBuilderString("_id");
static final XContentBuilderString CREATED = new XContentBuilderString("created");
}
}

View File

@ -18,63 +18,25 @@
*/
package org.elasticsearch.rest.action.template;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.uid.Versions;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestBuilderListener;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.script.RestDeleteIndexedScriptAction;
import static org.elasticsearch.rest.RestRequest.Method.DELETE;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
public class RestDeleteSearchTemplateAction extends BaseRestHandler {
private ScriptService scriptService;
public class RestDeleteSearchTemplateAction extends RestDeleteIndexedScriptAction {
@Inject
public RestDeleteSearchTemplateAction(Settings settings, Client client, RestController controller, ScriptService scriptService) {
public RestDeleteSearchTemplateAction(Settings settings, Client client, RestController controller) {
super(settings, client);
controller.registerHandler(DELETE, "/_search/template/{id}", this);
this.scriptService = scriptService;
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, Client client) {
final String id = request.param("id");
final long version = request.paramAsLong("version", Versions.MATCH_ANY);
scriptService.deleteScriptFromIndex(client,"mustache", id, version, new RestBuilderListener<DeleteResponse>(channel) {
@Override
public RestResponse buildResponse(DeleteResponse result, XContentBuilder builder) throws Exception {
builder.startObject()
.field(Fields.FOUND, result.isFound())
.field(Fields._INDEX, result.getIndex())
.field(Fields._TYPE, result.getType())
.field(Fields._ID, result.getId())
.field(Fields._VERSION, result.getVersion())
.endObject();
RestStatus status = OK;
if (!result.isFound()) {
status = NOT_FOUND;
}
return new BytesRestResponse(status, builder);
}
});
protected String getScriptLang(RestRequest request) {
return "mustache";
}
static final class Fields {
static final XContentBuilderString FOUND = new XContentBuilderString("found");
static final XContentBuilderString _INDEX = new XContentBuilderString("_index");
static final XContentBuilderString _TYPE = new XContentBuilderString("_type");
static final XContentBuilderString _ID = new XContentBuilderString("_id");
static final XContentBuilderString _VERSION = new XContentBuilderString("_version");
}
}

View File

@ -18,28 +18,19 @@
*/
package org.elasticsearch.rest.action.template;
import org.elasticsearch.ElasticsearchIllegalStateException;
import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptRequest;
import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestResponseListener;
import java.io.IOException;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.script.RestGetIndexedScriptAction;
import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
import static org.elasticsearch.rest.RestStatus.OK;
/**
*
*/
public class RestGetSearchTemplateAction extends BaseRestHandler {
public class RestGetSearchTemplateAction extends RestGetIndexedScriptAction {
@Inject
public RestGetSearchTemplateAction(Settings settings, Client client, RestController controller) {
@ -48,27 +39,12 @@ public class RestGetSearchTemplateAction extends BaseRestHandler {
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, Client client) {
final GetIndexedScriptRequest getRequest = new GetIndexedScriptRequest("mustache", request.param("id"));
RestResponseListener<GetIndexedScriptResponse> responseListener = new RestResponseListener<GetIndexedScriptResponse>(channel) {
@Override
public RestResponse buildResponse(GetIndexedScriptResponse response) throws Exception {
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
if (!response.isExists()) {
return new BytesRestResponse(NOT_FOUND, builder);
} else {
try{
String templateString = response.getScript();
builder.startObject();
builder.field("template",templateString);
builder.endObject();
return new BytesRestResponse(OK, builder);
} catch( IOException|ClassCastException e ){
throw new ElasticsearchIllegalStateException("Unable to parse " + response.getScript() + " as json",e);
}
}
}
};
client.getIndexedScript(getRequest, responseListener);
protected String getScriptLang(RestRequest request) {
return "mustache";
}
@Override
protected String getScriptFieldName() {
return "template";
}
}

View File

@ -18,30 +18,22 @@
*/
package org.elasticsearch.rest.action.template;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequest;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestBuilderListener;
import java.io.IOException;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.action.script.RestPutIndexedScriptAction;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.rest.RestRequest.Method.PUT;
import static org.elasticsearch.rest.RestStatus.CREATED;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.rest.RestStatus.BAD_REQUEST;
/**
*
*/
public class RestPutSearchTemplateAction extends BaseRestHandler {
public class RestPutSearchTemplateAction extends RestPutIndexedScriptAction {
@Inject
public RestPutSearchTemplateAction(Settings settings, Client client, RestController controller) {
@ -67,51 +59,8 @@ public class RestPutSearchTemplateAction extends BaseRestHandler {
}
}
@Override
public void handleRequest(final RestRequest request, final RestChannel channel, Client client) {
PutIndexedScriptRequest putRequest = new PutIndexedScriptRequest("mustache", request.param("id"));
putRequest.listenerThreaded(false);
putRequest.source(request.content(), request.contentUnsafe());
String sOpType = request.param("op_type");
if (sOpType != null) {
try {
putRequest.opType(IndexRequest.OpType.fromString(sOpType));
} catch (ElasticsearchIllegalArgumentException eia){
try {
XContentBuilder builder = channel.newBuilder();
channel.sendResponse(new BytesRestResponse(BAD_REQUEST, builder.startObject().field("error", eia.getMessage()).endObject()));
return;
} catch (IOException e1) {
logger.warn("Failed to send response", e1);
return;
}
}
}
client.putIndexedScript(putRequest, new RestBuilderListener<PutIndexedScriptResponse>(channel) {
@Override
public RestResponse buildResponse(PutIndexedScriptResponse response, XContentBuilder builder) throws Exception {
builder.startObject()
.field(Fields._ID, response.getId())
.field(Fields._VERSION, response.getVersion())
.field(Fields.CREATED, response.isCreated());
builder.endObject();
RestStatus status = OK;
if (response.isCreated()) {
status = CREATED;
}
return new BytesRestResponse(status, builder);
}
});
protected String getScriptLang(RestRequest request) {
return "mustache";
}
static final class Fields {
static final XContentBuilderString _VERSION = new XContentBuilderString("_version");
static final XContentBuilderString _ID = new XContentBuilderString("_id");
static final XContentBuilderString CREATED = new XContentBuilderString("created");
}
}

View File

@ -26,13 +26,15 @@ import com.google.common.collect.ImmutableMap;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.ElasticsearchIllegalStateException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.ListenableActionFuture;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptRequest;
import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptRequest;
import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
@ -44,7 +46,6 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.Streams;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.uid.Versions;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
@ -53,7 +54,6 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.index.query.TemplateQueryParser;
import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.watcher.FileChangesListener;
@ -81,10 +81,9 @@ public class ScriptService extends AbstractComponent {
public static final String SCRIPT_CACHE_EXPIRE_SETTING = "script.cache.expire";
public static final String DISABLE_DYNAMIC_SCRIPTING_DEFAULT = "sandbox";
public static final String SCRIPT_INDEX = ".scripts";
public static final String DEFAULT_LANG = "groovy";
//Make static so that it has visibility in IndexedScript
//Looked up from settings in ctor
private static String defaultLang = "groovy";
private final String defaultLang;
private final ImmutableMap<String, ScriptEngineService> scriptEngines;
@ -108,7 +107,7 @@ public class ScriptService extends AbstractComponent {
ONLY_DISK_ALLOWED,
SANDBOXED_ONLY;
public static final DynamicScriptDisabling parse(String s) {
static DynamicScriptDisabling parse(String s) {
switch (s.toLowerCase(Locale.ROOT)) {
// true for "disable_dynamic" means only on-disk scripts are enabled
case "true":
@ -129,15 +128,6 @@ public class ScriptService extends AbstractComponent {
}
public static final ParseField SCRIPT_LANG = new ParseField("lang","script_lang");
public static final ParseField VALUE_SCRIPT_FILE = new ParseField("value_script_file");
public static final ParseField VALUE_SCRIPT_ID = new ParseField("value_script_id");
public static final ParseField VALUE_SCRIPT_INLINE = new ParseField("value_script");
public static final ParseField KEY_SCRIPT_FILE = new ParseField("key_script_file");
public static final ParseField KEY_SCRIPT_ID = new ParseField("key_script_id");
public static final ParseField KEY_SCRIPT_INLINE = new ParseField("key_script");
public static final ParseField SCRIPT_FILE = new ParseField("script_file","file");
public static final ParseField SCRIPT_ID = new ParseField("script_id", "id");
public static final ParseField SCRIPT_INLINE = new ParseField("script","scriptField");
@ -165,8 +155,7 @@ public class ScriptService extends AbstractComponent {
throw new ElasticsearchIllegalArgumentException("Unexpected value read for ScriptType got [" + scriptTypeVal +
"] expected one of ["+INLINE_VAL +"," + INDEXED_VAL + "," + FILE_VAL+"]");
}
}
}
public static void writeTo(ScriptType scriptType, StreamOutput out) throws IOException{
if (scriptType != null) {
@ -190,17 +179,14 @@ public class ScriptService extends AbstractComponent {
}
static class IndexedScript {
String lang;
String id;
private final String lang;
private final String id;
IndexedScript(String lang, String script) {
this.lang = lang;
final String[] parts = script.split("/");
if (parts.length == 1) {
this.id = script;
if (this.lang == null){
this.lang = defaultLang;
}
} else {
if (parts.length != 3) {
throw new ElasticsearchIllegalArgumentException("Illegal index script format [" + script + "]" +
@ -224,7 +210,7 @@ public class ScriptService extends AbstractComponent {
TimeValue cacheExpire = settings.getAsTime(SCRIPT_CACHE_EXPIRE_SETTING, null);
logger.debug("using script cache with max_size [{}], expire [{}]", cacheMaxSize, cacheExpire);
this.defaultLang = settings.get(DEFAULT_SCRIPTING_LANGUAGE_SETTING, "groovy");
this.defaultLang = settings.get(DEFAULT_SCRIPTING_LANGUAGE_SETTING, DEFAULT_LANG);
this.dynamicScriptingDisabled = DynamicScriptDisabling.parse(settings.get(DISABLE_DYNAMIC_SCRIPTING_SETTING, DISABLE_DYNAMIC_SCRIPTING_DEFAULT));
CacheBuilder cacheBuilder = CacheBuilder.newBuilder();
@ -292,17 +278,16 @@ public class ScriptService extends AbstractComponent {
CacheKey cacheKey;
CompiledScript compiled;
if (lang == null) {
lang = defaultLang;
}
if(scriptType == ScriptType.INDEXED) {
if (client == null) {
throw new ElasticsearchIllegalArgumentException("Got an indexed script with no Client registered.");
}
final IndexedScript indexedScript = new IndexedScript(lang,script);
if (lang != null && !lang.equals(indexedScript.lang)) {
logger.trace("Overriding lang to " + indexedScript.lang);
lang = indexedScript.lang;
}
final IndexedScript indexedScript = new IndexedScript(lang, script);
verifyDynamicScripting(indexedScript.lang); //Since anyone can index a script, disable indexed scripting
// if dynamic scripting is disabled, perhaps its own setting ?
@ -328,10 +313,6 @@ public class ScriptService extends AbstractComponent {
}
}
if (lang == null) {
lang = defaultLang;
}
//This is an inline script check to see if we have it in the cache
verifyDynamicScripting(lang);
@ -373,32 +354,27 @@ public class ScriptService extends AbstractComponent {
}
}
public GetResponse queryScriptIndex(Client client, String scriptLang, String id){
return queryScriptIndex(client, scriptLang, id, Versions.MATCH_ANY, VersionType.INTERNAL);
}
public GetResponse queryScriptIndex(Client client, String scriptLang, String id,
long version, VersionType versionType) {
scriptLang = validateScriptLanguage(scriptLang);
return client.prepareGet(SCRIPT_INDEX, scriptLang, id)
.setVersion(version)
.setVersionType(versionType)
.setPreference("_local") //Set preference for no forking
.setOperationThreaded(false)
.get();
public GetResponse queryScriptIndex(GetIndexedScriptRequest request) {
String scriptLang = validateScriptLanguage(request.scriptLang());
GetRequest getRequest = new GetRequest(request, SCRIPT_INDEX).type(scriptLang).id(request.id())
.version(request.version()).versionType(request.versionType())
.operationThreaded(false).preference("_local"); //Set preference for no forking
return client.get(getRequest).actionGet();
}
private String validateScriptLanguage(String scriptLang) {
if (scriptLang == null){
if (scriptLang == null) {
scriptLang = defaultLang;
} else if (!scriptEngines.containsKey(scriptLang)){
} else if (!scriptEngines.containsKey(scriptLang)) {
throw new ElasticsearchIllegalArgumentException("script_lang not supported ["+scriptLang+"]");
}
return scriptLang;
}
private String getScriptFromIndex(Client client, String scriptLang, String id) {
GetResponse responseFields = queryScriptIndex(client,scriptLang,id);
scriptLang = validateScriptLanguage(scriptLang);
GetRequest getRequest = new GetRequest(SCRIPT_INDEX, scriptLang, id);
GetResponse responseFields = client.get(getRequest).actionGet();
if (responseFields.isExists()) {
return getScriptFromResponse(responseFields);
}
@ -406,63 +382,47 @@ public class ScriptService extends AbstractComponent {
+ scriptLang + "/" + id + "]");
}
private void validate(BytesReference scriptBytes, String scriptLang) throws IOException{
XContentParser parser = XContentFactory.xContent(scriptBytes).createParser(scriptBytes);
TemplateQueryParser.TemplateContext context = TemplateQueryParser.parse(parser, "params", "script", "template");
if (Strings.hasLength(context.template()) == true){
//Just try and compile it
//This will have the benefit of also adding the script to the cache if it compiles
try {
CompiledScript compiledScript = compile(scriptLang, context.template(), ScriptService.ScriptType.INLINE);
if (compiledScript == null) {
throw new ElasticsearchIllegalArgumentException("Unable to parse [" + context.template() +
"] lang [" + scriptLang + "] (ScriptService.compile returned null)");
}
} catch (Exception e) {
throw new ElasticsearchIllegalArgumentException("Unable to parse [" + context.template() +
"] lang [" + scriptLang + "]", e);
}
} else {
throw new ElasticsearchIllegalArgumentException("Unable to find script in : " + scriptBytes.toUtf8());
}
}
public void putScriptToIndex(Client client, BytesReference scriptBytes, @Nullable String scriptLang, String id,
@Nullable TimeValue timeout, @Nullable String sOpType, long version,
VersionType versionType, ActionListener<IndexResponse> listener) {
private void validate(BytesReference scriptBytes, String scriptLang) {
try {
scriptLang = validateScriptLanguage(scriptLang);
//verify that the script compiles
validate(scriptBytes, scriptLang);
IndexRequest indexRequest = new IndexRequest(SCRIPT_INDEX, scriptLang, id);
indexRequest.listenerThreaded(false);
indexRequest.operationThreaded(false);
indexRequest.version(version);
indexRequest.versionType(versionType);
indexRequest.refresh(true); //Always refresh after indexing a template
indexRequest.source(scriptBytes, true);
if (timeout != null) {
indexRequest.timeout(timeout);
XContentParser parser = XContentFactory.xContent(scriptBytes).createParser(scriptBytes);
TemplateQueryParser.TemplateContext context = TemplateQueryParser.parse(parser, "params", "script", "template");
if (Strings.hasLength(context.template())){
//Just try and compile it
//This will have the benefit of also adding the script to the cache if it compiles
try {
CompiledScript compiledScript = compile(scriptLang, context.template(), ScriptService.ScriptType.INLINE);
if (compiledScript == null) {
throw new ElasticsearchIllegalArgumentException("Unable to parse [" + context.template() +
"] lang [" + scriptLang + "] (ScriptService.compile returned null)");
}
} catch (Exception e) {
throw new ElasticsearchIllegalArgumentException("Unable to parse [" + context.template() +
"] lang [" + scriptLang + "]", e);
}
} else {
throw new ElasticsearchIllegalArgumentException("Unable to find script in : " + scriptBytes.toUtf8());
}
if (sOpType != null) {
indexRequest.opType(IndexRequest.OpType.fromString(sOpType));
}
client.index(indexRequest, listener);
} catch (Throwable t){
listener.onFailure(t);
} catch (IOException e) {
throw new ElasticsearchIllegalArgumentException("failed to parse template script", e);
}
}
public void deleteScriptFromIndex(Client client, @Nullable String scriptLang, String id,
long version, ActionListener<DeleteResponse> listener) {
scriptLang = validateScriptLanguage(scriptLang);
client.delete((new DeleteRequest(SCRIPT_INDEX,scriptLang,id)).refresh(true).version(version), listener);
public void putScriptToIndex(PutIndexedScriptRequest request, ActionListener<IndexResponse> listener) {
String scriptLang = validateScriptLanguage(request.scriptLang());
//verify that the script compiles
validate(request.safeSource(), scriptLang);
IndexRequest indexRequest = new IndexRequest(request).index(SCRIPT_INDEX).type(scriptLang).id(request.id())
.listenerThreaded(false).operationThreaded(false).version(request.version()).versionType(request.versionType())
.source(request.safeSource(), true).opType(request.opType()).refresh(true); //Always refresh after indexing a template
client.index(indexRequest, listener);
}
public void deleteScriptFromIndex(DeleteIndexedScriptRequest request, ActionListener<DeleteResponse> listener) {
String scriptLang = validateScriptLanguage(request.scriptLang());
DeleteRequest deleteRequest = new DeleteRequest(request).index(SCRIPT_INDEX).type(scriptLang).id(request.id())
.refresh(true).version(request.version()).versionType(request.versionType());
client.delete(deleteRequest, listener);
}
public static String getScriptFromResponse(GetResponse responseFields) {
@ -602,6 +562,9 @@ public class ScriptService extends AbstractComponent {
@Override
public boolean equals(Object o) {
if (! (o instanceof CacheKey)) {
return false;
}
CacheKey other = (CacheKey) o;
return lang.equals(other.lang) && script.equals(other.script);
}