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

GetIndexMappings index pattern support #265

Merged
merged 15 commits into from
Jan 11, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,27 @@ public void onFailure(Exception e) {
}

public void getMappingAction(String indexName, ActionListener<GetIndexMappingsResponse> actionListener) {
try {
// We are returning mappings view for only 1 index: writeIndex or latest from the pattern
resolveConcreteIndex(indexName, new ActionListener<>() {
@Override
public void onResponse(String concreteIndex) {
doGetMappingAction(concreteIndex, actionListener);
}

@Override
public void onFailure(Exception e) {
actionListener.onFailure(e);
}
});


} catch (IOException e) {
throw SecurityAnalyticsException.wrap(e);
}
}

public void doGetMappingAction(String indexName, ActionListener<GetIndexMappingsResponse> actionListener) {
GetMappingsRequest getMappingsRequest = new GetMappingsRequest().indices(indexName);
indicesClient.getMappings(getMappingsRequest, new ActionListener<>() {
@Override
Expand Down Expand Up @@ -287,36 +308,13 @@ public void onResponse(GetMappingsResponse getMappingsResponse) {

// Traverse mappings and do copy with excluded type=alias properties
MappingsTraverser mappingsTraverser = new MappingsTraverser(mappingMetadata);
// Resulting properties after filtering
Map<String, Object> filteredProperties = new HashMap<>();

mappingsTraverser.addListener(new MappingsTraverser.MappingsTraverserListener() {
@Override
public void onLeafVisited(MappingsTraverser.Node node) {
// Skip everything except aliases we found
if (appliedAliases.contains(node.currentPath) == false) {
return;
}
MappingsTraverser.Node n = node;
while (n.parent != null) {
n = n.parent;
}
if (n == null) {
n = node;
}
filteredProperties.put(n.getNodeName(), n.getProperties());
}
// Resulting mapping after filtering
Map<String, Object> filteredMapping = mappingsTraverser.traverseAndCopyWithFilter(appliedAliases);


@Override
public void onError(String error) {
throw new IllegalArgumentException("");
}
});
mappingsTraverser.traverse();
// Construct filtered mappings and return them as result
ImmutableOpenMap.Builder<String, MappingMetadata> outIndexMappings = ImmutableOpenMap.builder();
Map<String, Object> outRootProperties = Map.of(PROPERTIES, filteredProperties);
Map<String, Object> root = Map.of(org.opensearch.index.mapper.MapperService.SINGLE_MAPPING_NAME, outRootProperties);
Map<String, Object> root = Map.of(org.opensearch.index.mapper.MapperService.SINGLE_MAPPING_NAME, filteredMapping);
MappingMetadata outMappingMetadata = new MappingMetadata(org.opensearch.index.mapper.MapperService.SINGLE_MAPPING_NAME, root);
outIndexMappings.put(indexName, outMappingMetadata);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

package org.opensearch.securityanalytics.mapper;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.ListIterator;
import org.apache.commons.lang3.tuple.Pair;
import org.opensearch.cluster.metadata.MappingMetadata;
import org.opensearch.common.xcontent.DeprecationHandler;
Expand Down Expand Up @@ -154,7 +157,7 @@ public void traverse() {
try {

Map<String, Object> rootProperties = (Map<String, Object>) this.mappingsMap.get(PROPERTIES);
rootProperties.forEach((k, v) -> nodeStack.push(new Node(Map.of(k, v), "")));
rootProperties.forEach((k, v) -> nodeStack.push(new Node(Map.of(k, v), null, rootProperties, "", "")));

while (nodeStack.size() > 0) {
Node node = nodeStack.pop();
Expand Down Expand Up @@ -182,7 +185,7 @@ public void traverse() {
node.currentPath.length() > 0 ?
node.currentPath + "." + currentNodeName :
currentNodeName;
nodeStack.push(new Node(Map.of(k, v), node, currentPath));
nodeStack.push(new Node(Map.of(k, v), node, children, currentNodeName, currentPath));
});
}
}
Expand Down Expand Up @@ -211,6 +214,70 @@ private boolean shouldSkipNode(Map<String, Object> properties) {
return false;
}

public Map<String, Object> traverseAndCopyWithFilter(List<String> nodePathsToCopy) {

Map<String, Object> outRoot = new LinkedHashMap<>(Map.of(PROPERTIES, new LinkedHashMap()));
this.addListener(new MappingsTraverserListener() {
@Override
public void onLeafVisited(Node node) {
if (nodePathsToCopy.contains(node.currentPath) == false) {
return;
}
// Collect all nodes from root to this leaf.
List<Node> nodes = new ArrayList<>();
Node n = node;
nodes.add(n);
while (n.parent != null) {
n = n.parent;
nodes.add(n);
}
// Iterate from root node up to this leaf and copy node in each iteration to "out" tree
ListIterator<Node> nodesIterator = nodes.listIterator(nodes.size());
Map<String, Object> outNode = outRoot;
while (nodesIterator.hasPrevious()) {
Node currentNode = nodesIterator.previous();

appendNode(currentNode, outNode, !nodesIterator.hasPrevious());
// Move to next output node
outNode = (Map<String, Object>) ((Map<?, ?>) outNode.get(PROPERTIES)).get(currentNode.getNodeName());
}
}

@Override
public void onError(String error) {
throw new IllegalArgumentException("");
}
});
traverse();
return outRoot;
}

/**
* Appends src node to dst node's properties
* @param srcNode source node
* @param dstNode destination node where source node is appended
* @param isSourceLeaf flag which indicated if source node is leaf
*/
private void appendNode(Node srcNode, Map<String, Object> dstNode, boolean isSourceLeaf) {
Map<String, Object> existingProps = (Map<String, Object>) ((Map) dstNode.get(PROPERTIES)).get(srcNode.getNodeName());
if (existingProps == null) {
Map<String, Object> srcNodeProps = srcNode.getProperties();
Map<String, Object> newProps = isSourceLeaf ?
srcNodeProps :
new LinkedHashMap();
// In case of type="nested" node, we need to copy that type field too, beside properties
if (srcNodeProps.containsKey(TYPE) && srcNodeProps.get(TYPE).equals(NESTED)) {
((Map) dstNode.get(PROPERTIES)).put(srcNode.getNodeName(), new LinkedHashMap(Map.of(PROPERTIES, newProps, TYPE, NESTED)));
} else {
// Append src node to dst node's properties
((Map) dstNode.get(PROPERTIES)).put(
srcNode.getNodeName(),
isSourceLeaf ? newProps : new LinkedHashMap(Map.of(PROPERTIES, newProps))
);
}
}
}

/**
* Traverses index mappings tree and copies it into 1-level tree with flatten nodes. (level1.level2.level3) Listeners are notified when leaves are visited,
* just like during {@link #traverse()} call.
Expand Down Expand Up @@ -254,20 +321,27 @@ private void notifyLeafVisited(Node node) {
);
}

public Map<String, Object> getMappingsMap() {
return mappingsMap;
}

static class Node {
Map<String, Object> node;
Node parent;
Map<String, Object> properties;
Map<String, Object> parentProperties;
String parentKey;
String currentPath;
String name;

public Node(Map<String, Object> node, String currentPath) {
this.node = node;
this.currentPath = currentPath;
}
public Node(Map<String, Object> node, Node parent, String currentPath) {
public Node(Map<String, Object> node, Node parent, Map<String, Object> parentProperties, String parentKey, String currentPath) {
this.node = node;
this.parent = parent;
this.parentProperties = parentProperties;
this.currentPath = currentPath;
}
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,7 @@ public TransportGetIndexMappingsAction(
@Override
protected void doExecute(Task task, GetIndexMappingsRequest request, ActionListener<GetIndexMappingsResponse> actionListener) {
this.threadPool.getThreadContext().stashContext();
IndexMetadata index = clusterService.state().metadata().index(request.getIndexName());
if (index == null) {
actionListener.onFailure(
SecurityAnalyticsException.wrap(
new OpenSearchStatusException(
"Could not find index [" + request.getIndexName() + "]", RestStatus.NOT_FOUND
)
)
);
return;
}

mapperService.getMappingAction(request.getIndexName(), actionListener);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import java.util.stream.Collectors;

import static org.opensearch.action.admin.indices.create.CreateIndexRequest.MAPPINGS;
import static org.opensearch.securityanalytics.SecurityAnalyticsPlugin.MAPPER_BASE_URI;
import static org.opensearch.securityanalytics.TestHelpers.sumAggregationTestRule;
import static org.opensearch.securityanalytics.TestHelpers.productIndexAvgAggRule;
import static org.opensearch.securityanalytics.TestHelpers.windowsIndexMapping;
Expand Down Expand Up @@ -1405,7 +1406,7 @@ protected void createComposableIndexTemplate(String templateName, List<String> i
assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
}

protected Map<String, Object> getIndexMappingsFlat(String indexName) throws IOException {
protected Map<String, Object> getIndexMappingsAPIFlat(String indexName) throws IOException {
Request request = new Request("GET", indexName + "/_mapping");
Response response = client().performRequest(request);
assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
Expand All @@ -1416,8 +1417,28 @@ protected Map<String, Object> getIndexMappingsFlat(String indexName) throws IOEx
return (Map<String, Object>) flatMappings.get("properties");
}

protected Map<String, Object> getIndexMappingsAPI(String indexName) throws IOException {
Request request = new Request("GET", indexName + "/_mapping");
Response response = client().performRequest(request);
assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
Map<String, Object> respMap = (Map<String, Object>) responseAsMap(response).values().iterator().next();
return (Map<String, Object>) respMap.get("mappings");
}

protected Map<String, Object> getIndexMappingsSAFlat(String indexName) throws IOException {
Request request = new Request("GET", MAPPER_BASE_URI + "?index_name=" + indexName);
Response response = client().performRequest(request);
assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
Map<String, Object> respMap = (Map<String, Object>) responseAsMap(response).values().iterator().next();

MappingsTraverser mappingsTraverser = new MappingsTraverser((Map<String, Object>) respMap.get("mappings"), Set.of());
Map<String, Object> flatMappings = mappingsTraverser.traverseAndCopyAsFlat();
return (Map<String, Object>) flatMappings.get("properties");
}


protected void createMappingsAPI(String indexName, String topicName) throws IOException {
Request request = new Request("POST", SecurityAnalyticsPlugin.MAPPER_BASE_URI);
Request request = new Request("POST", MAPPER_BASE_URI);
// both req params and req body are supported
request.setJsonEntity(
"{ \"index_name\":\"" + indexName + "\"," +
Expand Down
Loading