Skip to content

Commit c076366

Browse files
committed
HLRC: execute watch API (#35868)
This change adds support for the execute watch API in the high level rest client
1 parent 9fb2e0c commit c076366

File tree

13 files changed

+948
-20
lines changed

13 files changed

+948
-20
lines changed

client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java

+29
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.elasticsearch.client.watcher.ActivateWatchResponse;
2727
import org.elasticsearch.client.watcher.AckWatchRequest;
2828
import org.elasticsearch.client.watcher.AckWatchResponse;
29+
import org.elasticsearch.client.watcher.ExecuteWatchRequest;
30+
import org.elasticsearch.client.watcher.ExecuteWatchResponse;
2931
import org.elasticsearch.client.watcher.GetWatchRequest;
3032
import org.elasticsearch.client.watcher.GetWatchResponse;
3133
import org.elasticsearch.client.watcher.StartWatchServiceRequest;
@@ -269,6 +271,33 @@ public void activateWatchAsync(ActivateWatchRequest request, RequestOptions opti
269271
ActivateWatchResponse::fromXContent, listener, singleton(404));
270272
}
271273

274+
/**
275+
* Execute a watch on the cluster
276+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-execute-watch.html">
277+
* the docs</a> for more.
278+
* @param request the request
279+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
280+
* @return the response
281+
* @throws IOException if there is a problem sending the request or parsing the response
282+
*/
283+
public ExecuteWatchResponse executeWatch(ExecuteWatchRequest request, RequestOptions options) throws IOException {
284+
return restHighLevelClient.performRequestAndParseEntity(request, WatcherRequestConverters::executeWatch, options,
285+
ExecuteWatchResponse::fromXContent, emptySet());
286+
}
287+
288+
/**
289+
* Asynchronously execute a watch on the cluster
290+
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-execute-watch.html">
291+
* the docs</a> for more.
292+
* @param request the request
293+
* @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
294+
* @param listener the listener to be notifed upon request completion
295+
*/
296+
public void executeWatchAsync(ExecuteWatchRequest request, RequestOptions options, ActionListener<ExecuteWatchResponse> listener) {
297+
restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::executeWatch, options,
298+
ExecuteWatchResponse::fromXContent, listener, emptySet());
299+
}
300+
272301
/**
273302
* Get the watcher stats
274303
* See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-stats.html">

client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherRequestConverters.java

+28-2
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@
2525
import org.apache.http.client.methods.HttpPut;
2626
import org.apache.http.entity.ByteArrayEntity;
2727
import org.apache.http.entity.ContentType;
28-
import org.elasticsearch.client.watcher.DeactivateWatchRequest;
29-
import org.elasticsearch.client.watcher.ActivateWatchRequest;
3028
import org.elasticsearch.client.watcher.AckWatchRequest;
29+
import org.elasticsearch.client.watcher.ActivateWatchRequest;
30+
import org.elasticsearch.client.watcher.DeactivateWatchRequest;
3131
import org.elasticsearch.client.watcher.DeleteWatchRequest;
32+
import org.elasticsearch.client.watcher.ExecuteWatchRequest;
3233
import org.elasticsearch.client.watcher.GetWatchRequest;
3334
import org.elasticsearch.client.watcher.PutWatchRequest;
3435
import org.elasticsearch.client.watcher.StartWatchServiceRequest;
3536
import org.elasticsearch.client.watcher.StopWatchServiceRequest;
3637
import org.elasticsearch.client.watcher.WatcherStatsRequest;
3738
import org.elasticsearch.common.bytes.BytesReference;
39+
import org.elasticsearch.common.xcontent.XContentType;
40+
41+
import java.io.IOException;
3842

3943
final class WatcherRequestConverters {
4044

@@ -108,6 +112,28 @@ static Request deleteWatch(DeleteWatchRequest deleteWatchRequest) {
108112
return request;
109113
}
110114

115+
static Request executeWatch(ExecuteWatchRequest executeWatchRequest) throws IOException {
116+
String endpoint = new RequestConverters.EndpointBuilder()
117+
.addPathPartAsIs("_xpack", "watcher", "watch")
118+
.addPathPart(executeWatchRequest.getId()) // will ignore if ID is null
119+
.addPathPartAsIs("_execute").build();
120+
121+
Request request = new Request(HttpPost.METHOD_NAME, endpoint);
122+
RequestConverters.Params params = new RequestConverters.Params(request);
123+
if (executeWatchRequest.isDebug()) {
124+
params.putParam("debug", "true");
125+
}
126+
if (executeWatchRequest.ignoreCondition()) {
127+
params.putParam("ignore_condition", "true");
128+
}
129+
if (executeWatchRequest.recordExecution()) {
130+
params.putParam("record_execution", "true");
131+
}
132+
133+
request.setEntity(RequestConverters.createEntity(executeWatchRequest, XContentType.JSON));
134+
return request;
135+
}
136+
111137
public static Request ackWatch(AckWatchRequest ackWatchRequest) {
112138
String endpoint = new RequestConverters.EndpointBuilder()
113139
.addPathPartAsIs("_xpack", "watcher", "watch")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.client.common;
21+
22+
import org.elasticsearch.common.xcontent.ObjectPath;
23+
import org.elasticsearch.common.xcontent.XContentParser;
24+
import org.elasticsearch.common.xcontent.XContentUtils;
25+
26+
import java.io.IOException;
27+
import java.util.List;
28+
import java.util.Map;
29+
30+
/**
31+
* Encapsulates the xcontent source
32+
*/
33+
public class XContentSource {
34+
35+
private final Object data;
36+
37+
/**
38+
* Constructs a new XContentSource out of the given parser
39+
*/
40+
public XContentSource(XContentParser parser) throws IOException {
41+
this.data = XContentUtils.readValue(parser, parser.nextToken());
42+
}
43+
44+
/**
45+
* @return true if the top level value of the source is a map
46+
*/
47+
public boolean isMap() {
48+
return data instanceof Map;
49+
}
50+
51+
/**
52+
* @return The source as a map
53+
*/
54+
@SuppressWarnings("unchecked")
55+
public Map<String, Object> getAsMap() {
56+
return (Map<String, Object>) data;
57+
}
58+
59+
/**
60+
* @return true if the top level value of the source is a list
61+
*/
62+
public boolean isList() {
63+
return data instanceof List;
64+
}
65+
66+
/**
67+
* @return The source as a list
68+
*/
69+
@SuppressWarnings("unchecked")
70+
public List<Object> getAsList() {
71+
return (List<Object>) data;
72+
}
73+
74+
/**
75+
* Extracts a value identified by the given path in the source.
76+
*
77+
* @param path a dot notation path to the requested value
78+
* @return The extracted value or {@code null} if no value is associated with the given path
79+
*/
80+
@SuppressWarnings("unchecked")
81+
public <T> T getValue(String path) {
82+
return (T) ObjectPath.eval(path, data);
83+
}
84+
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.client.watcher;
21+
22+
import org.elasticsearch.client.Validatable;
23+
import org.elasticsearch.common.Nullable;
24+
import org.elasticsearch.common.bytes.BytesArray;
25+
import org.elasticsearch.common.bytes.BytesReference;
26+
import org.elasticsearch.common.xcontent.ToXContentObject;
27+
import org.elasticsearch.common.xcontent.XContentBuilder;
28+
import org.elasticsearch.common.xcontent.XContentType;
29+
30+
import java.io.IOException;
31+
import java.util.HashMap;
32+
import java.util.Map;
33+
import java.util.Objects;
34+
35+
/**
36+
* An execute watch request to execute a watch by id or inline
37+
*/
38+
public class ExecuteWatchRequest implements Validatable, ToXContentObject {
39+
40+
public enum ActionExecutionMode {
41+
SIMULATE, FORCE_SIMULATE, EXECUTE, FORCE_EXECUTE, SKIP
42+
}
43+
44+
private final String id;
45+
private final BytesReference watchContent;
46+
47+
private boolean ignoreCondition = false;
48+
private boolean recordExecution = false;
49+
private boolean debug = false;
50+
51+
@Nullable
52+
private BytesReference triggerData = null;
53+
54+
@Nullable
55+
private BytesReference alternativeInput = null;
56+
57+
private Map<String, ActionExecutionMode> actionModes = new HashMap<>();
58+
59+
/**
60+
* Execute an existing watch on the cluster
61+
*
62+
* @param id the id of the watch to execute
63+
*/
64+
public static ExecuteWatchRequest byId(String id) {
65+
return new ExecuteWatchRequest(Objects.requireNonNull(id, "Watch id cannot be null"), null);
66+
}
67+
68+
/**
69+
* Execute an inline watch
70+
* @param watchContent the JSON definition of the watch
71+
*/
72+
public static ExecuteWatchRequest inline(String watchContent) {
73+
return new ExecuteWatchRequest(null, Objects.requireNonNull(watchContent, "Watch content cannot be null"));
74+
}
75+
76+
private ExecuteWatchRequest(String id, String watchContent) {
77+
this.id = id;
78+
this.watchContent = watchContent == null ? null : new BytesArray(watchContent);
79+
}
80+
81+
public String getId() {
82+
return this.id;
83+
}
84+
85+
/**
86+
* @param ignoreCondition set if the condition for this execution be ignored
87+
*/
88+
public void setIgnoreCondition(boolean ignoreCondition) {
89+
this.ignoreCondition = ignoreCondition;
90+
}
91+
92+
public boolean ignoreCondition() {
93+
return ignoreCondition;
94+
}
95+
96+
/**
97+
* @param recordExecution Sets if this execution be recorded in the history index
98+
*/
99+
public void setRecordExecution(boolean recordExecution) {
100+
if (watchContent != null && recordExecution) {
101+
throw new IllegalArgumentException("The execution of an inline watch cannot be recorded");
102+
}
103+
this.recordExecution = recordExecution;
104+
}
105+
106+
public boolean recordExecution() {
107+
return recordExecution;
108+
}
109+
110+
/**
111+
* @param alternativeInput Sets the alternative input
112+
*/
113+
public void setAlternativeInput(String alternativeInput) {
114+
this.alternativeInput = new BytesArray(alternativeInput);
115+
}
116+
117+
/**
118+
* @param data A JSON string representing the data that should be associated with the trigger event.
119+
*/
120+
public void setTriggerData(String data) {
121+
this.triggerData = new BytesArray(data);
122+
}
123+
124+
/**
125+
* Sets the action execution mode for the give action (identified by its id).
126+
*
127+
* @param actionId the action id.
128+
* @param actionMode the execution mode of the action.
129+
*/
130+
public void setActionMode(String actionId, ActionExecutionMode actionMode) {
131+
Objects.requireNonNull(actionId, "actionId cannot be null");
132+
actionModes.put(actionId, actionMode);
133+
}
134+
135+
public Map<String, ActionExecutionMode> getActionModes() {
136+
return this.actionModes;
137+
}
138+
139+
/**
140+
* @param debug indicates whether the watch should execute in debug mode. In debug mode the
141+
* returned watch record will hold the execution {@code vars}
142+
*/
143+
public void setDebug(boolean debug) {
144+
this.debug = debug;
145+
}
146+
147+
public boolean isDebug() {
148+
return debug;
149+
}
150+
151+
@Override
152+
public String toString() {
153+
return "execute[" + id + "]";
154+
}
155+
156+
@Override
157+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
158+
builder.startObject();
159+
if (triggerData != null) {
160+
builder.rawField("trigger_data", triggerData.streamInput(), XContentType.JSON);
161+
}
162+
if (alternativeInput != null) {
163+
builder.rawField("alternative_input", alternativeInput.streamInput(), XContentType.JSON);
164+
}
165+
if (actionModes.size() > 0) {
166+
builder.field("action_modes", actionModes);
167+
}
168+
if (watchContent != null) {
169+
builder.rawField("watch", watchContent.streamInput(), XContentType.JSON);
170+
}
171+
builder.endObject();
172+
return builder;
173+
}
174+
}
175+

0 commit comments

Comments
 (0)