Skip to content

Commit

Permalink
Watcher understands hidden expand wildcard value (#65372)
Browse files Browse the repository at this point in the history
Watcher has a search template that stores indices options to be used as
part of a search during watch execution, but this was not updated to be
aware of hidden indices and the `hidden` expand_wildcards option. This
change makes use of the `IndicesOptions#toXContent` method in Watcher,
which already handles the new value. Additionally, the XContent parsing
is moved to the IndicesOptions class so that we will be less likely to
miss updating this in the future.

Closes #65148
Backport of #65332
  • Loading branch information
jaymode committed Nov 23, 2020
1 parent 1828a8b commit 1a13a0b
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@
package org.elasticsearch.action.support;

import org.elasticsearch.Version;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParser.Token;
import org.elasticsearch.rest.RestRequest;

import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Map;
Expand Down Expand Up @@ -59,25 +62,7 @@ public static EnumSet<WildcardStates> parseParameter(Object value, EnumSet<Wildc
// TODO why do we let patterns like "none,all" or "open,none,closed" get used. The location of 'none' in the array changes the
// meaning of the resulting value
for (String wildcard : wildcards) {
switch (wildcard) {
case "open":
states.add(OPEN);
break;
case "closed":
states.add(CLOSED);
break;
case "hidden":
states.add(HIDDEN);
break;
case "none":
states.clear();
break;
case "all":
states = EnumSet.allOf(WildcardStates.class);
break;
default:
throw new IllegalArgumentException("No valid expand wildcard value [" + wildcard + "]");
}
updateSetForValue(states, wildcard);
}

return states;
Expand All @@ -94,6 +79,28 @@ public static XContentBuilder toXContent(EnumSet<WildcardStates> states, XConten
}
return builder;
}

private static void updateSetForValue(EnumSet<WildcardStates> states, String wildcard) {
switch (wildcard) {
case "open":
states.add(OPEN);
break;
case "closed":
states.add(CLOSED);
break;
case "hidden":
states.add(HIDDEN);
break;
case "none":
states.clear();
break;
case "all":
states.addAll(EnumSet.allOf(WildcardStates.class));
break;
default:
throw new IllegalArgumentException("No valid expand wildcard value [" + wildcard + "]");
}
}
}

public enum Option {
Expand Down Expand Up @@ -148,11 +155,6 @@ public IndicesOptions(EnumSet<Option> options, EnumSet<WildcardStates> expandWil
this.expandWildcards = expandWildcards;
}

private IndicesOptions(Collection<Option> options, Collection<WildcardStates> expandWildcards) {
this(options.isEmpty() ? Option.NONE : EnumSet.copyOf(options),
expandWildcards.isEmpty() ? WildcardStates.NONE : EnumSet.copyOf(expandWildcards));
}

/**
* @return Whether specified concrete indices should be ignored when unavailable (missing or closed)
*/
Expand Down Expand Up @@ -380,6 +382,84 @@ public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params par
return builder;
}

private static final ParseField EXPAND_WILDCARDS_FIELD = new ParseField("expand_wildcards");
private static final ParseField IGNORE_UNAVAILABLE_FIELD = new ParseField("ignore_unavailable");
private static final ParseField IGNORE_THROTTLED_FIELD = new ParseField("ignore_throttled");
private static final ParseField ALLOW_NO_INDICES_FIELD = new ParseField("allow_no_indices");

