/*
* Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hawkular.inventory.rest.test;
import static org.hawkular.inventory.api.Relationships.WellKnown.contains;
import static org.hawkular.inventory.api.Relationships.WellKnown.defines;
import static org.hawkular.inventory.api.Relationships.WellKnown.incorporates;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.hawkular.inventory.api.Action;
import org.hawkular.inventory.api.Inventory;
import org.hawkular.inventory.api.model.Change;
import org.hawkular.inventory.api.model.Entity;
import org.hawkular.inventory.api.model.Environment;
import org.hawkular.inventory.api.model.Feed;
import org.hawkular.inventory.api.model.Metric;
import org.hawkular.inventory.api.model.MetricDataType;
import org.hawkular.inventory.api.model.MetricType;
import org.hawkular.inventory.api.model.MetricUnit;
import org.hawkular.inventory.api.model.OperationType;
import org.hawkular.inventory.api.model.Relationship;
import org.hawkular.inventory.api.model.Resource;
import org.hawkular.inventory.api.model.ResourceType;
import org.hawkular.inventory.api.model.SyncHash;
import org.hawkular.inventory.api.model.Tenant;
import org.hawkular.inventory.json.InventoryJacksonConfig;
import org.hawkular.inventory.paths.CanonicalPath;
import org.hawkular.inventory.paths.Path;
import org.hawkular.inventory.paths.PathSegmentCodec;
import org.hawkular.inventory.paths.SegmentType;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.squareup.okhttp.Response;
/**
* @author <a href="https://github.com/ppalaga">Peter Palaga</a>
*
*/
public class InventoryITest extends AbstractTestBase {
protected static final String urlTypeId = "URL";
protected static final String environmentId = "itest-env-" + UUID.randomUUID().toString();
protected static final String pingableHostRTypeId = "itest-pingable-host-" + UUID.randomUUID().toString();
protected static final String roomRTypeId = "itest-room-type-" + UUID.randomUUID().toString();
protected static final String copyMachineRTypeId = "itest-copy-machine-type-" + UUID.randomUUID().toString();
protected static final String date20150626 = "2015-06-26";
protected static final String date20160801 = "2016-08-01";
protected static final String expectedLifetime15years = "15y";
protected static final String facilitiesDept = "Facilities";
protected static final String itDept = "IT";
protected static final String typeVersion = "1.0";
protected static final String responseTimeMTypeId = "itest-response-time-" + UUID.randomUUID().toString();
protected static final String responseStatusCodeMTypeId = "itest-response-status-code-" +
UUID.randomUUID().toString();
protected static final String statusDurationMTypeId = "status.duration.type";
protected static final String statusCodeMTypeId = "status.code.type";
protected static final String host1ResourceId = "itest-host1-" + UUID.randomUUID().toString();
protected static final String host2ResourceId = "itest-host2-" + UUID.randomUUID().toString();
protected static final String room1ResourceId = "itest-room1-" + UUID.randomUUID().toString();
protected static final String copyMachine1ResourceId = "itest-copy-machine1-" + UUID.randomUUID().toString();
protected static final String copyMachine2ResourceId = "itest-copy-machine2-" + UUID.randomUUID().toString();
protected static final String responseTimeMetricId = "itest-response-time-" + host1ResourceId;
protected static final String responseStatusCodeMetricId = "itest-response-status-code-" + host1ResourceId;
protected static final String feedId = "itest-feed-" + UUID.randomUUID().toString();
protected static final String bulkResourcePrefix = "bulk-resource-" + UUID.randomUUID().toString();
protected static final String bulkResourceTypePrefix = "bulk-resource-type-" + UUID.randomUUID().toString();
protected static final String bulkMetricTypePrefix = "bulk-metric-type-" + UUID.randomUUID().toString();
protected static final String customRelationName = "inTheSameRoom";
/* key is the path to delete while value is the path to GET to verify the deletion */
protected static Map<String, String> pathsToDelete = new LinkedHashMap<>();
@BeforeClass
public static void setupData() throws Throwable {
CanonicalPath tenantPath = CanonicalPath.of().tenant(tenantId).get();
/* assert the test environment exists */
/* There is a race condition when WildFly agent is enabled:
both this test and Agent trigger the autocreation of test entities simultaneously,
and one of them may get only a partially initialized state.
That is why we do several delayed attempts do perform the first request.
*/
String path = "/hawkular/inventory/entity/e;" + testEnvId;
Environment env = getWithRetries(path, Environment.class, 10, 2000);
assertEquals("Unable to get the '" + testEnvId + "' environment.", testEnvId, env.getId());
/* Create an environment that will be used exclusively by this test */
Response response = postDeletable(tenantPath,
Environment.Blueprint.builder().withId(environmentId).build());
assertEquals(201, response.code());
Environment environment = mapper.readValue(response.body().string(), Environment.class);
assertEquals(environmentId, environment.getId());
assertEquals(CanonicalPath.of().tenant(tenantId).environment(environmentId).get(), environment.getPath());
assertEquals(baseURI + basePath + "/entity/e;" + environmentId, response.headers().get("Location"));
/* URL resource type should have been autocreated */
path = basePath + "/entity/rt;" + urlTypeId;
ResourceType resourceType = getWithRetries(path, ResourceType.class, 10, 2000);
assertEquals("Unable to get the '" + urlTypeId + "' resource type.", urlTypeId, resourceType.getId());
assertEquals(urlTypeId, resourceType.getId());
/* Create pingable host resource type */
response = postDeletable(tenantPath, ResourceType.Blueprint.builder().withId(pingableHostRTypeId).build());
assertEquals(201, response.code());
ResourceType pingableHost = mapper.readValue(response.body().string(), ResourceType.class);
assertEquals(pingableHostRTypeId, pingableHost.getId());
assertEquals(baseURI + basePath + "/entity/rt;" + pingableHostRTypeId,
response.headers().get("Location"));
/* Create room resource type */
response = postDeletable(tenantPath, ResourceType.Blueprint.builder().withId(roomRTypeId)
.withProperty("expectedLifetime", expectedLifetime15years)//
.withProperty("ownedByDepartment", facilitiesDept).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath + "/entity/rt;" + roomRTypeId, response.headers().get("Location"));
/* Create copy machine resource type */
response = postDeletable(tenantPath, ResourceType.Blueprint.builder().withId(copyMachineRTypeId)
.withProperty("expectedLifetime", expectedLifetime15years)//
.withProperty("ownedByDepartment", itDept).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath + "/entity/rt;" + copyMachineRTypeId,
response.headers().get("Location"));
/* Create a metric type */
response = postDeletable(tenantPath, MetricType.Blueprint.builder(MetricDataType.COUNTER)
.withId(responseTimeMTypeId)//
.withUnit(MetricUnit.MILLISECONDS)//
.withInterval(1L)//
.build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath + "/entity/mt;" + responseTimeMTypeId,
response.headers().get("Location"));
/* Create another metric type */
response = postDeletable(tenantPath, MetricType.Blueprint.builder(MetricDataType.GAUGE)
.withId(responseStatusCodeMTypeId)//
.withUnit(MetricUnit.NONE)//
.withInterval(1L)//
.build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath + "/entity/mt;" + responseStatusCodeMTypeId,
response.headers().get("Location"));
/* link pingableHostRTypeId with responseTimeMTypeId and responseStatusCodeMTypeId */
path = basePath + "/entity/rt;" + pingableHostRTypeId +"/relationship";
//just testing that both relative and canonical paths work when referencing the types
response = post(path, "[{\"otherEnd\": \"../mt;" + responseTimeMTypeId +"\", \"name\": \"incorporates\"}, " +
"{\"otherEnd\": \"/mt;" + responseStatusCodeMTypeId +"\", \"name\": \"incorporates\"}]");
assertEquals(201, response.code());
//we will try deleting the associations between resource types and metric types, too
//this is not necessary because deleting either the resource type or the metric type will take care of it anyway
//but this is to test that explicit deletes work, too
// XXX this should check for removal of a entity association.
// OkHttp unconditionally canonicalizes the URL paths, which makes the below constructs impossible to send
// over the wire using OkHttp (even though they're perfectly valid URLs).
//pathsToDelete.put(path + "/../" + responseTimeMTypeId, path +"/../" + responseTimeMTypeId);
// XXX again, this is impossible due to OkHttp unconditionally canonicalizing the URL paths
//pathsToDelete.put(path + "/../" + responseStatusCodeMTypeId, path +"/../" + responseStatusCodeMTypeId);
CanonicalPath environmentPath = tenantPath.extend(SegmentType.e, environmentId).get();
/* add a metric */
response = postDeletable(environmentPath, Metric.Blueprint.builder()
.withId(responseTimeMetricId) //
.withMetricTypePath("../" + responseTimeMTypeId) //
.build());
//path relative to env
assertEquals(201, response.code());
Metric responseTimeMetric = mapper.readValue(response.body().string(), Metric.class);
assertEquals(responseTimeMetricId, responseTimeMetric.getId());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/m;" + responseTimeMetricId,
response.headers().get("Location"));
/* add another metric */
response = postDeletable(environmentPath, Metric.Blueprint.builder()
.withId(responseStatusCodeMetricId) //
.withMetricTypePath("/" + responseStatusCodeMTypeId) //
.build());
assertEquals(201, response.code());
Metric responseStatusCode = mapper.readValue(response.body().string(), Metric.class);
assertEquals(responseStatusCodeMetricId, responseStatusCode.getId());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/m;" + responseStatusCodeMetricId,
response.headers().get("Location"));
/* add a resource */
response = postDeletable(environmentPath,
Resource.Blueprint.builder() //
.withId(host1ResourceId) //
.withResourceTypePath("../" + pingableHostRTypeId) //
.build());
assertEquals(201, response.code());
Resource host1Resource = mapper.readValue(response.body().string(), Resource.class);
assertEquals(host1ResourceId, host1Resource.getId());
assertEquals(CanonicalPath.of().tenant(tenantId).environment(environmentId).
resource(host1ResourceId).get(), host1Resource.getPath());
assertEquals(CanonicalPath.of().tenant(tenantId).resourceType(pingableHostRTypeId).get(),
host1Resource.getType().getPath());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + host1ResourceId,
response.headers().get("Location"));
/* add another resource */
response = postDeletable(environmentPath,
Resource.Blueprint.builder()//
.withId(host2ResourceId)//
.withResourceTypePath("../" + pingableHostRTypeId)//
.build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + host2ResourceId,
response.headers().get("Location"));
/* add a room resource */
response = postDeletable(environmentPath,
Resource.Blueprint.builder().withId(room1ResourceId).withResourceTypePath("../" + roomRTypeId)
.withProperty("purchaseDate", date20150626).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + room1ResourceId,
response.headers().get("Location"));
/* add a copy machine resource */
response = postDeletable(environmentPath,
Resource.Blueprint.builder() //
.withId(copyMachine1ResourceId) //
.withResourceTypePath("../" + copyMachineRTypeId)//
.withProperty("purchaseDate", date20150626)//
.withProperty("nextMaintenanceDate", date20160801)//
.build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + copyMachine1ResourceId,
response.headers().get("Location"));
response = postDeletable(environmentPath,
Resource.Blueprint.builder() //
.withId(copyMachine2ResourceId) //
.withResourceTypePath("../" + copyMachineRTypeId) //
.withProperty("purchaseDate", date20160801) //
.build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + copyMachine2ResourceId,
response.headers().get("Location"));
/* add child resources */
CanonicalPath room1Path = environmentPath.extend(SegmentType.r, room1ResourceId).get();
response = postDeletable(room1Path,
Resource.Blueprint.builder().withId("table").withResourceTypePath("/" + roomRTypeId).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + room1ResourceId +"/r;table",
response.headers().get("Location"));
CanonicalPath tablePath = room1Path.extend(SegmentType.r, "table").get();
response = postDeletable(tablePath,
Resource.Blueprint.builder().withId("leg/1").withResourceTypePath("/" + roomRTypeId).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + room1ResourceId +"/r;table/r;leg%2F1",
response.headers().get("Location"));
response = postDeletable(tablePath,
Resource.Blueprint.builder().withId("leg 2").withResourceTypePath("/" + roomRTypeId).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + room1ResourceId +"/r;table/r;leg%202",
response.headers().get("Location"));
response = postDeletable(tablePath,
Resource.Blueprint.builder().withId("leg;3").withResourceTypePath("/" + roomRTypeId).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + room1ResourceId +"/r;table/r;leg%3B3",
response.headers().get("Location"));
response = postDeletable(tablePath,
Resource.Blueprint.builder().withId("leg-4").withResourceTypePath("/" + roomRTypeId).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;" + room1ResourceId +"/r;table/r;leg-4",
response.headers().get("Location"));
//alternative child hierarchies
response = postDeletable(environmentPath,
Resource.Blueprint.builder().withId("weapons").withResourceTypePath("/" + roomRTypeId).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId + "/r;weapons",
response.headers().get("Location"));
path = basePath + "/entity/e;" + environmentId + "/r;weapons/relationship";
response = post(path, interpolate("[" +
"{\"otherEnd\": \"/e;${environmentId}/r;${room1ResourceId}/r;table/r;leg%2F1\", " +
"\"name\": \"isParentOf\"}," +
"{\"otherEnd\": \"../r;${room1ResourceId}/r;table/r;leg-4\", \"name\": \"isParentOf\"}]",
MapBuilder.<String, String>map()
.put("environmentId", environmentId)
.put("room1ResourceId", room1ResourceId)
.create()
));
assertEquals(201, response.code());
// XXX again, this is impossible due to OkHttp unconditionally canonicalizing the URL paths
// pathsToDelete.put(path + "/../table/leg%2F1", path + "/../table/leg%2F1")
// pathsToDelete.put(path + "/../table/leg-4", path + "/../table/leg-4")
/* link the metric to resource */
path = basePath + "/entity/e;" + environmentId + "/r;" + host1ResourceId + "/relationship";
response = post(path, interpolate("[" +
"{\"otherEnd\": \"/e;${environmentId}/m;${responseTimeMetricId}\", \"name\": \"incorporates\"}," +
"{\"otherEnd\": \"/e;${environmentId}/m;${responseStatusCodeMetricId}\", \"name\": \"incorporates\"}" +
"]", MapBuilder.<String, String>map()
.put("environmentId", environmentId)
.put("responseTimeMetricId", responseTimeMetricId)
.put("responseStatusCodeMetricId", responseStatusCodeMetricId)
.create()
));
assertEquals(201, response.code());
// XXX again, this is impossible due to OkHttp unconditionally canonicalizing the URL paths
// pathsToDelete.put(path + "/../" + responseTimeMetricId, path + "/../" + responseTimeMetricId);
// XXX again, this is impossible due to OkHttp unconditionally canonicalizing the URL paths
//pathsToDelete.put(path + "/../" + responseStatusCodeMetricId, path + "/../" + responseStatusCodeMetricId);
/* add a feed */
response = postDeletable(tenantPath, Feed.Blueprint.builder().withId(feedId).build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/f;" + feedId, response.headers().get("Location"));
/* add a custom relationship, no need to clean up, it'll be deleted together with the resources */
Map<String, Object> properties = new LinkedHashMap<>();
properties.put("from", "2000-01-01");
properties.put("confidence", "90%");
CanonicalPath target = CanonicalPath.fromString("/t;" + tenantId + "/e;" + environmentId + "/r;"
+ host1ResourceId);
Relationship.Blueprint h1h2Rel = Relationship.Blueprint.builder().withName(customRelationName)
.withOtherEnd(target).withProperties(properties).build();
response = postNew(basePath + "/entity/e;" + environmentId +"/r;" + host2ResourceId +"/relationship", h1h2Rel);
assertEquals(201, response.code());
JsonNode h1h2Json = mapper.readTree(response.body().string());
assertEquals(customRelationName, h1h2Json.get("name").asText());
// relationship with tenant
Relationship.Blueprint tenantRel = Relationship.Blueprint.builder().withName("sampleRelationship")
.withOtherEnd(tenantPath).build();
post(basePath + "/tenant/relationship", mapper.writeValueAsString(tenantRel));
assertEquals(201, response.code());
// add operation type to the resource type
CanonicalPath pingableHostRType = tenantPath.extend(SegmentType.rt, pingableHostRTypeId).get();
response = postDeletable(pingableHostRType,
OperationType.Blueprint.builder().withId("start").build());
assertEquals(201, response.code());
response = postDeletable(pingableHostRType,
OperationType.Blueprint.builder().withId("stop").build());
assertEquals(201, response.code());
// add some parameters to it
String startOpParamTypes = "{" //
+ "\"role\" : \"parameterTypes\"," //
+ "\"value\": {" //
+ "\"title\" : \"blah\"," //
+ "\"type\": \"object\"," //
+ "\"properties\": { \"quick\": { \"type\": \"boolean\"}}" //
+ "}" //
+ "}";
response = post(basePath + "/entity/rt;" + pingableHostRTypeId +"/ot;start/data",
startOpParamTypes);
assertEquals(201, response.code());
response = post(basePath + "/entity/rt;" + pingableHostRTypeId +"/ot;start/data",
"{\"role\": \"returnType\", \"value\": {\"title\": \"blah\", \"type\": \"boolean\"}}");
assertEquals(201, response.code());
/* add a resource type json schema */
String schema = "{"
+ "\"value\": {" //
+ "\"title\" : \"Character\"," //
+ "\"type\" : \"object\"," //
+ "\"properties\": {" //
+ "\"firstName\" : {\"type\": \"string\"}," //
+ "\"secondName\": {\"type\": \"string\"}," //
+ "\"age\" : {" //
+ "\"type\" : \"integer\"," //
+ "\"minimum\" : 0," //
+ "\"exclusiveMinimum\": false" //
+ "}," //
+ "\"male\" : {" //
+ "\"description\": \"true if the character is a male\"," //
+ "\"type\" : \"boolean\"" //
+ "}," //
+ "\"foo\" : {" //
+ "\"type\" : \"object\"," //
+ "\"properties\": {" //
+ "\"something\": {\"type\": \"string\"}," //
+ "\"someArray\": {" //
+ "\"type\" : \"array\"," //
+ "\"minItems\" : 3," //
+ "\"items\" : {\"type\": \"integer\"}," //
+ "\"uniqueItems\": false" //
+ "}," //
+ "\"foo\" : {" //
+ "\"type\" : \"object\"," //
+ "\"properties\": {" //
+ "\"question\": {" //
+ "\"type\" : \"string\"," //
+ "\"pattern\": \"^.*\\\\?$\"" //
+ "}," //
+ "\"answer\" : {" //
+ "\"description\": \"the answer (example of any type)\"" //
+ "}," //
+ "\"foo\" : {" //
+ "\"type\" : \"object\"," //
+ "\"properties\": {" //
+ "\"foo\": {" //
+ "\"type\" : \"object\"," //
+ "\"properties\": {" //
+ "\"fear\" : {" //
+ "\"type\": \"string\"," //
+ "\"enum\": [\"dentists\", \"lawyers\", \"rats\"]" //
+ "}" //
+ "}" //
+ "}" //
+ "}" //
+ "}" //
+ "}" //
+ "}" //
+ "}" //
+ "}" //
+ "}," //
+ "\"required\" : [\"firstName\", \"secondName\", \"male\", \"age\", \"foo\"]" //
+ "}," //
+ "\"role\" : \"configurationSchema\"" //
+ "}";
response = post(basePath + "/entity/rt;" + pingableHostRTypeId +"/data", schema);
assertEquals(201, response.code());
/* add an invalid config data to a resource (invalid ~ not valid against the json schema) */
String invalidData = "{" //
+ "\"value\": {" //
+ "\"firstName\": \"John\"," //
+ "\"secondName\": \"Smith\"" //
+ "}," //
+ "\"role\" : \"configuration\"" //
+ "}";
response = post(basePath + "/entity/e;" + environmentId +"/r;" + host2ResourceId +"/data", invalidData);
assertEquals(400, response.code());
/* add a config data to a resource, no need to clean up, it'll be deleted together with the resources */
String data = "{" //
+ "\"value\" : {" //
+ "\"firstName\" : \"Winston\"," //
+ "\"secondName\": \"Smith\"," //
+ "\"sdf\" : \"sdf\"," //
+ "\"male\" : true," //
+ "\"age\" : 42," //
+ "\"foo\" : {" //
+ "\"something\": \"whatever\"," //
+ "\"someArray\": [1, 1, 2, 3, 5, 8]," //
+ "\"foo\" : {" //
+ "\"answer\" : 5," //
+ "\"question\": \"2+2=?\"," //
+ "\"foo\" : {" //
+ "\"foo\": {" //
+ "\"fear\": \"rats\"" //
+ "}" //
+ "}" //
+ "}" //
+ "}" //
+ "}," //
+ "\"role\" : \"configuration\"," //
+ "\"properties\": {" //
+ "\"war\" : \"peace\"," //
+ "\"freedom\" : \"slavery\"," //
+ "\"ignorance\": \"strength\"" //
+ "}" //
+ "}";
response = post(basePath + "/entity/e;" + environmentId +"/r;" + host2ResourceId +"/data", data);
assertEquals(201, response.code());
//add resource-owner metric
response = postDeletable(environmentPath.extend(SegmentType.r, host2ResourceId).get(),
Metric.Blueprint.builder() //
.withId("resource-owned-metric") //
.withMetricTypePath("/"+responseTimeMTypeId) //
.build());
assertEquals(201, response.code());
assertEquals(baseURI + basePath +"/entity/e;" + environmentId +"/r;" + host2ResourceId
+"/m;resource-owned-metric",
response.headers().get("Location"));
}
@AfterClass
public static void deleteEverything() throws IOException {
/* the following would delete all data of the present user. We cannot do that as long as we do not have
* a dedicated user for running this very entity test class. */
// Response response = client.delete(path : basePath + "/tenant")
// assertEquals(204, response.code())
/* Let's delete the entities one after another in the reverse order as we created them */
List<Map.Entry<String, String>> entries = new ArrayList<>(pathsToDelete.entrySet());
Collections.reverse(entries);
for (Map.Entry<String, String> en : entries) {
String path = en.getKey();
String getValidationPath = en.getValue();
Response response = client.newCall(newAuthRequest().url(baseURI + path).delete().build()).execute();
assertEquals(
"Could not delete path [" + baseURI + path + "]: " + response.body().string(), 204,
response.code());
if (getValidationPath != null) {
response = client.newCall(newAuthRequest().url(baseURI + path).build()).execute();
assertEquals("The path " + getValidationPath
+ " should not exist after the entity was deleted: " + response.body().string(), 404,
response.code());
}
}
}
@Test
public void ping() throws Throwable {
Response response = get(basePath + "");
assertEquals(200, response.code());
}
@Test
public void testEnvironmentsCreated() throws Throwable {
assertEntitiesExist("traversal/type=e", "/e;"+ testEnvId, "/e;"+ environmentId);
}
@Test
public void testResourceTypesCreated() throws Throwable {
assertEntityExists("entity/rt;" + urlTypeId, "/rt;" + urlTypeId);
assertEntityExists("entity/rt;" + pingableHostRTypeId, "/rt;" + pingableHostRTypeId);
assertEntityExists("entity/rt;" + roomRTypeId, "/rt;" + roomRTypeId);
// commented out as it interfers with WildFly Agent
// assertEntitiesExist("resourceTypes", [urlTypeId, pingableHostRTypeId, roomRTypeId])
}
@Test
public void testMetricTypesCreated() throws Throwable {
assertEntityExists("entity/mt;" + responseTimeMTypeId, "/mt;" + responseTimeMTypeId);
assertEntityExists("entity/mt;" + statusDurationMTypeId, "/mt;" + statusDurationMTypeId);
assertEntityExists("entity/mt;" + statusCodeMTypeId, "/mt;" + statusCodeMTypeId);
// commented out as it interfers with WildFly Agent
// assertEntitiesExist("metricTypes",
// [responseTimeMTypeId, responseStatusCodeMTypeId, statusDurationMTypeId, statusCodeMTypeId])
}
@Test
public void testOperationTypesCreated() throws Throwable {
Response response = get(basePath + "/traversal/rt;" + pingableHostRTypeId +"/type=operationType");
JsonNode json = mapper.readTree(response.body().string());
assertEquals(2, json.size());
assertEntityExists("entity/rt;" + pingableHostRTypeId +"/ot;start",
"/rt;" + pingableHostRTypeId + "/ot;start");
assertEntityExists("entity/rt;" + pingableHostRTypeId +"/ot;start/d;returnType",
"/rt;" + pingableHostRTypeId + "/ot;start/d;returnType");
assertEntityExists("entity/rt;" + pingableHostRTypeId + "/ot;start/d;parameterTypes",
"/rt;" + pingableHostRTypeId + "/ot;start/d;parameterTypes");
}
@Test
public void testMetricTypesLinked() throws Throwable {
assertEntitiesExist("traversal/rt;" + pingableHostRTypeId +"/rl;incorporates/type=mt", "/mt;" + responseTimeMTypeId,
"/mt;" + responseStatusCodeMTypeId);
}
@Test
public void testResourcesCreated() throws Throwable {
assertEntityExists("entity/e;" + environmentId + "/r;" + host1ResourceId, "/e;" + environmentId + "/r;"
+ host1ResourceId);
assertEntityExists("entity/e;" + environmentId + "/r;" + host2ResourceId, "/e;" + environmentId + "/r;"
+ host2ResourceId);
assertEntityExists("entity/e;" + environmentId + "/r;" + room1ResourceId, "/e;" + environmentId + "/r;"
+ room1ResourceId);
}
@Test
public void testResourcesFilters() throws Throwable {
/* filter by resource properties */
Response response = get(
basePath + "/traversal/e;" + environmentId
+ "/type=r;propertyName=purchaseDate;propertyValue=" + date20150626,
"sort", "id");
JsonNode json = mapper.readTree(response.body().string());
assertEquals(2, json.size());
assertEquals(copyMachine1ResourceId, json.get(0).get("id").asText());
assertEquals(room1ResourceId, json.get(1).get("id").asText());
response = get(basePath + "/traversal/e;" + environmentId +"/type=r;propertyName=nextMaintenanceDate;propertyValue="
+ date20160801);
json = mapper.readTree(response.body().string());
assertEquals(1, json.size());
assertEquals(copyMachine1ResourceId, json.get(0).get("id").asText());
/* query by two props at once */
response = get(basePath + "/traversal/e;" + environmentId +"/type=r;propertyName=nextMaintenanceDate;propertyValue="
+ date20160801 + ";propertyName=purchaseDate;propertyValue=" + date20150626);
json = mapper.readTree(response.body().string());
assertEquals(1, json.size());
assertEquals(copyMachine1ResourceId, json.get(0).get("id").asText());
/* query by property existence */
response = get(basePath + "/traversal/e;" + environmentId +"/type=resource;propertyName=purchaseDate",
"sort", "id");
json = mapper.readTree(response.body().string());
assertEquals(3, json.size());
assertEquals(copyMachine1ResourceId, json.get(0).get("id").asText());
assertEquals(copyMachine2ResourceId, json.get(1).get("id").asText());
assertEquals(room1ResourceId, json.get(2).get("id").asText());
/* filter by type */
response = get(basePath + "/traversal/e;" + environmentId +"/type=r;definedBy=%2Frt%3B" + pingableHostRTypeId);
json = mapper.readTree(response.body().string());
assertEquals(2, json.size());
response = get(basePath + "/traversal/e;" + environmentId +"/type=r;definedBy=%2Frt%3B" + roomRTypeId);
json = mapper.readTree(response.body().string());
assertEquals(2, json.size());
}
@Test
public void testMetricsCreated() throws Throwable {
assertEntityExists("entity/e;" + environmentId + "/m;" + responseTimeMetricId,
"/e;"+ environmentId + "/m;"+ responseTimeMetricId);
assertEntityExists("entity/e;" + environmentId + "/m;" + responseStatusCodeMetricId,
"/e;"+ environmentId + "/m;"+ responseStatusCodeMetricId);
assertEntitiesExist("traversal/e;" + environmentId + "/recursive/type=m",
"/e;"+ environmentId + "/m;"+ responseTimeMetricId,
"/e;"+ environmentId + "/m;"+ responseStatusCodeMetricId,
"/e;"+ environmentId + "/r;"+ host2ResourceId + "/m;resource-owned-metric");
}
@Test
public void testMetricsLinked() throws Throwable {
assertEntitiesExist("traversal/e;" + environmentId +"/r;" + host1ResourceId +"/rl;incorporates/type=m",
"/e;" + environmentId + "/m;" + responseTimeMetricId, "/e;" + environmentId + "/m;" +
responseStatusCodeMetricId);
}
@Test
public void testConfigCreated() throws Throwable {
assertEntityExists("entity/e;" + environmentId +"/r;" + host2ResourceId +"/d;configuration",
"/e;" + environmentId + "/r;" + host2ResourceId + "/d;configuration");
// assertEntitiesExist(environmentId +"/resources/"
// + host2ResourceId%2Ftable/data?dataType=connectionConfiguration",
// ["/e;" + environmentId + "/r;" + host2ResourceId + "/d;connectionConfiguration"])
}
@Test
public void testPaging() throws Throwable {
String path = basePath + "/traversal/e;" + environmentId +"/type=r;definedBy=%2Frt%3B" + pingableHostRTypeId;
Response response = get(path, "page", "0", "per_page", "2", "sort", "id");
JsonNode json = mapper.readTree(response.body().string());
assertEquals(2, json.size());
JsonNode first = json.get(0);
JsonNode second = json.get(1);
response = get(path, "page", "0", "per_page", "1", "sort", "id");
json = mapper.readTree(response.body().string());
assertEquals(1, json.size());
assertEquals(first, json.get(0));
response = get(path, "page", "1", "per_page", "1", "sort", "id");
json = mapper.readTree(response.body().string());
assertEquals(1, json.size());
assertEquals(second, json.get(0));
response = get(path, "page", "0", "per_page", "1", "sort", "id",
"order", "desc");
json = mapper.readTree(response.body().string());
assertEquals(1, json.size());
assertEquals(second, json.get(0));
response = get(path, "page", "1", "per_page", "1", "sort", "id",
"order", "desc");
json = mapper.readTree(response.body().string());
assertEquals(1, json.size());
assertEquals(first, json.get(0));
}
@Test
public void testTenantsContainEnvironments() throws Throwable {
assertRelationshipExists("tenant/relationships",
CanonicalPath.of().tenant(tenantId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).get());
// jsonld dropped
// assertRelationshipJsonldExists("tenant/relationships",
// tenantId,
// contains.name(),
// environmentId);
}
@Test
public void testTenantsContainResourceTypes() throws Throwable {
assertRelationshipExists("traversal/rt;" + urlTypeId +"/relationships;in",
CanonicalPath.of().tenant(tenantId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).resourceType(urlTypeId).get());
assertRelationshipExists("tenant/relationships",
CanonicalPath.of().tenant(tenantId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).resourceType(pingableHostRTypeId).get());
}
@Test
public void testTenantsContainMetricTypes() throws Throwable {
assertRelationshipExists("traversal/mt;" + responseTimeMTypeId +"/relationships;in",
CanonicalPath.of().tenant(tenantId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).metricType(responseTimeMTypeId).get());
assertRelationshipExists("tenant/relationships",
CanonicalPath.of().tenant(tenantId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).metricType(statusCodeMTypeId).get());
}
@Test
public void testEnvironmentsContainResources() throws Throwable {
assertRelationshipExists("traversal/e;" + environmentId +"/relationships",
CanonicalPath.of().tenant(tenantId).environment(environmentId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host2ResourceId).get());
assertRelationshipExists("traversal/e;" + environmentId +"/relationships",
CanonicalPath.of().tenant(tenantId).environment(environmentId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host1ResourceId).get());
// jsonld dropped
// assertRelationshipJsonldExists("/entity/e;" + environmentId +"/relationships",
// environmentId,
// contains.name(),
// host1ResourceId);
//
// assertRelationshipJsonldExists("environments/" + environmentId +"/relationships",
// environmentId,
// contains.name(),
// host2ResourceId);
}
@Test
public void testTenantsContainFeeds() throws Throwable {
assertRelationshipExists("traversal/f;" + feedId +"/relationships;in",
CanonicalPath.of().tenant(tenantId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).feed(feedId).get());
// jsonld dropped
// assertRelationshipJsonldExists("feeds/" + feedId +"/relationships",
// tenantId,
// contains.name(),
// feedId);
}
@Test
public void testEnvironmentsContainMetrics() throws Throwable {
assertRelationshipExists("traversal/e;" + environmentId +"/relationships",
CanonicalPath.of().tenant(tenantId).environment(environmentId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).metric(responseTimeMetricId).get());
assertRelationshipExists("traversal/e;" + environmentId +"/relationships",
CanonicalPath.of().tenant(tenantId).environment(environmentId).get(),
contains.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).metric(responseStatusCodeMetricId)
.get());
// jsonld dropped
// assertRelationshipJsonldExists("/traversal/e;" + environmentId +"/relationships",
// environmentId,
// contains.name(),
// responseTimeMetricId);
//
// assertRelationshipJsonldExists("/traversal/e;" + environmentId +"/relationships",
// environmentId,
// contains.name(),
// responseStatusCodeMetricId);
}
@Test
public void testResourceTypesIncorporatesMetricTypes() throws Throwable {
assertRelationshipExists("traversal/rt;" + pingableHostRTypeId +"/relationships",
CanonicalPath.of().tenant(tenantId).resourceType(pingableHostRTypeId).get(),
incorporates.name(),
CanonicalPath.of().tenant(tenantId).metricType(responseTimeMTypeId).get());
assertRelationshipExists("traversal/mt;" + responseStatusCodeMTypeId +"/relationships;in",
CanonicalPath.of().tenant(tenantId).resourceType(pingableHostRTypeId).get(),
incorporates.name(),
CanonicalPath.of().tenant(tenantId).metricType(responseStatusCodeMTypeId).get());
// jsonld dropped
// assertRelationshipJsonldExists("resourceTypes/" + pingableHostRTypeId +"/relationships",
// pingableHostRTypeId,
// incorporates.name(),
// responseTimeMTypeId);
}
@Test
public void testResourcesIncorporatesMetrics() throws Throwable {
assertRelationshipExists("traversal/e;" + environmentId +"/r;" + host1ResourceId +"/relationships",
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host1ResourceId).get(),
incorporates.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).metric(responseStatusCodeMetricId)
.get());
assertRelationshipExists("traversal/e;" + environmentId + "/r;" + host1ResourceId +"/relationships",
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host1ResourceId).get(),
incorporates.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).metric(responseTimeMetricId).get());
// jsonld dropped
// assertRelationshipJsonldExists(environmentId +"/resources/" + host1ResourceId +"/relationships",
// host1ResourceId,
// incorporates.name(),
// responseTimeMetricId);
}
@Test
public void testResourceTypesDefinesResources() throws Throwable {
assertRelationshipExists("traversal/rt;" + pingableHostRTypeId +"/relationships",
CanonicalPath.of().tenant(tenantId).resourceType(pingableHostRTypeId).get(),
defines.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host2ResourceId).get());
}
@Test
public void testMetricTypesDefinesMetrics() throws Throwable {
assertRelationshipExists("traversal/mt;" + responseStatusCodeMTypeId +"/relationships",
CanonicalPath.of().tenant(tenantId).metricType(responseStatusCodeMTypeId).get(),
defines.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).metric(responseStatusCodeMetricId)
.get());
assertRelationshipExists("traversal/mt;" + responseTimeMTypeId +"/relationships",
CanonicalPath.of().tenant(tenantId).metricType(responseTimeMTypeId).get(),
defines.name(),
CanonicalPath.of().tenant(tenantId).environment(environmentId).metric(responseTimeMetricId).get());
}
@Test
public void testCustomRelationship() throws Throwable {
assertRelationshipExists("traversal/e;" + environmentId +"/r;" + host2ResourceId +"/relationships",
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host2ResourceId).get(),
customRelationName,
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host1ResourceId).get());
}
@Test
public void testRelationshipFiltering() throws Throwable {
assertRelationshipExists("traversal/e;" + environmentId +"/r;" + host2ResourceId
+ "/relationships;propertyName=from;propertyValue=2000-01-01",
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host2ResourceId).get(),
customRelationName,
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host1ResourceId).get());
assertRelationshipExists("traversal/e;" + environmentId +"/r;" + host2ResourceId
+ "/relationships;propertyName=confidence;propertyValue=90%25",
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host2ResourceId).get(),
customRelationName,
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host1ResourceId).get());
assertRelationshipExists("traversal/e;" + environmentId +"/r;" + host2ResourceId
+ "/relationships;name=" + customRelationName,
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host2ResourceId).get(),
customRelationName,
CanonicalPath.of().tenant(tenantId).environment(environmentId).resource(host1ResourceId).get());
}
@Test
public void testResourceHierarchyQuerying() throws Throwable {
assertEntitiesExist("traversal/e;" + environmentId +"/r;" + room1ResourceId +"/type=resource",
"/e;"+ environmentId + "/r;"+ room1ResourceId + "/r;table");
String base = "/e;"+ environmentId + "/r;"+ room1ResourceId + "/r;table";
assertEntitiesExist("traversal/e;" + environmentId +"/r;" + room1ResourceId +"/r;table/type=r",
base + "/r;leg%2F1", base + "/r;leg%202", base + "/r;leg%3B3", base + "/r;leg-4");
assertEntitiesExist("traversal/e;" + environmentId +"/r;weapons/rl;isParentOf/type=r",
"/e;"+ environmentId + "/r;"+ room1ResourceId + "/r;table/r;leg%2F1",
"/e;"+ environmentId + "/r;"+ room1ResourceId + "/r;table/r;leg-4");
}
@Test @Ignore
public void testResourceBulkCreate() throws Throwable {
StringBuilder payload = new StringBuilder("{\"/e;test\": {\"resource\": [");
for (int i = 0; i < 100; i++) {
payload.append("{ \"id\": \""
+ bulkResourcePrefix + "-" + i + "\", \"resourceTypePath\": \"/rt;" + roomRTypeId +
"\"}");
if (i != 0) {
payload.append(",");
}
}
payload.append("]}}");
Response response = post(basePath + "/bulk", payload.toString());
assertEquals(201, response.code());
JsonNode json = mapper.readTree(response.body().string());
assertEquals(100, json.size());
for (Iterator<Entry<String, JsonNode>> it = json.fields(); it.hasNext(); ) {
Entry<String, JsonNode> en = it.next();
CanonicalPath p = CanonicalPath.fromString(en.getKey());
String env = p.ids().getEnvironmentId();
String rid = p.ids().getResourcePath().getSegment().getElementId();
delete(basePath + "/entity/e;" + env +"/r;" + rid);
}
}
@Test
public void testResourceBulkCreateUnderFeedWithDuplicates() throws Throwable {
String pathToResType = "/t;"+ tenantId + "/f;"+ feedId + "/rt;"+ bulkResourceTypePrefix + ".1";
String payload =
"{"
+ " \"/t;"+ tenantId + "/f;"+ feedId + "\": {" //
+ " \"resourceType\": [" //
+ " {" //
+ " \"id\": \""+ bulkResourceTypePrefix +".1\"" //
+ " }," //
+ " {" //
+ " \"id\": \""+ bulkResourceTypePrefix +".1\"" //
+ " }" //
+ " ]," //
+ " \"resource\": [" //
+ " {" //
+ " \"id\" : \""+ bulkResourcePrefix + ".1\"," //
+ " \"resourceTypePath\": \""+ pathToResType + "\"" //
+ " }," //
+ " {" //
+ " \"id\" : \""+ bulkResourcePrefix + ".2\"," //
+ " \"resourceTypePath\": \""+ pathToResType + "\"" //
+ " }" //
+ " ]," //
+ " \"metricType\": [" //
+ " {" //
+ " \"id\" : \""+ bulkMetricTypePrefix + ".1\"," //
+ " \"unit\" : \"BYTES\"," //
+ " \"type\" : \"GAUGE\"," //
+ " \"collectionInterval\": \"300\"" //
+ " }," //
+ " {" //
+ " \"id\" : \""+ bulkMetricTypePrefix + ".2\"," //
+ " \"unit\" : \"BYTES\"," //
+ " \"type\" : \"GAUGE\"," //
+ " \"collectionInterval\": \"300\"" //
+ " }" //
+ " ]" //
+ " }," //
+ " \"" + pathToResType +"\": {" //
+ " \"relationship\": [" //
+ " {" //
+ " \"name\" : \"incorporates\"," //
+ " \"otherEnd\" : \"/t;"+ tenantId + "/f;"+ feedId + "/mt;"+ bulkMetricTypePrefix + ".1\"," //
+ " \"direction\": \"outgoing\"" //
+ " }," //
+ " {" //
+ " \"name\" : \"incorporates\"," //
+ " \"otherEnd\" : \"/t;"+ tenantId + "/f;"+ feedId + "/mt;"+ bulkMetricTypePrefix + ".1\"," //
+ " \"direction\": \"outgoing\"" //
+ " }," //
+ " {" //
+ " \"name\" : \"incorporates\"," //
+ " \"otherEnd\" : \"/t;"+ tenantId + "/f;"+ feedId + "/mt;"+ bulkMetricTypePrefix + ".2\"," //
+ " \"direction\": \"outgoing\"" //
+ " }" //
+ " ]" //
+ " }" //
+ "}";
Response response = post(basePath + "/bulk", payload);
assertEquals(201, response.code());
JsonNode json = mapper.readTree(response.body().string());
JsonNode resourceCodes = json.get("resource");
JsonNode metricTypeCodes = json.get("metricType");
JsonNode resourceTypeCodes = json.get("resourceType");
JsonNode relationshipCodes = json.get("relationship");
// check, there are no dupes
assertEquals(2, resourceCodes.size());
assertEquals(2, metricTypeCodes.size());
assertEquals(1, resourceTypeCodes.size());
assertEquals(2, relationshipCodes.size());
// check, no 409 was raised, because only the first status code is taken
assertEquals(201, resourceCodes.get("/t;" + tenantId + "/f;" + feedId + "/r;" + bulkResourcePrefix + ".1")
.asInt());
assertEquals(201, resourceCodes.get("/t;" + tenantId + "/f;" + feedId + "/r;" + bulkResourcePrefix + ".2")
.asInt());
assertEquals(201, resourceTypeCodes.get(pathToResType).asInt());
assertEquals(201,
metricTypeCodes.get("/t;" + tenantId + "/f;" + feedId + "/mt;" + bulkMetricTypePrefix + ".1").asInt());
assertEquals(201,
metricTypeCodes.get("/t;" + tenantId + "/f;" + feedId + "/mt;" + bulkMetricTypePrefix + ".2").asInt());
assertEquals(201, relationshipCodes.get("/rl;" + PathSegmentCodec.encode(pathToResType
+ "-(incorporates)->"
+ "/t;"+ tenantId + "/f;"+ feedId + "/mt;"+ bulkMetricTypePrefix + "" + ".1")).asInt());
assertEquals(201, relationshipCodes.get("/rl;" + PathSegmentCodec.encode(pathToResType
+ "-(incorporates)->"
+ "/t;"+ tenantId + "/f;"+ feedId + "/mt;"+ bulkMetricTypePrefix + "" + ".2")).asInt());
delete(basePath + "/entity/f;" + feedId +"/r;" + bulkResourcePrefix + ".1");
delete(basePath + "/entity/f;" + feedId +"/mt;" + bulkMetricTypePrefix + ".1");
delete(basePath + "/entity/f;" + feedId +"/mt;" + bulkMetricTypePrefix + ".2");
// client.delete(path: basePath + "/feeds/" + feedId +"/resourceTypes/" + bulkResourceTypePrefix" + ".1");
}
@Test
public void testResourceBulkCreateWithErrors() throws Throwable {
StringBuilder payload = new StringBuilder("{\"/e;" + environmentId + "\": {\"resource\": [");
//this should fail
payload.append("{\"id\": \"" + room1ResourceId + "\", \"resourceTypePath\": \"/rt;" + roomRTypeId + "\"},");
//this should succeed
payload.append("{\"id\": \"" + bulkResourcePrefix + "-1\", \"resourceTypePath\": \"/rt;" + roomRTypeId + "\"}");
payload.append("]}}");
Response response = post(basePath + "/bulk", payload.toString());
assertEquals(201, response.code());
JsonNode json = mapper.readTree(response.body().string());
JsonNode codes = json.get("resource");
assertEquals(2, codes.size());
assertEquals(409, codes.get("/t;" + tenantId + "/e;" + environmentId + "/r;" + room1ResourceId).asInt());
assertEquals(201,
codes.get("/t;" + tenantId + "/e;" + environmentId + "/r;" + bulkResourcePrefix + "-1").asInt());
delete(basePath + "/entity/e;" + environmentId +"/r;" + bulkResourcePrefix + "-1");
}
@Test
public void testBulkCreateAndRelate() throws Throwable {
String epath = "/t;"+ tenantId + "/e;"+ environmentId;
String rpath = epath +"/r;" + bulkResourcePrefix + "-1";
String mpath = epath +"/m;"+ responseTimeMetricId + "";
String payload = "{" //
+ "\"" + epath + "\": {" //
+ "\"resource\": [" //
+ "{" //
+ "\"id\": \"" + bulkResourcePrefix + "-1\"," //
+ "\"resourceTypePath\": \"/rt;" + roomRTypeId + "\"" //
+ "}" //
+ "]" //
+ "}," +
"\"" + rpath + "\": {" //
+ "\"relationship\" : [" //
+ "{" //
+ "\"name\": \"incorporates\"," //
+ "\"otherEnd\": \"" + mpath + "\"," //
+ "\"direction\": \"outgoing\"" //
+ "}" //
+ "]" //
+ "}" //
+ "}";
Response response = post(basePath + "/bulk", payload);
assertEquals(201, response.code());
JsonNode json = mapper.readTree(response.body().string());
JsonNode resourceCodes = json.get("resource");
JsonNode relationshipCodes = json.get("relationship");
assertEquals(1, resourceCodes.size());
assertEquals(201, resourceCodes.get(rpath).asInt());
assertEquals(1, relationshipCodes.size());
assertEquals(201, relationshipCodes.fields().next().getValue().asInt());
// TODO : find out if this returning 404 instead of 204 is a bug or feature
//delete(basePath + "/" + environmentId + "/resources/" + bulkResourcePrefix + "-1/metrics/../"
// + responseTimeMetricId);
delete(basePath + "/entity/e;" + environmentId +"/r;" + bulkResourcePrefix +"-1");
}
@Test
public void testComplexBulkCreate() throws Throwable {
String env1 = "bulk-env-" + UUID.randomUUID().toString();
String env2 = "bulk-env-" + UUID.randomUUID().toString();
String rt1 = "bulk-URL" + UUID.randomUUID().toString();
String rt2 = "bulk-URL2" + UUID.randomUUID().toString();
String mt1 = "bulk-ResponseTime" + UUID.randomUUID().toString();
String payload =
"{"//
+ " \"/t;"+ tenantId + "\": {"//
+ " \"environment\": ["//
+ " {"//
+ " \"id\": \"" + env1 + "\","//
+ " \"properties\": {\"p_key\": \"value\"},"//
+ " \"outgoing\": {"//
+ " \"customRel\": [\"/t;"+ tenantId + "\"]"//
+ " }"//
+ " },"//
+ " {"//
+ " \"id\": \""+ env2 +"\","//
+ " \"properties\": {\"p_key\": \"value2\"}"//
+ " }"//
+ " ],"//
+ " \"resourceType\": ["//
+ " {"//
+ " \"id\": \"" + rt1 +"\""//
+ " },"//
+ " {"//
+ " \"id\": \""+ rt2 +"\""//
+ " }"//
+ " ],"//
+ " \"metricType\": ["//
+ " {"//
+ " \"id\": \""+ mt1 +"\","//
+ " \"type\": \"GAUGE\","//
+ " \"unit\": \"MILLISECONDS\","//
+ " \"collectionInterval\": \"1\""//
+ " }"//
+ " ]"//
+ " },"//
+ " \"/t;"+ tenantId + "/rt;" + rt1 + "\": {"//
+ " \"dataEntity\": ["//
+ " {"//
+ " \"role\": \"configurationSchema\","//
+ " \"value\": {"//
+ " \"title\": \"URL config schema\","//
+ " \"description\": \"A json schema describing configuration of an URL\","//
+ " \"type\": \"string\""//
+ " }"//
+ " }"//
+ " ],"//
+ " \"operationType\": ["//
+ " {"//
+ " \"id\": \"ping\""//
+ " }"//
+ " ]"//
+ " },"//
+ " \"/t;"+ tenantId + "/rt;" + rt2 + "\": {"//
+ " \"dataEntity\": ["//
+ " {"//
+ " \"role\": \"connectionConfigurationSchema\","//
+ " \"value\": {"//
+ " \"title\": \"URL2 connection config schema\","//
+ " \"description\": \"A json schema describing connection to an URL\","//
+ " \"type\": \"string\""//
+ " }"//
+ " }"//
+ " ],"//
+ " \"operationType\": ["//
+ " {"//
+ " \"id\": \"ping-pong\""//
+ " }"//
+ " ]"//
+ " },"//
+ " \"/t;"+ tenantId + "/e;" + env1 + "\": {"//
+ " \"resource\": ["//
+ " {"//
+ " \"id\": \"url1\","//
+ " \"resourceTypePath\": \"/t;"+ tenantId + "/rt;" + rt1 +"\""//
+ " }"//
+ " ],"//
+ " \"metric\": ["//
+ " {"//
+ " \"id\": \"url1_responseTime\","//
+ " \"metricTypePath\": \"/t;"+ tenantId + "/mt;"+ mt1 +"\""//
+ " }"//
+ " ]"//
+ " },"//
+ " \"/t;"+ tenantId + "/e;" + env1 +"/r;url1\": {"//
+ " \"dataEntity\": ["//
+ " {"//
+ " \"role\": \"configuration\","//
+ " \"value\": \"http://redhat.com\""//
+ " }"//
+ " ],"//
+ " \"relationship\": ["//
+ " {"//
+ " \"name\": \"incorporates\","//
+ " \"otherEnd\": \"/t;"+ tenantId + "/e;" + env1 +"/m;url1_responseTime\","//
+ " \"direction\": \"outgoing\""//
+ " }"//
+ " ]"//
+ " }"//
+ "}";
Response response = post(basePath + "/bulk", payload);
assertEquals(201, response.code());
JsonNode json = mapper.readTree(response.body().string());
JsonNode environmentCodes = json.get("environment");
JsonNode resourceTypeCodes = json.get("resourceType");
JsonNode metricTypeCodes = json.get("metricType");
JsonNode dataCodes = json.get("dataEntity");
JsonNode operationTypeCodes = json.get("operationType");
JsonNode resourceCodes = json.get("resource");
JsonNode metricCodes = json.get("metric");
JsonNode relationshipCodes = json.get("relationship");
//now make a second call, this time only create a metadata pack.
//this has to be done in two requests, because the resource types need to be fully populated before they can
//be put into the pack because afterwards they're frozen
payload =
"{"//
+ " \"/t;"+ tenantId + "\": {"//
+ " \"metadataPack\": ["//
+ " {"//
+ " \"members\": [\"/t;"+ tenantId + "/rt;" + rt1 + "\", \"/t;"+ tenantId + "/rt;" + rt2 +"\","//
+ " \"/t;"+ tenantId + "/mt;"+ mt1 +"\"]"//
+ " }"//
+ " ]"//
+ " }"//
+ "}";
response = post(basePath + "/bulk", payload);
assertEquals(201, response.code());
json = mapper.readTree(response.body().string());
JsonNode metadataPackCodes = json.get("metadataPack");
assertEquals(2, environmentCodes.size());
assertEquals(201, environmentCodes.get("/t;"+ tenantId + "/e;" + env1).asInt());
assertEquals(201, environmentCodes.get("/t;"+ tenantId + "/e;" + env2).asInt());
assertEquals(2, resourceTypeCodes.size());
assertEquals(201, resourceTypeCodes.get("/t;"+ tenantId + "/rt;" + rt1).asInt());
assertEquals(201, resourceTypeCodes.get("/t;"+ tenantId + "/rt;" + rt2).asInt());
assertEquals(1, metricTypeCodes.size());
assertEquals(201, metricTypeCodes.get("/t;"+ tenantId + "/mt;" + mt1).asInt());
assertEquals(3, dataCodes.size());
assertEquals(201, dataCodes.get("/t;"+ tenantId + "/rt;" + rt1 +"/d;configurationSchema").asInt());
assertEquals(201, dataCodes.get("/t;"+ tenantId + "/rt;" + rt2 +"/d;connectionConfigurationSchema").asInt());
assertEquals(201, dataCodes.get("/t;"+ tenantId + "/e;" + env1 +"/r;url1/d;configuration").asInt());
assertEquals(2, operationTypeCodes.size());
assertEquals(201, operationTypeCodes.get("/t;"+ tenantId + "/rt;" + rt1 +"/ot;ping").asInt());
assertEquals(201, operationTypeCodes.get("/t;"+ tenantId + "/rt;" + rt2 +"/ot;ping-pong").asInt());
assertEquals(1, resourceCodes.size());
assertEquals(201, resourceCodes.get("/t;"+ tenantId + "/e;" + env1 +"/r;url1").asInt());
assertEquals(1, metricCodes.size());
assertEquals(201, metricCodes.get("/t;"+ tenantId + "/e;" + env1 +"/m;url1_responseTime").asInt());
assertEquals(1, relationshipCodes.size());
assertEquals(201, relationshipCodes.fields().next().getValue().asInt());
assertEquals(1, metadataPackCodes.size());
assertEquals(201, metadataPackCodes.fields().next().getValue().asInt());
response = get(basePath + "/traversal/e;" + env1 +"/r;url1/rl;incorporates/type=metric");
json = mapper.readTree(response.body().string());
assertEquals("/t;"+ tenantId + "/e;" + env1 +"/m;url1_responseTime", json.get(0).get("path").asText());
String mpPath = metadataPackCodes.fields().next().getKey();
String mpId = mpPath.substring(mpPath.lastIndexOf(";") + 1);
delete(basePath + "/entity/mp;" + mpId);
delete(basePath + "/entity/e;" + env1);
delete(basePath + "/entity/e;" + env2);
delete(basePath + "/entity/rt;" + rt1);
delete(basePath + "/entity/mt;" + mt1);
}
@Test
public void testMetadataPacks() throws Throwable {
Response response = post(basePath + "/entity/metadataPack",
"{ \"members\": [\"/t;" + tenantId + "/rt;" + urlTypeId + "\"]}");
JsonNode json = mapper.readTree(response.body().string());
String mpId = json.get("id").asText();
String url = baseURI + basePath + "/entity/rt;" + urlTypeId;
response = client.newCall(newAuthRequest().url(url).delete().build()).execute();
assertEquals("Deleting a resource type that is part of metadatapack should not be possible.",
400, response.code());
delete(basePath + "/entity/mp;" + mpId);
}
@Test
public void testRecursiveChildren() throws Throwable {
try {
Response response = post(basePath + "/entity/e;" + environmentId +"/resource",
"{ \"id\": \"rootResource\", \"resourceTypePath\": \"/" + urlTypeId +"\"}");
assertEquals(201, response.code());
response = post(basePath + "/entity/e;" + environmentId +"/r;rootResource/resource",
"{\"id\": \"childResource\", \"resourceTypePath\": \"/" + urlTypeId +"\"}" );
assertEquals(201, response.code());
response = post(basePath + "/entity/e;" + environmentId +"/r;rootResource/r;childResource/resource",
"{\"id\": \"grandChildResource\", \"resourceTypePath\": \"/" + urlTypeId +"\"}");
assertEquals(201, response.code());
response = post(basePath + "/entity/e;" + environmentId +"/r;rootResource/r;childResource/resource",
"{\"id\": \"grandChildResource2\", \"resourceTypePath\": \"/" + roomRTypeId + "\"}");
assertEquals(201, response.code());
response = get(basePath + "/traversal/e;" + environmentId +"/r;rootResource/recursive/definedBy=%2Frt%3B"
+ urlTypeId);
JsonNode ret = mapper.readTree(response.body().string());
assertEquals(2, ret.size());
Assert.assertTrue(toStream(ret).anyMatch(node -> "childResource".equals(node.get("id").asText())));
Assert.assertTrue(toStream(ret).anyMatch(node -> "grandChildResource".equals(node.get("id").asText())));
response = get(basePath + "/traversal/e;" + environmentId +"/r;rootResource/recursive;type=r" +
"/definedBy=%2Frt%3B" + roomRTypeId);
ret = mapper.readTree(response.body().string());
assertEquals(1, ret.size());
Assert.assertTrue(toStream(ret).anyMatch(node -> "grandChildResource2".equals(node.get("id").asText())));
} finally {
delete(basePath + "/entity/e;" + environmentId +"/r;rootResource");
}
}
@Test
public void testSync() throws Throwable {
String structure = "{ \"structure\": {"//
+ "\"type\": \"feed\","//
+ "\"data\": {"//
+ " \"id\": \"sync-feed\""//
+ "},"//
+ "\"children\": {"//
+ " \"resource\": ["//
+ " {"//
+ " \"data\": {"//
+ " \"id\": \"resource\","//
+ " \"resourceTypePath\": \"resourceType\""//
+ " },"//
+ " \"children\": {"//
+ " \"resource\": ["//
+ " {"//
+ " \"data\": {"//
+ " \"id\": \"childResource\","//
+ " \"resourceTypePath\": \"../resourceType\""//
+ " }"//
+ " }"//
+ " ]"//
+ " }"//
+ " }"//
+ " ],"//
+ " \"resourceType\": ["//
+ " {"//
+ " \"data\": {"//
+ " \"id\": \"resourceType\","//
+ " \"name\": \"My Resource Type With A Friendly Name\""//
+ " }"//
+ " }"//
+ " ],"//
+ " \"metric\": ["//
+ " {"//
+ " \"data\": {"//
+ " \"id\": \"metric\","//
+ " \"metricTypePath\": \"metricType\","//
+ " \"collectionInterval\": 0"//
+ " }"//
+ " }"//
+ " ],"//
+ " \"metricType\": ["//
+ " {"//
+ " \"data\": {"//
+ " \"id\": \"metricType\","//
+ " \"type\": \"GAUGE\","//
+ " \"unit\": \"NONE\","//
+ " \"collectionInterval\": 0,"//
+ " \"name\": \"My Metric Type With A Friendly Name\""//
+ " }"//
+ " }"//
+ " ]"//
+ "}"//
+ "}}";
try {
Response response = post(basePath + "/entity/feed", "{\"id\": \"sync-feed\"}");
assertEquals(201, response.code());
response = post(basePath + "/entity/f;sync-feed/resourceType", "{\"id\": \"doomed\"}");
assertEquals(201, response.code());
//check that the doomed resource type is there
response = get(basePath + "/entity/f;sync-feed/rt;doomed");
assertEquals(200, response.code());
response = post(basePath + "/sync/f;sync-feed", structure);
assertEquals(204, response.code());
//check that stuff is there
response = get(basePath + "/entity/f;sync-feed");
assertEquals(200, response.code());
response = get(basePath + "/entity/f;sync-feed/r;resource");
assertEquals(200, response.code());
response = get(basePath + "/entity/f;sync-feed/r;resource/r;childResource");
assertEquals(200, response.code());
response = get(basePath + "/entity/f;sync-feed/rt;resourceType");
assertEquals(200, response.code());
response = get(basePath + "/entity/f;sync-feed/mt;metricType");
assertEquals(200, response.code());
//check that the doomed resource type is gone, because it was not part of the payload from the feed
response = get(basePath + "/entity/f;sync-feed/rt;doomed");
assertEquals(404, response.code());
} finally {
Response response = get(basePath + "/entity/f;sync-feed");
if (response.code() == 200) {
delete(basePath + "/entity/f;sync-feed");
}
}
}
@Test
public void testTreeHash() throws Throwable {
Response response = get(basePath + "/entity/e;" + environmentId + "/r;" + room1ResourceId + "/treeHash");
assertEquals(200, response.code());
ObjectMapper mapper = new ObjectMapper();
InventoryJacksonConfig.configure(mapper);
SyncHash.Tree tree = mapper.readValue(response.body().string(), SyncHash.Tree.class);
Resource room1Resource = readAs("/entity/e;" + environmentId + "/r;" + room1ResourceId, mapper, Resource.class);
Resource table = readAs("/entity/e;" + environmentId + "/r;" + room1ResourceId + "/r;table", mapper,
Resource.class);
assertEquals(tree.getHash(), room1Resource.getSyncHash());
assertEquals(tree.getChild(Path.Segment.from("r;table")).getHash(), table.getSyncHash());
}
@Test
public void testInvalidTreeHash() throws Throwable {
Response response = get(basePath + "/entity/e;" + environmentId + "/treeHash");
assertEquals(400, response.code());
}
@Test
public void testHistory_onEntity() throws Throwable {
Instant beforeCreate = Instant.now();
Response response = post(basePath + "/entity/environment",
mapper.writeValueAsString(Environment.Blueprint.builder().withId("testHistory_onEntity").build()));
assertEquals(201, response.code());
Thread.sleep(10);
Instant afterCreate = Instant.now();
//do 2 updates
response = put(basePath + "/entity/e;testHistory_onEntity",
Environment.Update.builder().withName("My Env").build());
assertEquals(204, response.code());
Thread.sleep(10);
Instant afterFirstUpdate = Instant.now();
response = put(basePath + "/entity/e;testHistory_onEntity",
Environment.Update.builder().withName("My Env, like").build());
assertEquals(204, response.code());
Thread.sleep(10);
Instant afterSecondUpdate = Instant.now();
response = client.newCall(newAuthRequest().url(baseURI + basePath + "/entity/e;testHistory_onEntity").delete()
.build()).execute();
assertEquals(204, response.code());
Thread.sleep(10);
Instant afterDelete = Instant.now();
response = get(basePath + "/entity/e;testHistory_onEntity/history");
assertEquals(200, response.code());
List<Change<?>> changes = mapper.readValue(response.body().byteStream(),
new TypeReference<List<Change<?>>>() {});
assertEquals(4, changes.size());
Change<?> createChange = changes.get(0);
Change<?> firstUpdateChange = changes.get(1);
Change<?> secondUpdateChange = changes.get(2);
Change<?> deleteChange = changes.get(3);
assertEquals(Action.created().asEnum(), createChange.getAction().asEnum());
assertTrue(createChange.getTime().compareTo(beforeCreate) > 0);
assertTrue(createChange.getTime().compareTo(afterCreate) < 0);
assertEquals("testHistory_onEntity", ((Environment) createChange.getActionContext()).getId());
assertEquals(Action.updated().asEnum(), firstUpdateChange.getAction().asEnum());
assertTrue(firstUpdateChange.getTime().compareTo(afterCreate) > 0);
assertTrue(firstUpdateChange.getTime().compareTo(afterFirstUpdate) < 0);
assertEquals("testHistory_onEntity",
((Action.Update<Environment, Environment.Update>) firstUpdateChange.getActionContext())
.getOriginalEntity().getId());
assertNull(((Action.Update<Environment, Environment.Update>) firstUpdateChange.getActionContext())
.getOriginalEntity().getName());
assertEquals("My Env", ((Action.Update<Environment, Environment.Update>) firstUpdateChange.getActionContext())
.getUpdate().getName());
assertEquals(Action.updated().asEnum(), secondUpdateChange.getAction().asEnum());
assertTrue(secondUpdateChange.getTime().compareTo(afterFirstUpdate) > 0);
assertTrue(secondUpdateChange.getTime().compareTo(afterSecondUpdate) < 0);
assertEquals("testHistory_onEntity", ((Action.Update<Environment, Environment.Update>) firstUpdateChange.getActionContext())
.getOriginalEntity().getId());
assertEquals("My Env", ((Action.Update<Environment, Environment.Update>) secondUpdateChange.getActionContext())
.getOriginalEntity().getName());
assertEquals("My Env, like", ((Action.Update<Environment, Environment.Update>) secondUpdateChange
.getActionContext()).getUpdate().getName());
assertEquals(Action.deleted().asEnum(), deleteChange.getAction().asEnum());
assertTrue(deleteChange.getTime().compareTo(afterSecondUpdate) > 0);
assertTrue(deleteChange.getTime().compareTo(afterDelete) < 0);
assertEquals("testHistory_onEntity", ((Environment) createChange.getActionContext()).getId());
assertEquals("My Env, like", ((Environment) deleteChange.getActionContext()).getName());
response = get(basePath + "/entity/e;testHistory_onEntity/history",
"to", Long.toString(afterFirstUpdate.toEpochMilli()));
assertEquals(200, response.code());
changes = mapper.readValue(response.body().byteStream(), new TypeReference<List<Change<?>>>() {});
assertEquals(2, changes.size());
assertEquals(Action.created().asEnum(), changes.get(0).getAction().asEnum());
assertEquals(Action.updated().asEnum(), changes.get(1).getAction().asEnum());
response = get(basePath + "/entity/e;testHistory_onEntity/history",
"from", Long.toString(afterSecondUpdate.toEpochMilli()));
assertEquals(200, response.code());
changes = mapper.readValue(response.body().byteStream(), new TypeReference<List<Change<?>>>() {});
assertEquals(1, changes.size());
assertEquals(Action.deleted().asEnum(), changes.get(0).getAction().asEnum());
response = get(basePath + "/entity/e;testHistory_onEntity/history",
"from", Long.toString(afterCreate.toEpochMilli()),
"to", Long.toString(afterSecondUpdate.toEpochMilli()));
assertEquals(200, response.code());
changes = mapper.readValue(response.body().byteStream(), new TypeReference<List<Change<?>>>() {});
assertEquals(2, changes.size());
assertEquals(Action.updated().asEnum(), changes.get(0).getAction().asEnum());
assertEquals(Action.updated().asEnum(), changes.get(0).getAction().asEnum());
}
@Test
public void testHistory_onTenant() throws Throwable {
Instant beforeUpdate = Instant.now();
//do 2 updates
Response response = put(basePath + "/tenant",
Tenant.Update.builder().withName("My Tenant").build());
assertEquals(204, response.code());
Instant afterFirstUpdate = Instant.now();
response = put(basePath + "/tenant",
Tenant.Update.builder().withName("My Tenant, like").build());
assertEquals(204, response.code());
Instant afterSecondUpdate = Instant.now();
response = get(basePath + "/tenant/history");
assertEquals(200, response.code());
List<Change<?>> changes = mapper.readValue(response.body().byteStream(),
new TypeReference<List<Change<?>>>() {});
//we need to have at least 3 changes - 1 create and two of our updates. There might be other if other tests
//changed the tenant, too.
assertTrue(changes.size() > 2);
//first is the initial create... we don't care about it here
Change<?> firstUpdateChange = changes.get(changes.size() - 2);
Change<?> secondUpdateChange = changes.get(changes.size() - 1);
assertEquals(Action.updated().asEnum(), firstUpdateChange.getAction().asEnum());
assertTrue(firstUpdateChange.getTime().compareTo(beforeUpdate) > 0);
assertTrue(firstUpdateChange.getTime().compareTo(afterFirstUpdate) < 0);
assertEquals(tenantId,
((Action.Update<Tenant, Tenant.Update>) firstUpdateChange.getActionContext())
.getOriginalEntity().getId());
assertNull(((Action.Update<Tenant, Tenant.Update>) firstUpdateChange.getActionContext())
.getOriginalEntity().getName());
assertEquals("My Tenant", ((Action.Update<Tenant, Tenant.Update>) firstUpdateChange.getActionContext())
.getUpdate().getName());
assertEquals(Action.updated().asEnum(), secondUpdateChange.getAction().asEnum());
assertTrue(secondUpdateChange.getTime().compareTo(afterFirstUpdate) > 0);
assertTrue(secondUpdateChange.getTime().compareTo(afterSecondUpdate) < 0);
assertEquals(tenantId, ((Action.Update<Tenant, Tenant.Update>) firstUpdateChange.getActionContext())
.getOriginalEntity().getId());
assertEquals("My Tenant", ((Action.Update<Tenant, Tenant.Update>) secondUpdateChange.getActionContext())
.getOriginalEntity().getName());
assertEquals("My Tenant, like", ((Action.Update<Tenant, Tenant.Update>) secondUpdateChange
.getActionContext()).getUpdate().getName());
response = get(basePath + "/tenant/history",
"to", Long.toString(afterFirstUpdate.toEpochMilli()));
assertEquals(200, response.code());
changes = mapper.readValue(response.body().byteStream(), new TypeReference<List<Change<?>>>() {});
assertTrue(changes.size() > 1);
//again, the first one is the create
assertEquals(Action.updated().asEnum(), changes.get(changes.size() - 1).getAction().asEnum());
response = get(basePath + "/tenant/history",
"from", Long.toString(beforeUpdate.toEpochMilli()),
"to", Long.toString(afterSecondUpdate.toEpochMilli()));
assertEquals(200, response.code());
changes = mapper.readValue(response.body().byteStream(), new TypeReference<List<Change<?>>>() {});
assertEquals(2, changes.size());
assertEquals(Action.updated().asEnum(), changes.get(0).getAction().asEnum());
assertEquals(Action.updated().asEnum(), changes.get(1).getAction().asEnum());
}
@Test
public void testTimeConstraint_onEntity() throws Throwable {
Response response = get(basePath + "/entity/rt;" + urlTypeId, "at", "0");
assertEquals(404, response.code());
Instant beforeUpdate = Instant.now();
response = put(basePath + "/entity/rt;" + urlTypeId, ResourceType.Update.builder().withName("Changed name")
.build());
assertEquals(204, response.code());
Instant afterUpdate = Instant.now();
response = get(basePath + "/entity/rt;" + urlTypeId, "at", Long.toString(beforeUpdate.toEpochMilli()));
assertEquals(200, response.code());
ResourceType rt = mapper.readValue(response.body().byteStream(), ResourceType.class);
assertNotEquals("Changed name", rt.getName());
//try passing the time as a string instead of a numeric timestamp
response = get(basePath + "/entity/rt;" + urlTypeId, "at", afterUpdate.toString());
assertEquals(200, response.code());
rt = mapper.readValue(response.body().byteStream(), ResourceType.class);
assertEquals("Changed name", rt.getName());
//check the current state without any time specification
response = get(basePath + "/entity/rt;" + urlTypeId);
assertEquals(200, response.code());
rt = mapper.readValue(response.body().byteStream(), ResourceType.class);
assertEquals("Changed name", rt.getName());
}
@Test
public void testTimeConstraint_onTraversal() throws Throwable {
Response response = get(basePath + "/traversal/type=rt", "at", "0");
assertEquals(200, response.code());
List<ResourceType> res = mapper.readValue(response.body().byteStream(),
new TypeReference<List<ResourceType>>() {});
assertTrue(res.isEmpty());
response = get(basePath + "/traversal/type=rt", "at", Instant.now().toString());
assertEquals(200, response.code());
res = mapper.readValue(response.body().byteStream(),
new TypeReference<List<ResourceType>>() {});
assertFalse(res.isEmpty());
}
@Test
public void testTimeConstraint_onTenant() throws Throwable {
Response response = get(basePath + "/tenant");
assertEquals(200, response.code());
Tenant origTenant = mapper.readValue(response.body().byteStream(), Tenant.class);
Instant beforeUpdate = Instant.now();
response = put(basePath + "/tenant", Tenant.Update.builder().withName(UUID.randomUUID().toString()).build());
assertEquals(204, response.code());
response = get(basePath + "/tenant");
assertEquals(200, response.code());
Tenant newTenant = mapper.readValue(response.body().byteStream(), Tenant.class);
assertNotEquals(newTenant.getName(), origTenant.getName());
response = get(basePath + "/tenant", "at", beforeUpdate.toString());
assertEquals(200, response.code());
Tenant checkTenant = mapper.readValue(response.body().byteStream(), Tenant.class);
assertEquals(origTenant.getName(), checkTenant.getName());
}
private <T> T readAs(String path, ObjectMapper mapper, Class<T> type) throws Throwable {
Response response = get(basePath + path);
assertEquals(200, response.code());
return mapper.readValue(response.body().string(), type);
}
protected static void assertEntityExists(String path, String cp) throws Throwable {
assertEntityExists(path, new String[0], cp);
}
protected static void assertEntityExists(String path, String[] queryParams, String cp) throws Throwable {
Response response = get(basePath + "/" + path, queryParams);
assertEquals(200, response.code());
JsonNode json = mapper.readTree(response.body().string());
assertEquals(fullCanonicalPath(cp), json.get("path").asText());
}
protected static void assertEntitiesExist(String path, String... cps) throws Throwable {
List<String> expectedPaths = Arrays.stream(cps).map(cp -> fullCanonicalPath(cp)).collect(Collectors.toList());
Response response = get(basePath + "/" + path);
JsonNode json = mapper.readTree(response.body().string());
List<String> entityPaths = toStream(json).map(node -> node.get("path").asText()).collect(Collectors.toList());
for (Iterator<String> it = expectedPaths.iterator(); it.hasNext();) {
String cp = it.next();
if (entityPaths.remove(cp)) {
it.remove();
}
}
Assert.assertTrue("Unexpected entities with paths: " + entityPaths, entityPaths.isEmpty());
Assert.assertTrue("Following entities not found: " + expectedPaths, expectedPaths.isEmpty());
}
protected static Stream<JsonNode> toStream(JsonNode json) {
Spliterator<JsonNode> jsonSpliterator = Spliterators.spliteratorUnknownSize(json.elements(), 0);
return StreamSupport.stream(jsonSpliterator, false);
}
protected static void assertRelationshipJsonldExists(String path, String source, String label, String target)
throws Throwable {
Response response = get(basePath + "/" + path, "jsonld", "true");
JsonNode json = mapper.readTree(response.body().string());
boolean found = toStream(json)
.anyMatch(node ->
source.equals(node.get("source").get("shortId").asText())
&& label.equals(node.get("name").asText())
&& target.equals(node.get("target").get("shortId").asText()));
Assert.assertTrue("Following edge not found: source: "+ source +", name: "+ label +", target: " + target,
found);
}
protected static void assertRelationshipExists(final String path, final CanonicalPath source, final String label,
CanonicalPath target, String... query) throws Throwable {
Response response = get(basePath + "/" + path, query);
List<Relationship> rels = mapper.readValue(response.body().string(), new TypeReference<List<Relationship>>(){});
Assert.assertTrue("Following Relationship not found: " + source +", " + label + ", " + target,
rels.stream().anyMatch(
r -> source.equals(r.getSource())
&& label.equals(r.getName())
&& target.equals(r.getTarget())
)
);
}
protected static Response postDeletable(CanonicalPath parent, Entity.Blueprint blueprint) throws Throwable {
SegmentType entityType = Inventory.types().byBlueprint(blueprint.getClass()).getSegmentType();
CanonicalPath childCp = parent.extend(entityType, blueprint.getId()).get();
String type =
Character.toLowerCase(entityType.getSimpleName().charAt(0)) + entityType.getSimpleName().substring(1);
if ("dataEntity".equals(type)) {
type = "data";
}
String postPath = basePath + "/entity" + parent.toString() + "/" + type;
String verificationPath = basePath + "/entity" + childCp.toString();
pathsToDelete.put(verificationPath, verificationPath);
return postNew(postPath, blueprint);
}
protected static String fullCanonicalPath(String cp) {
return CanonicalPath.fromPartiallyUntypedString(cp, CanonicalPath.of().tenant(tenantId).get(),
SegmentType.ANY_ENTITY)
.toString();
}
private static String interpolate(String string, Map<String, String> values) {
for (Map.Entry<String, String> e : values.entrySet()) {
string = string.replaceAll(Pattern.quote("${" + e.getKey() + "}"), e.getValue());
}
return string;
}
private static final class MapBuilder<K, V> {
private final Map<K, V> map = new HashMap<>();
public static <K, V> MapBuilder<K, V> map() {
return new MapBuilder<>();
}
private MapBuilder() {
}
MapBuilder<K, V> put(K key, V value) {
map.put(key, value);
return this;
}
public Map<K, V> create() {
return map;
}
}
}