Create a WatchStatus class for the high-level REST client. (#33527)
This class will be used in a few of the watcher responses ('get watch', 'ack watch', etc.), so it's being introduced first in its own PR.
This commit is contained in:
parent
a92dda2e7e
commit
c764012347
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* 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.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ActionStatus {
|
||||
|
||||
private final AckStatus ackStatus;
|
||||
@Nullable private final Execution lastExecution;
|
||||
@Nullable private final Execution lastSuccessfulExecution;
|
||||
@Nullable private final Throttle lastThrottle;
|
||||
|
||||
public ActionStatus(AckStatus ackStatus,
|
||||
@Nullable Execution lastExecution,
|
||||
@Nullable Execution lastSuccessfulExecution,
|
||||
@Nullable Throttle lastThrottle) {
|
||||
this.ackStatus = ackStatus;
|
||||
this.lastExecution = lastExecution;
|
||||
this.lastSuccessfulExecution = lastSuccessfulExecution;
|
||||
this.lastThrottle = lastThrottle;
|
||||
}
|
||||
|
||||
public AckStatus ackStatus() {
|
||||
return ackStatus;
|
||||
}
|
||||
|
||||
public Execution lastExecution() {
|
||||
return lastExecution;
|
||||
}
|
||||
|
||||
public Execution lastSuccessfulExecution() {
|
||||
return lastSuccessfulExecution;
|
||||
}
|
||||
|
||||
public Throttle lastThrottle() {
|
||||
return lastThrottle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ActionStatus that = (ActionStatus) o;
|
||||
|
||||
return Objects.equals(ackStatus, that.ackStatus) &&
|
||||
Objects.equals(lastExecution, that.lastExecution) &&
|
||||
Objects.equals(lastSuccessfulExecution, that.lastSuccessfulExecution) &&
|
||||
Objects.equals(lastThrottle, that.lastThrottle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(ackStatus, lastExecution, lastSuccessfulExecution, lastThrottle);
|
||||
}
|
||||
|
||||
public static ActionStatus parse(String actionId, XContentParser parser) throws IOException {
|
||||
AckStatus ackStatus = null;
|
||||
Execution lastExecution = null;
|
||||
Execution lastSuccessfulExecution = null;
|
||||
Throttle lastThrottle = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.ACK_STATUS.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
ackStatus = AckStatus.parse(actionId, parser);
|
||||
} else if (Field.LAST_EXECUTION.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
lastExecution = Execution.parse(actionId, parser);
|
||||
} else if (Field.LAST_SUCCESSFUL_EXECUTION.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
lastSuccessfulExecution = Execution.parse(actionId, parser);
|
||||
} else if (Field.LAST_THROTTLE.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
lastThrottle = Throttle.parse(actionId, parser);
|
||||
} else {
|
||||
parser.skipChildren();
|
||||
}
|
||||
}
|
||||
if (ackStatus == null) {
|
||||
throw new ElasticsearchParseException("could not parse action status for [{}]. missing required field [{}]",
|
||||
actionId, Field.ACK_STATUS.getPreferredName());
|
||||
}
|
||||
return new ActionStatus(ackStatus, lastExecution, lastSuccessfulExecution, lastThrottle);
|
||||
}
|
||||
|
||||
public static class AckStatus {
|
||||
|
||||
public enum State {
|
||||
AWAITS_SUCCESSFUL_EXECUTION,
|
||||
ACKABLE,
|
||||
ACKED;
|
||||
}
|
||||
|
||||
private final DateTime timestamp;
|
||||
private final State state;
|
||||
|
||||
public AckStatus(DateTime timestamp, State state) {
|
||||
this.timestamp = timestamp.toDateTime(DateTimeZone.UTC);
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public DateTime timestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public State state() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
AckStatus ackStatus = (AckStatus) o;
|
||||
|
||||
return Objects.equals(timestamp, ackStatus.timestamp) && Objects.equals(state, ackStatus.state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(timestamp, state);
|
||||
}
|
||||
|
||||
public static AckStatus parse(String actionId, XContentParser parser) throws IOException {
|
||||
DateTime timestamp = null;
|
||||
State state = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.TIMESTAMP.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
timestamp = WatchStatusDateParser.parseDate(parser.text());
|
||||
} else if (Field.ACK_STATUS_STATE.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
state = State.valueOf(parser.text().toUpperCase(Locale.ROOT));
|
||||
} else {
|
||||
parser.skipChildren();
|
||||
}
|
||||
}
|
||||
if (timestamp == null) {
|
||||
throw new ElasticsearchParseException("could not parse action status for [{}]. missing required field [{}.{}]",
|
||||
actionId, Field.ACK_STATUS.getPreferredName(), Field.TIMESTAMP.getPreferredName());
|
||||
}
|
||||
if (state == null) {
|
||||
throw new ElasticsearchParseException("could not parse action status for [{}]. missing required field [{}.{}]",
|
||||
actionId, Field.ACK_STATUS.getPreferredName(), Field.ACK_STATUS_STATE.getPreferredName());
|
||||
}
|
||||
return new AckStatus(timestamp, state);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Execution {
|
||||
|
||||
public static Execution successful(DateTime timestamp) {
|
||||
return new Execution(timestamp, true, null);
|
||||
}
|
||||
|
||||
public static Execution failure(DateTime timestamp, String reason) {
|
||||
return new Execution(timestamp, false, reason);
|
||||
}
|
||||
|
||||
private final DateTime timestamp;
|
||||
private final boolean successful;
|
||||
private final String reason;
|
||||
|
||||
private Execution(DateTime timestamp, boolean successful, String reason) {
|
||||
this.timestamp = timestamp.toDateTime(DateTimeZone.UTC);
|
||||
this.successful = successful;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public DateTime timestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public boolean successful() {
|
||||
return successful;
|
||||
}
|
||||
|
||||
public String reason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Execution execution = (Execution) o;
|
||||
|
||||
return Objects.equals(successful, execution.successful) &&
|
||||
Objects.equals(timestamp, execution.timestamp) &&
|
||||
Objects.equals(reason, execution.reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(timestamp, successful, reason);
|
||||
}
|
||||
|
||||
public static Execution parse(String actionId, XContentParser parser) throws IOException {
|
||||
DateTime timestamp = null;
|
||||
Boolean successful = null;
|
||||
String reason = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.TIMESTAMP.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
timestamp = WatchStatusDateParser.parseDate(parser.text());
|
||||
} else if (Field.EXECUTION_SUCCESSFUL.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
successful = parser.booleanValue();
|
||||
} else if (Field.REASON.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
reason = parser.text();
|
||||
} else {
|
||||
parser.skipChildren();
|
||||
}
|
||||
}
|
||||
if (timestamp == null) {
|
||||
throw new ElasticsearchParseException("could not parse action status for [{}]. missing required field [{}.{}]",
|
||||
actionId, Field.LAST_EXECUTION.getPreferredName(), Field.TIMESTAMP.getPreferredName());
|
||||
}
|
||||
if (successful == null) {
|
||||
throw new ElasticsearchParseException("could not parse action status for [{}]. missing required field [{}.{}]",
|
||||
actionId, Field.LAST_EXECUTION.getPreferredName(), Field.EXECUTION_SUCCESSFUL.getPreferredName());
|
||||
}
|
||||
if (successful) {
|
||||
return successful(timestamp);
|
||||
}
|
||||
if (reason == null) {
|
||||
throw new ElasticsearchParseException("could not parse action status for [{}]. missing required field for unsuccessful" +
|
||||
" execution [{}.{}]", actionId, Field.LAST_EXECUTION.getPreferredName(), Field.REASON.getPreferredName());
|
||||
}
|
||||
return failure(timestamp, reason);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Throttle {
|
||||
|
||||
private final DateTime timestamp;
|
||||
private final String reason;
|
||||
|
||||
public Throttle(DateTime timestamp, String reason) {
|
||||
this.timestamp = timestamp.toDateTime(DateTimeZone.UTC);
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public DateTime timestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String reason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Throttle throttle = (Throttle) o;
|
||||
return Objects.equals(timestamp, throttle.timestamp) && Objects.equals(reason, throttle.reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(timestamp, reason);
|
||||
}
|
||||
|
||||
public static Throttle parse(String actionId, XContentParser parser) throws IOException {
|
||||
DateTime timestamp = null;
|
||||
String reason = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.TIMESTAMP.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
timestamp = WatchStatusDateParser.parseDate(parser.text());
|
||||
} else if (Field.REASON.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
reason = parser.text();
|
||||
} else {
|
||||
parser.skipChildren();
|
||||
}
|
||||
}
|
||||
if (timestamp == null) {
|
||||
throw new ElasticsearchParseException("could not parse action status for [{}]. missing required field [{}.{}]",
|
||||
actionId, Field.LAST_THROTTLE.getPreferredName(), Field.TIMESTAMP.getPreferredName());
|
||||
}
|
||||
if (reason == null) {
|
||||
throw new ElasticsearchParseException("could not parse action status for [{}]. missing required field [{}.{}]",
|
||||
actionId, Field.LAST_THROTTLE.getPreferredName(), Field.REASON.getPreferredName());
|
||||
}
|
||||
return new Throttle(timestamp, reason);
|
||||
}
|
||||
}
|
||||
|
||||
private interface Field {
|
||||
ParseField ACK_STATUS = new ParseField("ack");
|
||||
ParseField ACK_STATUS_STATE = new ParseField("state");
|
||||
ParseField LAST_EXECUTION = new ParseField("last_execution");
|
||||
ParseField LAST_SUCCESSFUL_EXECUTION = new ParseField("last_successful_execution");
|
||||
ParseField EXECUTION_SUCCESSFUL = new ParseField("successful");
|
||||
ParseField LAST_THROTTLE = new ParseField("last_throttle");
|
||||
ParseField TIMESTAMP = new ParseField("timestamp");
|
||||
ParseField REASON = new ParseField("reason");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 java.util.Locale;
|
||||
|
||||
public enum ExecutionState {
|
||||
|
||||
// the condition of the watch was not met
|
||||
EXECUTION_NOT_NEEDED,
|
||||
|
||||
// Execution has been throttled due to time-based throttling - this might only affect a single action though
|
||||
THROTTLED,
|
||||
|
||||
// Execution has been throttled due to ack-based throttling/muting of an action - this might only affect a single action though
|
||||
ACKNOWLEDGED,
|
||||
|
||||
// regular execution
|
||||
EXECUTED,
|
||||
|
||||
// an error in the condition or the execution of the input
|
||||
FAILED,
|
||||
|
||||
// a rejection due to a filled up threadpool
|
||||
THREADPOOL_REJECTION,
|
||||
|
||||
// the execution was scheduled, but in between the watch was deleted
|
||||
NOT_EXECUTED_WATCH_MISSING,
|
||||
|
||||
// even though the execution was scheduled, it was not executed, because the watch was already queued in the thread pool
|
||||
NOT_EXECUTED_ALREADY_QUEUED,
|
||||
|
||||
// this can happen when a watch was executed, but not completely finished (the triggered watch entry was not deleted), and then
|
||||
// watcher is restarted (manually or due to host switch) - the triggered watch will be executed but the history entry already
|
||||
// exists
|
||||
EXECUTED_MULTIPLE_TIMES;
|
||||
|
||||
public String id() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public static ExecutionState resolve(String id) {
|
||||
return valueOf(id.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return id();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* 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.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.unmodifiableMap;
|
||||
import static org.elasticsearch.client.watcher.WatchStatusDateParser.parseDate;
|
||||
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
public class WatchStatus {
|
||||
|
||||
private final State state;
|
||||
|
||||
private final ExecutionState executionState;
|
||||
private final DateTime lastChecked;
|
||||
private final DateTime lastMetCondition;
|
||||
private final long version;
|
||||
private final Map<String, ActionStatus> actions;
|
||||
|
||||
public WatchStatus(long version,
|
||||
State state,
|
||||
ExecutionState executionState,
|
||||
DateTime lastChecked,
|
||||
DateTime lastMetCondition,
|
||||
Map<String, ActionStatus> actions) {
|
||||
this.version = version;
|
||||
this.lastChecked = lastChecked;
|
||||
this.lastMetCondition = lastMetCondition;
|
||||
this.actions = actions;
|
||||
this.state = state;
|
||||
this.executionState = executionState;
|
||||
}
|
||||
|
||||
public State state() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public boolean checked() {
|
||||
return lastChecked != null;
|
||||
}
|
||||
|
||||
public DateTime lastChecked() {
|
||||
return lastChecked;
|
||||
}
|
||||
|
||||
public DateTime lastMetCondition() {
|
||||
return lastMetCondition;
|
||||
}
|
||||
|
||||
public ActionStatus actionStatus(String actionId) {
|
||||
return actions.get(actionId);
|
||||
}
|
||||
|
||||
public long version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public ExecutionState getExecutionState() {
|
||||
return executionState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
WatchStatus that = (WatchStatus) o;
|
||||
|
||||
return Objects.equals(lastChecked, that.lastChecked) &&
|
||||
Objects.equals(lastMetCondition, that.lastMetCondition) &&
|
||||
Objects.equals(version, that.version) &&
|
||||
Objects.equals(executionState, that.executionState) &&
|
||||
Objects.equals(actions, that.actions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(lastChecked, lastMetCondition, actions, version, executionState);
|
||||
}
|
||||
|
||||
public static WatchStatus parse(XContentParser parser) throws IOException {
|
||||
State state = null;
|
||||
ExecutionState executionState = null;
|
||||
DateTime lastChecked = null;
|
||||
DateTime lastMetCondition = null;
|
||||
Map<String, ActionStatus> actions = null;
|
||||
long version = -1;
|
||||
|
||||
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation);
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.STATE.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
try {
|
||||
state = State.parse(parser);
|
||||
} catch (ElasticsearchParseException e) {
|
||||
throw new ElasticsearchParseException("could not parse watch status. failed to parse field [{}]",
|
||||
e, currentFieldName);
|
||||
}
|
||||
} else if (Field.VERSION.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
if (token.isValue()) {
|
||||
version = parser.longValue();
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse watch status. expecting field [{}] to hold a long " +
|
||||
"value, found [{}] instead", currentFieldName, token);
|
||||
}
|
||||
} else if (Field.LAST_CHECKED.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
if (token.isValue()) {
|
||||
lastChecked = parseDate(currentFieldName, parser);
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse watch status. expecting field [{}] to hold a date " +
|
||||
"value, found [{}] instead", currentFieldName, token);
|
||||
}
|
||||
} else if (Field.LAST_MET_CONDITION.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
if (token.isValue()) {
|
||||
lastMetCondition = parseDate(currentFieldName, parser);
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse watch status. expecting field [{}] to hold a date " +
|
||||
"value, found [{}] instead", currentFieldName, token);
|
||||
}
|
||||
} else if (Field.EXECUTION_STATE.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
if (token.isValue()) {
|
||||
executionState = ExecutionState.resolve(parser.text());
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse watch status. expecting field [{}] to hold a string " +
|
||||
"value, found [{}] instead", currentFieldName, token);
|
||||
}
|
||||
} else if (Field.ACTIONS.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
actions = new HashMap<>();
|
||||
if (token == XContentParser.Token.START_OBJECT) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else {
|
||||
ActionStatus actionStatus = ActionStatus.parse(currentFieldName, parser);
|
||||
actions.put(currentFieldName, actionStatus);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ElasticsearchParseException("could not parse watch status. expecting field [{}] to be an object, " +
|
||||
"found [{}] instead", currentFieldName, token);
|
||||
}
|
||||
} else {
|
||||
parser.skipChildren();
|
||||
}
|
||||
}
|
||||
|
||||
actions = actions == null ? emptyMap() : unmodifiableMap(actions);
|
||||
return new WatchStatus(version, state, executionState, lastChecked, lastMetCondition, actions);
|
||||
}
|
||||
|
||||
public static class State {
|
||||
|
||||
private final boolean active;
|
||||
private final DateTime timestamp;
|
||||
|
||||
public State(boolean active, DateTime timestamp) {
|
||||
this.active = active;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public DateTime getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public static State parse(XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() != XContentParser.Token.START_OBJECT) {
|
||||
throw new ElasticsearchParseException("expected an object but found [{}] instead", parser.currentToken());
|
||||
}
|
||||
boolean active = true;
|
||||
DateTime timestamp = DateTime.now(UTC);
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (Field.ACTIVE.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
active = parser.booleanValue();
|
||||
} else if (Field.TIMESTAMP.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
timestamp = parseDate(currentFieldName, parser);
|
||||
}
|
||||
}
|
||||
return new State(active, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Field {
|
||||
ParseField STATE = new ParseField("state");
|
||||
ParseField ACTIVE = new ParseField("active");
|
||||
ParseField TIMESTAMP = new ParseField("timestamp");
|
||||
ParseField LAST_CHECKED = new ParseField("last_checked");
|
||||
ParseField LAST_MET_CONDITION = new ParseField("last_met_condition");
|
||||
ParseField ACTIONS = new ParseField("actions");
|
||||
ParseField VERSION = new ParseField("version");
|
||||
ParseField EXECUTION_STATE = new ParseField("execution_state");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.DateFieldMapper;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public final class WatchStatusDateParser {
|
||||
|
||||
private static final FormatDateTimeFormatter FORMATTER = DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER;
|
||||
|
||||
private WatchStatusDateParser() {
|
||||
// Prevent instantiation.
|
||||
}
|
||||
|
||||
public static DateTime parseDate(String fieldName, XContentParser parser) throws IOException {
|
||||
XContentParser.Token token = parser.currentToken();
|
||||
if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||
return new DateTime(parser.longValue(), DateTimeZone.UTC);
|
||||
}
|
||||
if (token == XContentParser.Token.VALUE_STRING) {
|
||||
DateTime dateTime = parseDate(parser.text());
|
||||
return dateTime.toDateTime(DateTimeZone.UTC);
|
||||
}
|
||||
if (token == XContentParser.Token.VALUE_NULL) {
|
||||
return null;
|
||||
}
|
||||
throw new ElasticsearchParseException("could not parse date/time. expected date field [{}] " +
|
||||
"to be either a number or a string but found [{}] instead", fieldName, token);
|
||||
}
|
||||
|
||||
public static DateTime parseDate(String text) {
|
||||
return FORMATTER.parser().parseDateTime(text);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* 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.bytes.BytesReference;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
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.test.ESTestCase;
|
||||
import org.elasticsearch.test.XContentTestUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class WatchStatusTests extends ESTestCase {
|
||||
|
||||
public void testBasicParsing() throws IOException {
|
||||
int expectedVersion = randomIntBetween(0, 100);
|
||||
ExecutionState expectedExecutionState = randomFrom(ExecutionState.values());
|
||||
boolean expectedActive = randomBoolean();
|
||||
ActionStatus.AckStatus.State expectedAckState = randomFrom(ActionStatus.AckStatus.State.values());
|
||||
|
||||
XContentBuilder builder = createTestXContent(expectedVersion, expectedExecutionState,
|
||||
expectedActive, expectedAckState);
|
||||
BytesReference bytes = BytesReference.bytes(builder);
|
||||
|
||||
WatchStatus watchStatus = parse(builder.contentType(), bytes);
|
||||
|
||||
assertEquals(expectedVersion, watchStatus.version());
|
||||
assertEquals(expectedExecutionState, watchStatus.getExecutionState());
|
||||
|
||||
assertEquals(new DateTime(1432663467763L, DateTimeZone.UTC), watchStatus.lastChecked());
|
||||
assertEquals(DateTime.parse("2015-05-26T18:04:27.763Z"), watchStatus.lastMetCondition());
|
||||
|
||||
WatchStatus.State watchState = watchStatus.state();
|
||||
assertEquals(expectedActive, watchState.isActive());
|
||||
assertEquals(DateTime.parse("2015-05-26T18:04:27.723Z"), watchState.getTimestamp());
|
||||
|
||||
ActionStatus actionStatus = watchStatus.actionStatus("test_index");
|
||||
assertNotNull(actionStatus);
|
||||
|
||||
ActionStatus.AckStatus ackStatus = actionStatus.ackStatus();
|
||||
assertEquals(DateTime.parse("2015-05-26T18:04:27.763Z"), ackStatus.timestamp());
|
||||
assertEquals(expectedAckState, ackStatus.state());
|
||||
|
||||
ActionStatus.Execution lastExecution = actionStatus.lastExecution();
|
||||
assertEquals(DateTime.parse("2015-05-25T18:04:27.733Z"), lastExecution.timestamp());
|
||||
assertFalse(lastExecution.successful());
|
||||
assertEquals("failed to send email", lastExecution.reason());
|
||||
|
||||
ActionStatus.Execution lastSuccessfulExecution = actionStatus.lastSuccessfulExecution();
|
||||
assertEquals(DateTime.parse("2015-05-25T18:04:27.773Z"), lastSuccessfulExecution.timestamp());
|
||||
assertTrue(lastSuccessfulExecution.successful());
|
||||
assertNull(lastSuccessfulExecution.reason());
|
||||
|
||||
ActionStatus.Throttle lastThrottle = actionStatus.lastThrottle();
|
||||
assertEquals(DateTime.parse("2015-04-25T18:05:23.445Z"), lastThrottle.timestamp());
|
||||
assertEquals("throttling interval is set to [5 seconds] ...", lastThrottle.reason());
|
||||
}
|
||||
|
||||
public void testParsingWithUnknownKeys() throws IOException {
|
||||
int expectedVersion = randomIntBetween(0, 100);
|
||||
ExecutionState expectedExecutionState = randomFrom(ExecutionState.values());
|
||||
boolean expectedActive = randomBoolean();
|
||||
ActionStatus.AckStatus.State expectedAckState = randomFrom(ActionStatus.AckStatus.State.values());
|
||||
|
||||
XContentBuilder builder = createTestXContent(expectedVersion, expectedExecutionState,
|
||||
expectedActive, expectedAckState);
|
||||
BytesReference bytes = BytesReference.bytes(builder);
|
||||
|
||||
Predicate<String> excludeFilter = field -> field.equals("actions");
|
||||
BytesReference bytesWithRandomFields = XContentTestUtils.insertRandomFields(
|
||||
builder.contentType(), bytes, excludeFilter, random());
|
||||
|
||||
WatchStatus watchStatus = parse(builder.contentType(), bytesWithRandomFields);
|
||||
|
||||
assertEquals(expectedVersion, watchStatus.version());
|
||||
assertEquals(expectedExecutionState, watchStatus.getExecutionState());
|
||||
}
|
||||
|
||||
public void testOptionalFieldsParsing() throws IOException {
|
||||
XContentType contentType = randomFrom(XContentType.values());
|
||||
XContentBuilder builder = XContentFactory.contentBuilder(contentType).startObject()
|
||||
.field("version", 42)
|
||||
.startObject("actions")
|
||||
.startObject("test_index")
|
||||
.startObject("ack")
|
||||
.field("timestamp", "2015-05-26T18:04:27.763Z")
|
||||
.field("state", "ackable")
|
||||
.endObject()
|
||||
.startObject("last_execution")
|
||||
.field("timestamp", "2015-05-25T18:04:27.733Z")
|
||||
.field("successful", false)
|
||||
.field("reason", "failed to send email")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject();
|
||||
BytesReference bytes = BytesReference.bytes(builder);
|
||||
|
||||
WatchStatus watchStatus = parse(builder.contentType(), bytes);
|
||||
|
||||
assertEquals(42, watchStatus.version());
|
||||
assertNull(watchStatus.getExecutionState());
|
||||
assertFalse(watchStatus.checked());
|
||||
}
|
||||
|
||||
private XContentBuilder createTestXContent(int version,
|
||||
ExecutionState executionState,
|
||||
boolean active,
|
||||
ActionStatus.AckStatus.State ackState) throws IOException {
|
||||
XContentType contentType = randomFrom(XContentType.values());
|
||||
return XContentFactory.contentBuilder(contentType).startObject()
|
||||
.field("version", version)
|
||||
.field("execution_state", executionState)
|
||||
.field("last_checked", 1432663467763L)
|
||||
.field("last_met_condition", "2015-05-26T18:04:27.763Z")
|
||||
.startObject("state")
|
||||
.field("active", active)
|
||||
.field("timestamp", "2015-05-26T18:04:27.723Z")
|
||||
.endObject()
|
||||
.startObject("actions")
|
||||
.startObject("test_index")
|
||||
.startObject("ack")
|
||||
.field("timestamp", "2015-05-26T18:04:27.763Z")
|
||||
.field("state", ackState)
|
||||
.endObject()
|
||||
.startObject("last_execution")
|
||||
.field("timestamp", "2015-05-25T18:04:27.733Z")
|
||||
.field("successful", false)
|
||||
.field("reason", "failed to send email")
|
||||
.endObject()
|
||||
.startObject("last_successful_execution")
|
||||
.field("timestamp", "2015-05-25T18:04:27.773Z")
|
||||
.field("successful", true)
|
||||
.endObject()
|
||||
.startObject("last_throttle")
|
||||
.field("timestamp", "2015-04-25T18:05:23.445Z")
|
||||
.field("reason", "throttling interval is set to [5 seconds] ...")
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject();
|
||||
}
|
||||
|
||||
private WatchStatus parse(XContentType contentType, BytesReference bytes) throws IOException {
|
||||
XContentParser parser = XContentFactory.xContent(contentType)
|
||||
.createParser(NamedXContentRegistry.EMPTY, null, bytes.streamInput());
|
||||
parser.nextToken();
|
||||
|
||||
return WatchStatus.parse(parser);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue