From 18aa1c1381b76769ed5ebaaf4b6840aa26301d75 Mon Sep 17 00:00:00 2001 From: Andy Bristol Date: Wed, 17 Oct 2018 08:02:04 -0700 Subject: [PATCH] add start trial API to HLRC (#33406) Introduces client-specific request and response classes that do not depend on the server The `type` parameter is named `licenseType` in the response class to be more descriptive. The parts that make up the acknowledged-required response are given slightly different names than their server-response types to be consistent with the naming in the put license API Tests do not cover all cases because the integ test cluster starts up with a trial license - this will be addressed in a future commit --- .../elasticsearch/client/LicenseClient.java | 27 +++ .../client/LicenseRequestConverters.java | 26 ++- .../client/RequestConverters.java | 2 +- .../client/license/StartTrialRequest.java | 50 +++++ .../client/license/StartTrialResponse.java | 188 ++++++++++++++++++ .../{LicensingIT.java => LicenseIT.java} | 60 +++++- .../client/LicenseRequestConvertersTests.java | 29 ++- .../LicensingDocumentationIT.java | 61 +++++- .../high-level/licensing/start-trial.asciidoc | 70 +++++++ .../high-level/supported-apis.asciidoc | 2 + 10 files changed, 491 insertions(+), 24 deletions(-) create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/license/StartTrialRequest.java create mode 100644 client/rest-high-level/src/main/java/org/elasticsearch/client/license/StartTrialResponse.java rename client/rest-high-level/src/test/java/org/elasticsearch/client/{LicensingIT.java => LicenseIT.java} (65%) create mode 100644 docs/java-rest/high-level/licensing/start-trial.asciidoc diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseClient.java index a5ce0a3cb27..1d337af10f8 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseClient.java @@ -22,6 +22,8 @@ package org.elasticsearch.client; import org.apache.http.HttpEntity; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.support.master.AcknowledgedResponse; +import org.elasticsearch.client.license.StartTrialRequest; +import org.elasticsearch.client.license.StartTrialResponse; import org.elasticsearch.client.license.StartBasicRequest; import org.elasticsearch.client.license.StartBasicResponse; import org.elasticsearch.common.Strings; @@ -44,6 +46,7 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; /** * A wrapper for the {@link RestHighLevelClient} that provides methods for @@ -123,6 +126,30 @@ public final class LicenseClient { AcknowledgedResponse::fromXContent, listener, emptySet()); } + /** + * Starts a trial license on the cluster. + * @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 StartTrialResponse startTrial(StartTrialRequest request, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, LicenseRequestConverters::startTrial, options, + StartTrialResponse::fromXContent, singleton(403)); + } + + /** + * Asynchronously starts a trial license on the cluster. + * @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 + */ + public void startTrialAsync(StartTrialRequest request, + RequestOptions options, + ActionListener listener) { + + restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::startTrial, options, + StartTrialResponse::fromXContent, listener, singleton(403)); + } + /** * Initiates an indefinite basic license. * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseRequestConverters.java index 159ab569034..ffa7227ef17 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseRequestConverters.java @@ -23,6 +23,7 @@ import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; +import org.elasticsearch.client.license.StartTrialRequest; import org.elasticsearch.client.license.StartBasicRequest; import org.elasticsearch.protocol.xpack.license.DeleteLicenseRequest; import org.elasticsearch.protocol.xpack.license.GetLicenseRequest; @@ -30,10 +31,7 @@ import org.elasticsearch.protocol.xpack.license.PutLicenseRequest; public class LicenseRequestConverters { static Request putLicense(PutLicenseRequest putLicenseRequest) { - String endpoint = new RequestConverters.EndpointBuilder() - .addPathPartAsIs("_xpack") - .addPathPartAsIs("license") - .build(); + String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "license").build(); Request request = new Request(HttpPut.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(request); parameters.withTimeout(putLicenseRequest.timeout()); @@ -46,10 +44,7 @@ public class LicenseRequestConverters { } static Request getLicense(GetLicenseRequest getLicenseRequest) { - String endpoint = new RequestConverters.EndpointBuilder() - .addPathPartAsIs("_xpack") - .addPathPartAsIs("license") - .build(); + String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "license").build(); Request request = new Request(HttpGet.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(request); parameters.withLocal(getLicenseRequest.local()); @@ -57,13 +52,26 @@ public class LicenseRequestConverters { } static Request deleteLicense(DeleteLicenseRequest deleteLicenseRequest) { - Request request = new Request(HttpDelete.METHOD_NAME, "/_xpack/license"); + String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "license").build(); + Request request = new Request(HttpDelete.METHOD_NAME, endpoint); RequestConverters.Params parameters = new RequestConverters.Params(request); parameters.withTimeout(deleteLicenseRequest.timeout()); parameters.withMasterTimeout(deleteLicenseRequest.masterNodeTimeout()); return request; } + static Request startTrial(StartTrialRequest startTrialRequest) { + final String endpoint = new RequestConverters.EndpointBuilder().addPathPartAsIs("_xpack", "license", "start_trial").build(); + final Request request = new Request(HttpPost.METHOD_NAME, endpoint); + + RequestConverters.Params parameters = new RequestConverters.Params(request); + parameters.putParam("acknowledge", Boolean.toString(startTrialRequest.isAcknowledge())); + if (startTrialRequest.getLicenseType() != null) { + parameters.putParam("type", startTrialRequest.getLicenseType()); + } + return request; + } + static Request startBasic(StartBasicRequest startBasicRequest) { String endpoint = new RequestConverters.EndpointBuilder() .addPathPartAsIs("_xpack", "license", "start_basic") diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index 57e63791fd3..3087c16fcf9 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -980,7 +980,7 @@ final class RequestConverters { return this; } - EndpointBuilder addPathPartAsIs(String ... parts) { + EndpointBuilder addPathPartAsIs(String... parts) { for (String part : parts) { if (Strings.hasLength(part)) { joiner.add(part); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/license/StartTrialRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/license/StartTrialRequest.java new file mode 100644 index 00000000000..57858f8de05 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/license/StartTrialRequest.java @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.license; + +import org.elasticsearch.client.Validatable; +import org.elasticsearch.common.Nullable; + +public class StartTrialRequest implements Validatable { + + private final boolean acknowledge; + private final String licenseType; + + public StartTrialRequest() { + this(false); + } + + public StartTrialRequest(boolean acknowledge) { + this(acknowledge, null); + } + + public StartTrialRequest(boolean acknowledge, @Nullable String licenseType) { + this.acknowledge = acknowledge; + this.licenseType = licenseType; + } + + public boolean isAcknowledge() { + return acknowledge; + } + + public String getLicenseType() { + return licenseType; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/license/StartTrialResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/license/StartTrialResponse.java new file mode 100644 index 00000000000..3b259960a3c --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/license/StartTrialResponse.java @@ -0,0 +1,188 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.client.license; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.collect.Tuple; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; +import org.elasticsearch.common.xcontent.XContentParseException; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; + +public class StartTrialResponse { + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "start_trial_response", + true, + (Object[] arguments, Void aVoid) -> { + final boolean acknowledged = (boolean) arguments[0]; + final boolean trialWasStarted = (boolean) arguments[1]; + final String licenseType = (String) arguments[2]; + final String errorMessage = (String) arguments[3]; + + @SuppressWarnings("unchecked") + final Tuple> acknowledgeDetails = (Tuple>) arguments[4]; + final String acknowledgeHeader; + final Map acknowledgeMessages; + + if (acknowledgeDetails != null) { + acknowledgeHeader = acknowledgeDetails.v1(); + acknowledgeMessages = acknowledgeDetails.v2(); + } else { + acknowledgeHeader = null; + acknowledgeMessages = null; + } + + return new StartTrialResponse(acknowledged, trialWasStarted, licenseType, errorMessage, acknowledgeHeader, + acknowledgeMessages); + } + ); + + static { + PARSER.declareBoolean(constructorArg(), new ParseField("acknowledged")); + PARSER.declareBoolean(constructorArg(), new ParseField("trial_was_started")); + PARSER.declareString(optionalConstructorArg(), new ParseField("type")); + PARSER.declareString(optionalConstructorArg(), new ParseField("error_message")); + // todo consolidate this parsing with the parsing in PutLicenseResponse + PARSER.declareObject(optionalConstructorArg(), (parser, aVoid) -> { + final Map acknowledgeMessages = new HashMap<>(); + String message = null; + + final Map parsedMap = parser.map(); + for (Map.Entry entry : parsedMap.entrySet()) { + if (entry.getKey().equals("message")) { + if (entry.getValue() instanceof String) { + message = (String) entry.getValue(); + } else { + throw new XContentParseException(parser.getTokenLocation(), "unexpected acknowledgement header type"); + } + } else { + if (entry.getValue() instanceof List) { + final List messageStrings = new ArrayList<>(); + @SuppressWarnings("unchecked") + final List messageObjects = (List) entry.getValue(); + for (Object messageObject : messageObjects) { + if (messageObject instanceof String) { + messageStrings.add((String) messageObject); + } else { + throw new XContentParseException(parser.getTokenLocation(), "expected text in acknowledgement message"); + } + } + + acknowledgeMessages.put(entry.getKey(), messageStrings.toArray(new String[messageStrings.size()])); + } else { + throw new XContentParseException(parser.getTokenLocation(), "unexpected acknowledgement message type"); + } + } + } + + if (message == null) { + throw new XContentParseException(parser.getTokenLocation(), "expected acknowledgement header"); + } + + return new Tuple<>(message, acknowledgeMessages); + + }, new ParseField("acknowledge")); + } + + public static StartTrialResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.apply(parser, null); + } + + private final boolean acknowledged; + private final boolean trialWasStarted; + private final String licenseType; + private final String errorMessage; + private final String acknowledgeHeader; + private final Map acknowledgeMessages; + + public StartTrialResponse(boolean acknowledged, + boolean trialWasStarted, + String licenseType, + String errorMessage, + String acknowledgeHeader, + Map acknowledgeMessages) { + + this.acknowledged = acknowledged; + this.trialWasStarted = trialWasStarted; + this.licenseType = licenseType; + this.errorMessage = errorMessage; + this.acknowledgeHeader = acknowledgeHeader; + this.acknowledgeMessages = acknowledgeMessages; + } + + /** + * Returns true if the request that corresponds to this response acknowledged license changes that would occur as a result of starting + * a trial license + */ + public boolean isAcknowledged() { + return acknowledged; + } + + /** + * Returns true if a trial license was started as a result of the request corresponding to this response. Returns false if the cluster + * did not start a trial, or a trial had already been started before the corresponding request was made + */ + public boolean isTrialWasStarted() { + return trialWasStarted; + } + + /** + * If a trial license was started as a result of the request corresponding to this response (see {@link #isTrialWasStarted()}) then + * returns the type of license that was started on the cluster. Returns null otherwise + */ + public String getLicenseType() { + return licenseType; + } + + /** + * If a trial license was not started as a result of the request corresponding to this response (see {@link #isTrialWasStarted()} then + * returns a brief message explaining why the trial could not be started. Returns false otherwise + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * If the request corresponding to this response did not acknowledge licensing changes that would result from starting a trial license + * (see {@link #isAcknowledged()}), returns a message describing how the user must acknowledge licensing changes as a result of + * such a request. Returns null otherwise + */ + public String getAcknowledgeHeader() { + return acknowledgeHeader; + } + + /** + * If the request corresponding to this response did not acknowledge licensing changes that would result from starting a trial license + * (see {@link #isAcknowledged()}, returns a map. The map's keys are names of commercial Elasticsearch features, and their values are + * messages about how those features will be affected by licensing changes as a result of starting a trial license + */ + public Map getAcknowledgeMessages() { + return acknowledgeMessages; + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/LicensingIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/LicenseIT.java similarity index 65% rename from client/rest-high-level/src/test/java/org/elasticsearch/client/LicensingIT.java rename to client/rest-high-level/src/test/java/org/elasticsearch/client/LicenseIT.java index 506eb71d66d..f804d171511 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/LicensingIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/LicenseIT.java @@ -16,11 +16,14 @@ * specific language governing permissions and limitations * under the License. */ + package org.elasticsearch.client; import org.elasticsearch.Build; import org.elasticsearch.client.license.StartBasicRequest; import org.elasticsearch.client.license.StartBasicResponse; +import org.elasticsearch.client.license.StartTrialRequest; +import org.elasticsearch.client.license.StartTrialResponse; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.MapBuilder; import org.elasticsearch.protocol.xpack.license.LicensesStatus; @@ -31,7 +34,6 @@ import org.junit.BeforeClass; import java.io.IOException; import java.util.Arrays; -import java.util.List; import java.util.Map; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; @@ -42,7 +44,13 @@ import static org.hamcrest.Matchers.isEmptyOrNullString; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.empty; -public class LicensingIT extends ESRestHighLevelClientTestCase { +public class LicenseIT extends ESRestHighLevelClientTestCase { + + /* + * todo there are some cases we can't test here because this gradle project starts the integ test cluster so that it'll generate + * a trial license at startup. we need to add a separate gradle project for the license-related tests so that we can start the + * integ test cluster without generating a trial license + */ @BeforeClass public static void checkForSnapshot() { @@ -55,6 +63,37 @@ public class LicensingIT extends ESRestHighLevelClientTestCase { putTrialLicense(); } + public void testStartTrial() throws Exception { + + // todo add case where we successfully start a trial - see note above + + // case where we don't acknowledge trial license conditions + { + final StartTrialRequest request = new StartTrialRequest(); + final StartTrialResponse response = highLevelClient().license().startTrial(request, RequestOptions.DEFAULT); + + assertThat(response.isAcknowledged(), equalTo(false)); + assertThat(response.isTrialWasStarted(), equalTo(false)); + assertThat(response.getLicenseType(), nullValue()); + assertThat(response.getErrorMessage(), equalTo("Operation failed: Needs acknowledgement.")); + assertThat(response.getAcknowledgeHeader(), containsString("This API initiates a free 30-day trial for all platinum features")); + assertNotEmptyAcknowledgeMessages(response.getAcknowledgeMessages()); + } + + // case where we acknowledge, but the trial is already started at cluster startup + { + final StartTrialRequest request = new StartTrialRequest(true); + final StartTrialResponse response = highLevelClient().license().startTrial(request, RequestOptions.DEFAULT); + + assertThat(response.isAcknowledged(), equalTo(true)); + assertThat(response.isTrialWasStarted(), equalTo(false)); + assertThat(response.getLicenseType(), nullValue()); + assertThat(response.getErrorMessage(), equalTo("Operation failed: Trial was already activated.")); + assertThat(response.getAcknowledgeHeader(), nullValue()); + assertThat(response.getAcknowledgeMessages(), nullValue()); + } + } + public static void putTrialLicense() throws IOException { assumeTrue("Trial license is only valid when tested against snapshot/test builds", Build.CURRENT.isSnapshot()); @@ -76,9 +115,9 @@ public class LicensingIT extends ESRestHighLevelClientTestCase { .put("start_date_in_millis", "-1") .put("signature", "AAAABAAAAA3FXON9kGmNqmH+ASDWAAAAIAo5/x6hrsGh1GqqrJmy4qgmEC7gK0U4zQ6q5ZEMhm4jAAABAAcdKHL0BfM2uqTgT7BDuFxX5lb" - + "t/bHDVJ421Wwgm5p3IMbw/W13iiAHz0hhDziF7acJbc/y65L+BKGtVC1gSSHeLDHaAD66VrjKxfc7VbGyJIAYBOdujf0rheurmaD3IcNo" - + "/tWDjCdtTwrNziFkorsGcPadBP5Yc6csk3/Q74DlfiYweMBxLUfkBERwxwd5OQS6ujGvl/4bb8p5zXvOw8vMSaAXSXXnExP6lam+0934W" - + "0kHvU7IGk+fCUjOaiSWKSoE4TEcAtVNYj/oRoRtfQ1KQGpdCHxTHs1BimdZaG0nBHDsvhYlVVLSvHN6QzqsHWgFDG6JJxhtU872oTRSUHA=") + + "t/bHDVJ421Wwgm5p3IMbw/W13iiAHz0hhDziF7acJbc/y65L+BKGtVC1gSSHeLDHaAD66VrjKxfc7VbGyJIAYBOdujf0rheurmaD3IcNo" + + "/tWDjCdtTwrNziFkorsGcPadBP5Yc6csk3/Q74DlfiYweMBxLUfkBERwxwd5OQS6ujGvl/4bb8p5zXvOw8vMSaAXSXXnExP6lam+0934W" + + "0kHvU7IGk+fCUjOaiSWKSoE4TEcAtVNYj/oRoRtfQ1KQGpdCHxTHs1BimdZaG0nBHDsvhYlVVLSvHN6QzqsHWgFDG6JJxhtU872oTRSUHA=") .immutableMap())) .endObject()); @@ -103,7 +142,7 @@ public class LicensingIT extends ESRestHighLevelClientTestCase { assertThat(response.getAcknowledgeMessage(), containsString("This license update requires acknowledgement. " + "To acknowledge the license, please read the following messages and call /start_basic again")); - assertNotEmptyAcknowledgeMessages(response); + assertNotEmptyAcknowledgeMessages(response.getAcknowledgeMessages()); } // case where we acknowledge and the basic is started successfully { @@ -117,12 +156,11 @@ public class LicensingIT extends ESRestHighLevelClientTestCase { } } - private static void assertNotEmptyAcknowledgeMessages(StartBasicResponse response) { - assertThat(response.getAcknowledgeMessages().entrySet(), not(empty())); - for (Map.Entry entry : response.getAcknowledgeMessages().entrySet()) { + private static void assertNotEmptyAcknowledgeMessages(Map acknowledgeMessages) { + assertThat(acknowledgeMessages.entrySet(), not(empty())); + for (Map.Entry entry : acknowledgeMessages.entrySet()) { assertThat(entry.getKey(), not(isEmptyOrNullString())); - final List messages = Arrays.asList(entry.getValue()); - for (String message : messages) { + for (String message : entry.getValue()) { assertThat(message, not(isEmptyOrNullString())); } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/LicenseRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/LicenseRequestConvertersTests.java index 9fb8ae033d8..cb509b034d3 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/LicenseRequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/LicenseRequestConvertersTests.java @@ -16,9 +16,11 @@ * specific language governing permissions and limitations * under the License. */ + package org.elasticsearch.client; import org.apache.http.client.methods.HttpPost; +import org.elasticsearch.client.license.StartTrialRequest; import org.elasticsearch.action.support.master.AcknowledgedRequest; import org.elasticsearch.client.license.StartBasicRequest; import org.elasticsearch.test.ESTestCase; @@ -26,13 +28,36 @@ import org.elasticsearch.test.ESTestCase; import java.util.HashMap; import java.util.Map; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.nullValue; import static org.elasticsearch.client.RequestConvertersTests.setRandomMasterTimeout; import static org.elasticsearch.client.RequestConvertersTests.setRandomTimeout; -import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; + public class LicenseRequestConvertersTests extends ESTestCase { + + public void testStartTrial() { + final boolean acknowledge = randomBoolean(); + final String licenseType = randomBoolean() + ? randomAlphaOfLengthBetween(3, 10) + : null; + + final Map expectedParams = new HashMap<>(); + expectedParams.put("acknowledge", Boolean.toString(acknowledge)); + if (licenseType != null) { + expectedParams.put("type", licenseType); + } + + final StartTrialRequest hlrcRequest = new StartTrialRequest(acknowledge, licenseType); + final Request restRequest = LicenseRequestConverters.startTrial(hlrcRequest); + + assertThat(restRequest.getMethod(), equalTo(HttpPost.METHOD_NAME)); + assertThat(restRequest.getEndpoint(), equalTo("/_xpack/license/start_trial")); + assertThat(restRequest.getParameters(), equalTo(expectedParams)); + assertThat(restRequest.getEntity(), nullValue()); + } + public void testStartBasic() { final boolean acknowledge = randomBoolean(); StartBasicRequest startBasicRequest = new StartBasicRequest(acknowledge); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/LicensingDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/LicensingDocumentationIT.java index d1987420f02..9544dd14db6 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/LicensingDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/LicensingDocumentationIT.java @@ -26,6 +26,8 @@ import org.elasticsearch.action.support.master.AcknowledgedResponse; import org.elasticsearch.client.ESRestHighLevelClientTestCase; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; +import org.elasticsearch.client.license.StartTrialRequest; +import org.elasticsearch.client.license.StartTrialResponse; import org.elasticsearch.client.license.StartBasicRequest; import org.elasticsearch.client.license.StartBasicResponse; import org.elasticsearch.common.Booleans; @@ -43,12 +45,14 @@ import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import static org.elasticsearch.client.LicensingIT.putTrialLicense; +import static org.elasticsearch.client.LicenseIT.putTrialLicense; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; +import static org.hamcrest.core.Is.is; /** * Documentation for Licensing APIs in the high level java client. @@ -232,6 +236,61 @@ public class LicensingDocumentationIT extends ESRestHighLevelClientTestCase { } } + public void testStartTrial() throws Exception { + RestHighLevelClient client = highLevelClient(); + + { + // tag::start-trial-execute + StartTrialRequest request = new StartTrialRequest(true); // <1> + + StartTrialResponse response = client.license().startTrial(request, RequestOptions.DEFAULT); + // end::start-trial-execute + + // tag::start-trial-response + boolean acknowledged = response.isAcknowledged(); // <1> + boolean trialWasStarted = response.isTrialWasStarted(); // <2> + String licenseType = response.getLicenseType(); // <3> + String errorMessage = response.getErrorMessage(); // <4> + String acknowledgeHeader = response.getAcknowledgeHeader(); // <5> + Map acknowledgeMessages = response.getAcknowledgeMessages(); // <6> + // end::start-trial-response + + assertTrue(acknowledged); + assertFalse(trialWasStarted); + assertThat(licenseType, nullValue()); + assertThat(errorMessage, is("Operation failed: Trial was already activated.")); + assertThat(acknowledgeHeader, nullValue()); + assertThat(acknowledgeMessages, nullValue()); + } + + { + StartTrialRequest request = new StartTrialRequest(); + + // tag::start-trial-execute-listener + ActionListener listener = new ActionListener() { + @Override + public void onResponse(StartTrialResponse response) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::start-trial-execute-listener + + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::start-trial-execute-async + client.license().startTrialAsync(request, RequestOptions.DEFAULT, listener); + // end::start-trial-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + public void testPostStartBasic() throws Exception { RestHighLevelClient client = highLevelClient(); { diff --git a/docs/java-rest/high-level/licensing/start-trial.asciidoc b/docs/java-rest/high-level/licensing/start-trial.asciidoc new file mode 100644 index 00000000000..0f198a391f0 --- /dev/null +++ b/docs/java-rest/high-level/licensing/start-trial.asciidoc @@ -0,0 +1,70 @@ +[[java-rest-high-start-trial]] +=== Start Trial + +[[java-rest-high-start-trial-execution]] +==== Execution + +This API creates and enables a trial license using the `startTrial()` +method. + +["source","java",subs="attributes,callouts,macros"] +--------------------------------------------------- +include-tagged::{doc-tests}/LicensingDocumentationIT.java[start-trial-execute] +--------------------------------------------------- +<1> Sets the "acknowledge" parameter to true, indicating the user has +acknowledged that starting a trial license may affect commercial features + +[[java-rest-high-start-trial-response]] +==== Response + +The returned `StartTrialResponse` returns a field indicating whether the +trial was started. If it was started, the response returns a the type of +license started. If it was not started, it returns an error message describing +why. + +Acknowledgement messages may also be returned if this API was called without +the `acknowledge` flag set to `true`. In this case you need to display the +messages to the end user and if they agree, resubmit the request with the +`acknowledge` flag set to `true`. Please note that the response will still +return a 200 return code even if it requires an acknowledgement. So, it is +necessary to check the `acknowledged` flag. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/LicensingDocumentationIT.java[start-trial-response] +-------------------------------------------------- +<1> Whether or not the request had the `acknowledge` flag set +<2> Whether or not this request caused a trial to start +<3> If this request caused a trial to start, which type of license it +registered +<4> If this request did not cause a trial to start, a message explaining why +<5> If the user's request did not have the `acknowledge` flag set, a summary +of the user's acknowledgement required for this API +<6> If the user's request did not have the `acknowledge` flag set, contains +keys of commercial features and values of messages describing how they will +be affected by licensing changes as the result of starting a trial + +[[java-rest-high-start-trial-async]] +==== Asynchronous execution + +This request can be executed asynchronously: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/LicensingDocumentationIT.java[start-trial-execute-async] +-------------------------------------------------- + +The asynchronous method does not block and returns immediately. Once it is +completed the `ActionListener` is called back using the `onResponse` method +if the execution successfully completed or using the `onFailure` method if +it failed. + +A typical listener for `StartTrialResponse` looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/LicensingDocumentationIT.java[start-trial-execute-listener] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument +<2> Called in case of failure. The raised exception is provided as an argument \ No newline at end of file diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 9896c6d0552..5190d6ec13c 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -214,11 +214,13 @@ The Java High Level REST Client supports the following Licensing APIs: * <> * <> * <> +* <> * <> include::licensing/put-license.asciidoc[] include::licensing/get-license.asciidoc[] include::licensing/delete-license.asciidoc[] +include::licensing/start-trial.asciidoc[] include::licensing/start-basic.asciidoc[] == Machine Learning APIs