Feature target metadata (#757)

* Defined the model for target matadata and the corresponding repository layer/management
* Added target metadata quotas incl enforcement
* Extended Target Mgmt REST API to allow for metadata CRUD operations
* Added migration scripts for each database
* Added back reference to target metadata in JpaTarget
* Added tests for target management, Mgmt REST API, target metadata RSQL, and REST documentation
* Updated asciidocs for target rest documentation
* Fix Allure imports and annotations
* Fix review findings

Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com>
Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>
This commit is contained in:
Bondar Bogdan
2018-10-29 11:28:34 +01:00
committed by Stefan Behl
parent 1cbde47370
commit 0cf4f8e8b9
37 changed files with 1886 additions and 216 deletions

View File

@@ -1036,7 +1036,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final String updateValue = "valueForUpdate";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateMetadata(knownKey, knownValue));
createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue);
@@ -1060,7 +1060,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateMetadata(knownKey, knownValue));
createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
@@ -1076,7 +1076,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateMetadata(knownKey, knownValue));
createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
mvc.perform(delete("/rest/v1/distributionsets/{dsId}/metadata/XXX", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
@@ -1094,7 +1094,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
createDistributionSetMetadata(testDS.getId(), entityFactory.generateMetadata(knownKey, knownValue));
createDistributionSetMetadata(testDS.getId(), entityFactory.generateDsMetadata(knownKey, knownValue));
mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata/{key}", testDS.getId(), knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
@@ -1113,7 +1113,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) {
createDistributionSetMetadata(testDS.getId(),
entityFactory.generateMetadata(knownKeyPrefix + index, knownValuePrefix + index));
entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index));
}
mvc.perform(get("/rest/v1/distributionsets/{dsId}/metadata?offset=" + offsetParam + "&limit=" + limitParam,
@@ -1189,7 +1189,7 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
final DistributionSet testDS = testdataFactory.createDistributionSet("one");
for (int index = 0; index < totalMetadata; index++) {
createDistributionSetMetadata(testDS.getId(),
entityFactory.generateMetadata(knownKeyPrefix + index, knownValuePrefix + index));
entityFactory.generateDsMetadata(knownKeyPrefix + index, knownValuePrefix + index));
}
final String rsqlSearchValue1 = "value==knownValue1";

View File

@@ -26,8 +26,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -45,8 +47,10 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetMetadata;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
@@ -54,6 +58,7 @@ import org.eclipse.hawkbit.rest.json.model.ExceptionInfo;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.util.IpUtil;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
@@ -1564,8 +1569,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final String body = new JSONObject().put("requestAttributes", true).toString();
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue();
}
@@ -1575,8 +1580,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final String body = new JSONObject().put("description", "verify attribute can be missing").toString();
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
}
@Step
@@ -1584,8 +1589,8 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
final String body = new JSONObject().put("requestAttributes", false).toString();
mvc.perform(put(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + knownTargetId).content(body)
.contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest());
assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue();
}
@@ -1649,4 +1654,193 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest
assertThat(actionsByTarget.getContent()).hasSize(1);
return targetManagement.getByControllerID(tA.getControllerId()).get();
}
@Test
@Description("Ensures that the metadata creation through API is reflected by the repository.")
public void createMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
testdataFactory.createTarget(knownControllerId);
final String knownKey1 = "knownKey1";
final String knownKey2 = "knownKey2";
final String knownValue1 = "knownValue1";
final String knownValue2 = "knownValue2";
final JSONArray metaData1 = new JSONArray();
metaData1.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
metaData1.put(new JSONObject().put("key", knownKey2).put("value", knownValue2));
mvc.perform(
post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("[0]key", equalTo(knownKey1))).andExpect(jsonPath("[0]value", equalTo(knownValue1)))
.andExpect(jsonPath("[1]key", equalTo(knownKey2)))
.andExpect(jsonPath("[1]value", equalTo(knownValue2)));
final TargetMetadata metaKey1 = targetManagement.getMetaDataByControllerId(knownControllerId, knownKey1).get();
final TargetMetadata metaKey2 = targetManagement.getMetaDataByControllerId(knownControllerId, knownKey2).get();
assertThat(metaKey1.getValue()).isEqualTo(knownValue1);
assertThat(metaKey2.getValue()).isEqualTo(knownValue2);
// verify quota enforcement
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerTarget();
final JSONArray metaData2 = new JSONArray();
for (int i = 0; i < maxMetaData - metaData1.length() + 1; ++i) {
metaData2.put(new JSONObject().put("key", knownKey1 + i).put("value", knownValue1 + i));
}
mvc.perform(
post("/rest/v1/targets/{targetId}/metadata", knownControllerId).accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden());
// verify that the number of meta data entries has not changed
// (we cannot use the PAGE constant here as it tries to sort by ID)
assertThat(targetManagement.findMetaDataByControllerId(new PageRequest(0, Integer.MAX_VALUE), knownControllerId)
.getTotalElements()).isEqualTo(metaData1.length());
}
@Test
@Description("Ensures that a metadata update through API is reflected by the repository.")
public void updateMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
// prepare and create metadata for update
final String knownKey = "knownKey";
final String knownValue = "knownValue";
final String updateValue = "valueForUpdate";
setupTargetWithMetadata(knownControllerId, knownKey, knownValue);
final JSONObject jsonObject = new JSONObject().put("key", knownKey).put("value", updateValue);
mvc.perform(put("/rest/v1/targets/{targetId}/metadata/{key}", knownControllerId, knownKey)
.accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON)
.content(jsonObject.toString())).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("key", equalTo(knownKey))).andExpect(jsonPath("value", equalTo(updateValue)));
final TargetMetadata updatedTargetMetadata = targetManagement
.getMetaDataByControllerId(knownControllerId, knownKey).get();
assertThat(updatedTargetMetadata.getValue()).isEqualTo(updateValue);
}
private void setupTargetWithMetadata(final String knownControllerId, final String knownKey,
final String knownValue) {
testdataFactory.createTarget(knownControllerId);
targetManagement.createMetaData(knownControllerId,
Collections.singletonList(entityFactory.generateTargetMetadata(knownKey, knownValue)));
}
@Test
@Description("Ensures that a metadata entry deletion through API is reflected by the repository.")
public void deleteMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
// prepare and create metadata for deletion
final String knownKey = "knownKey";
final String knownValue = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKey, knownValue);
mvc.perform(delete("/rest/v1/targets/{targetId}/metadata/{key}", knownControllerId, knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getMetaDataByControllerId(knownControllerId, knownKey)).isNotPresent();
}
@Test
@Description("Ensures that target metadata deletion request to API on an entity that does not exist results in NOT_FOUND.")
public void deleteMetadataThatDoesNotExistLeadsToNotFound() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
// prepare and create metadata for deletion
final String knownKey = "knownKey";
final String knownValue = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKey, knownValue);
mvc.perform(delete("/rest/v1/targets/{targetId}/metadata/XXX", knownControllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
mvc.perform(delete("/rest/v1/targets/1234/metadata/{key}", knownKey)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
assertThat(targetManagement.getMetaDataByControllerId(knownControllerId, knownKey)).isPresent();
}
@Test
@Description("Ensures that a metadata entry selection through API reflectes the repository content.")
public void getSingleMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
// prepare and create metadata for deletion
final String knownKey = "knownKey";
final String knownValue = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKey, knownValue);
mvc.perform(get("/rest/v1/targets/{targetId}/metadata/{key}", knownControllerId, knownKey))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("key", equalTo(knownKey))).andExpect(jsonPath("value", equalTo(knownValue)));
}
@Test
@Description("Ensures that a metadata entry paged list selection through API reflectes the repository content.")
public void getPagedListOfMetadata() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
final int totalMetadata = 10;
final int limitParam = 5;
final String offsetParam = "0";
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKeyPrefix, knownValuePrefix, totalMetadata);
mvc.perform(get("/rest/v1/targets/{targetId}/metadata?offset=" + offsetParam + "&limit=" + limitParam,
knownControllerId)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("size", equalTo(limitParam))).andExpect(jsonPath("total", equalTo(totalMetadata)))
.andExpect(jsonPath("content[0].key", equalTo("knownKey0")))
.andExpect(jsonPath("content[0].value", equalTo("knownValue0")));
}
private void setupTargetWithMetadata(final String knownControllerId, final String knownKeyPrefix,
final String knownValuePrefix, final int totalMetadata) {
testdataFactory.createTarget(knownControllerId);
final List<MetaData> targetMetadataEntries = new LinkedList<>();
for (int index = 0; index < totalMetadata; index++) {
targetMetadataEntries
.add(entityFactory.generateTargetMetadata(knownKeyPrefix + index, knownValuePrefix + index));
}
targetManagement.createMetaData(knownControllerId, targetMetadataEntries);
}
@Test
@Description("Ensures that a target metadata filtered query with value==knownValue1 parameter returns only the metadata entries with that value.")
public void searchDistributionSetMetadataRsql() throws Exception {
final String knownControllerId = "targetIdWithMetadata";
final int totalMetadata = 10;
final String knownKeyPrefix = "knownKey";
final String knownValuePrefix = "knownValue";
setupTargetWithMetadata(knownControllerId, knownKeyPrefix, knownValuePrefix, totalMetadata);
final String rsqlSearchValue1 = "value==knownValue1";
mvc.perform(get("/rest/v1/targets/{targetId}/metadata?q=" + rsqlSearchValue1, knownControllerId))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andExpect(jsonPath("size", equalTo(1)))
.andExpect(jsonPath("total", equalTo(1))).andExpect(jsonPath("content[0].key", equalTo("knownKey1")))
.andExpect(jsonPath("content[0].value", equalTo("knownValue1")));
}
}