Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Watcher understands hidden expand wildcard value #65332

Merged
merged 2 commits into from
Nov 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@
*/
package org.elasticsearch.action.support;

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 @@ -58,25 +61,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 @@ -93,6 +78,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 @@ -145,11 +152,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 @@ -369,6 +371,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 @@ -281,13 +281,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 @@ -306,4 +306,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 @@ -141,19 +141,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 @@ -200,51 +188,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 @@ -309,9 +253,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