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:
Julie Tibshirani 2018-09-19 12:17:08 -07:00 committed by GitHub
parent a92dda2e7e
commit c764012347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 874 additions and 0 deletions

View File

@ -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");
}
}

View File

@ -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();
}
}

View File

@ -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");
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}