From 8b9b2cb4b98360411c5031e7221e9616bf977b29 Mon Sep 17 00:00:00 2001 From: Martijn van Groningen Date: Wed, 5 Dec 2018 08:41:27 +0100 Subject: [PATCH] [CCR] Change get autofollow patterns API response format (#36203) The current response format is: ``` { "pattern1": { ... }, "pattern2": { ... } } ``` The new format is: ``` { "patterns": [ { "name": "pattern1", "pattern": { ... } }, { "name": "pattern2", "pattern": { ... } } ] } ``` This format is more structured and more friendly for parsing and generating specs. This is a breaking change, but it is better to do this now while ccr is still a beta feature than later. Follow up from #36049 --- .../ccr/GetAutoFollowPatternResponse.java | 52 +++++++++++++------ .../GetAutoFollowPatternResponseTests.java | 28 ++++++---- .../get-auto-follow-pattern.asciidoc | 22 ++++---- .../rest-api-spec/test/ccr/auto_follow.yml | 14 ++--- .../action/GetAutoFollowPatternAction.java | 19 +++++-- 5 files changed, 90 insertions(+), 45 deletions(-) diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java index f4afb2d650e9..ce42c98e57c4 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponse.java @@ -19,41 +19,59 @@ package org.elasticsearch.client.ccr; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.common.xcontent.XContentParser.Token; -import java.io.IOException; +import java.util.AbstractMap; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.NavigableMap; import java.util.Objects; +import java.util.TreeMap; +import java.util.stream.Collectors; public final class GetAutoFollowPatternResponse { - public static GetAutoFollowPatternResponse fromXContent(final XContentParser parser) throws IOException { - final Map patterns = new HashMap<>(); - for (Token token = parser.nextToken(); token != Token.END_OBJECT; token = parser.nextToken()) { - if (token == Token.FIELD_NAME) { - final String name = parser.currentName(); - final Pattern pattern = Pattern.PARSER.parse(parser, null); - patterns.put(name, pattern); - } - } - return new GetAutoFollowPatternResponse(patterns); + static final ParseField PATTERNS_FIELD = new ParseField("patterns"); + static final ParseField NAME_FIELD = new ParseField("name"); + static final ParseField PATTERN_FIELD = new ParseField("pattern"); + + private static final ConstructingObjectParser, Void> ENTRY_PARSER = new ConstructingObjectParser<>( + "get_auto_follow_pattern_response", args -> new AbstractMap.SimpleEntry<>((String) args[0], (Pattern) args[1])); + + static { + ENTRY_PARSER.declareString(ConstructingObjectParser.constructorArg(), NAME_FIELD); + ENTRY_PARSER.declareObject(ConstructingObjectParser.constructorArg(), Pattern.PARSER, PATTERN_FIELD); + } + + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>( + "get_auto_follow_pattern_response", args -> { + @SuppressWarnings("unchecked") + List> entries = (List>) args[0]; + return new GetAutoFollowPatternResponse(new TreeMap<>(entries.stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))); + }); + + static { + PARSER.declareObjectArray(ConstructingObjectParser.constructorArg(), ENTRY_PARSER, PATTERNS_FIELD); + } + + public static GetAutoFollowPatternResponse fromXContent(final XContentParser parser) { + return PARSER.apply(parser, null); } - private final Map patterns; + private final NavigableMap patterns; - GetAutoFollowPatternResponse(Map patterns) { - this.patterns = Collections.unmodifiableMap(patterns); + GetAutoFollowPatternResponse(NavigableMap patterns) { + this.patterns = Collections.unmodifiableNavigableMap(patterns); } - public Map getPatterns() { + public NavigableMap getPatterns() { return patterns; } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java index 64eb9ba4f9f7..b4a37286b4ac 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/ccr/GetAutoFollowPatternResponseTests.java @@ -27,8 +27,9 @@ import java.io.IOException; import java.util.Collections; -import java.util.HashMap; import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; import static org.elasticsearch.client.ccr.PutAutoFollowPatternRequest.FOLLOW_PATTERN_FIELD; import static org.elasticsearch.client.ccr.PutAutoFollowPatternRequest.LEADER_PATTERNS_FIELD; @@ -48,7 +49,7 @@ public void testFromXContent() throws IOException { private GetAutoFollowPatternResponse createTestInstance() { int numPatterns = randomIntBetween(0, 16); - Map patterns = new HashMap<>(numPatterns); + NavigableMap patterns = new TreeMap<>(); for (int i = 0; i < numPatterns; i++) { GetAutoFollowPatternResponse.Pattern pattern = new GetAutoFollowPatternResponse.Pattern( randomAlphaOfLength(4), Collections.singletonList(randomAlphaOfLength(4)), randomAlphaOfLength(4)); @@ -90,17 +91,26 @@ private GetAutoFollowPatternResponse createTestInstance() { public static void toXContent(GetAutoFollowPatternResponse response, XContentBuilder builder) throws IOException { builder.startObject(); { + builder.startArray(GetAutoFollowPatternResponse.PATTERNS_FIELD.getPreferredName()); for (Map.Entry entry : response.getPatterns().entrySet()) { - builder.startObject(entry.getKey()); - GetAutoFollowPatternResponse.Pattern pattern = entry.getValue(); - builder.field(REMOTE_CLUSTER_FIELD.getPreferredName(), pattern.getRemoteCluster()); - builder.field(LEADER_PATTERNS_FIELD.getPreferredName(), pattern.getLeaderIndexPatterns()); - if (pattern.getFollowIndexNamePattern()!= null) { - builder.field(FOLLOW_PATTERN_FIELD.getPreferredName(), pattern.getFollowIndexNamePattern()); + builder.startObject(); + { + builder.field(GetAutoFollowPatternResponse.NAME_FIELD.getPreferredName(), entry.getKey()); + builder.startObject(GetAutoFollowPatternResponse.PATTERN_FIELD.getPreferredName()); + { + GetAutoFollowPatternResponse.Pattern pattern = entry.getValue(); + builder.field(REMOTE_CLUSTER_FIELD.getPreferredName(), pattern.getRemoteCluster()); + builder.field(LEADER_PATTERNS_FIELD.getPreferredName(), pattern.getLeaderIndexPatterns()); + if (pattern.getFollowIndexNamePattern()!= null) { + builder.field(FOLLOW_PATTERN_FIELD.getPreferredName(), pattern.getFollowIndexNamePattern()); + } + entry.getValue().toXContentFragment(builder, ToXContent.EMPTY_PARAMS); + } + builder.endObject(); } - entry.getValue().toXContentFragment(builder, ToXContent.EMPTY_PARAMS); builder.endObject(); } + builder.endArray(); } builder.endObject(); } diff --git a/docs/reference/ccr/apis/auto-follow/get-auto-follow-pattern.asciidoc b/docs/reference/ccr/apis/auto-follow/get-auto-follow-pattern.asciidoc index b154f6b907e9..19eb2b928ae0 100644 --- a/docs/reference/ccr/apis/auto-follow/get-auto-follow-pattern.asciidoc +++ b/docs/reference/ccr/apis/auto-follow/get-auto-follow-pattern.asciidoc @@ -87,15 +87,19 @@ The API returns the following result: [source,js] -------------------------------------------------- { - "my_auto_follow_pattern" : - { - "remote_cluster" : "remote_cluster", - "leader_index_patterns" : - [ - "leader_index*" - ], - "follow_index_pattern" : "{{leader_index}}-follower" - } + "patterns": [ + { + "name": "my_auto_follow_pattern", + "pattern": { + "remote_cluster" : "remote_cluster", + "leader_index_patterns" : + [ + "leader_index*" + ], + "follow_index_pattern" : "{{leader_index}}-follower" + } + } + ] } -------------------------------------------------- // TESTRESPONSE diff --git a/x-pack/plugin/ccr/qa/rest/src/test/resources/rest-api-spec/test/ccr/auto_follow.yml b/x-pack/plugin/ccr/qa/rest/src/test/resources/rest-api-spec/test/ccr/auto_follow.yml index 4d4026f46a47..ebf9176c30a9 100644 --- a/x-pack/plugin/ccr/qa/rest/src/test/resources/rest-api-spec/test/ccr/auto_follow.yml +++ b/x-pack/plugin/ccr/qa/rest/src/test/resources/rest-api-spec/test/ccr/auto_follow.yml @@ -31,15 +31,17 @@ - do: ccr.get_auto_follow_pattern: name: my_pattern - - match: { my_pattern.remote_cluster: 'local' } - - match: { my_pattern.leader_index_patterns: ['logs-*'] } - - match: { my_pattern.max_outstanding_read_requests: 2 } + - match: { patterns.0.name: 'my_pattern' } + - match: { patterns.0.pattern.remote_cluster: 'local' } + - match: { patterns.0.pattern.leader_index_patterns: ['logs-*'] } + - match: { patterns.0.pattern.max_outstanding_read_requests: 2 } - do: ccr.get_auto_follow_pattern: {} - - match: { my_pattern.remote_cluster: 'local' } - - match: { my_pattern.leader_index_patterns: ['logs-*'] } - - match: { my_pattern.max_outstanding_read_requests: 2 } + - match: { patterns.0.name: 'my_pattern' } + - match: { patterns.0.pattern.remote_cluster: 'local' } + - match: { patterns.0.pattern.leader_index_patterns: ['logs-*'] } + - match: { patterns.0.pattern.max_outstanding_read_requests: 2 } - do: ccr.delete_auto_follow_pattern: diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/GetAutoFollowPatternAction.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/GetAutoFollowPatternAction.java index 72618926a894..1de530b78c70 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/GetAutoFollowPatternAction.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ccr/action/GetAutoFollowPatternAction.java @@ -119,10 +119,21 @@ public void writeTo(StreamOutput out) throws IOException { @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); - for (Map.Entry entry : autoFollowPatterns.entrySet()) { - builder.startObject(entry.getKey()); - entry.getValue().toXContent(builder, params); - builder.endObject(); + { + builder.startArray("patterns"); + for (Map.Entry entry : autoFollowPatterns.entrySet()) { + builder.startObject(); + { + builder.field("name", entry.getKey()); + builder.startObject("pattern"); + { + entry.getValue().toXContent(builder, params); + } + builder.endObject(); + } + builder.endObject(); + } + builder.endArray(); } builder.endObject(); return builder;