Skip to content

[REST Compatible API] Typed endpoints for Index and Get APIs #69131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 52 commits into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e13468b
refactor
jakelandis Feb 11, 2021
0fbd2f9
Merge branch 'master' into refactor_inject_headers
elasticmachine Feb 12, 2021
3cb7200
start of support for allowed_warnings
jakelandis Feb 12, 2021
526cdd3
start of injectWarning
jakelandis Feb 12, 2021
19141dd
start remove warning
jakelandis Feb 15, 2021
bc7b2aa
review changes
jakelandis Feb 15, 2021
a6c2433
Merge branch 'refactor_inject_headers' into warnings_transform
jakelandis Feb 15, 2021
4cea425
clean up match tests
jakelandis Feb 15, 2021
757200e
more refactoring and tests passing
jakelandis Feb 15, 2021
2178081
Merge remote-tracking branch 'upstream/master' into warnings_transform
jakelandis Feb 15, 2021
4b0629e
more refactor of tests
jakelandis Feb 15, 2021
3884c3d
add missing file
jakelandis Feb 15, 2021
106f226
spotless
jakelandis Feb 15, 2021
511a3db
Merge branch 'another_refactor' into warnings_transform
jakelandis Feb 15, 2021
d9ba6c0
checkstyle
jakelandis Feb 15, 2021
b47d98b
Merge branch 'another_refactor' into warnings_transform
jakelandis Feb 15, 2021
857b2c6
review changes
jakelandis Feb 15, 2021
775565e
Merge branch 'another_refactor' into warnings_transform
jakelandis Feb 15, 2021
98ee1f7
more tests
jakelandis Feb 15, 2021
cdd8ad5
finish up testing
jakelandis Feb 15, 2021
cd2d042
Merge remote-tracking branch 'upstream/master' into warnings_transform
jakelandis Feb 15, 2021
b51b72f
spotless
jakelandis Feb 15, 2021
230d0ae
Merge remote-tracking branch 'upstream/master' into warnings_transform
jakelandis Feb 16, 2021
5de4a30
fix merge
jakelandis Feb 16, 2021
b60bf0c
add @Internal for gradle tracked objects that look like getters
jakelandis Feb 16, 2021
7390a91
index and get and testing
pgomulka Feb 17, 2021
c83fdc1
import
pgomulka Feb 17, 2021
ec3cd22
move blacklist
pgomulka Feb 17, 2021
90ab29a
Merge remote-tracking branch 'upstream/master' into compat/index_get
pgomulka Feb 17, 2021
67c499a
Merge remote-tracking branch 'upstream/master' into compat/index_get
pgomulka Feb 18, 2021
448f793
Merge branch 'warnings_transform' of git://github.com/jakelandis/elas…
pgomulka Feb 18, 2021
a5024f5
Merge branch 'jakelandis-warnings_transform' into compat/index_get
pgomulka Feb 18, 2021
b99c751
testing not working
pgomulka Feb 18, 2021
bb5db62
Merge remote-tracking branch 'upstream/master' into compat/index_get
pgomulka Feb 23, 2021
002ad68
unmute all
pgomulka Feb 23, 2021
0c61f18
unit tests
pgomulka Feb 24, 2021
d6d729c
cleanup
pgomulka Feb 25, 2021
f0c48ac
Merge branch 'master' into compat/index_get
pgomulka Mar 9, 2021
0155e2f
cleanup tests
pgomulka Mar 9, 2021
8566123
remove assertion skip
pgomulka Mar 9, 2021
80dd793
skip section
pgomulka Mar 9, 2021
af12ce3
surgical appproach
pgomulka Mar 16, 2021
595f59c
Merge remote-tracking branch 'upstream/master' into compat/index_get
pgomulka Mar 16, 2021
a699329
remove empty space
pgomulka Mar 16, 2021
e0ba336
Merge remote-tracking branch 'upstream/master' into compat/index_get
pgomulka Mar 17, 2021
8dcea4c
enable get tests and use allowed warnings
pgomulka Mar 17, 2021
ee88fab
make classes final and remove empty line
pgomulka Mar 18, 2021
2cb33c2
Merge branch 'master' into compat/index_get
elasticmachine Mar 18, 2021
5899451
use Mapping service type fields
pgomulka Mar 22, 2021
3680b39
Merge branch 'compat/index_get' of github.com:pgomulka/elasticsearch …
pgomulka Mar 22, 2021
7993dd1
empty line
pgomulka Mar 22, 2021
8e1aaf9
Merge branch 'master' into compat/index_get
elasticmachine Mar 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 10 additions & 30 deletions rest-api-spec/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -143,22 +143,9 @@ tasks.named("yamlRestCompatTest").configure {
'explain/40_mix_typeless_typeful/Explain with typeless API on an index that has types',
'field_caps/10_basic/Get date_nanos field caps',
'field_caps/30_filter/Field caps with index filter',
'get/100_mix_typeless_typeful/GET with typeless API on an index that has types',
'get/10_basic/Basic',
'get/11_basic_with_types/Basic',
'get/15_default_values/Default values',
'get/16_default_values_with_types/Default values',
'get/20_stored_fields/Stored fields',
'get/21_stored_fields_with_types/Stored fields',
'get/41_routing_with_types/Routing',
'get/50_with_headers/REST test with headers',
'get/51_with_headers_with_types/REST test with headers',
'get/61_realtime_refresh_with_types/Realtime Refresh',
'get/70_source_filtering/Source filtering',
'get/71_source_filtering_with_types/Source filtering',
'get/81_missing_with_types/Missing document with catch',
'get/81_missing_with_types/Missing document with ignore',
'get/91_versions_with_types/Versions',
'get/100_mix_typeless_typeful/GET with typeless API on an index that has types',// failing due to include_type_name #48632
'get/21_stored_fields_with_types/Stored fields', // failing due to include_type_name #48632
'get/71_source_filtering_with_types/Source filtering',// failing due to include_type_name #48632
'get_source/11_basic_with_types/Basic with types',
'get_source/16_default_values_with_types/Default values',
'get_source/41_routing_with_types/Routing',
Expand All @@ -168,20 +155,8 @@ tasks.named("yamlRestCompatTest").configure {
'get_source/81_missing_with_types/Missing document with ignore',
'get_source/86_source_missing_with_types/Missing document source with catch',
'get_source/86_source_missing_with_types/Missing document source with ignore',
'index/10_with_id/Index with ID',
'index/11_with_id_with_types/Index with ID',
'index/13_result_with_types/Index result field',
'index/15_without_id/Index without ID',
'index/16_without_id_with_types/Index without ID',
'index/21_optype_with_types/Optype',
'index/37_external_version_with_types/External version',
'index/38_external_gte_version_with_types/External GTE version',
'index/41_routing_with_types/Routing',
'index/61_refresh_with_types/Refresh',
'index/61_refresh_with_types/When refresh url parameter is an empty string that means "refresh immediately"',
'index/61_refresh_with_types/refresh=wait_for waits until changes are visible in search',
'index/70_mix_typeless_typeful/Index call that introduces new field mappings',
'index/70_mix_typeless_typeful/Index with typeless API on an index that has types',
'index/70_mix_typeless_typeful/Index call that introduces new field mappings', // failing due to include_type_name #48632
'index/70_mix_typeless_typeful/Index with typeless API on an index that has types', // failing due to include_type_name #48632
'indices.clone/10_basic/Clone index via API',
'indices.create/10_basic/Create index with explicit _doc type',
'indices.create/10_basic/Create index without soft deletes',
Expand Down Expand Up @@ -376,3 +351,8 @@ tasks.named("yamlRestCompatTest").configure {
'update/90_mix_typeless_typeful/Update with typeless API on an index that has types'
].join(',')
}

tasks.named("transformV7RestTests").configure({ task ->
task.replaceMatch("_type", "_doc")
task.addAllowedWarningRegex("\\[types removal\\].*")
})
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.RestApiVersion;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
Expand Down Expand Up @@ -293,6 +294,9 @@ private void writeWithoutShardId(StreamOutput out) throws IOException {
@Override
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (builder.getRestApiVersion() == RestApiVersion.V_7) {
builder.field(MapperService.TYPE_FIELD_NAME, MapperService.SINGLE_MAPPING_NAME);
}
innerToXContent(builder, params);
builder.endObject();
return builder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.Version;
import org.elasticsearch.common.RestApiVersion;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.compress.CompressorFactory;
Expand Down Expand Up @@ -282,6 +283,9 @@ public XContentBuilder toXContentEmbedded(XContentBuilder builder, Params params
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.field(_INDEX, index);
if (builder.getRestApiVersion() == RestApiVersion.V_7) {
builder.field(MapperService.TYPE_FIELD_NAME, MapperService.SINGLE_MAPPING_NAME);
}
builder.field(_ID, id);
if (isExists()) {
if (version != -1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public enum MergeReason {
}

public static final String SINGLE_MAPPING_NAME = "_doc";
public static final String TYPE_FIELD_NAME = "_type";
public static final Setting<Long> INDEX_MAPPING_NESTED_FIELDS_LIMIT_SETTING =
Setting.longSetting("index.mapping.nested_fields.limit", 50L, 0, Property.Dynamic, Property.IndexScope);
// maximum allowed number of nested json objects across all fields in a single document
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.RestApiVersion;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.rest.BaseRestHandler;
Expand All @@ -29,6 +30,8 @@
import static org.elasticsearch.rest.RestStatus.OK;

public class RestGetAction extends BaseRestHandler {
static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Specifying types in "
+ "document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead.";

@Override
public String getName() {
Expand All @@ -39,11 +42,21 @@ public String getName() {
public List<Route> routes() {
return List.of(
new Route(GET, "/{index}/_doc/{id}"),
new Route(HEAD, "/{index}/_doc/{id}"));
new Route(HEAD, "/{index}/_doc/{id}"),
Route.builder(GET, "/{index}/{type}/{id}")
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
.build(),
Route.builder(HEAD, "/{index}/{type}/{id}")
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
.build());
}

@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
if (request.getRestApiVersion() == RestApiVersion.V_7) {
request.param("type"); // consume and ignore the type
}

GetRequest getRequest = new GetRequest(request.param("index"), request.param("id"));

getRequest.refresh(request.paramAsBoolean("refresh", getRequest.refresh()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.action.support.ActiveShardCount;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.cluster.node.DiscoveryNodes;
import org.elasticsearch.common.RestApiVersion;
import org.elasticsearch.index.VersionType;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.RestRequest;
Expand All @@ -29,12 +30,21 @@
import static org.elasticsearch.rest.RestRequest.Method.PUT;

public class RestIndexAction extends BaseRestHandler {
static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Specifying types in document "
+ "index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, "
+ "or /{index}/_create/{id}).";

@Override
public List<Route> routes() {
return List.of(
new Route(POST, "/{index}/_doc/{id}"),
new Route(PUT, "/{index}/_doc/{id}"));
new Route(PUT, "/{index}/_doc/{id}"),
Route.builder(POST, "/{index}/{type}/{id}")
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
.build(),
Route.builder(PUT, "/{index}/{type}/{id}")
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
.build());
}

@Override
Expand All @@ -53,7 +63,13 @@ public String getName() {
public List<Route> routes() {
return List.of(
new Route(POST, "/{index}/_create/{id}"),
new Route(PUT, "/{index}/_create/{id}"));
new Route(PUT, "/{index}/_create/{id}"),
Route.builder(POST, "/{index}/{type}/{id}/_create")
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
.build(),
Route.builder(PUT, "/{index}/{type}/{id}/_create")
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
.build());
}

@Override
Expand Down Expand Up @@ -85,7 +101,11 @@ public String getName() {

@Override
public List<Route> routes() {
return List.of(new Route(POST, "/{index}/_doc"));
return List.of(
new Route(POST, "/{index}/_doc"),
Route.builder(POST, "/{index}/{type}")
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
.build());
}

@Override
Expand All @@ -101,6 +121,10 @@ public RestChannelConsumer prepareRequest(RestRequest request, final NodeClient

@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
if (request.getRestApiVersion() == RestApiVersion.V_7) {
request.param("type"); // consume and ignore the type
}

IndexRequest indexRequest = new IndexRequest(request.param("index"));
indexRequest.id(request.param("id"));
indexRequest.routing(request.param("routing"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@

import org.elasticsearch.action.DocWriteResponse.Result;
import org.elasticsearch.action.support.replication.ReplicationResponse.ShardInfo;
import org.elasticsearch.common.RestApiVersion;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.seqno.SequenceNumbers;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.test.ESTestCase;
Expand Down Expand Up @@ -96,4 +98,35 @@ public void testToXContentDoesntIncludeForcedRefreshUnlessForced() throws IOExce
}
}
}

public void testTypeWhenCompatible() throws IOException {
DocWriteResponse response = new DocWriteResponse(
new ShardId("index", "uuid", 0),
"id",
SequenceNumbers.UNASSIGNED_SEQ_NO,
17,
0,
DocWriteResponse.Result.CREATED
) {
// DocWriteResponse is abstract so we have to sneak a subclass in here to test it.
};
try (XContentBuilder builder = JsonXContent.contentBuilder()) {
builder.withCompatibleVersion(RestApiVersion.V_7);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit uneasy that we can just change the compatible version on the XContentBuilder at any time, as that means that a consumer could change it accidentally; I think I'd prefer that it's a parameter passed to contentBuilder(). But that's not really part of this issue, so we can look at it separately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, these should be fixed, but also feel that this should be in a separate PR. The change might require touching many files and would be unrelated to the index/get api.
I will follow up in a separate PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also take a look at https://github.com/elastic/elasticsearch/pull/64423/files#r518884033
we try to prevent accidental change of the version with the assert inside withCompatibleVersion

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to making it immutable or explicitly 'set once', that would clearly indicate the intent that it should never be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored the XContentBuilder to have this field final and set in a constructor #70878

response.toXContent(builder, ToXContent.EMPTY_PARAMS);

try (XContentParser parser = createParser(JsonXContent.jsonXContent, BytesReference.bytes(builder))) {
assertThat(parser.map(), hasEntry(MapperService.TYPE_FIELD_NAME, MapperService.SINGLE_MAPPING_NAME));
}
}

try (XContentBuilder builder = JsonXContent.contentBuilder()) {
builder.withCompatibleVersion(RestApiVersion.V_8);
response.toXContent(builder, ToXContent.EMPTY_PARAMS);

try (XContentParser parser = createParser(JsonXContent.jsonXContent, BytesReference.bytes(builder))) {
assertThat(parser.map(), not(hasKey(MapperService.TYPE_FIELD_NAME)));
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@

package org.elasticsearch.index.get;

import org.elasticsearch.common.RestApiVersion;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.document.DocumentField;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.mapper.IdFieldMapper;
import org.elasticsearch.index.mapper.IndexFieldMapper;
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
Expand Down Expand Up @@ -86,6 +89,34 @@ public void testToXContent() throws IOException {
}
}

public void testToCompatibleXContent() throws IOException {
{
GetResult getResult = new GetResult("index", "id", 0, 1, 1, true, new BytesArray("{ \"field1\" : " +
"\"value1\", \"field2\":\"value2\"}"), singletonMap("field1", new DocumentField("field1",
singletonList("value1"))), singletonMap("field1", new DocumentField("metafield",
singletonList("metavalue"))));

try (XContentBuilder builder = JsonXContent.contentBuilder()) {
builder.withCompatibleVersion(RestApiVersion.V_7);
getResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
String output = Strings.toString(builder);
assertEquals("{\"_index\":\"index\",\"_type\":\"_doc\",\"_id\":\"id\",\"_version\":1,\"_seq_no\":0,\"_primary_term\":1," +
"\"metafield\":\"metavalue\",\"found\":true,\"_source\":{ \"field1\" : \"value1\", \"field2\":\"value2\"}," +
"\"fields\":{\"field1\":[\"value1\"]}}", output);
}
}
{
GetResult getResult = new GetResult("index", "id", UNASSIGNED_SEQ_NO, 0, 1, false, null, null, null);

try (XContentBuilder builder = JsonXContent.contentBuilder()) {
builder.withCompatibleVersion(RestApiVersion.V_7);
getResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
String output = Strings.toString(builder);
assertEquals("{\"_index\":\"index\",\"_type\":\"_doc\",\"_id\":\"id\",\"found\":false}", output);
}
}
}

public void testToAndFromXContentEmbedded() throws Exception {
XContentType xContentType = randomFrom(XContentType.values());
Tuple<GetResult, GetResult> tuple = randomGetResult(xContentType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

package org.elasticsearch.rest;

import org.elasticsearch.Version;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.common.RestApiVersion;
import org.elasticsearch.common.breaker.CircuitBreaker;
Expand Down Expand Up @@ -630,7 +629,7 @@ public void testDispatchCompatibleHandler() {

RestController restController = new RestController(Collections.emptySet(), null, client, circuitBreakerService, usageService);

final byte version = RestApiVersion.minimumSupported().major;
final RestApiVersion version = RestApiVersion.minimumSupported();

final String mediaType = randomCompatibleMediaType(version);
FakeRestRequest fakeRestRequest = requestWithContent(mediaType);
Expand All @@ -654,7 +653,7 @@ public void testDispatchCompatibleRequestToNewlyAddedHandler() {

RestController restController = new RestController(Collections.emptySet(), null, client, circuitBreakerService, usageService);

final byte version = RestApiVersion.minimumSupported().major;
final RestApiVersion version = RestApiVersion.minimumSupported();

final String mediaType = randomCompatibleMediaType(version);
FakeRestRequest fakeRestRequest = requestWithContent(mediaType);
Expand Down Expand Up @@ -690,7 +689,7 @@ private FakeRestRequest requestWithContent(String mediaType) {
public void testCurrentVersionVNDMediaTypeIsNotUsingCompatibility() {
RestController restController = new RestController(Collections.emptySet(), null, client, circuitBreakerService, usageService);

final byte version = Version.CURRENT.major;
final RestApiVersion version = RestApiVersion.current();

final String mediaType = randomCompatibleMediaType(version);
FakeRestRequest fakeRestRequest = requestWithContent(mediaType);
Expand Down
Loading