From: Toine Siebelink Date: Mon, 16 Oct 2023 16:14:49 +0000 (+0000) Subject: Merge "Clean up Dependencies" X-Git-Tag: 3.3.9~15 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=fc7a5d30953cfedd7e2ea2f969adab03716de6df;hp=42daf6319fcb5b2857381fd3a98ed12c43b7ddd9;p=cps.git Merge "Clean up Dependencies" --- diff --git a/.gitignore b/.gitignore index b4abc48bc..c59fc4731 100755 --- a/.gitignore +++ b/.gitignore @@ -27,10 +27,10 @@ tmp/ .checkstyle /.tox -/_build/* /__pycache__/* /docs/docs/ /docs/.vscode/ +/docs/_build/* /test-tools/metrics-reports/ diff --git a/cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json b/cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json new file mode 100644 index 000000000..e1796fbc7 --- /dev/null +++ b/cps-ncmp-events/src/main/resources/schemas/trustlevel/device-trust-level-event-schema-1.0.0.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "urn:cps:org.onap.cps.ncmp.events:device-trust-level-event-schema:1.0.0", + "$ref": "#/definitions/DeviceTrustLevel", + "definitions": { + "DeviceTrustLevel" : { + "description": "The payload for device trust level event.", + "type": "object", + "javaType": "org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel", + "properties": { + "data": { + "type": "object", + "properties": { + "trustLevel": { + "type": "string" + } + }, + "required": [ + "trustLevel" + ], + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + } + } +} \ No newline at end of file diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml index 83699b934..d2cce8797 100644 --- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/pom.xml @@ -46,11 +46,42 @@ org.onap.cps cps-ncmp-rest + + org.eclipse.jetty + jetty-server + + + jakarta.servlet + jakarta.servlet-api + org.spockframework spock-core test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + org.spockframework + spock-spring + test + + + org.spockframework + spock-core + test + + \ No newline at end of file diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java index 198b14fc3..e33af45f9 100644 --- a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/main/java/org/onap/cps/ncmp/rest/stub/controller/NetworkCmProxyStubController.java @@ -141,13 +141,13 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity createResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier, - final String datastoreName, final String cmHandle, - @Valid final Object body, + public ResponseEntity createResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle, + @NotNull @Valid final String resourceIdentifier, + @Valid final Object body, final String contentType) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } - + @Override public ResponseEntity deleteResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle, @NotNull @Valid final String resourceIdentifier, @@ -183,13 +183,13 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity patchResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier, - final String datastoreName, final String cmHandle, - @Valid final Object body, + public ResponseEntity patchResourceDataRunningForCmHandle(final String datastoreName, final String cmHandle, + @NotNull @Valid final String resourceIdentifier, + @Valid final Object body, final String contentType) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); } - + @Override public ResponseEntity queryResourceDataForCmHandle(final String datastoreName, final String cmHandle, @Valid final String cpsPath, @Valid final String options, @@ -218,9 +218,10 @@ public class NetworkCmProxyStubController implements NetworkCmProxyApi { } @Override - public ResponseEntity updateResourceDataRunningForCmHandle(@NotNull @Valid final String resourceIdentifier, - final String datastoreName, - final String cmHandle, @Valid final Object body, + public ResponseEntity updateResourceDataRunningForCmHandle(final String datastoreName, + final String cmHandle, + @NotNull @Valid final String resourceIdentifier, + @Valid final Object body, final String contentType) { return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy new file mode 100644 index 000000000..b36e25ada --- /dev/null +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/SampleCpsNcmpClientSpec.groovy @@ -0,0 +1,82 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.cps.ncmp.rest.stub + +import org.onap.cps.ncmp.api.impl.operations.DatastoreType +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.EnableAutoConfiguration +import org.springframework.boot.test.context.SpringBootContextLoader +import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.web.client.TestRestTemplate +import org.springframework.boot.test.web.server.LocalServerPort +import org.springframework.http.HttpStatus +import org.springframework.test.context.ActiveProfiles +import org.springframework.test.context.ContextConfiguration + +import com.fasterxml.jackson.core.JsonProcessingException +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.JsonMappingException +import com.fasterxml.jackson.databind.ObjectMapper + +import spock.lang.Shared +import spock.lang.Specification + +@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("test") +class SampleCpsNcmpClientSpec extends Specification { + + static final String CM_HANDLE = "anything" + + static final String DATA_STORE_NAME = DatastoreType.PASSTHROUGH_OPERATIONAL.getDatastoreName() + + @LocalServerPort + def port + + final def testRestTemplate = new TestRestTemplate() + + @Value('${rest.api.ncmp-stub-base-path}') + def stubBasePath + + @Autowired + ObjectMapper objectMapper + + def 'Test the invocation of the stub API' () throws JsonMappingException, JsonProcessingException { + + when: 'Get resource data for cm handle URL is invoked' + def url = "${getBaseUrl()}/v1/ch/${CM_HANDLE}/data/ds/${DATA_STORE_NAME}?resourceIdentifier=parent&options=(a=1,b=2)" + def response = testRestTemplate.getForEntity(url, String.class) + then: 'Response is OK' + response.getStatusCode() == HttpStatus.OK + + and: 'Response body contains customized stub response that contains correct bookstore category code' + def typeRef = new TypeReference>() {} + def map = objectMapper.readValue(response.getBody(), typeRef) + def obj1 = (Map) map.get("stores:bookstore") + def obj2 = (List) obj1.get("categories") + def obj3 = (Map) obj2.iterator().next() + + assert obj3.get("code") == "02" + } + + def String getBaseUrl() { + return "http://localhost:" + port + stubBasePath + } +} diff --git a/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy new file mode 100644 index 000000000..79384239a --- /dev/null +++ b/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/TestApplication.groovy @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.onap.cps.ncmp.rest.stub + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.annotation.ComponentScan + +@SpringBootApplication +@ComponentScan(basePackages = ["org.onap.cps.ncmp.rest.stub", "org.onap.cps.ncmp.rest.stub.controller", "org.onap.cps.ncmp.rest.api"]) +class TestApplication { +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java index 7475cdd4a..1f6c94869 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceImpl.java @@ -24,6 +24,7 @@ import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DM import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_MODULES; import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.HAS_ALL_PROPERTIES; import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_CPS_PATH; +import static org.onap.cps.ncmp.api.impl.utils.CmHandleQueryConditions.WITH_TRUST_LEVEL; import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateCpsPathConditionProperties; import static org.onap.cps.ncmp.api.impl.utils.RestQueryParametersValidator.validateModuleNameConditionProperties; import static org.onap.cps.ncmp.api.impl.utils.YangDataConverter.convertYangModelCmHandleToNcmpServiceCmHandle; @@ -70,7 +71,8 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH return executeQueries(cmHandleQueryServiceParameters, this::executeCpsPathQuery, this::queryCmHandlesByPublicProperties, - this::executeModuleNameQuery); + this::executeModuleNameQuery, + this::queryCmHandlesByTrustLevel); } @Override @@ -117,9 +119,10 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), InventoryQueryConditions.HAS_ALL_ADDITIONAL_PROPERTIES.getName()); - return privatePropertyQueryPairs.isEmpty() - ? NO_QUERY_TO_EXECUTE - : cmHandleQueries.queryCmHandleAdditionalProperties(privatePropertyQueryPairs); + if (privatePropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueries.queryCmHandleAdditionalProperties(privatePropertyQueryPairs); } private Collection queryCmHandlesByPublicProperties( @@ -129,9 +132,23 @@ public class NetworkCmProxyCmHandleQueryServiceImpl implements NetworkCmProxyCmH getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), HAS_ALL_PROPERTIES.getConditionName()); - return publicPropertyQueryPairs.isEmpty() - ? NO_QUERY_TO_EXECUTE - : cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs); + if (publicPropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueries.queryCmHandlePublicProperties(publicPropertyQueryPairs); + } + + private Collection queryCmHandlesByTrustLevel(final CmHandleQueryServiceParameters + cmHandleQueryServiceParameters) { + + final Map trustLevelPropertyQueryPairs = + getPropertyPairs(cmHandleQueryServiceParameters.getCmHandleQueryParameters(), + WITH_TRUST_LEVEL.getConditionName()); + + if (trustLevelPropertyQueryPairs.isEmpty()) { + return NO_QUERY_TO_EXECUTE; + } + return cmHandleQueries.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs); } private Collection executeModuleNameQuery( diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java index 692a9f2a9..a37b27199 100755 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImpl.java @@ -99,7 +99,7 @@ public class NetworkCmProxyDataServiceImpl implements NetworkCmProxyDataService private final LcmEventsCmHandleStateHandler lcmEventsCmHandleStateHandler; private final CpsDataService cpsDataService; private final IMap moduleSyncStartedOnCmHandles; - private final IMap trustLevelPerDmiPlugin; + private final Map trustLevelPerDmiPlugin; @Override public DmiPluginRegistrationResponse updateDmiRegistrationAndSyncModule( diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java index 6a8310c20..e8ce050b2 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/client/DmiRestClient.java @@ -75,10 +75,8 @@ public class DmiRestClient { final HttpEntity httpHeaders = new HttpEntity<>(configureHttpHeaders(new HttpHeaders())); final JsonNode dmiPluginHealthStatus = restTemplate.getForObject(dmiPluginBaseUrl + "/manage/health", JsonNode.class, httpHeaders); - if (dmiPluginHealthStatus != null) { - if (dmiPluginHealthStatus.get("status").asText().equals("UP")) { - return DmiPluginStatus.UP; - } + if (dmiPluginHealthStatus != null && dmiPluginHealthStatus.get("status").asText().equals("UP")) { + return DmiPluginStatus.UP; } } catch (final Exception exception) { log.warn("Could not send request for health check since {}", exception.getMessage()); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java index ebe99057d..171db5299 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfig.java @@ -20,10 +20,8 @@ package org.onap.cps.ncmp.api.impl.config.embeddedcache; -import com.hazelcast.collection.ISet; import com.hazelcast.config.MapConfig; -import com.hazelcast.config.SetConfig; -import com.hazelcast.map.IMap; +import java.util.Map; import org.onap.cps.cache.HazelcastCacheConfig; import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; import org.springframework.context.annotation.Bean; @@ -32,21 +30,21 @@ import org.springframework.context.annotation.Configuration; @Configuration public class TrustLevelCacheConfig extends HazelcastCacheConfig { - private static final SetConfig untrustworthyCmHandlesSetConfig = - createSetConfig("untrustworthyCmHandlesSetConfig"); + private static final MapConfig trustLevelPerCmHandleCacheConfig = + createMapConfig("trustLevelPerCmHandleCacheConfig"); private static final MapConfig trustLevelPerDmiPluginCacheConfig = createMapConfig("trustLevelPerDmiPluginCacheConfig"); /** - * Distributed collection of untrustworthy cm handles. + * Distributed instance of trust level cache containing the trust level per cm handle. * - * @return instance of distributed set of untrustworthy cm handles. + * @return configured map of cm handle name as keys to trust-level for values. */ @Bean - public ISet untrustworthyCmHandlesSet() { - return createHazelcastInstance("untrustworthyCmHandlesSet", - untrustworthyCmHandlesSetConfig).getSet("untrustworthyCmHandlesSet"); + public Map trustLevelPerCmHandle() { + return createHazelcastInstance("hazelcastInstanceTrustLevelPerCmHandleMap", + trustLevelPerCmHandleCacheConfig).getMap("trustLevelPerCmHandle"); } /** @@ -55,7 +53,7 @@ public class TrustLevelCacheConfig extends HazelcastCacheConfig { * @return configured map of dmi-plugin name as keys to trust-level for values. */ @Bean - public IMap trustLevelPerDmiPlugin() { + public Map trustLevelPerDmiPlugin() { return createHazelcastInstance("hazelcastInstanceTrustLevelPerDmiPluginMap", trustLevelPerDmiPluginCacheConfig).getMap("trustLevelPerDmiPlugin"); } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java index 98ba95386..4120970e5 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapper.java @@ -25,7 +25,6 @@ import io.cloudevents.CloudEvent; import io.cloudevents.core.CloudEventUtils; import io.cloudevents.core.data.PojoCloudEventData; import io.cloudevents.jackson.PojoCloudEventDataMapper; -import io.cloudevents.rw.CloudEventRWException; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -51,9 +50,9 @@ public class CloudEventMapper { mappedCloudEvent = CloudEventUtils.mapData(cloudEvent, PojoCloudEventDataMapper.from(objectMapper, targetEventClass)); - } catch (final CloudEventRWException cloudEventRwException) { + } catch (final RuntimeException runtimeException) { log.error("Unable to map cloud event to target event class type : {} with cause : {}", targetEventClass, - cloudEventRwException.getMessage()); + runtimeException.getMessage()); } return mappedCloudEvent == null ? null : mappedCloudEvent.getValue(); diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java index a5892afc3..81467dbb3 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueries.java @@ -44,6 +44,14 @@ public interface CmHandleQueries { */ Collection queryCmHandlePublicProperties(Map publicPropertyQueryPairs); + /** + * Query CmHandles based on Trust Level. + * + * @param trustLevelPropertyQueryPairs trust level properties for query + * @return CmHandles which have desired trust level + */ + Collection queryCmHandlesByTrustLevel(Map trustLevelPropertyQueryPairs); + /** * Method which returns cm handles by the cm handles state. * diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java index c4e3fd098..e5cf8edd6 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/inventory/CmHandleQueriesImpl.java @@ -35,6 +35,8 @@ import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.onap.cps.ncmp.api.impl.inventory.enums.PropertyType; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel; +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevelFilter; import org.onap.cps.spi.CpsDataPersistenceService; import org.onap.cps.spi.FetchDescendantsOption; import org.onap.cps.spi.model.DataNode; @@ -45,9 +47,9 @@ import org.springframework.stereotype.Component; public class CmHandleQueriesImpl implements CmHandleQueries { private static final String DESCENDANT_PATH = "//"; - - private final CpsDataPersistenceService cpsDataPersistenceService; private static final String ANCESTOR_CM_HANDLES = "/ancestor::cm-handles"; + private final CpsDataPersistenceService cpsDataPersistenceService; + private final Map trustLevelPerCmHandle; @Override public Collection queryCmHandleAdditionalProperties(final Map privatePropertyQueryPairs) { @@ -59,6 +61,15 @@ public class CmHandleQueriesImpl implements CmHandleQueries { return queryCmHandleAnyProperties(publicPropertyQueryPairs, PropertyType.PUBLIC); } + @Override + public Collection queryCmHandlesByTrustLevel(final Map trustLevelPropertyQueryPairs) { + final String trustLevelProperty = trustLevelPropertyQueryPairs.values().iterator().next(); + final TrustLevel targetTrustLevel = TrustLevel.valueOf(trustLevelProperty); + + final TrustLevelFilter trustLevelFilter = new TrustLevelFilter(targetTrustLevel, trustLevelPerCmHandle); + return trustLevelFilter.getAllCmHandleIdsByTargetTrustLevel(); + } + @Override public List queryCmHandlesByState(final CmHandleState cmHandleState) { return queryCmHandleAncestorsByCpsPath("//state[@cm-handle-state=\"" + cmHandleState + "\"]", diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java index 458c1b851..b6d74d980 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumer.java @@ -20,14 +20,14 @@ package org.onap.cps.ncmp.api.impl.trustlevel; -import static org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper.toTargetEvent; - -import com.hazelcast.collection.ISet; import io.cloudevents.CloudEvent; import io.cloudevents.kafka.impl.KafkaHeaders; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.onap.cps.ncmp.api.impl.events.mapper.CloudEventMapper; +import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; @@ -36,7 +36,8 @@ import org.springframework.stereotype.Component; @RequiredArgsConstructor public class DeviceHeartbeatConsumer { - private final ISet untrustworthyCmHandlesSet; + private static final String CLOUD_EVENT_ID_HEADER_NAME = "ce_id"; + private final Map trustLevelPerCmHandle; /** * Listening the device heartbeats. @@ -47,23 +48,16 @@ public class DeviceHeartbeatConsumer { containerFactory = "cloudEventConcurrentKafkaListenerContainerFactory") public void heartbeatListener(final ConsumerRecord deviceHeartbeatConsumerRecord) { - final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(), "ce_id"); + final String cmHandleId = KafkaHeaders.getParsedKafkaHeader(deviceHeartbeatConsumerRecord.headers(), + CLOUD_EVENT_ID_HEADER_NAME); final DeviceTrustLevel deviceTrustLevel = - toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class); - - if (deviceTrustLevel == null || deviceTrustLevel.getTrustLevel() == null) { - log.warn("No or Invalid trust level defined"); - return; - } + CloudEventMapper.toTargetEvent(deviceHeartbeatConsumerRecord.value(), DeviceTrustLevel.class); - if (deviceTrustLevel.getTrustLevel().equals(TrustLevel.NONE)) { - untrustworthyCmHandlesSet.add(cmHandleId); - log.debug("Added cmHandleId to untrustworthy set : {}", cmHandleId); - } else if (deviceTrustLevel.getTrustLevel().equals(TrustLevel.COMPLETE) && untrustworthyCmHandlesSet.contains( - cmHandleId)) { - untrustworthyCmHandlesSet.remove(cmHandleId); - log.debug("Removed cmHandleId from untrustworthy set : {}", cmHandleId); + if (cmHandleId != null && deviceTrustLevel != null) { + final String trustLevel = deviceTrustLevel.getData().getTrustLevel(); + trustLevelPerCmHandle.put(cmHandleId, TrustLevel.valueOf(trustLevel)); + log.debug("Added cmHandleId to trustLevelPerCmHandle map as {}:{}", cmHandleId, trustLevel); } } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java index f4254bb47..8d1f8e90f 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevel.java @@ -20,6 +20,16 @@ package org.onap.cps.ncmp.api.impl.trustlevel; +import lombok.Getter; + +@Getter public enum TrustLevel { - NONE, COMPLETE; + NONE(0), COMPLETE(99); + + private final int value; + + TrustLevel(final int value) { + this.value = value; + } + } \ No newline at end of file diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java new file mode 100644 index 000000000..3b704ae4f --- /dev/null +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilter.java @@ -0,0 +1,58 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2023 Nordix Foundation + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.cps.ncmp.api.impl.trustlevel; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import lombok.EqualsAndHashCode; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class TrustLevelFilter implements Comparable { + + @EqualsAndHashCode.Include + private final TrustLevel targetTrustLevel; + private final Map trustLevelPerCmHandle; + + @Override + public int compareTo(@NonNull final TrustLevel other) { + return Integer.compare(this.targetTrustLevel.getValue(), other.getValue()); + } + + /** + * This method return cm handles that matches with given trust level. + * + * @return cm handle ids. + */ + public Collection getAllCmHandleIdsByTargetTrustLevel() { + final Collection resultCmHandleIds = new HashSet<>(); + trustLevelPerCmHandle.entrySet().forEach(cmHandleTrustLevelEntrySet -> { + if (compareTo(cmHandleTrustLevelEntrySet.getValue()) == 0) { + resultCmHandleIds.add(cmHandleTrustLevelEntrySet.getKey()); + } + }); + return resultCmHandleIds; + } + +} diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDog.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDog.java index d3b95eacb..39f880257 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDog.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDog.java @@ -20,7 +20,7 @@ package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability; -import com.hazelcast.map.IMap; +import java.util.Map; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.onap.cps.ncmp.api.impl.client.DmiRestClient; @@ -33,7 +33,7 @@ import org.springframework.stereotype.Service; @Service public class DMiPluginWatchDog { - private final IMap trustLevelPerDmiPlugin; + private final Map trustLevelPerDmiPlugin; private final DmiRestClient dmiRestClient; diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java index b1bb7f767..a59776036 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java +++ b/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditions.java @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (C) 2022 Nordix Foundation + * Copyright (C) 2022-2023 Nordix Foundation * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,7 +29,8 @@ import lombok.Getter; public enum CmHandleQueryConditions { HAS_ALL_PROPERTIES("hasAllProperties"), HAS_ALL_MODULES("hasAllModules"), - WITH_CPS_PATH("cmHandleWithCpsPath"); + WITH_CPS_PATH("cmHandleWithCpsPath"), + WITH_TRUST_LEVEL("cmHandleWithTrustLevel"); public static final Collection ALL_CONDITION_NAMES = Arrays.stream(CmHandleQueryConditions.values()) .map(CmHandleQueryConditions::getConditionName).collect(Collectors.toList()); diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy index ce6d85658..7c410cc58 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyCmHandleQueryServiceSpec.groovy @@ -39,7 +39,7 @@ import spock.lang.Specification class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { def cmHandleQueries = Mock(CmHandleQueries) - def partiallyMockedCmHandleQueries = Spy(CmHandleQueriesImpl) + def partiallyMockedCmHandleQueries = Spy(CmHandleQueries) def mockInventoryPersistence = Mock(InventoryPersistence) def dmiRegistry = new DataNode(xpath: NCMP_DMI_REGISTRY_PARENT, childDataNodes: createDataNodeList(['PNFDemo1', 'PNFDemo2', 'PNFDemo3', 'PNFDemo4'])) @@ -106,6 +106,17 @@ class NetworkCmProxyCmHandleQueryServiceSpec extends Specification { 'No anchors are returned' | [] } + def 'Query cm handles with some trust level query parameters'() { + given: 'a trust level condition property' + def trustLevelQueryParameters = new CmHandleQueryServiceParameters() + def trustLevelConditionProperties = createConditionProperties('cmHandleWithTrustLevel', [['trustLevel': 'COMPLETE'] as Map]) + trustLevelQueryParameters.setCmHandleQueryParameters([trustLevelConditionProperties]) + when: 'the query is being executed' + objectUnderTest.queryCmHandleIds(trustLevelQueryParameters) + then: 'the query is being delegated to the cm handle query service with correct parameter' + 1 * cmHandleQueries.queryCmHandlesByTrustLevel(['trustLevel': 'COMPLETE'] as Map) + } + def 'Query cm handle details with module names when #scenario from query.'() { given: 'a modules condition property' def cmHandleQueryParameters = new CmHandleQueryServiceParameters() diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index 0d9aa6124..af65cfc1a 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -76,7 +76,7 @@ class NetworkCmProxyDataServiceImplSpec extends Specification { def mockCpsCmHandlerQueryService = Mock(NetworkCmProxyCmHandleQueryService) def mockLcmEventsCmHandleStateHandler = Mock(LcmEventsCmHandleStateHandler) def stubModuleSyncStartedOnCmHandles = Stub(IMap) - def stubTrustLevelPerDmiPlugin = Stub(IMap) + def stubTrustLevelPerDmiPlugin = Stub(Map) def NO_TOPIC = null def NO_REQUEST_ID = null diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfigSpec.groovy index 6184a9722..3eff96d79 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfigSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/TrustLevelCacheConfigSpec.groovy @@ -22,7 +22,6 @@ package org.onap.cps.ncmp.api.impl.config.embeddedcache import com.hazelcast.config.Config import com.hazelcast.core.Hazelcast -import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest @@ -32,7 +31,10 @@ import spock.lang.Specification class TrustLevelCacheConfigSpec extends Specification { @Autowired - private IMap trustLevelPerDmiPlugin + private Map trustLevelPerDmiPlugin + + @Autowired + private Map trustLevelPerCmHandle def 'Hazelcast cache for trust level per dmi plugin'() { expect: 'system is able to create an instance of the trust level per dmi plugin cache' @@ -43,23 +45,29 @@ class TrustLevelCacheConfigSpec extends Specification { assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceTrustLevelPerDmiPluginMap') } - def 'Verify Trust Level Per Dmi Plugin Cache for basic hazelcast map operations'() { - when: 'the key inserted into Trust Level Per Dmi Plugin Cache' - trustLevelPerDmiPlugin.put('dmi1', TrustLevel.COMPLETE) - trustLevelPerDmiPlugin.put('dmi2', TrustLevel.NONE) - then: 'the value for each dmi can be retrieved' - assert trustLevelPerDmiPlugin.get('dmi1') == TrustLevel.COMPLETE - assert trustLevelPerDmiPlugin.get('dmi2') == TrustLevel.NONE + def 'Hazelcast cache for trust level per cm handle'() { + expect: 'system is able to create an instance of the trust level per cm handle cache' + assert null != trustLevelPerCmHandle + and: 'there is at least 1 instance' + assert Hazelcast.allHazelcastInstances.size() > 0 + and: 'Hazelcast cache instance for trust level is present' + assert Hazelcast.allHazelcastInstances.name.contains('hazelcastInstanceTrustLevelPerCmHandleMap') } - def 'Verify configs for Distributed Caches'(){ - given: 'the Trust Level Per Dmi Plugin Cache config' - def trustLevelDmiPerPluginCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceTrustLevelPerDmiPluginMap').config - def trustLevelDmiPerPluginCacheMapConfig = trustLevelDmiPerPluginCacheConfig.mapConfigs.get('trustLevelPerDmiPluginCacheConfig') - expect: 'system created instance with correct config' - assert trustLevelDmiPerPluginCacheConfig.clusterName == 'cps-and-ncmp-test-caches' - assert trustLevelDmiPerPluginCacheMapConfig.backupCount == 3 - assert trustLevelDmiPerPluginCacheMapConfig.asyncBackupCount == 3 + def 'Trust level cache configurations: #scenario'() { + when: 'retrieving the cache config for trustLevel' + def cacheConfig = Hazelcast.getHazelcastInstanceByName(hazelcastInstanceName).config + then: 'the cache config has the right cluster' + assert cacheConfig.clusterName == 'cps-and-ncmp-test-caches' + when: 'retrieving the map config for trustLevel' + def mapConfig = cacheConfig.mapConfigs.get(hazelcastMapConfigName) + then: 'the map config has the correct backup counts' + assert mapConfig.backupCount == 3 + assert mapConfig.asyncBackupCount == 3 + where: 'the following caches are used' + scenario | hazelcastInstanceName | hazelcastMapConfigName + 'cmhandle map' | 'hazelcastInstanceTrustLevelPerCmHandleMap' | 'trustLevelPerCmHandleCacheConfig' + 'dmi plugin map' | 'hazelcastInstanceTrustLevelPerDmiPluginMap' | 'trustLevelPerDmiPluginCacheConfig' } def 'Verify deployment network configs for Distributed Caches'() { @@ -70,6 +78,14 @@ class TrustLevelCacheConfigSpec extends Specification { assert !trustLevelDmiPerPluginCacheConfig.join.kubernetesConfig.enabled } + def 'Verify deployment network configs for Cm Handle Distributed Caches'() { + given: 'the Trust Level Per Cm Handle Cache config' + def trustLevelPerCmHandlePluginCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelcastInstanceTrustLevelPerCmHandleMap').config.networkConfig + expect: 'system created instance with correct config' + assert trustLevelPerCmHandlePluginCacheConfig.join.autoDetectionConfig.enabled + assert !trustLevelPerCmHandlePluginCacheConfig.join.kubernetesConfig.enabled + } + def 'Verify network config'() { given: 'Synchronization config object and test configuration' def objectUnderTest = new TrustLevelCacheConfig() diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy index 380aea405..e6d383128 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/mapper/CloudEventMapperSpec.groovy @@ -34,17 +34,30 @@ class CloudEventMapperSpec extends Specification { @Autowired JsonObjectMapper jsonObjectMapper - def 'Cloud event to Target event type when it is #scenario'() { - expect: 'Events mapped correctly' - assert mappedCloudEvent == (CloudEventMapper.toTargetEvent(testCloudEvent(), targetClass) != null) + def 'Cloud event to target event type'() { + given: 'a cloud event with valid payload' + def cloudEvent = testCloudEvent(new CmSubscriptionNcmpInEvent()) + when: 'the cloud event mapped to target event' + def result = CloudEventMapper.toTargetEvent((cloudEvent), CmSubscriptionNcmpInEvent.class) + then: 'the cloud event is mapped' + assert result instanceof CmSubscriptionNcmpInEvent + } + + def 'Cloud event to target event type when it is #scenario'() { + given: 'a cloud event with invalid payload' + def cloudEvent = testCloudEvent(payload) + when: 'the cloud event mapped to target event' + def result = CloudEventMapper.toTargetEvent(cloudEvent, CmSubscriptionNcmpInEvent.class) + then: 'result is null' + assert result == null where: 'below are the scenarios' - scenario | targetClass || mappedCloudEvent - 'valid concrete type' | CmSubscriptionNcmpInEvent.class || true - 'invalid concrete type' | ArrayList.class || false + scenario | payload + 'invalid payload type' | ArrayList.class + 'without payload' | null } - def testCloudEvent() { - return CloudEventBuilder.v1().withData(jsonObjectMapper.asJsonBytes(new CmSubscriptionNcmpInEvent())) + def testCloudEvent(payload) { + return CloudEventBuilder.v1().withData(jsonObjectMapper.asJsonBytes(payload)) .withId("cmhandle1") .withSource(URI.create('test-source')) .withDataSchema(URI.create('test')) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy index 48de23dca..80778b9c7 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/DeviceHeartbeatConsumerSpec.groovy @@ -21,81 +21,70 @@ package org.onap.cps.ncmp.api.impl.trustlevel import com.fasterxml.jackson.databind.ObjectMapper -import com.hazelcast.collection.ISet +import com.hazelcast.map.IMap import io.cloudevents.CloudEvent import io.cloudevents.core.builder.CloudEventBuilder import org.apache.kafka.clients.consumer.ConsumerRecord +import org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel import org.onap.cps.utils.JsonObjectMapper +import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import spock.lang.Specification @SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) class DeviceHeartbeatConsumerSpec extends Specification { - def mockUntrustworthyCmHandlesSet = Mock(ISet) + def mockTrustLevelPerCmHandle = Mock(Map) + + def objectUnderTest = new DeviceHeartbeatConsumer(mockTrustLevelPerCmHandle) def objectMapper = new ObjectMapper() - def objectUnderTest = new DeviceHeartbeatConsumer(mockUntrustworthyCmHandlesSet) + @Autowired + JsonObjectMapper jsonObjectMapper + + def static trustLevelString = '{"data":{"trustLevel": "COMPLETE"}}' - def 'Operations to be done in an empty untrustworthy set for #scenario'() { - given: 'an event with trustlevel as #trustLevel' - def incomingEvent = testCloudEvent(trustLevel) - and: 'transformed as a kafka record' - def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'cmhandle1', incomingEvent) + def 'Consume a trustlevel event'() { + given: 'an event from dmi with trust level complete' + def payload = jsonObjectMapper.convertJsonString(trustLevelString, DeviceTrustLevel.class) + def eventFromDmi = createTrustLevelEvent(payload) + and: 'transformed to a consumer record with a cloud event id (ce_id)' + def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'sample-message-key', eventFromDmi) consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1')) when: 'the event is consumed' objectUnderTest.heartbeatListener(consumerRecord) - then: 'untrustworthy cmhandles are stored' - untrustworthyCmHandlesSetInvocationForAdd * mockUntrustworthyCmHandlesSet.add(_) - and: 'trustworthy cmHandles will be removed from untrustworthy set' - untrustworthyCmHandlesSetInvocationForContains * mockUntrustworthyCmHandlesSet.contains(_) - - where: 'below scenarios are applicable' - scenario | trustLevel || untrustworthyCmHandlesSetInvocationForAdd | untrustworthyCmHandlesSetInvocationForContains - 'None trust' | TrustLevel.NONE || 1 | 0 - 'Complete trust' | TrustLevel.COMPLETE || 0 | 1 + then: 'cm handles are stored with correct trust level' + 1 * mockTrustLevelPerCmHandle.put('"cmhandle1"', TrustLevel.COMPLETE) } - def 'Invalid trust'() { - when: 'we provide an invalid trust in the event' - def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'cmhandle1', testCloudEvent(null)) - consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1')) + def 'Consume trustlevel event without cloud event id'() { + given: 'an event from dmi' + def payload = jsonObjectMapper.convertJsonString(trustLevelString, DeviceTrustLevel.class) + def eventFromDmi = createTrustLevelEvent(payload) + and: 'transformed to a consumer record WITHOUT Cloud event ID (ce_id)' + def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'sample-message-key', eventFromDmi) + when: 'the event is consumed' objectUnderTest.heartbeatListener(consumerRecord) - then: 'no interaction with the untrustworthy cmhandles set' - 0 * mockUntrustworthyCmHandlesSet.add(_) - 0 * mockUntrustworthyCmHandlesSet.contains(_) - 0 * mockUntrustworthyCmHandlesSet.remove(_) - and: 'control flow returns without any exception' - noExceptionThrown() - + then: 'no cm handle has been stored in the map' + 0 * mockTrustLevelPerCmHandle.put(*_) } - def 'Remove trustworthy cmhandles from untrustworthy cmhandles set'() { - given: 'an event with COMPLETE trustlevel' - def incomingEvent = testCloudEvent(TrustLevel.COMPLETE) - and: 'transformed as a kafka record' - def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'cmhandle1', incomingEvent) - consumerRecord.headers().add('ce_id', objectMapper.writeValueAsBytes('cmhandle1')) - and: 'untrustworthy cmhandles set contains cmhandle1' - 1 * mockUntrustworthyCmHandlesSet.contains(_) >> true + def 'Consume a trust level event without payload'() { + given: 'a consumer record with ce_id header but without payload' + def consumerRecord = new ConsumerRecord('test-device-heartbeat', 0, 0, 'cmhandle1', createTrustLevelEvent(null)) + consumerRecord.headers().add('some_other_header_value', objectMapper.writeValueAsBytes('cmhandle1')) when: 'the event is consumed' objectUnderTest.heartbeatListener(consumerRecord) - then: 'cmhandle removed from untrustworthy cmhandles set' - 1 * mockUntrustworthyCmHandlesSet.remove(_) >> { - args -> - { - args[0].equals('cmhandle1') - } - } - + then: 'no cm handle has been stored in the map' + 0 * mockTrustLevelPerCmHandle.put(*_) } - def testCloudEvent(trustLevel) { - return CloudEventBuilder.v1().withData(objectMapper.writeValueAsBytes(new DeviceTrustLevel(trustLevel))) + def createTrustLevelEvent(eventPayload) { + return CloudEventBuilder.v1().withData(objectMapper.writeValueAsBytes(eventPayload)) .withId("cmhandle1") .withSource(URI.create('DMI')) .withDataSchema(URI.create('test')) - .withType('org.onap.cm.events.trustlevel-notification') + .withType('org.onap.cps.ncmp.events.trustlevel.DeviceTrustLevel') .build() } diff --git a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy similarity index 55% rename from cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java rename to cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy index 2ed4e4522..8f6621d24 100644 --- a/cps-ncmp-service/src/main/java/org/onap/cps/ncmp/api/impl/trustlevel/DeviceTrustLevel.java +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/TrustLevelFilterSpec.groovy @@ -18,20 +18,24 @@ * ============LICENSE_END========================================================= */ -package org.onap.cps.ncmp.api.impl.trustlevel; +package org.onap.cps.ncmp.api.impl.trustlevel -import java.io.Serializable; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -@AllArgsConstructor -@Data -@NoArgsConstructor -class DeviceTrustLevel implements Serializable { +import spock.lang.Specification - private static final long serialVersionUID = -1705715024067165212L; +class TrustLevelFilterSpec extends Specification { - private TrustLevel trustLevel; + def targetTrustLevel = TrustLevel.COMPLETE + def trustLevelPerCmHandle = [ 'my completed cm handle': TrustLevel.COMPLETE, 'my untrusted cm handle': TrustLevel.NONE ] + + def objectUnderTest = new TrustLevelFilter(targetTrustLevel, trustLevelPerCmHandle) + + def 'Obtain cm handle ids by a given trust level value'() { + when: 'cm handles are retrieved' + def result = objectUnderTest.getAllCmHandleIdsByTargetTrustLevel() + then: 'the result only contains the completed cm handle' + assert result.size() == 1 + assert result[0] == 'my completed cm handle' + } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy index af546b7f5..b6259bdf3 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/trustlevel/dmiavailability/DMiPluginWatchDogSpec.groovy @@ -20,7 +20,6 @@ package org.onap.cps.ncmp.api.impl.trustlevel.dmiavailability -import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.impl.client.DmiRestClient import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel import spock.lang.Specification @@ -28,7 +27,7 @@ import spock.lang.Specification class DMiPluginWatchDogSpec extends Specification { - def mockTrustLevelPerDmiPlugin = Mock(IMap) + def mockTrustLevelPerDmiPlugin = Mock(Map) def mockDmiRestClient = Mock(DmiRestClient) def objectUnderTest = new DMiPluginWatchDog(mockTrustLevelPerDmiPlugin, mockDmiRestClient) diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy index f0e2d9f0b..100705ff5 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/utils/CmHandleQueryConditionsSpec.groovy @@ -26,10 +26,11 @@ class CmHandleQueryConditionsSpec extends Specification { def 'CmHandle query condition names.'() { expect: '3 conditions with the correct names' - assert CmHandleQueryConditions.ALL_CONDITION_NAMES.size() == 3 + assert CmHandleQueryConditions.ALL_CONDITION_NAMES.size() == 4 assert CmHandleQueryConditions.ALL_CONDITION_NAMES.containsAll('hasAllProperties', 'hasAllModules', - 'cmHandleWithCpsPath') + 'cmHandleWithCpsPath', + 'cmHandleWithTrustLevel') } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy index e7c337cc2..a3a5efc74 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/inventory/CmHandleQueriesImplSpec.groovy @@ -21,12 +21,15 @@ package org.onap.cps.ncmp.api.inventory +import org.onap.cps.ncmp.api.impl.trustlevel.TrustLevel + import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DATASPACE_NAME import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_ANCHOR import static org.onap.cps.ncmp.api.impl.ncmppersistence.NcmpPersistence.NCMP_DMI_REGISTRY_PARENT import static org.onap.cps.spi.FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS +import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.impl.inventory.CmHandleQueriesImpl import org.onap.cps.ncmp.api.impl.inventory.CmHandleState import org.onap.cps.ncmp.api.impl.inventory.DataStoreSyncState @@ -37,8 +40,9 @@ import spock.lang.Specification class CmHandleQueriesImplSpec extends Specification { def cpsDataPersistenceService = Mock(CpsDataPersistenceService) + def trustLevelPerCmHandle = [ 'my completed cm handle': TrustLevel.COMPLETE, 'my untrusted cm handle': TrustLevel.NONE ] - def objectUnderTest = new CmHandleQueriesImpl(cpsDataPersistenceService) + def objectUnderTest = new CmHandleQueriesImpl(cpsDataPersistenceService, trustLevelPerCmHandle) @Shared def static sampleDataNodes = [new DataNode()] @@ -60,11 +64,21 @@ class CmHandleQueriesImplSpec extends Specification { result.containsAll(expectedCmHandleIds) result.size() == expectedCmHandleIds.size() where: 'the following data is used' - scenario | publicPropertyPairs || expectedCmHandleIds - 'single property matches' | ['Contact' : 'newemailforstore@bookstore.com'] || ['PNFDemo', 'PNFDemo2', 'PNFDemo4'] - 'public property does not match' | ['wont_match' : 'wont_match'] || [] - '2 properties, only one match' | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': 'newemailforstore2@bookstore.com'] || ['PNFDemo4'] - '2 properties, no matches' | ['Contact' : 'newemailforstore@bookstore.com', 'Contact2': ''] || [] + scenario | publicPropertyPairs || expectedCmHandleIds + 'single property matches' | [Contact: 'newemailforstore@bookstore.com'] || ['PNFDemo', 'PNFDemo2', 'PNFDemo4'] + 'public property does not match' | [wont_match: 'wont_match'] || [] + '2 properties, only one match' | [Contact: 'newemailforstore@bookstore.com', Contact2: 'newemailforstore2@bookstore.com'] || ['PNFDemo4'] + '2 properties, no matches' | [Contact: 'newemailforstore@bookstore.com', Contact2: ''] || [] + } + + def 'Query cm handles on trust level'() { + given: 'query properties for trustlevel COMPLETE' + def trustLevelPropertyQueryPairs = ['trustLevel' : TrustLevel.COMPLETE.toString()] + when: 'the query is executed' + def result = objectUnderTest.queryCmHandlesByTrustLevel(trustLevelPropertyQueryPairs) + then: 'the result only contains the completed cm handle' + assert result.size() == 1 + assert result[0] == 'my completed cm handle' } def 'Query CmHandles using empty public properties query pair.'() { diff --git a/docs/cps-path.rst b/docs/cps-path.rst index 661178954..eb203d891 100644 --- a/docs/cps-path.rst +++ b/docs/cps-path.rst @@ -247,7 +247,7 @@ leaf-conditions - Using comparative operators with string values will lead to an error at runtime. This error can't be validated earlier as the datatype is unknown until the execution phase. - The key should be supplied with correct data type for it to be queried from DB. In the last example above the attribute code is of type Integer so the cps query will not work if the value is passed as string. - eg: ``//categories[@code="1"]`` or ``//categories[@code='1']`` will not work because the key attribute code is treated a string. + e.g.: ``//categories[@code="1"]`` or ``//categories[@code='1']`` will not work because the key attribute code is treated a string. **Notes** - For performance reasons it does not make sense to query using key leaf as attribute. If the key value is known it is better to execute a get request with the complete xpath. @@ -260,7 +260,7 @@ The text()-condition can be added to any CPS path query. **Syntax**: `` ( '/' '[text()=' ']' )?`` - ``cps-path``: Any CPS path query. - ``leaf-name``: The name of the leaf or leaf-list which value needs to be compared. - - ``string-value``: The required value of the leaf or leaf-list element as a string wrapped in quotation marks (U+0022) or apostrophes (U+0027). This wil still match integer values. + - ``string-value``: The required value of the leaf or leaf-list element as a string wrapped in quotation marks (U+0022) or apostrophes (U+0027). This will still match integer values. **Examples** - ``//book/label[text()="classic"]`` diff --git a/docs/cps-stubs.rst b/docs/cps-stubs.rst new file mode 100644 index 000000000..00577eb0d --- /dev/null +++ b/docs/cps-stubs.rst @@ -0,0 +1,120 @@ +.. This work is licensed under a Creative Commons Attribution 4.0 International License. +.. http://creativecommons.org/licenses/by/4.0 +.. Copyright (C) 2023 Nordix Foundation + +.. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING + +.. _cpsStubs: + + +CPS Stubs +######### + +.. toctree:: + :maxdepth: 1 + +NCMP Stubs +========== + +The CPS NCMP stub module provides the capability to create dynamic and customizable stubs, offering control over the responses generated for each endpoint. This capability ensures that client interactions adhere to a specified NCMP interface, enabling comprehensive testing and validation of your applications. + +The NCMP stub RestController is an extended implementation of the actual NCMP interface. It can be deployed as part of the application JAR or within a SpringBootTest JUnit environment, allowing you to define dynamic responses for each endpoint and allowing testing against real stub interfaces. + +Prerequisites +============= + +Ensure you meet the following prerequisites: + +1. **Required Java Installation:** + + Ensure that you have the required Java installed on your system. + +2. **Access to Gerrit and Maven Installation (for building CPS project locally):** + + - Ensure you have access to the ONAP Gerrit repository. + + - If you plan to build the CPS project locally, make sure you have Maven installed. + +Method 1: Running Stubs as an Application +========================================= + +Follow these steps to run the CPS-NCMP stub application: + +1. **Download Application Jar:** + + You can obtain the CPS-NCMP stub application jar in one of the following ways: + + - **Option 1: Download from Nexus Repository:** + + Download the application jar from the Nexus repository at `https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/`_. + + - **Option 2: Build Locally:** + + To build the CPS project locally, navigate to the project's root directory. Once there, you can build the project using :code:`mvn clean install`, and the application CPS-NCMP stub application jar can be found in the following location: + + :: + + cps/cps-ncmp-rest-stub/cps-ncmp-rest-stub-app/target/ + +2. **Run the Application:** + + After obtaining the application jar, use the following command to run it: + + .. code-block:: bash + + java -jar ./cps-ncmp-rest-stub-app-.jar + + Replace ```` with the actual version number of the application jar. + +This will start the CPS-NCMP stub application, and you can interact with it as needed. + +.. _`https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/`: https://nexus.onap.org/content/repositories/releases/org/onap/cps/cps-ncmp-rest-stub-app/ + +Method 2: Using Stubs in Unit Tests +=================================== +1. **Add Dependency to pom.xml:** + + To include the required module in your project, add the following dependency to your `pom.xml` file: + + .. code-block:: xml + + + org.onap.cps + cps-ncmp-rest-stub-service + VERSION + + + Replace ``VERSION`` with the actual version number. + +2. **Using Custom Response Objects:** + + If you prefer to use custom response objects instead of the built-in ones, follow these steps: + + Modify the `application.yaml` file located in your project's test resources directory (`src/test/resources`). + + Add the following property to the `application.yaml` file, specifying the directory that contains your custom response objects: + + .. code-block:: yaml + + stub: + path: "/my_custom_stubs/" + + **Note:** Custom response objects can be placed in the `src/test/resources` directory of your project under the directory defined in above property. Refer to the `examples `_ included in the CPS source repository for reference. + +3. **Simple Test Code:** + + You may refer to the sample test code 'SampleCpsNcmpClientSpec.groovy' in the local CPS project under the following directory: + + :: + + /cps/cps-ncmp-rest-stub/cps-ncmp-rest-stub-service/src/test/groovy/org/onap/cps/ncmp/rest/stub/ + + Alternatively, you can refer to the `example `_ included in the CPS source repository. + +**Custom Responses for Supported Endpoints** + + Only the following endpoints are supported for the first draft. To use your custom response objects for these endpoints, create the corresponding JSON files: + + - For RequestMethod.GET /v1/ch/{cm-handle}/data/ds/{datastore-name}, create "passthrough-operational-example.json". + + - For RequestMethod.POST /v1/ch/searches, create "cmHandlesSearch.json". diff --git a/docs/deployment.rst b/docs/deployment.rst index 0642e6a8e..0fee55969 100644 --- a/docs/deployment.rst +++ b/docs/deployment.rst @@ -197,8 +197,8 @@ Any spring supported property can be configured by providing in ``config.additio | logging.level | Logging level set in cps-core | info | | | | | +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+ -| config.useStrimziKafka | If targeting a custom kafka cluster, ie useStrimziKafka: false, the config.eventPublisher.spring.kafka | true | -| | values below must be set. | | +| config.useStrimziKafka | If targeting a custom kafka cluster, i.e. useStrimziKafka: false, the | true | +| | config.eventPublisher.spring.kafka values below must be set. | | +---------------------------------------+---------------------------------------------------------------------------------------------------------+-------------------------------+ | config.eventPublisher. | Kafka hostname and port | ``:9092`` | | spring.kafka.bootstrap-servers | | | diff --git a/docs/design.rst b/docs/design.rst index 5ad86a3ae..80eb5f152 100755 --- a/docs/design.rst +++ b/docs/design.rst @@ -1,6 +1,6 @@ .. This work is licensed under a Creative Commons Attribution 4.0 International License. .. http://creativecommons.org/licenses/by/4.0 -.. Copyright (C) 2021-2022 Nordix Foundation +.. Copyright (C) 2021-2023 Nordix Foundation .. DO NOT CHANGE THIS LABEL FOR RELEASE NOTES - EVEN THOUGH IT GIVES A WARNING .. _design: @@ -93,3 +93,24 @@ NCMP uses common responses codes in REST responses and events. Also the DMI plug :maxdepth: 1 cps-ncmp-message-status-codes.rst + +Contract Testing (stubs) +======================== + +The CPS team is committed to supporting consumers of our APIs through contract testing. +Obviously we test our own contracts on a continuous basis as part of the build and delivery process. +CPS uses a contract-first approach. That means we write our OpenAPi contracts first and then generate the interface code from that. +This means our interface implementation simply cannot deviate from the OpenApi contracts we deliver. + +Another advantage is that we can also generate 'stubs'. Stubs are a basic implementation of the same interface for testing purposes. +These stubs can be used by clients for unit testing but also for more higher level integration-like testing where the real service is replaced by a stub. +This can be useful for faster feedback loops where deployment of a full stack is difficult and not strictly needed for the purpose of the tests. + +Stubs for contract testing typically always return the same response which is sufficient for the strict definition of a contract test. +However it is often useful to allow more variation in the responses so different clients or the same client can test different scenarios without having to mock the service. +CPS has implemented what we call 'extended stubs' that allow clients to provide alternate responses.implementation + +The available stubs and how to use them are described in :doc:'cps-stubs'. + + + diff --git a/docs/index.rst b/docs/index.rst index c1427ad38..573bd5412 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,6 +25,7 @@ CPS cps-events.rst deployment.rst release-notes.rst + cps-stubs.rst DMI-Plugin ========== diff --git a/docs/release-notes.rst b/docs/release-notes.rst index d891162c3..e6b81fcba 100755 --- a/docs/release-notes.rst +++ b/docs/release-notes.rst @@ -515,7 +515,7 @@ Bug Fixes - `CPS-1289 `_ Getting wrong error code for create node api - `CPS-1326 `_ Creation of DataNodeBuilder with module name prefix is very slow - `CPS-1344 `_ Top level container (prefix) is not always the first module - - `CPS-1350 `_ Add Basic Auth to CPS/NCMP OpenAPI Definitions. + - `CPS-1350 `_ Add Basic Authentication to CPS/NCMP OpenAPI Definitions. - `CPS-1352 `_ Handle YangChoiceNode in right format. - `CPS-1409 `_ Fix Delete uses case with '/' in path. - `CPS-1433 `_ Fix to allow posting data with '/' key fields. @@ -701,7 +701,7 @@ Bug Fixes - `CPS-957 `_ NCMP: fix getResourceDataForPassthroughOperational endpoint - `CPS-1020 `_ DuplicatedYangResourceException error at parallel cmHandle registration - `CPS-1056 `_ Wrong error response format in case of Dmi plugin error - - `CPS-1067 `_ NCMP returns 500 error on searches endpoint when No DMi Handles registered + - `CPS-1067 `_ NCMP returns 500 error on searches endpoint when No DMI Handles registered - `CPS-1085 `_ Performance degradation on ncmp/v1/ch/searches endpoint - `CPS-1088 `_ Kafka consumer can not be turned off - `CPS-1097 `_ Unable to change state from LOCKED to ADVISED @@ -813,7 +813,7 @@ Bug Fixes - `CPS-886 `_ Fragment handling decreasing performance for large number of cmHandles - `CPS-887 `_ Increase performance of cmHandle registration for large number of schema sets in DB - `CPS-892 `_ Fixed the response code during CM-Handle Registration from 201 CREATED to 204 NO_CONTENT - - `CPS-893 `_ NCMP Java API depends on NCM-Rest-API (cyclic) through json properties on Java API + - `CPS-893 `_ NCMP Java API depends on NCMP-Rest-API (cyclic) through json properties on Java API Known Limitations, Issues and Workarounds ----------------------------------------- @@ -1268,7 +1268,7 @@ Security Notes *Known Security Issues* - * Weak Crytography using md5 + * Weak Cryptography using md5 * Risk seen in Zip file expansion *Known Vulnerabilities in Used Modules* diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt new file mode 100644 index 000000000..3bfa9a0a3 --- /dev/null +++ b/docs/spelling_wordlist.txt @@ -0,0 +1,67 @@ +api +async +authorization +boolean +behavior +camelCasing +cmHandle +cmHandles +config +color +cps +cpsPath +cpsPaths +csit +customizations +dataschema +dataspace +dataspaces +datasource +datastore +datastores +deliverables +dmi +dmiPlugin +dmiPlugins +rnv +hazelcast +hostname +jira +json +kafka +kibana +kubernetes +kohn +lifecycle +liquibase +logback +modeled +modeling +normalized +ncmp +onap +openapi +optimized +passthrough +performant +postgres +queryable +repo +runtime +schemas +sql +ssl +standardized +synchronize +synchronization +undeploying +utilizes +xml +xNF +xNFs +xpath +xpaths +xPath +XPath +XPaths +yaml \ No newline at end of file diff --git a/docs/tox.ini b/docs/tox.ini index 8684276f9..7020752c8 100644 --- a/docs/tox.ini +++ b/docs/tox.ini @@ -21,23 +21,23 @@ minversion = 1.6 envlist = docs,docs-linkcheck,docs-spellcheck skipsdist = true [testenv:docs] -basepython = python3.8 +basepython = python3.10 deps = -r{toxinidir}/requirements-docs.txt -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master commands = - sphinx-build -W -q -b html -n -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/html + sphinx-build -q -b html -n -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/html [testenv:docs-linkcheck] -basepython = python3.8 +basepython = python3.10 deps = -r{toxinidir}/requirements-docs.txt -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt?h=master commands = - sphinx-build -W -q -b linkcheck -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/linkcheck + sphinx-build -q -b linkcheck -d {envtmpdir}/doctrees {toxinidir} {toxinidir}/_build/linkcheck [testenv:docs-spellcheck] -basepython = python3.8 +basepython = python3.10 deps = -r{toxinidir}/requirements-docs.txt -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt