Skip to content

Commit e1012ef

Browse files
authored
Use optype CREATE for single auto-id index requests (#47353)
Changes auto-id index requests to use optype CREATE, making it compliant with our docs. This will also make these auto-id index requests compatible with the new "create-doc" index privilege (which is based on the optype), the default optype is changed to create, just as it is already documented.
1 parent c585efe commit e1012ef

File tree

8 files changed

+93
-19
lines changed

8 files changed

+93
-19
lines changed

rest-api-spec/src/main/resources/rest-api-spec/api/create.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@
8383
"options":[
8484
"internal",
8585
"external",
86-
"external_gte",
87-
"force"
86+
"external_gte"
8887
],
8988
"description":"Specific version type"
9089
},

rest-api-spec/src/main/resources/rest-api-spec/api/index.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,7 @@
9696
"index",
9797
"create"
9898
],
99-
"default":"index",
100-
"description":"Explicit operation type"
99+
"description":"Explicit operation type. Defaults to `index` for requests with an explicit document ID, and to `create`for requests without an explicit document ID"
101100
},
102101
"refresh":{
103102
"type":"enum",
@@ -125,8 +124,7 @@
125124
"options":[
126125
"internal",
127126
"external",
128-
"external_gte",
129-
"force"
127+
"external_gte"
130128
],
131129
"description":"Specific version type"
132130
},

