Skip to content

Commit

Permalink
Change Get Snapshottable Features endpoint to _features (elastic#69755
Browse files Browse the repository at this point in the history
)

The endpoint `_snapshottable_features` is long and implies incorrect
things about this API - it is used not just for snapshots, but also for
the upcoming reset API. Following discussions on the team, this commit
changes the endpoint to `_features` and removes the connection between
this API and snapshots, as snapshots are not the only use for the output
of this API.
  • Loading branch information
gwbrown authored Mar 2, 2021
1 parent be3fdd2 commit 2657496
Show file tree
Hide file tree
Showing 20 changed files with 184 additions and 100 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.client;

import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.snapshots.GetFeaturesRequest;
import org.elasticsearch.client.snapshots.GetFeaturesResponse;

import java.io.IOException;

import static java.util.Collections.emptySet;

/**
* A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Snapshot API.
* <p>
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/features-apis.html">Snapshot API on elastic.co</a>
*/
public class FeaturesClient {
private final RestHighLevelClient restHighLevelClient;

FeaturesClient(RestHighLevelClient restHighLevelClient) {
this.restHighLevelClient = restHighLevelClient;
}

/**
* Get a list of features which can be included in a snapshot as feature states.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/get-features-api.html"> Get Snapshottable
* Features API on elastic.co</a>
*
* @param getFeaturesRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public GetFeaturesResponse getFeatures(GetFeaturesRequest getFeaturesRequest, RequestOptions options)
throws IOException {
return restHighLevelClient.performRequestAndParseEntity(
getFeaturesRequest,
FeaturesRequestConverters::getFeatures,
options,
GetFeaturesResponse::parse,
emptySet()
);
}

/**
* Asynchronously get a list of features which can be included in a snapshot as feature states.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/get-features-api.html"> Get Snapshottable
* Features API on elastic.co</a>
*
* @param getFeaturesRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
* @return cancellable that may be used to cancel the request
*/
public Cancellable getFeaturesAsync(
GetFeaturesRequest getFeaturesRequest, RequestOptions options,
ActionListener<GetFeaturesResponse> listener) {
return restHighLevelClient.performRequestAsyncAndParseEntity(
getFeaturesRequest,
FeaturesRequestConverters::getFeatures,
options,
GetFeaturesResponse::parse,
listener,
emptySet()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.client;

import org.apache.http.client.methods.HttpGet;
import org.elasticsearch.client.snapshots.GetFeaturesRequest;

public class FeaturesRequestConverters {

private FeaturesRequestConverters() {}

static Request getFeatures(GetFeaturesRequest getFeaturesRequest) {
String endpoint = "/_features";
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
RequestConverters.Params parameters = new RequestConverters.Params();
parameters.withMasterTimeout(getFeaturesRequest.masterNodeTimeout());
request.addParameters(parameters.asMap());
return request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ public class RestHighLevelClient implements Closeable {
private final AsyncSearchClient asyncSearchClient = new AsyncSearchClient(this);
private final TextStructureClient textStructureClient = new TextStructureClient(this);
private final SearchableSnapshotsClient searchableSnapshotsClient = new SearchableSnapshotsClient(this);
private final FeaturesClient featuresClient = new FeaturesClient(this);

/**
* Creates a {@link RestHighLevelClient} given the low level {@link RestClientBuilder} that allows to build the
Expand Down Expand Up @@ -465,6 +466,16 @@ public SearchableSnapshotsClient searchableSnapshots() {
return searchableSnapshotsClient;
}

/**
* A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Searchable Snapshots APIs.
* <p>
* See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/searchable-snapshots-apis.html">Searchable Snapshots
* APIs on elastic.co</a> for more information.
*/
public FeaturesClient features() {
return featuresClient;
}

/**
* Provides methods for accessing the Elastic Licensed Migration APIs that
* are shipped with the default distribution of Elasticsearch. All of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.snapshots.GetSnapshottableFeaturesRequest;
import org.elasticsearch.client.snapshots.GetSnapshottableFeaturesResponse;

import java.io.IOException;

Expand Down Expand Up @@ -380,47 +378,4 @@ public Cancellable deleteAsync(DeleteSnapshotRequest deleteSnapshotRequest, Requ
SnapshotRequestConverters::deleteSnapshot, options,
AcknowledgedResponse::fromXContent, listener, emptySet());
}

/**
* Get a list of features which can be included in a snapshot as feature states.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/get-snapshottable-features-api.html"> Get Snapshottable
* Features API on elastic.co</a>
*
* @param getFeaturesRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @return the response
* @throws IOException in case there is a problem sending the request or parsing back the response
*/
public GetSnapshottableFeaturesResponse getFeatures(GetSnapshottableFeaturesRequest getFeaturesRequest, RequestOptions options)
throws IOException {
return restHighLevelClient.performRequestAndParseEntity(
getFeaturesRequest,
SnapshotRequestConverters::getSnapshottableFeatures,
options,
GetSnapshottableFeaturesResponse::parse,
emptySet()
);
}

/**
* Asynchronously get a list of features which can be included in a snapshot as feature states.
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/get-snapshottable-features-api.html"> Get Snapshottable
* Features API on elastic.co</a>
*
* @param getFeaturesRequest the request
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
* @param listener the listener to be notified upon request completion
* @return cancellable that may be used to cancel the request
*/
public Cancellable getFeaturesAsync(GetSnapshottableFeaturesRequest getFeaturesRequest, RequestOptions options,
ActionListener<GetSnapshottableFeaturesResponse> listener) {
return restHighLevelClient.performRequestAsyncAndParseEntity(
getFeaturesRequest,
SnapshotRequestConverters::getSnapshottableFeatures,
options,
GetSnapshottableFeaturesResponse::parse,
listener,
emptySet()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.elasticsearch.action.admin.cluster.snapshots.get.GetSnapshotsRequest;
import org.elasticsearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.client.snapshots.GetSnapshottableFeaturesRequest;
import org.elasticsearch.common.Strings;

import java.io.IOException;
Expand Down Expand Up @@ -191,13 +190,4 @@ static Request deleteSnapshot(DeleteSnapshotRequest deleteSnapshotRequest) {
request.addParameters(parameters.asMap());
return request;
}

static Request getSnapshottableFeatures(GetSnapshottableFeaturesRequest getSnapshottableFeaturesRequest) {
String endpoint = "/_snapshottable_features";
Request request = new Request(HttpGet.METHOD_NAME, endpoint);
RequestConverters.Params parameters = new RequestConverters.Params();
parameters.withMasterTimeout(getSnapshottableFeaturesRequest.masterNodeTimeout());
request.addParameters(parameters.asMap());
return request;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
/**
* A {@link TimedRequest} to get the list of features available to be included in snapshots in the cluster.
*/
public class GetSnapshottableFeaturesRequest extends TimedRequest {
public class GetFeaturesRequest extends TimedRequest {
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,38 @@
import java.util.List;
import java.util.Objects;

public class GetSnapshottableFeaturesResponse {
public class GetFeaturesResponse {

private final List<SnapshottableFeature> features;

private static final ParseField FEATURES = new ParseField("features");

@SuppressWarnings("unchecked")
private static final ConstructingObjectParser<GetSnapshottableFeaturesResponse, Void> PARSER = new ConstructingObjectParser<>(
"snapshottable_features_response", true, (a, ctx) -> new GetSnapshottableFeaturesResponse((List<SnapshottableFeature>) a[0])
private static final ConstructingObjectParser<GetFeaturesResponse, Void> PARSER = new ConstructingObjectParser<>(
"snapshottable_features_response", true, (a, ctx) -> new GetFeaturesResponse((List<SnapshottableFeature>) a[0])
);

static {
PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), SnapshottableFeature::parse, FEATURES);
}

public GetSnapshottableFeaturesResponse(List<SnapshottableFeature> features) {
public GetFeaturesResponse(List<SnapshottableFeature> features) {
this.features = features;
}

public List<SnapshottableFeature> getFeatures() {
return features;
}

public static GetSnapshottableFeaturesResponse parse(XContentParser parser) {
public static GetFeaturesResponse parse(XContentParser parser) {
return PARSER.apply(parser, null);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if ((o instanceof GetSnapshottableFeaturesResponse) == false) return false;
GetSnapshottableFeaturesResponse that = (GetSnapshottableFeaturesResponse) o;
if ((o instanceof GetFeaturesResponse) == false) return false;
GetFeaturesResponse that = (GetFeaturesResponse) o;
return getFeatures().equals(that.getFeatures());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.client;

import org.elasticsearch.client.snapshots.GetFeaturesRequest;
import org.elasticsearch.client.snapshots.GetFeaturesResponse;

import java.io.IOException;

import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.notNullValue;

public class FeaturesIT extends ESRestHighLevelClientTestCase {
public void testGetFeatures() throws IOException {
GetFeaturesRequest request = new GetFeaturesRequest();

GetFeaturesResponse response = execute(request,
highLevelClient().features()::getFeatures, highLevelClient().features()::getFeaturesAsync);

assertThat(response, notNullValue());
assertThat(response.getFeatures(), notNullValue());
assertThat(response.getFeatures().size(), greaterThan(1));
assertTrue(response.getFeatures().stream().anyMatch(feature -> "tasks".equals(feature.getFeatureName())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusRequest;
import org.elasticsearch.action.admin.cluster.snapshots.status.SnapshotsStatusResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.snapshots.GetSnapshottableFeaturesRequest;
import org.elasticsearch.client.snapshots.GetSnapshottableFeaturesResponse;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentType;
Expand All @@ -52,7 +50,6 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;

public class SnapshotIT extends ESRestHighLevelClientTestCase {

Expand Down Expand Up @@ -384,18 +381,6 @@ public void testCloneSnapshot() throws IOException {
assertTrue(response.isAcknowledged());
}

public void testGetFeatures() throws IOException {
GetSnapshottableFeaturesRequest request = new GetSnapshottableFeaturesRequest();

GetSnapshottableFeaturesResponse response = execute(request,
highLevelClient().snapshot()::getFeatures, highLevelClient().snapshot()::getFeaturesAsync);

assertThat(response, notNullValue());
assertThat(response.getFeatures(), notNullValue());
assertThat(response.getFeatures().size(), greaterThan(1));
assertTrue(response.getFeatures().stream().anyMatch(feature -> "tasks".equals(feature.getFeatureName())));
}

private static Map<String, Object> randomUserMetadata() {
if (randomBoolean()) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@
import static org.hamcrest.Matchers.in;
import static org.hamcrest.Matchers.is;

public class GetSnapshottableFeaturesResponseTests extends AbstractResponseTestCase<
org.elasticsearch.action.admin.cluster.snapshots.features.GetSnapshottableFeaturesResponse,
GetSnapshottableFeaturesResponse> {
public class GetFeaturesResponseTests extends AbstractResponseTestCase<
org.elasticsearch.action.admin.cluster.snapshots.features.GetSnapshottableFeaturesResponse, GetFeaturesResponse> {

@Override
protected org.elasticsearch.action.admin.cluster.snapshots.features.GetSnapshottableFeaturesResponse createServerTestInstance(
Expand All @@ -41,14 +40,14 @@ protected org.elasticsearch.action.admin.cluster.snapshots.features.GetSnapshott
}

@Override
protected GetSnapshottableFeaturesResponse doParseToClientInstance(XContentParser parser) throws IOException {
return GetSnapshottableFeaturesResponse.parse(parser);
protected GetFeaturesResponse doParseToClientInstance(XContentParser parser) throws IOException {
return GetFeaturesResponse.parse(parser);
}

@Override
protected void assertInstances(
org.elasticsearch.action.admin.cluster.snapshots.features.GetSnapshottableFeaturesResponse serverTestInstance,
GetSnapshottableFeaturesResponse clientInstance
GetFeaturesResponse clientInstance
) {
assertNotNull(serverTestInstance.getSnapshottableFeatures());
assertNotNull(serverTestInstance.getSnapshottableFeatures());
Expand Down
11 changes: 11 additions & 0 deletions docs/reference/features/apis/features-apis.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[[features-apis]]
== Features APIs

You can use the following APIs to introspect and manage Features provided
by Elasticsearch and Elasticsearch plugins.

[discrete]
=== Features APIs
* <<get-features-api,Get Features API>>

include::get-features-api.asciidoc[]
Loading

0 comments on commit 2657496

Please sign in to comment.