public static IndicesOptions fromXContent(XContentParser parser) throws IOException {
EnumSet<WildcardStates> wildcardStates = null;
Boolean allowNoIndices = null;
Boolean ignoreUnavailable = null;
boolean ignoreThrottled = false;
Token token = parser.currentToken() == Token.START_OBJECT ? parser.currentToken() : parser.nextToken();
String currentFieldName = null;
if (token != Token.START_OBJECT) {
throw new ElasticsearchParseException("expected START_OBJECT as the token but was " + token);
}
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == Token.START_ARRAY) {
if (EXPAND_WILDCARDS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
if (wildcardStates == null) {
wildcardStates = EnumSet.noneOf(WildcardStates.class);
while ((token = parser.nextToken()) != Token.END_ARRAY) {
if (token.isValue()) {
WildcardStates.updateSetForValue(wildcardStates, parser.text());
} else {
throw new ElasticsearchParseException("expected values within array for " +
EXPAND_WILDCARDS_FIELD.getPreferredName());
}
}
} else {
throw new ElasticsearchParseException("already parsed expand_wildcards");
}
} else {
throw new ElasticsearchParseException(EXPAND_WILDCARDS_FIELD.getPreferredName() +
" is the only field that is an array in IndicesOptions");
}
} else if (token.isValue()) {
if (EXPAND_WILDCARDS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
if (wildcardStates == null) {
wildcardStates = EnumSet.noneOf(WildcardStates.class);
WildcardStates.updateSetForValue(wildcardStates, parser.text());
} else {
throw new ElasticsearchParseException("already parsed expand_wildcards");
}
} else if (IGNORE_UNAVAILABLE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
ignoreUnavailable = parser.booleanValue();
} else if (ALLOW_NO_INDICES_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
allowNoIndices = parser.booleanValue();
} else if (IGNORE_THROTTLED_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
ignoreThrottled = parser.booleanValue();
} else {
throw new ElasticsearchParseException("could not read indices options. unexpected index option [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read indices options. unexpected object field [" +
currentFieldName + "]");
}
}

if (wildcardStates == null) {
throw new ElasticsearchParseException("indices options xcontent did not contain " + EXPAND_WILDCARDS_FIELD.getPreferredName());
}
if (ignoreUnavailable == null) {
throw new ElasticsearchParseException("indices options xcontent did not contain " +
IGNORE_UNAVAILABLE_FIELD.getPreferredName());
}
if (allowNoIndices == null) {
throw new ElasticsearchParseException("indices options xcontent did not contain " +
ALLOW_NO_INDICES_FIELD.getPreferredName());
}

return IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, wildcardStates.contains(WildcardStates.OPEN),
wildcardStates.contains(WildcardStates.CLOSED), wildcardStates.contains(WildcardStates.HIDDEN), true, false, false,
ignoreThrottled);
}

/**
* @return indices options that requires every specified index to exist, expands wildcards only to open indices and
* allows that no indices are resolved from wildcard expressions (not returning an error).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,13 @@ public void testToXContent() throws IOException {
options.isEmpty() ? Option.NONE : EnumSet.copyOf(options),
wildcardStates.isEmpty() ? WildcardStates.NONE : EnumSet.copyOf(wildcardStates));

XContentBuilder builder = XContentFactory.jsonBuilder();
builder.startObject();
indicesOptions.toXContent(builder, new MapParams(Collections.emptyMap()));
builder.endObject();
XContentParser parser = XContentType.JSON.xContent().createParser(
NamedXContentRegistry.EMPTY, null, BytesReference.bytes(builder).streamInput());
Map<String, Object> map = parser.mapOrdered();
XContentType type = randomFrom(XContentType.values());
BytesReference xContentBytes = toXContentBytes(indicesOptions, type);
Map<String, Object> map;
try (XContentParser parser = type.xContent().createParser(
NamedXContentRegistry.EMPTY, null, xContentBytes.streamInput())) {
map = parser.mapOrdered();
}

boolean open = wildcardStates.contains(WildcardStates.OPEN);
if (open) {
Expand All @@ -349,4 +349,83 @@ public void testToXContent() throws IOException {
assertEquals(map.get("allow_no_indices"), options.contains(Option.ALLOW_NO_INDICES));
assertEquals(map.get("ignore_throttled"), options.contains(Option.IGNORE_THROTTLED));
}

public void testFromXContent() throws IOException {
Collection<WildcardStates> wildcardStates = randomSubsetOf(Arrays.asList(WildcardStates.values()));
Collection<Option> options = randomSubsetOf(Arrays.asList(Option.values()));

IndicesOptions indicesOptions = new IndicesOptions(
options.isEmpty() ? Option.NONE : EnumSet.copyOf(options),
wildcardStates.isEmpty() ? WildcardStates.NONE : EnumSet.copyOf(wildcardStates));

XContentType type = randomFrom(XContentType.values());
BytesReference xContentBytes = toXContentBytes(indicesOptions, type);
IndicesOptions fromXContentOptions;
try (XContentParser parser = type.xContent().createParser(
NamedXContentRegistry.EMPTY, null, xContentBytes.streamInput())) {
fromXContentOptions = IndicesOptions.fromXContent(parser);
}

assertEquals(indicesOptions.expandWildcardsClosed(), fromXContentOptions.expandWildcardsClosed());
assertEquals(indicesOptions.expandWildcardsHidden(), fromXContentOptions.expandWildcardsHidden());
assertEquals(indicesOptions.expandWildcardsOpen(), fromXContentOptions.expandWildcardsOpen());
assertEquals(indicesOptions.ignoreUnavailable(), fromXContentOptions.ignoreUnavailable());
assertEquals(indicesOptions.allowNoIndices(), fromXContentOptions.allowNoIndices());
assertEquals(indicesOptions.ignoreThrottled(), fromXContentOptions.ignoreThrottled());
}

public void testFromXContentWithWildcardSpecialValues() throws IOException {
XContentType type = randomFrom(XContentType.values());
final boolean ignoreUnavailable = randomBoolean();
final boolean allowNoIndices = randomBoolean();

BytesReference xContentBytes;
try (XContentBuilder builder = XContentFactory.contentBuilder(type)) {
builder.startObject();
builder.field("expand_wildcards", "all");
builder.field("ignore_unavailable", ignoreUnavailable);
builder.field("allow_no_indices", allowNoIndices);
builder.endObject();
xContentBytes = BytesReference.bytes(builder);
}

IndicesOptions fromXContentOptions;
try (XContentParser parser = type.xContent().createParser(
NamedXContentRegistry.EMPTY, null, xContentBytes.streamInput())) {
fromXContentOptions = IndicesOptions.fromXContent(parser);
}
assertEquals(ignoreUnavailable, fromXContentOptions.ignoreUnavailable());
assertEquals(allowNoIndices, fromXContentOptions.allowNoIndices());
assertTrue(fromXContentOptions.expandWildcardsClosed());
assertTrue(fromXContentOptions.expandWildcardsHidden());
assertTrue(fromXContentOptions.expandWildcardsOpen());

try (XContentBuilder builder = XContentFactory.contentBuilder(type)) {
builder.startObject();
builder.field("expand_wildcards", "none");
builder.field("ignore_unavailable", ignoreUnavailable);
builder.field("allow_no_indices", allowNoIndices);
builder.endObject();
xContentBytes = BytesReference.bytes(builder);
}

try (XContentParser parser = type.xContent().createParser(
NamedXContentRegistry.EMPTY, null, xContentBytes.streamInput())) {
fromXContentOptions = IndicesOptions.fromXContent(parser);
}
assertEquals(ignoreUnavailable, fromXContentOptions.ignoreUnavailable());
assertEquals(allowNoIndices, fromXContentOptions.allowNoIndices());
assertFalse(fromXContentOptions.expandWildcardsClosed());
assertFalse(fromXContentOptions.expandWildcardsHidden());
assertFalse(fromXContentOptions.expandWildcardsOpen());
}

private BytesReference toXContentBytes(IndicesOptions indicesOptions, XContentType type) throws IOException {
try (XContentBuilder builder = XContentFactory.contentBuilder(type)) {
builder.startObject();
indicesOptions.toXContent(builder, new MapParams(Collections.emptyMap()));
builder.endObject();
return BytesReference.bytes(builder);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
}
if (indicesOptions != DEFAULT_INDICES_OPTIONS) {
builder.startObject(INDICES_OPTIONS_FIELD.getPreferredName());
String value;
if (indicesOptions.expandWildcardsClosed() && indicesOptions.expandWildcardsOpen()) {
value = "all";
} else if (indicesOptions.expandWildcardsOpen()) {
value = "open";
} else if (indicesOptions.expandWildcardsClosed()) {
value = "closed";
} else {
value = "none";
}
builder.field(EXPAND_WILDCARDS_FIELD.getPreferredName(), value);
builder.field(IGNORE_UNAVAILABLE_FIELD.getPreferredName(), indicesOptions.ignoreUnavailable());
builder.field(ALLOW_NO_INDICES_FIELD.getPreferredName(), indicesOptions.allowNoIndices());
indicesOptions.toXContent(builder, params);
builder.endObject();
}
if (template != null) {
Expand Down Expand Up @@ -228,51 +216,7 @@ public static WatcherSearchTemplateRequest fromXContent(XContentParser parser, S
searchSource = BytesReference.bytes(builder);
}
} else if (INDICES_OPTIONS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
boolean expandOpen = DEFAULT_INDICES_OPTIONS.expandWildcardsOpen();
boolean expandClosed = DEFAULT_INDICES_OPTIONS.expandWildcardsClosed();
boolean allowNoIndices = DEFAULT_INDICES_OPTIONS.allowNoIndices();
boolean ignoreUnavailable = DEFAULT_INDICES_OPTIONS.ignoreUnavailable();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token.isValue()) {
if (EXPAND_WILDCARDS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
switch (parser.text()) {
case "all":
expandOpen = true;
expandClosed = true;
break;
case "open":
expandOpen = true;
expandClosed = false;
break;
case "closed":
expandOpen = false;
expandClosed = true;
break;
case "none":
expandOpen = false;
expandClosed = false;
break;
default:
throw new ElasticsearchParseException("could not read search request. unknown value [" +
parser.text() + "] for [" + currentFieldName + "] field ");
}
} else if (IGNORE_UNAVAILABLE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
ignoreUnavailable = parser.booleanValue();
} else if (ALLOW_NO_INDICES_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
allowNoIndices = parser.booleanValue();
} else {
throw new ElasticsearchParseException("could not read search request. unexpected index option [" +
currentFieldName + "]");
}
} else {
throw new ElasticsearchParseException("could not read search request. unexpected object field [" +
currentFieldName + "]");
}
}
indicesOptions = IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, expandOpen, expandClosed,
DEFAULT_INDICES_OPTIONS);
indicesOptions = IndicesOptions.fromXContent(parser);
} else if (TEMPLATE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
template = Script.parse(parser, Script.DEFAULT_TEMPLATE_LANG);
} else {
Expand Down Expand Up @@ -343,9 +287,6 @@ public int hashCode() {
private static final ParseField BODY_FIELD = new ParseField("body");
private static final ParseField SEARCH_TYPE_FIELD = new ParseField("search_type");
private static final ParseField INDICES_OPTIONS_FIELD = new ParseField("indices_options");
private static final ParseField EXPAND_WILDCARDS_FIELD = new ParseField("expand_wildcards");
private static final ParseField IGNORE_UNAVAILABLE_FIELD = new ParseField("ignore_unavailable");
private static final ParseField ALLOW_NO_INDICES_FIELD = new ParseField("allow_no_indices");
private static final ParseField TEMPLATE_FIELD = new ParseField("template");
private static final ParseField REST_TOTAL_HITS_AS_INT_FIELD = new ParseField("rest_total_hits_as_int");

Expand Down

0 comments on commit 1a13a0b

Please sign in to comment.