diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java
index c06493aea7381..ed0043c801c7b 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java
@@ -26,6 +26,8 @@
import org.elasticsearch.client.watcher.ActivateWatchResponse;
import org.elasticsearch.client.watcher.AckWatchRequest;
import org.elasticsearch.client.watcher.AckWatchResponse;
+import org.elasticsearch.client.watcher.GetWatchRequest;
+import org.elasticsearch.client.watcher.GetWatchResponse;
import org.elasticsearch.client.watcher.StartWatchServiceRequest;
import org.elasticsearch.client.watcher.StopWatchServiceRequest;
import org.elasticsearch.client.watcher.DeleteWatchRequest;
@@ -129,6 +131,34 @@ public void putWatchAsync(PutWatchRequest request, RequestOptions options,
PutWatchResponse::fromXContent, listener, emptySet());
}
+ /**
+ * Gets a watch from the cluster
+ * See
+ * the docs for more.
+ * @param request 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 GetWatchResponse getWatch(GetWatchRequest request, RequestOptions options) throws IOException {
+ return restHighLevelClient.performRequestAndParseEntity(request, WatcherRequestConverters::getWatch, options,
+ GetWatchResponse::fromXContent, emptySet());
+ }
+
+ /**
+ * Asynchronously gets a watch into the cluster
+ * See
+ * the docs for more.
+ * @param request 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
+ */
+ public void getWatchAsync(GetWatchRequest request, RequestOptions options,
+ ActionListener listener) {
+ restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::getWatch, options,
+ GetWatchResponse::fromXContent, listener, emptySet());
+ }
+
/**
* Deactivate an existing watch
* See
diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java
index a017779495b9f..1da7ef4c617ff 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java
@@ -28,12 +28,13 @@
import org.elasticsearch.client.watcher.DeactivateWatchRequest;
import org.elasticsearch.client.watcher.ActivateWatchRequest;
import org.elasticsearch.client.watcher.AckWatchRequest;
+import org.elasticsearch.client.watcher.DeleteWatchRequest;
+import org.elasticsearch.client.watcher.GetWatchRequest;
+import org.elasticsearch.client.watcher.PutWatchRequest;
import org.elasticsearch.client.watcher.StartWatchServiceRequest;
import org.elasticsearch.client.watcher.StopWatchServiceRequest;
import org.elasticsearch.client.watcher.WatcherStatsRequest;
import org.elasticsearch.common.bytes.BytesReference;
-import org.elasticsearch.client.watcher.DeleteWatchRequest;
-import org.elasticsearch.client.watcher.PutWatchRequest;
final class WatcherRequestConverters {
@@ -76,6 +77,16 @@ static Request putWatch(PutWatchRequest putWatchRequest) {
return request;
}
+
+ static Request getWatch(GetWatchRequest getWatchRequest) {
+ String endpoint = new RequestConverters.EndpointBuilder()
+ .addPathPartAsIs("_xpack", "watcher", "watch")
+ .addPathPart(getWatchRequest.getId())
+ .build();
+
+ return new Request(HttpGet.METHOD_NAME, endpoint);
+ }
+
static Request deactivateWatch(DeactivateWatchRequest deactivateWatchRequest) {
String endpoint = new RequestConverters.EndpointBuilder()
.addPathPartAsIs("_xpack")
diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/GetWatchRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/GetWatchRequest.java
new file mode 100644
index 0000000000000..fae2d31a256be
--- /dev/null
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/GetWatchRequest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.watcher;
+
+import org.elasticsearch.client.Validatable;
+import org.elasticsearch.client.ValidationException;
+
+/**
+ * The request to get the watch by name (id)
+ */
+public final class GetWatchRequest implements Validatable {
+
+ private final String id;
+
+ public GetWatchRequest(String watchId) {
+ validateId(watchId);
+ this.id = watchId;
+ }
+
+ private void validateId(String id) {
+ ValidationException exception = new ValidationException();
+ if (id == null) {
+ exception.addValidationError("watch id is missing");
+ } else if (PutWatchRequest.isValidId(id) == false) {
+ exception.addValidationError("watch id contains whitespace");
+ }
+ if (exception.validationErrors().isEmpty() == false) {
+ throw exception;
+ }
+ }
+
+ /**
+ * @return The name of the watch to retrieve
+ */
+ public String getId() {
+ return id;
+ }
+}
diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/GetWatchResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/GetWatchResponse.java
new file mode 100644
index 0000000000000..9f5934b33eb30
--- /dev/null
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/GetWatchResponse.java
@@ -0,0 +1,148 @@
+/*
+ * 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.watcher;
+
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.lucene.uid.Versions;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+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 java.io.IOException;
+import java.util.Map;
+import java.util.Objects;
+
+public class GetWatchResponse {
+ private final String id;
+ private final long version;
+ private final WatchStatus status;
+
+ private final BytesReference source;
+ private final XContentType xContentType;
+
+ /**
+ * Ctor for missing watch
+ */
+ public GetWatchResponse(String id) {
+ this(id, Versions.NOT_FOUND, null, null, null);
+ }
+
+ public GetWatchResponse(String id, long version, WatchStatus status, BytesReference source, XContentType xContentType) {
+ this.id = id;
+ this.version = version;
+ this.status = status;
+ this.source = source;
+ this.xContentType = xContentType;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public long getVersion() {
+ return version;
+ }
+
+ public boolean isFound() {
+ return version != Versions.NOT_FOUND;
+ }
+
+ public WatchStatus getStatus() {
+ return status;
+ }
+
+ /**
+ * Returns the {@link XContentType} of the source
+ */
+ public XContentType getContentType() {
+ return xContentType;
+ }
+
+ /**
+ * Returns the serialized watch
+ */
+ public BytesReference getSource() {
+ return source;
+ }
+
+ /**
+ * Returns the source as a map
+ */
+ public Map getSourceAsMap() {
+ return source == null ? null : XContentHelper.convertToMap(source, false, getContentType()).v2();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ GetWatchResponse that = (GetWatchResponse) o;
+ return version == that.version &&
+ Objects.equals(id, that.id) &&
+ Objects.equals(status, that.status) &&
+ Objects.equals(xContentType, that.xContentType) &&
+ Objects.equals(source, that.source);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, status, source, version);
+ }
+
+ private static final ParseField ID_FIELD = new ParseField("_id");
+ private static final ParseField FOUND_FIELD = new ParseField("found");
+ private static final ParseField VERSION_FIELD = new ParseField("_version");
+ private static final ParseField STATUS_FIELD = new ParseField("status");
+ private static final ParseField WATCH_FIELD = new ParseField("watch");
+
+ private static ConstructingObjectParser PARSER =
+ new ConstructingObjectParser<>("get_watch_response", true,
+ a -> {
+ boolean isFound = (boolean) a[1];
+ if (isFound) {
+ XContentBuilder builder = (XContentBuilder) a[4];
+ BytesReference source = BytesReference.bytes(builder);
+ return new GetWatchResponse((String) a[0], (long) a[2], (WatchStatus) a[3], source, builder.contentType());
+ } else {
+ return new GetWatchResponse((String) a[0]);
+ }
+ });
+
+ static {
+ PARSER.declareString(ConstructingObjectParser.constructorArg(), ID_FIELD);
+ PARSER.declareBoolean(ConstructingObjectParser.constructorArg(), FOUND_FIELD);
+ PARSER.declareLong(ConstructingObjectParser.optionalConstructorArg(), VERSION_FIELD);
+ PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(),
+ (parser, context) -> WatchStatus.parse(parser), STATUS_FIELD);
+ PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(),
+ (parser, context) -> {
+ try (XContentBuilder builder = XContentBuilder.builder(parser.contentType().xContent())) {
+ builder.copyCurrentStructure(parser);
+ return builder;
+ }
+ }, WATCH_FIELD);
+ }
+
+ public static GetWatchResponse fromXContent(XContentParser parser) throws IOException {
+ return PARSER.parse(parser, null);
+ }
+}
diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatchStatus.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatchStatus.java
index 04b747c03635a..ab543abbf594a 100644
--- a/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatchStatus.java
+++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/watcher/WatchStatus.java
@@ -20,6 +20,7 @@
package org.elasticsearch.client.watcher;
import org.elasticsearch.ElasticsearchParseException;
+import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.xcontent.XContentParser;
import org.joda.time.DateTime;
@@ -44,19 +45,22 @@ public class WatchStatus {
private final DateTime lastMetCondition;
private final long version;
private final Map actions;
+ @Nullable private Map headers;
public WatchStatus(long version,
State state,
ExecutionState executionState,
DateTime lastChecked,
DateTime lastMetCondition,
- Map actions) {
+ Map actions,
+ Map headers) {
this.version = version;
this.lastChecked = lastChecked;
this.lastMetCondition = lastMetCondition;
this.actions = actions;
this.state = state;
this.executionState = executionState;
+ this.headers = headers;
}
public State state() {
@@ -79,6 +83,10 @@ public ActionStatus actionStatus(String actionId) {
return actions.get(actionId);
}
+ public Map getActions() {
+ return actions;
+ }
+
public long version() {
return version;
}
@@ -87,6 +95,10 @@ public ExecutionState getExecutionState() {
return executionState;
}
+ public Map getHeaders() {
+ return headers;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -98,7 +110,8 @@ public boolean equals(Object o) {
Objects.equals(lastMetCondition, that.lastMetCondition) &&
Objects.equals(version, that.version) &&
Objects.equals(executionState, that.executionState) &&
- Objects.equals(actions, that.actions);
+ Objects.equals(actions, that.actions) &&
+ Objects.equals(headers, that.headers);
}
@Override
@@ -112,6 +125,7 @@ public static WatchStatus parse(XContentParser parser) throws IOException {
DateTime lastChecked = null;
DateTime lastMetCondition = null;
Map actions = null;
+ Map headers = null;
long version = -1;
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation);
@@ -172,13 +186,17 @@ public static WatchStatus parse(XContentParser parser) throws IOException {
throw new ElasticsearchParseException("could not parse watch status. expecting field [{}] to be an object, " +
"found [{}] instead", currentFieldName, token);
}
+ } else if (Field.HEADERS.match(currentFieldName, parser.getDeprecationHandler())) {
+ if (token == XContentParser.Token.START_OBJECT) {
+ headers = parser.mapStrings();
+ }
} else {
parser.skipChildren();
}
}
actions = actions == null ? emptyMap() : unmodifiableMap(actions);
- return new WatchStatus(version, state, executionState, lastChecked, lastMetCondition, actions);
+ return new WatchStatus(version, state, executionState, lastChecked, lastMetCondition, actions, headers);
}
public static class State {
@@ -214,6 +232,8 @@ public static State parse(XContentParser parser) throws IOException {
active = parser.booleanValue();
} else if (Field.TIMESTAMP.match(currentFieldName, parser.getDeprecationHandler())) {
timestamp = parseDate(currentFieldName, parser);
+ } else {
+ parser.skipChildren();
}
}
return new State(active, timestamp);
@@ -229,5 +249,6 @@ public interface Field {
ParseField ACTIONS = new ParseField("actions");
ParseField VERSION = new ParseField("version");
ParseField EXECUTION_STATE = new ParseField("execution_state");
+ ParseField HEADERS = new ParseField("headers");
}
}
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherRequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherRequestConvertersTests.java
index 2712dbc0438db..ff7050fd67ce7 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherRequestConvertersTests.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/WatcherRequestConvertersTests.java
@@ -28,6 +28,7 @@
import org.elasticsearch.client.watcher.DeactivateWatchRequest;
import org.elasticsearch.client.watcher.DeleteWatchRequest;
import org.elasticsearch.client.watcher.PutWatchRequest;
+import org.elasticsearch.client.watcher.GetWatchRequest;
import org.elasticsearch.client.watcher.StartWatchServiceRequest;
import org.elasticsearch.client.watcher.StopWatchServiceRequest;
import org.elasticsearch.client.watcher.WatcherStatsRequest;
@@ -91,6 +92,16 @@ public void testPutWatch() throws Exception {
assertThat(bos.toString("UTF-8"), is(body));
}
+ public void testGetWatch() throws Exception {
+ String watchId = randomAlphaOfLength(10);
+ GetWatchRequest getWatchRequest = new GetWatchRequest(watchId);
+
+ Request request = WatcherRequestConverters.getWatch(getWatchRequest);
+ assertEquals(HttpGet.METHOD_NAME, request.getMethod());
+ assertEquals("/_xpack/watcher/watch/" + watchId, request.getEndpoint());
+ assertThat(request.getEntity(), nullValue());
+ }
+
public void testDeactivateWatch() {
String watchId = randomAlphaOfLength(10);
DeactivateWatchRequest deactivateWatchRequest = new DeactivateWatchRequest(watchId);
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/WatcherDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/WatcherDocumentationIT.java
index 74562a1d17fd0..03dfc2ea7e088 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/WatcherDocumentationIT.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/WatcherDocumentationIT.java
@@ -34,6 +34,8 @@
import org.elasticsearch.client.watcher.ActionStatus.AckStatus;
import org.elasticsearch.client.watcher.DeactivateWatchRequest;
import org.elasticsearch.client.watcher.DeactivateWatchResponse;
+import org.elasticsearch.client.watcher.GetWatchRequest;
+import org.elasticsearch.client.watcher.GetWatchResponse;
import org.elasticsearch.client.watcher.StartWatchServiceRequest;
import org.elasticsearch.client.watcher.StopWatchServiceRequest;
import org.elasticsearch.client.watcher.WatchStatus;
@@ -197,6 +199,51 @@ public void onFailure(Exception e) {
assertTrue(latch.await(30L, TimeUnit.SECONDS));
}
+ {
+ //tag::get-watch-request
+ GetWatchRequest request = new GetWatchRequest("my_watch_id");
+ //end::get-watch-request
+
+ //tag::ack-watch-execute
+ GetWatchResponse response = client.watcher().getWatch(request, RequestOptions.DEFAULT);
+ //end::get-watch-request
+
+ //tag::get-watch-response
+ String watchId = response.getId(); // <1>
+ boolean found = response.isFound(); // <2>
+ long version = response.getVersion(); // <3>
+ WatchStatus status = response.getStatus(); // <4>
+ BytesReference source = response.getSource(); // <5>
+ //end::get-watch-response
+ }
+
+ {
+ GetWatchRequest request = new GetWatchRequest("my_other_watch_id");
+ // tag::get-watch-execute-listener
+ ActionListener listener = new ActionListener() {
+ @Override
+ public void onResponse(GetWatchResponse response) {
+ // <1>
+ }
+
+ @Override
+ public void onFailure(Exception e) {
+ // <2>
+ }
+ };
+ // end::get-watch-execute-listener
+
+ // Replace the empty listener by a blocking listener in test
+ final CountDownLatch latch = new CountDownLatch(1);
+ listener = new LatchedActionListener<>(listener, latch);
+
+ // tag::get-watch-execute-async
+ client.watcher().getWatchAsync(request, RequestOptions.DEFAULT, listener); // <1>
+ // end::get-watch-execute-async
+
+ assertTrue(latch.await(30L, TimeUnit.SECONDS));
+ }
+
{
//tag::x-pack-delete-watch-execute
DeleteWatchRequest request = new DeleteWatchRequest("my_watch_id");
diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/watcher/WatchRequestValidationTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/watcher/WatchRequestValidationTests.java
index 1fea3bccb62a7..7f3bc2e0c8931 100644
--- a/client/rest-high-level/src/test/java/org/elasticsearch/client/watcher/WatchRequestValidationTests.java
+++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/watcher/WatchRequestValidationTests.java
@@ -91,4 +91,10 @@ public void testPutWatchContentNull() {
() -> new PutWatchRequest("foo", BytesArray.EMPTY, null));
assertThat(exception.getMessage(), is("request body is missing"));
}
+
+ public void testGetWatchInvalidWatchId() {
+ ValidationException e = expectThrows(ValidationException.class,
+ () -> new GetWatchRequest("id with whitespaces"));
+ assertThat(e.validationErrors(), hasItem("watch id contains whitespace"));
+ }
}
diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc
index 096a9e593827c..c7ecccc01c9bc 100644
--- a/docs/java-rest/high-level/supported-apis.asciidoc
+++ b/docs/java-rest/high-level/supported-apis.asciidoc
@@ -420,6 +420,7 @@ The Java High Level REST Client supports the following Watcher APIs:
* <<{upid}-start-watch-service>>
* <<{upid}-stop-watch-service>>
* <>
+* <>
* <>
* <>
* <<{upid}-ack-watch>>
@@ -429,6 +430,7 @@ The Java High Level REST Client supports the following Watcher APIs:
include::watcher/start-watch-service.asciidoc[]
include::watcher/stop-watch-service.asciidoc[]
include::watcher/put-watch.asciidoc[]
+include::watcher/get-watch.asciidoc[]
include::watcher/delete-watch.asciidoc[]
include::watcher/ack-watch.asciidoc[]
include::watcher/deactivate-watch.asciidoc[]
diff --git a/docs/java-rest/high-level/watcher/get-watch.asciidoc b/docs/java-rest/high-level/watcher/get-watch.asciidoc
new file mode 100644
index 0000000000000..c4773d70ad731
--- /dev/null
+++ b/docs/java-rest/high-level/watcher/get-watch.asciidoc
@@ -0,0 +1,36 @@
+--
+:api: get-watch
+:request: GetWatchRequest
+:response: GetWatchResponse
+--
+
+[id="{upid}-{api}"]
+=== Get Watch API
+
+[id="{upid}-{api}-request"]
+==== Execution
+
+A watch can be retrieved as follows:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-request]
+--------------------------------------------------
+
+[id="{upid}-{api}-response"]
+==== Response
+
+The returned +{response}+ contains `id`, `version`, `status` and `source`
+information.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-response]
+--------------------------------------------------
+<1> `_id`, id of the watch
+<2> `found` is a boolean indicating whether the watch was found
+<2> `_version` returns the version of the watch
+<3> `status` contains status of the watch
+<4> `source` the source of the watch
+
+include::../execution.asciidoc[]
\ No newline at end of file
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java
index e0724795c297c..b54acc441e74a 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/support/xcontent/XContentSource.java
@@ -23,6 +23,7 @@
import java.io.InputStream;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Encapsulates the xcontent source
@@ -51,6 +52,13 @@ public XContentSource(XContentBuilder builder) {
this(BytesReference.bytes(builder), builder.contentType());
}
+ /**
+ * @return The content type of the source
+ */
+ public XContentType getContentType() {
+ return contentType;
+ }
+
/**
* @return The bytes reference of the source
*/
@@ -133,4 +141,17 @@ private Object data() {
return data;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ XContentSource that = (XContentSource) o;
+ return Objects.equals(bytes, that.bytes) &&
+ contentType == that.contentType;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(bytes, contentType);
+ }
}
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/get/GetWatchResponse.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/get/GetWatchResponse.java
index 7b94b446ffefa..d92ae1dcc4626 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/get/GetWatchResponse.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/transport/actions/get/GetWatchResponse.java
@@ -6,21 +6,23 @@
package org.elasticsearch.xpack.core.watcher.transport.actions.get;
import org.elasticsearch.action.ActionResponse;
-import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.Strings;
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.xcontent.XContentType;
+import org.elasticsearch.common.xcontent.ToXContent;
+import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.core.watcher.watch.WatchStatus;
import java.io.IOException;
+import java.util.Objects;
-public class GetWatchResponse extends ActionResponse {
+public class GetWatchResponse extends ActionResponse implements ToXContent {
private String id;
private WatchStatus status;
- private boolean found = false;
+ private boolean found;
private XContentSource source;
private long version;
@@ -32,19 +34,20 @@ public GetWatchResponse() {
*/
public GetWatchResponse(String id) {
this.id = id;
+ this.status = null;
this.found = false;
this.source = null;
- version = Versions.NOT_FOUND;
+ this.version = Versions.NOT_FOUND;
}
/**
* ctor for found watch
*/
- public GetWatchResponse(String id, long version, WatchStatus status, BytesReference source, XContentType contentType) {
+ public GetWatchResponse(String id, long version, WatchStatus status, XContentSource source) {
this.id = id;
this.status = status;
this.found = true;
- this.source = new XContentSource(source, contentType);
+ this.source = source;
this.version = version;
}
@@ -77,6 +80,10 @@ public void readFrom(StreamInput in) throws IOException {
status = WatchStatus.read(in);
source = XContentSource.readFrom(in);
version = in.readZLong();
+ } else {
+ status = null;
+ source = null;
+ version = Versions.NOT_FOUND;
}
}
@@ -91,4 +98,37 @@ public void writeTo(StreamOutput out) throws IOException {
out.writeZLong(version);
}
}
+
+ @Override
+ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+ builder.field("found", found);
+ builder.field("_id", id);
+ if (found) {
+ builder.field("_version", version);
+ builder.field("status", status, params);
+ builder.field("watch", source, params);
+ }
+ return builder;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ GetWatchResponse that = (GetWatchResponse) o;
+ return version == that.version &&
+ Objects.equals(id, that.id) &&
+ Objects.equals(status, that.status) &&
+ Objects.equals(source, that.source);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, status, version);
+ }
+
+ @Override
+ public String toString() {
+ return Strings.toString(this);
+ }
}
diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/watch/WatchStatus.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/watch/WatchStatus.java
index 69d114bd2b045..c65e019780a90 100644
--- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/watch/WatchStatus.java
+++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/watcher/watch/WatchStatus.java
@@ -57,8 +57,8 @@ public WatchStatus(DateTime now, Map actions) {
this(-1, new State(true, now), null, null, null, actions, Collections.emptyMap());
}
- private WatchStatus(long version, State state, ExecutionState executionState, DateTime lastChecked, DateTime lastMetCondition,
- Map actions, Map headers) {
+ public WatchStatus(long version, State state, ExecutionState executionState, DateTime lastChecked, DateTime lastMetCondition,
+ Map actions, Map headers) {
this.version = version;
this.lastChecked = lastChecked;
this.lastMetCondition = lastMetCondition;
@@ -340,6 +340,8 @@ public static WatchStatus parse(String watchId, WatcherXContentParser parser) th
if (token == XContentParser.Token.START_OBJECT) {
headers = parser.mapStrings();
}
+ } else {
+ parser.skipChildren();
}
}
@@ -395,6 +397,8 @@ public static State parse(XContentParser parser) throws IOException {
active = parser.booleanValue();
} else if (Field.TIMESTAMP.match(currentFieldName, parser.getDeprecationHandler())) {
timestamp = parseDate(currentFieldName, parser, UTC);
+ } else {
+ parser.skipChildren();
}
}
return new State(active, timestamp);
diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/protocol/xpack/watcher/GetWatchResponseTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/protocol/xpack/watcher/GetWatchResponseTests.java
new file mode 100644
index 0000000000000..bcfed1c7b0be4
--- /dev/null
+++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/protocol/xpack/watcher/GetWatchResponseTests.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.protocol.xpack.watcher;
+
+import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.xcontent.DeprecationHandler;
+import org.elasticsearch.common.xcontent.NamedXContentRegistry;
+import org.elasticsearch.common.xcontent.ToXContent;
+import org.elasticsearch.common.xcontent.XContent;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.common.xcontent.XContentType;
+import org.elasticsearch.protocol.AbstractHlrcStreamableXContentTestCase;
+import org.elasticsearch.xpack.core.watcher.actions.ActionStatus;
+import org.elasticsearch.xpack.core.watcher.execution.ExecutionState;
+import org.elasticsearch.xpack.core.watcher.support.xcontent.XContentSource;
+import org.elasticsearch.xpack.core.watcher.transport.actions.get.GetWatchResponse;
+import org.elasticsearch.xpack.core.watcher.watch.WatchStatus;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Predicate;
+
+public class GetWatchResponseTests extends
+ AbstractHlrcStreamableXContentTestCase {
+
+ private static final String[] SHUFFLE_FIELDS_EXCEPTION = new String[] { "watch" };
+
+ @Override
+ protected String[] getShuffleFieldsExceptions() {
+ return SHUFFLE_FIELDS_EXCEPTION;
+ }
+
+ @Override
+ protected ToXContent.Params getToXContentParams() {
+ return new ToXContent.MapParams(Collections.singletonMap("hide_headers", "false"));
+ }
+
+ @Override
+ protected Predicate getRandomFieldsExcludeFilter() {
+ return f -> f.contains("watch") || f.contains("actions") || f.contains("headers");
+ }
+
+ @Override
+ protected void assertEqualInstances(GetWatchResponse expectedInstance, GetWatchResponse newInstance) {
+ if (expectedInstance.isFound() &&
+ expectedInstance.getSource().getContentType() != newInstance.getSource().getContentType()) {
+ /**
+ * The {@link GetWatchResponse#getContentType()} depends on the content type that
+ * was used to serialize the main object so we use the same content type than the
+ * expectedInstance
to translate the watch of the newInstance
.
+ */
+ XContent from = XContentFactory.xContent(newInstance.getSource().getContentType());
+ XContent to = XContentFactory.xContent(expectedInstance.getSource().getContentType());
+ final BytesReference newSource;
+ // It is safe to use EMPTY here because this never uses namedObject
+ try (InputStream stream = newInstance.getSource().getBytes().streamInput();
+ XContentParser parser = XContentFactory.xContent(from.type()).createParser(NamedXContentRegistry.EMPTY,
+ DeprecationHandler.THROW_UNSUPPORTED_OPERATION, stream)) {
+ parser.nextToken();
+ XContentBuilder builder = XContentFactory.contentBuilder(to.type());
+ builder.copyCurrentStructure(parser);
+ newSource = BytesReference.bytes(builder);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ newInstance = new GetWatchResponse(newInstance.getId(), newInstance.getVersion(),
+ newInstance.getStatus(), new XContentSource(newSource, expectedInstance.getSource().getContentType()));
+ }
+ super.assertEqualInstances(expectedInstance, newInstance);
+ }
+
+ @Override
+ protected GetWatchResponse createBlankInstance() {
+ return new GetWatchResponse();
+ }
+
+ @Override
+ protected GetWatchResponse createTestInstance() {
+ String id = randomAlphaOfLength(10);
+ if (rarely()) {
+ return new GetWatchResponse(id);
+ }
+ long version = randomLongBetween(0, 10);
+ WatchStatus status = randomWatchStatus();
+ BytesReference source = simpleWatch();
+ return new GetWatchResponse(id, version, status, new XContentSource(source, XContentType.JSON));
+ }
+
+ private static BytesReference simpleWatch() {
+ try {
+ XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent());
+ builder.startObject()
+ .startObject("trigger")
+ .startObject("schedule")
+ .field("interval", "10h")
+ .endObject()
+ .endObject()
+ .startObject("input")
+ .startObject("none").endObject()
+ .endObject()
+ .startObject("actions")
+ .startObject("logme")
+ .field("text", "{{ctx.payload}}")
+ .endObject()
+ .endObject().endObject();
+ return BytesReference.bytes(builder);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static WatchStatus randomWatchStatus() {
+ long version = randomLongBetween(-1, Long.MAX_VALUE);
+ WatchStatus.State state = new WatchStatus.State(randomBoolean(), DateTime.now(DateTimeZone.UTC));
+ ExecutionState executionState = randomFrom(ExecutionState.values());
+ DateTime lastChecked = rarely() ? null : DateTime.now(DateTimeZone.UTC);
+ DateTime lastMetCondition = rarely() ? null : DateTime.now(DateTimeZone.UTC);
+ int size = randomIntBetween(0, 5);
+ Map actionMap = new HashMap<>();
+ for (int i = 0; i < size; i++) {
+ ActionStatus.AckStatus ack = new ActionStatus.AckStatus(
+ DateTime.now(DateTimeZone.UTC),
+ randomFrom(ActionStatus.AckStatus.State.values())
+ );
+ ActionStatus actionStatus = new ActionStatus(
+ ack,
+ randomBoolean() ? null : randomExecution(),
+ randomBoolean() ? null : randomExecution(),
+ randomBoolean() ? null : randomThrottle()
+ );
+ actionMap.put(randomAlphaOfLength(10), actionStatus);
+ }
+ Map headers = randomBoolean() ? new HashMap<>() : null;
+ if (headers != null) {
+ int headerSize = randomIntBetween(1, 5);
+ for (int i = 0; i < headerSize; i++) {
+ headers.put(randomAlphaOfLengthBetween(5, 10), randomAlphaOfLengthBetween(1, 10));
+ }
+ }
+ return new WatchStatus(version, state, executionState, lastChecked, lastMetCondition, actionMap, headers);
+ }
+
+ private static ActionStatus.Throttle randomThrottle() {
+ return new ActionStatus.Throttle(DateTime.now(DateTimeZone.UTC), randomAlphaOfLengthBetween(10, 20));
+ }
+
+ private static ActionStatus.Execution randomExecution() {
+ if (randomBoolean()) {
+ return null;
+ } else if (randomBoolean()) {
+ return ActionStatus.Execution.failure(DateTime.now(DateTimeZone.UTC), randomAlphaOfLengthBetween(10, 20));
+ } else {
+ return ActionStatus.Execution.successful(DateTime.now(DateTimeZone.UTC));
+ }
+ }
+
+ @Override
+ public org.elasticsearch.client.watcher.GetWatchResponse doHlrcParseInstance(XContentParser parser) throws IOException {
+ return org.elasticsearch.client.watcher.GetWatchResponse.fromXContent(parser);
+ }
+
+ @Override
+ public GetWatchResponse convertHlrcToInternal(org.elasticsearch.client.watcher.GetWatchResponse instance) {
+ if (instance.isFound()) {
+ return new GetWatchResponse(instance.getId(), instance.getVersion(), convertHlrcToInternal(instance.getStatus()),
+ new XContentSource(instance.getSource(), instance.getContentType()));
+ } else {
+ return new GetWatchResponse(instance.getId());
+ }
+ }
+
+ private static WatchStatus convertHlrcToInternal(org.elasticsearch.client.watcher.WatchStatus status) {
+ final Map actions = new HashMap<>();
+ for (Map.Entry entry : status.getActions().entrySet()) {
+ actions.put(entry.getKey(), convertHlrcToInternal(entry.getValue()));
+ }
+ return new WatchStatus(status.version(),
+ convertHlrcToInternal(status.state()),
+ status.getExecutionState() == null ? null : convertHlrcToInternal(status.getExecutionState()),
+ status.lastChecked(), status.lastMetCondition(), actions, status.getHeaders()
+ );
+ }
+
+ private static ActionStatus convertHlrcToInternal(org.elasticsearch.client.watcher.ActionStatus actionStatus) {
+ return new ActionStatus(convertHlrcToInternal(actionStatus.ackStatus()),
+ actionStatus.lastExecution() == null ? null : convertHlrcToInternal(actionStatus.lastExecution()),
+ actionStatus.lastSuccessfulExecution() == null ? null : convertHlrcToInternal(actionStatus.lastSuccessfulExecution()),
+ actionStatus.lastThrottle() == null ? null : convertHlrcToInternal(actionStatus.lastThrottle())
+ );
+ }
+
+ private static ActionStatus.AckStatus convertHlrcToInternal(org.elasticsearch.client.watcher.ActionStatus.AckStatus ackStatus) {
+ return new ActionStatus.AckStatus(ackStatus.timestamp(), convertHlrcToInternal(ackStatus.state()));
+ }
+
+ private static ActionStatus.AckStatus.State convertHlrcToInternal(org.elasticsearch.client.watcher.ActionStatus.AckStatus.State state) {
+ return ActionStatus.AckStatus.State.valueOf(state.name());
+ }
+
+ private static WatchStatus.State convertHlrcToInternal(org.elasticsearch.client.watcher.WatchStatus.State state) {
+ return new WatchStatus.State(state.isActive(), state.getTimestamp());
+ }
+
+ private static ExecutionState convertHlrcToInternal(org.elasticsearch.client.watcher.ExecutionState executionState) {
+ return ExecutionState.valueOf(executionState.name());
+ }
+
+ private static ActionStatus.Execution convertHlrcToInternal(org.elasticsearch.client.watcher.ActionStatus.Execution execution) {
+ if (execution.successful()) {
+ return ActionStatus.Execution.successful(execution.timestamp());
+ } else {
+ return ActionStatus.Execution.failure(execution.timestamp(), execution.reason());
+ }
+ }
+
+ private static ActionStatus.Throttle convertHlrcToInternal(org.elasticsearch.client.watcher.ActionStatus.Throttle throttle) {
+ return new ActionStatus.Throttle(throttle.timestamp(), throttle.reason());
+ }
+}
diff --git a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/get/TransportGetWatchAction.java b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/get/TransportGetWatchAction.java
index 2e6a49ac8bdc1..4262703f7db1a 100644
--- a/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/get/TransportGetWatchAction.java
+++ b/x-pack/plugin/watcher/src/main/java/org/elasticsearch/xpack/watcher/transport/actions/get/TransportGetWatchAction.java
@@ -19,6 +19,7 @@
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.watcher.support.xcontent.WatcherParams;
+import org.elasticsearch.xpack.core.watcher.support.xcontent.XContentSource;
import org.elasticsearch.xpack.core.watcher.transport.actions.get.GetWatchAction;
import org.elasticsearch.xpack.core.watcher.transport.actions.get.GetWatchRequest;
import org.elasticsearch.xpack.core.watcher.transport.actions.get.GetWatchResponse;
@@ -71,7 +72,7 @@ protected void doExecute(GetWatchRequest request, ActionListener