server/src/main/java/org/elasticsearch/action/ActionModule.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@
203203
import org.elasticsearch.client.node.NodeClient;
204204
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
205205
import org.elasticsearch.cluster.node.DiscoveryNodes;
206+
import org.elasticsearch.cluster.service.ClusterService;
206207
import org.elasticsearch.common.NamedRegistry;
207208
import org.elasticsearch.common.inject.AbstractModule;
208209
import org.elasticsearch.common.inject.TypeLiteral;
@@ -369,17 +370,19 @@ public class ActionModule extends AbstractModule {
369370
private final RestController restController;
370371
private final RequestValidators<PutMappingRequest> mappingRequestValidators;
371372
private final RequestValidators<IndicesAliasesRequest> indicesAliasesRequestRequestValidators;
373+
private final ClusterService clusterService;
372374

373375
public ActionModule(Settings settings, IndexNameExpressionResolver indexNameExpressionResolver,
374376
IndexScopedSettings indexScopedSettings, ClusterSettings clusterSettings, SettingsFilter settingsFilter,
375377
ThreadPool threadPool, List<ActionPlugin> actionPlugins, NodeClient nodeClient,
376-
CircuitBreakerService circuitBreakerService, UsageService usageService) {
378+
CircuitBreakerService circuitBreakerService, UsageService usageService, ClusterService clusterService) {
377379
this.settings = settings;
378380
this.indexNameExpressionResolver = indexNameExpressionResolver;
379381
this.indexScopedSettings = indexScopedSettings;
380382
this.clusterSettings = clusterSettings;
381383
this.settingsFilter = settingsFilter;
382384
this.actionPlugins = actionPlugins;
385+
this.clusterService = clusterService;
383386
actions = setupActions(actionPlugins);
384387
actionFilters = setupActionFilters(actionPlugins);
385388
autoCreateIndex = new AutoCreateIndex(settings, clusterSettings, indexNameExpressionResolver);
@@ -629,7 +632,7 @@ public void initRestHandlers(Supplier<DiscoveryNodes> nodesInCluster) {
629632
registerHandler.accept(new RestUpgradeStatusAction(restController));
630633
registerHandler.accept(new RestClearIndicesCacheAction(restController));
631634

632-
registerHandler.accept(new RestIndexAction(restController));
635+
registerHandler.accept(new RestIndexAction(restController, clusterService));
633636
registerHandler.accept(new RestGetAction(restController));
634637
registerHandler.accept(new RestGetSourceAction(restController));
635638
registerHandler.accept(new RestMultiGetAction(settings, restController));

server/src/main/java/org/elasticsearch/node/Node.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ protected Node(
450450

451451
ActionModule actionModule = new ActionModule(settings, clusterModule.getIndexNameExpressionResolver(),
452452
settingsModule.getIndexScopedSettings(), settingsModule.getClusterSettings(), settingsModule.getSettingsFilter(),
453-
threadPool, pluginsService.filterPlugins(ActionPlugin.class), client, circuitBreakerService, usageService);
453+
threadPool, pluginsService.filterPlugins(ActionPlugin.class), client, circuitBreakerService, usageService, clusterService);
454454
modules.add(actionModule);
455455

456456
final RestController restController = actionModule.getRestController();

server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
package org.elasticsearch.rest.action.document;
2121

2222
import org.apache.logging.log4j.LogManager;
23+
import org.elasticsearch.Version;
2324
import org.elasticsearch.action.index.IndexRequest;
2425
import org.elasticsearch.action.support.ActiveShardCount;
2526
import org.elasticsearch.client.node.NodeClient;
27+
import org.elasticsearch.cluster.service.ClusterService;
2628
import org.elasticsearch.common.logging.DeprecationLogger;
2729
import org.elasticsearch.index.VersionType;
2830
import org.elasticsearch.index.mapper.MapperService;
@@ -45,8 +47,13 @@ public class RestIndexAction extends BaseRestHandler {
4547
"index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, " +
4648
"or /{index}/_create/{id}).";
4749

48-
public RestIndexAction(RestController controller) {
49-
controller.registerHandler(POST, "/{index}/_doc", this); // auto id creation
50+
private final ClusterService clusterService;
51+
52+
public RestIndexAction(RestController controller, ClusterService clusterService) {
53+
this.clusterService = clusterService;
54+
55+
AutoIdHandler autoIdHandler = new AutoIdHandler();
56+
controller.registerHandler(POST, "/{index}/_doc", autoIdHandler); // auto id creation
5057
controller.registerHandler(PUT, "/{index}/_doc/{id}", this);
5158
controller.registerHandler(POST, "/{index}/_doc/{id}", this);
5259

@@ -55,7 +62,7 @@ public RestIndexAction(RestController controller) {
5562
controller.registerHandler(POST, "/{index}/_create/{id}/", createHandler);
5663

5764
// Deprecated typed endpoints.
58-
controller.registerHandler(POST, "/{index}/{type}", this); // auto id creation
65+
controller.registerHandler(POST, "/{index}/{type}", autoIdHandler); // auto id creation
5966
controller.registerHandler(PUT, "/{index}/{type}/{id}", this);
6067
controller.registerHandler(POST, "/{index}/{type}/{id}", this);
6168
controller.registerHandler(PUT, "/{index}/{type}/{id}/_create", createHandler);
@@ -90,6 +97,26 @@ void validateOpType(String opType) {
9097
}
9198
}
9299

100+
final class AutoIdHandler extends BaseRestHandler {
101+
protected AutoIdHandler() {
102+
}
103+
104+
@Override
105+
public String getName() {
106+
return "document_create_action";
107+
}
108+
109+
@Override
110+
public RestChannelConsumer prepareRequest(RestRequest request, final NodeClient client) throws IOException {
111+
assert request.params().get("id") == null : "non-null id: " + request.params().get("id");
112+
if (request.params().get("op_type") == null && clusterService.state().nodes().getMinNodeVersion().onOrAfter(Version.CURRENT)) {
113+
// default to op_type create
114+
request.params().put("op_type", "create");
115+
}
116+
return RestIndexAction.this.prepareRequest(request, client);
117+
}
118+
}
119+
93120
@Override
94121
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
95122
IndexRequest indexRequest;

server/src/test/java/org/elasticsearch/action/ActionModuleTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public void testSetupRestHandlerContainsKnownBuiltin() {
109109
UsageService usageService = new UsageService();
110110
ActionModule actionModule = new ActionModule(settings.getSettings(), new IndexNameExpressionResolver(),
111111
settings.getIndexScopedSettings(), settings.getClusterSettings(), settings.getSettingsFilter(), null, emptyList(), null,
112-
null, usageService);
112+
null, usageService, null);
113113
actionModule.initRestHandlers(null);
114114
// At this point the easiest way to confirm that a handler is loaded is to try to register another one on top of it and to fail
115115
Exception e = expectThrows(IllegalArgumentException.class, () ->
@@ -132,7 +132,7 @@ public List<RestHandler> getRestHandlers(Settings settings, RestController restC
132132
UsageService usageService = new UsageService();
133133
ActionModule actionModule = new ActionModule(settings.getSettings(), new IndexNameExpressionResolver(),
134134
settings.getIndexScopedSettings(), settings.getClusterSettings(), settings.getSettingsFilter(), threadPool,
135-
singletonList(dupsMainAction), null, null, usageService);
135+
singletonList(dupsMainAction), null, null, usageService, null);
136136
Exception e = expectThrows(IllegalArgumentException.class, () -> actionModule.initRestHandlers(null));
137137
assertThat(e.getMessage(), startsWith("Cannot replace existing handler for [/] for method: GET"));
138138
} finally {
@@ -164,7 +164,7 @@ public List<RestHandler> getRestHandlers(Settings settings, RestController restC
164164
UsageService usageService = new UsageService();
165165
ActionModule actionModule = new ActionModule(settings.getSettings(), new IndexNameExpressionResolver(),
166166
settings.getIndexScopedSettings(), settings.getClusterSettings(), settings.getSettingsFilter(), threadPool,
167-
singletonList(registersFakeHandler), null, null, usageService);
167+
singletonList(registersFakeHandler), null, null, usageService, null);
168168
actionModule.initRestHandlers(null);
169169
// At this point the easiest way to confirm that a handler is loaded is to try to register another one on top of it and to fail
170170
Exception e = expectThrows(IllegalArgumentException.class, () ->

server/src/test/java/org/elasticsearch/rest/action/document/RestIndexActionTests.java

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,41 @@
2020
package org.elasticsearch.rest.action.document;
2121

2222
import org.elasticsearch.Version;
23-
import org.elasticsearch.common.settings.Settings;
23+
import org.elasticsearch.action.ActionListener;
24+
import org.elasticsearch.action.DocWriteRequest;
25+
import org.elasticsearch.action.index.IndexRequest;
26+
import org.elasticsearch.cluster.ClusterName;
27+
import org.elasticsearch.cluster.ClusterState;
28+
import org.elasticsearch.cluster.node.DiscoveryNode;
29+
import org.elasticsearch.cluster.node.DiscoveryNodes;
30+
import org.elasticsearch.cluster.service.ClusterService;
31+
import org.elasticsearch.common.bytes.BytesArray;
32+
import org.elasticsearch.common.xcontent.XContentType;
2433
import org.elasticsearch.rest.RestRequest;
34+
import org.elasticsearch.test.VersionUtils;
2535
import org.elasticsearch.test.rest.FakeRestRequest;
2636
import org.elasticsearch.test.rest.RestActionTestCase;
2737
import org.junit.Before;
38+
import org.mockito.ArgumentCaptor;
39+
40+
import java.util.concurrent.atomic.AtomicReference;
2841

2942
import static org.hamcrest.Matchers.equalTo;
43+
import static org.mockito.Matchers.any;
44+
import static org.mockito.Mockito.mock;
45+
import static org.mockito.Mockito.verify;
46+
import static org.mockito.Mockito.when;
3047

3148
public class RestIndexActionTests extends RestActionTestCase {
3249

3350
private RestIndexAction action;
51+
private final AtomicReference<ClusterState> clusterStateSupplier = new AtomicReference();
3452

3553
@Before
3654
public void setUpAction() {
37-
action = new RestIndexAction(controller());
55+
ClusterService clusterService = mock(ClusterService.class);
56+
when(clusterService.state()).thenAnswer(invocationOnMock -> clusterStateSupplier.get());
57+
action = new RestIndexAction(controller(), clusterService);
3858
}
3959

4060
public void testTypeInPath() {
@@ -68,7 +88,6 @@ public void testCreateWithTypeInPath() {
6888
}
6989

7090
public void testCreateOpTypeValidation() {
71-
Settings settings = settings(Version.CURRENT).build();
7291
RestIndexAction.CreateHandler create = action.new CreateHandler();
7392

7493
String opType = randomFrom("CREATE", null);
@@ -78,4 +97,30 @@ public void testCreateOpTypeValidation() {
7897
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> create.validateOpType(illegalOpType));
7998
assertThat(e.getMessage(), equalTo("opType must be 'create', found: [" + illegalOpType + "]"));
8099
}
100+
101+
public void testAutoIdDefaultsToOptypeCreate() {
102+
checkAutoIdOpType(Version.CURRENT, DocWriteRequest.OpType.CREATE);
103+
}
104+
105+
public void testAutoIdDefaultsToOptypeIndexForOlderVersions() {
106+
checkAutoIdOpType(VersionUtils.randomVersionBetween(random(), null,
107+
VersionUtils.getPreviousVersion(Version.CURRENT)), DocWriteRequest.OpType.INDEX);
108+
}
109+
110+
private void checkAutoIdOpType(Version minClusterVersion, DocWriteRequest.OpType expectedOpType) {
111+
RestRequest autoIdRequest = new FakeRestRequest.Builder(xContentRegistry())
112+
.withMethod(RestRequest.Method.POST)
113+
.withPath("/some_index/_doc")
114+
.withContent(new BytesArray("{}"), XContentType.JSON)
115+
.build();
116+
clusterStateSupplier.set(ClusterState.builder(ClusterName.DEFAULT)
117+
.nodes(DiscoveryNodes.builder()
118+
.add(new DiscoveryNode("test", buildNewFakeTransportAddress(), minClusterVersion))
119+
.build()).build());
120+
dispatchRequest(autoIdRequest);
121+
ArgumentCaptor<IndexRequest> argumentCaptor = ArgumentCaptor.forClass(IndexRequest.class);
122+
verify(nodeClient).index(argumentCaptor.capture(), any(ActionListener.class));
123+
IndexRequest indexRequest = argumentCaptor.getValue();
124+
assertEquals(expectedOpType, indexRequest.opType());
125+
}
81126
}

test/framework/src/main/java/org/elasticsearch/test/rest/RestActionTestCase.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@
3939
*/
4040
public abstract class RestActionTestCase extends ESTestCase {
4141
private RestController controller;
42+
protected NodeClient nodeClient;
4243

4344
@Before
4445
public void setUpController() {
46+
nodeClient = mock(NodeClient.class);
4547
controller = new RestController(Collections.emptySet(), null,
46-
mock(NodeClient.class),
48+
nodeClient,
4749
new NoneCircuitBreakerService(),
4850
new UsageService());
4951
}

0 commit comments

Comments
 (0)