package alien4cloud.it.components;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.alien4cloud.tosca.model.definitions.CapabilityDefinition;
import org.alien4cloud.tosca.model.definitions.RequirementDefinition;
import org.alien4cloud.tosca.model.types.AbstractToscaType;
import org.alien4cloud.tosca.model.types.NodeType;
import org.alien4cloud.tosca.model.types.RelationshipType;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.client.Client;
import org.elasticsearch.mapping.MappingBuilder;
import org.junit.Assert;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import alien4cloud.common.AlienConstants;
import alien4cloud.dao.ElasticSearchDAO;
import alien4cloud.dao.model.FacetedSearchFacet;
import alien4cloud.dao.model.FacetedSearchResult;
import alien4cloud.it.Context;
import alien4cloud.rest.component.QueryComponentType;
import alien4cloud.rest.component.SearchRequest;
import alien4cloud.rest.model.RestResponse;
import alien4cloud.rest.utils.JsonUtil;
import cucumber.api.DataTable;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SearchDefinitionSteps {
private static final String DEFAULT_ARCHIVE_VERSION = "1.0";
private static final Map<String, QueryComponentType> QUERY_TYPES;
private Map<String, String> indexedComponentTypes = Maps.newHashMap();
List<NodeType> testDataList = new ArrayList<>();
List<NodeType> notYetSearchedDataList = null;
private final Client esClient = Context.getEsClientInstance();
static {
QUERY_TYPES = Maps.newHashMap();
QUERY_TYPES.put("node types", QueryComponentType.NODE_TYPE);
QUERY_TYPES.put("capability types", QueryComponentType.CAPABILITY_TYPE);
QUERY_TYPES.put("relationship types", QueryComponentType.RELATIONSHIP_TYPE);
QUERY_TYPES.put("artifact types", QueryComponentType.ARTIFACT_TYPE);
}
@Given("^There is (\\d+) \"([^\"]*)\" indexed in ALIEN$")
public void There_is_indexed_in_ALIEN(int count, String type) throws Throwable {
createAndIndexComponent(count, type, null, 0, null, null);
}
@Given("^There is (\\d+) \"([^\"]*)\" with base name \"([^\"]*)\" indexed in ALIEN$")
public void There_is_indexed_in_ALIEN(int count, String type, String baseName) throws Throwable {
createAndIndexComponent(count, type, baseName, 0, null, null);
}
@When("^I search for \"([^\"]*)\" using query \"([^\"]*)\" from (\\d+) with result size of (\\d+)$")
public void I_search_for_with_query_from_with_result_size_of(String searchedComponentType, String query, int from, int size) throws Throwable {
SearchRequest req = new SearchRequest(QUERY_TYPES.get(searchedComponentType), query, from, size, null);
req.setType(req.getType());
String jSon = JsonUtil.toString(req);
Context.getInstance().registerRestResponse(Context.getRestClientInstance().postJSon("/rest/v1/components/search", jSon));
}
@When("^I search for \"([^\"]*)\" from (\\d+) with result size of (\\d+)$")
public void I_search_for_from_with_result_size_of(String searchedComponentType, int from, int size) throws Throwable {
SearchRequest req = new SearchRequest(QUERY_TYPES.get(searchedComponentType), null, from, size, null);
String jSon = JsonUtil.toString(req);
Context.getInstance().registerRestResponse(Context.getRestClientInstance().postJSon("/rest/v1/components/search", jSon));
}
@When("^I make a basic \"([^\"]*)\" search for \"([^\"]*)\" from (\\d+) with result size of (\\d+)$")
public void I_make_a_basic_search_for_from_with_result_size_of(String query, String searchedComponentType, int from, int size) throws Throwable {
// BasicSearchRequest req = new BasicSearchRequest(query, from, size);
SearchRequest req = new SearchRequest(QUERY_TYPES.get(searchedComponentType), query, from, size, null);
String jSon = JsonUtil.toString(req);
Context.getInstance().registerRestResponse(Context.getRestClientInstance().postJSon("/rest/v1/components/search", jSon));
}
@Then("^The response should contains (\\d+) elements from various types.$")
public void The_response_should_contains_elements_from_various_types(int expectedSize) throws Throwable {
RestResponse<FacetedSearchResult> restResponse = JsonUtil.read(Context.getInstance().getRestResponse(), FacetedSearchResult.class);
FacetedSearchResult searchResp = restResponse.getData();
assertNotNull(searchResp);
assertNotNull(searchResp.getTypes());
assertNotNull(searchResp.getData());
assertEquals(expectedSize, searchResp.getTypes().length);
assertEquals(expectedSize, searchResp.getData().length);
// check various types in result
ArrayList<String> resultTypes = new ArrayList<>(Arrays.asList(searchResp.getTypes()));
for (String indexedType : indexedComponentTypes.values()) {
assertTrue(" Result Types should contains " + indexedType + ".", resultTypes.contains(indexedType));
}
}
@Then("^The response should contains (\\d+) \"([^\"]*)\".$")
public void The_response_should_contains_(int expectedSize, String searchedComponentType) throws Throwable {
RestResponse<FacetedSearchResult> restResponse = JsonUtil.read(Context.getInstance().getRestResponse(), FacetedSearchResult.class);
FacetedSearchResult searchResp = restResponse.getData();
assertNotNull(searchResp);
assertNotNull(searchResp.getTypes());
assertNotNull(searchResp.getData());
assertTrue(searchResp.getTypes().length >= expectedSize);
assertTrue(searchResp.getData().length >= expectedSize);
// check result types
List<String> resultTypes = Lists.newArrayList(searchResp.getTypes());
int count = 0;
for (String type : resultTypes) {
count = type.equals(indexedComponentTypes.get(searchedComponentType)) ? count += 1 : count;
}
assertEquals("There should be " + expectedSize + " " + searchedComponentType + " in result.", expectedSize, count);
}
@Given("^There is (\\d+) \"([^\"]*)\" indexed in ALIEN with (\\d+) of them having a \"([^\"]*)\" \"([^\"]*)\"$")
public void There_is_indexed_in_ALIEN_with_of_them_having_a(int count, String type, int countHavingProperty, String propertyValue, String property)
throws Throwable {
createAndIndexComponent(count, type, null, countHavingProperty, property, propertyValue);
}
@When("^I search for \"([^\"]*)\" from (\\d+) with result size of (\\d+) and filter \"([^\"]*)\" set to \"([^\"]*)\"$")
public void I_search_for_from_with_result_size_of_and_filter_set_to(String searchedComponentType, int from, int size, String filterName, String filterValue)
throws Throwable {
Map<String, String[]> filters = Maps.newHashMap();
filters.put(filterName, new String[]{filterValue.toLowerCase()});
SearchRequest req = new SearchRequest(QUERY_TYPES.get(searchedComponentType), null, from, size, filters);
req.setType(req.getType());
String jSon = JsonUtil.toString(req);
Context.getInstance().registerRestResponse(Context.getRestClientInstance().postJSon("/rest/v1/components/search", jSon));
}
@Then("^The \"([^\"]*)\" in the response should all have the \"([^\"]*)\" \"([^\"]*)\"$")
public void The_in_the_response_should_all_have_the(String searchedComponentType, String propertyValue, String property) throws Throwable {
RestResponse<FacetedSearchResult> restResponse = JsonUtil.read(Context.getInstance().takeRestResponse(), FacetedSearchResult.class);
FacetedSearchResult searchResp = restResponse.getData();
List<Object> resultData = Lists.newArrayList(searchResp.getData());
if (searchedComponentType.equalsIgnoreCase("node types")) {
for (Object object : resultData) {
NodeType idnt = JsonUtil.readObject(JsonUtil.toString(object), NodeType.class);
switch (property) {
case "capability":
assertNotNull(idnt.getCapabilities());
assertTrue(idnt.getCapabilities().contains(new CapabilityDefinition(propertyValue, propertyValue, 1)));
break;
case "requirement":
assertNotNull(idnt.getRequirements());
assertTrue(idnt.getRequirements().contains(new RequirementDefinition(propertyValue, propertyValue)));
break;
default:
break;
}
}
} else if (searchedComponentType.equalsIgnoreCase("relationship types")) {
for (Object object : resultData) {
RelationshipType idrt = JsonUtil.readObject(JsonUtil.toString(object), RelationshipType.class);
switch (property) {
case "validSource":
assertNotNull(idrt.getValidSources());
assertTrue(Arrays.equals(new String[]{propertyValue}, idrt.getValidSources()));
break;
default:
break;
}
}
}
}
/**
* tototo
*
* @deprecated Do not use this method! pagination is broken with aggregations
*/
@Deprecated
// @Given("^I have already made a query to search the (\\d+) first \"([^\"]*)\"$")
public void I_have_already_made_a_query_to_search_the_first(int size, String searchedComponentType) throws Throwable {
I_search_for_from_with_result_size_of(searchedComponentType, 0, size);
notYetSearchedDataList = new ArrayList<>(testDataList);
RestResponse<FacetedSearchResult> restResponse = JsonUtil.read(Context.getInstance().takeRestResponse(), FacetedSearchResult.class);
checkPaginatedResult(size, restResponse);
}
/**
* tototo
*
* @deprecated Do not use this method! pagination is broken with aggregations
*/
@Deprecated
// @Then("^The response should contains (\\d+) other \"([^\"]*)\".$")
public void The_response_should_contains_other(int expectedSize, String searchedComponentType) throws Throwable {
RestResponse<FacetedSearchResult> restResponse = JsonUtil.read(Context.getInstance().getRestResponse(), FacetedSearchResult.class);
checkPaginatedResult(expectedSize, restResponse);
}
@Then("^The response should contains (\\d+) elements$")
public void The_response_should_contains(int expectedSize) throws Throwable {
RestResponse<FacetedSearchResult> restResponse = JsonUtil.read(Context.getInstance().getRestResponse(), FacetedSearchResult.class);
assertEquals(expectedSize, restResponse.getData().getTotalResults());
}
private void checkPaginatedResult(int expectedSize, RestResponse<FacetedSearchResult> restResponse) throws IOException {
List<NodeType> toCheckInDataList = notYetSearchedDataList == null ? new ArrayList<>(testDataList) : notYetSearchedDataList;
assertTrue(" The size of data to check (" + toCheckInDataList.size() + ") is less than the expected size (" + expectedSize + ") of search result ",
toCheckInDataList.size() >= expectedSize);
FacetedSearchResult searchResp;
searchResp = restResponse.getData();
assertNotNull(searchResp);
assertNotNull(searchResp.getTypes());
assertNotNull(searchResp.getData());
assertEquals(expectedSize, searchResp.getTypes().length);
assertEquals(expectedSize, searchResp.getData().length);
// testing the pertinence of returned data
Object[] data = searchResp.getData();
for (int i = 0; i < data.length; i++) {
NodeType nt = JsonUtil.readObject(JsonUtil.toString(data[i]), NodeType.class);
assertTrue(toCheckInDataList.contains(nt));
toCheckInDataList.remove(nt);
}
}
private void createAndIndexComponent(int count, String type, String baseName, int countHavingProperty, String property, String propertyValue)
throws Exception {
testDataList.clear();
Class<?> clazz = QUERY_TYPES.get(type).getIndexedToscaElementClass();
String typeName = MappingBuilder.indexTypeFromClass(clazz);
int remaining = countHavingProperty;
baseName = baseName == null || baseName.isEmpty() ? typeName : baseName;
for (int i = 0; i < count; i++) {
AbstractToscaType componentTemplate = (AbstractToscaType) clazz.newInstance();
String elementId = baseName + "_" + i;
componentTemplate.setElementId(elementId);
componentTemplate.setArchiveVersion(DEFAULT_ARCHIVE_VERSION);
componentTemplate.setWorkspace(AlienConstants.GLOBAL_WORKSPACE_ID);
if (property != null && remaining > 0) {
if (type.equalsIgnoreCase("node types")) {
switch (property) {
case "capability":
((NodeType) componentTemplate).setCapabilities(Lists.newArrayList(new CapabilityDefinition(propertyValue, propertyValue, 1)));
break;
case "requirement":
((NodeType) componentTemplate).setRequirements((Lists.newArrayList(new RequirementDefinition(propertyValue, propertyValue))));
break;
case "default capability":
((NodeType) componentTemplate).setDefaultCapabilities((Lists.newArrayList(propertyValue)));
break;
case "elementId":
((NodeType) componentTemplate).setElementId(propertyValue);
break;
default:
break;
}
} else if (type.equalsIgnoreCase("relationship types")) {
((RelationshipType) componentTemplate).setValidSources(new String[]{propertyValue});
}
remaining -= 1;
}
String serializeDatum = JsonUtil.toString(componentTemplate);
log.debug("Saving in ES: " + serializeDatum);
esClient.prepareIndex(ElasticSearchDAO.TOSCA_ELEMENT_INDEX, typeName).setSource(serializeDatum).setRefresh(true).execute().actionGet();
if (componentTemplate instanceof NodeType) {
testDataList.add((NodeType) (componentTemplate));
}
}
indexedComponentTypes.put(type, typeName);
}
@When("^I search for all components type from (\\d+) with result size of (\\d+)$")
public void iSearchForAllComponentsTypeFromWithResultSizeOf(int from, int size) throws Throwable {
I_search_for_from_with_result_size_of(null, from, size);
}
@Then("^The search result should contain (\\d+) data with (\\d+) facets and some of them are:$")
public void theSearchResultShouldContainDataWithFacetsAndSomeOfThemAre(int numberOfData, int numberOfFacets, DataTable dataTable) throws Throwable {
RestResponse<FacetedSearchResult> restResponse = JsonUtil.read(Context.getInstance().getRestResponse(), FacetedSearchResult.class);
Assert.assertNotNull(restResponse.getData());
Assert.assertNotNull(restResponse.getData().getData());
Assert.assertEquals(numberOfData, restResponse.getData().getData().length);
Assert.assertNotNull(restResponse.getData().getFacets());
Map<String, FacetedSearchFacet[]> facets = restResponse.getData().getFacets();
Assert.assertEquals(numberOfFacets, facets.size());
dataTable.raw().forEach(line -> {
String key = line.get(0);
String value = StringUtils.isBlank(line.get(1)) ? null : line.get(1);
long count = Long.parseLong(line.get(2));
Assert.assertTrue("Facet " + key + " not found", facets.containsKey(key));
Optional<FacetedSearchFacet> facetFound = Arrays.stream(facets.get(key)).filter(facet -> Objects.equals(value, facet.getFacetValue())).findFirst();
Assert.assertTrue("Facet " + key + " does not have value " + value, facetFound.isPresent());
Assert.assertEquals("Facet " + key + " does not have count " + count + " for value " + value, count, facetFound.get().getCount());
});
}
}