diff options
author | Serge Simard <serge@agilitae.com> | 2019-08-08 10:55:57 -0400 |
---|---|---|
committer | Serge Simard <serge@agilitae.com> | 2019-08-13 14:57:10 +0000 |
commit | 34c424689a52614fb414d65899282497fe25b164 (patch) | |
tree | 91fdafc99a280064f0ef19c1576fdcbbae303cfc /ms/blueprintsprocessor/modules/inbounds/configs-api/src/main | |
parent | 571c49342a905616298d923a9d29a201ae4ecd11 (diff) |
Resource Configuration Snapshots Executor and API
Issue-ID: CCSDK-1604
Signed-off-by: Serge Simard <serge@agilitae.com>
Change-Id: I349c649e941431b48a309123489d26fb22e0e50a
Signed-off-by: Serge Simard <serge@agilitae.com>
Diffstat (limited to 'ms/blueprintsprocessor/modules/inbounds/configs-api/src/main')
3 files changed, 262 insertions, 0 deletions
diff --git a/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotController.kt b/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotController.kt new file mode 100644 index 000000000..eb7929509 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotController.kt @@ -0,0 +1,124 @@ +/* + * Copyright © 2019 Bell Canada + * + * 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.onap.ccsdk.cds.blueprintsprocessor.configs.api + +import com.fasterxml.jackson.databind.JsonNode +import io.swagger.annotations.Api +import io.swagger.annotations.ApiOperation +import io.swagger.annotations.ApiParam +import kotlinx.coroutines.runBlocking +import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshot +import org.onap.ccsdk.cds.blueprintsprocessor.functions.config.snapshots.db.ResourceConfigSnapshotService +import org.onap.ccsdk.cds.controllerblueprints.core.asJsonPrimitive +import org.springframework.http.MediaType +import org.springframework.http.ResponseEntity +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* + +/** + * Exposes Resource Configuration Snapshot API to store and retrieve stored resource configurations. + * + * @author Serge Simard + * @version 1.0 + */ +@RestController +@RequestMapping("/api/v1/configs") +@Api(value = "/api/v1/configs", + description = "Interaction with stored configurations.") +open class ResourceConfigSnapshotController(private val resourceConfigSnapshotService: ResourceConfigSnapshotService) { + + @RequestMapping(path = ["/health-check"], + method = [RequestMethod.GET], + produces = [MediaType.APPLICATION_JSON_VALUE]) + @ResponseBody + @ApiOperation(value = "Health Check", hidden = true) + fun ressCfgSnapshotControllerHealthCheck(): JsonNode = runBlocking { + "Success".asJsonPrimitive() + } + + @RequestMapping(path = [""], + method = [RequestMethod.GET], + produces = [MediaType.TEXT_PLAIN_VALUE, MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE]) + @ApiOperation(value = "Retrieve a resource configuration snapshot.", + notes = "Retrieve a config snapshot, identified by its Resource Id and Type. " + + "An extra 'format' parameter can be passed to tell what content-type is expected.") + @ResponseBody + @PreAuthorize("hasRole('USER')") + fun get( + @ApiParam(value = "Resource Type associated of the resource configuration snapshot.", required = false) + @RequestParam(value = "resourceType", required = true) resourceType: String, + + @ApiParam(value = "Resource Id associated of the resource configuration snapshot.", required = false) + @RequestParam(value = "resourceId", required = true) resourceId: String, + + @ApiParam(value = "Status of the snapshot being retrieved.", defaultValue = "RUNNING", required = false) + @RequestParam(value = "status", required = false, defaultValue = "RUNNING") status: String, + + @ApiParam(value = "Expected format of the snapshot being retrieved.", defaultValue = MediaType.TEXT_PLAIN_VALUE, + required = false) + @RequestParam(value = "format", required = false, defaultValue = MediaType.TEXT_PLAIN_VALUE) format: String) + + : ResponseEntity<String> = runBlocking { + + var configSnapshot = "" + + if (resourceType.isNotEmpty() && resourceId.isNotEmpty()) { + try { + configSnapshot = resourceConfigSnapshotService.findByResourceIdAndResourceTypeAndStatus(resourceId, + resourceType, ResourceConfigSnapshot.Status.valueOf(status.toUpperCase())) + } catch (ex : NoSuchElementException) { + throw ResourceConfigSnapshotException( + "Could not find configuration snapshot entry for type $resourceType and Id $resourceId") + } + } else { + throw IllegalArgumentException("Missing param. You must specify resource-id and resource-type.") + } + + var expectedContentType = format + if (expectedContentType.indexOf('/') < 0) { + expectedContentType = "application/$expectedContentType" + } + val expectedMediaType: MediaType = MediaType.valueOf(expectedContentType) + + ResponseEntity.ok().contentType(expectedMediaType).body(configSnapshot) + } + + @PostMapping("/{resourceType}/{resourceId}/{status}", + produces = [MediaType.APPLICATION_JSON_VALUE]) + @ApiOperation(value = "Store a resource configuration snapshot identified by resourceId, resourceType, status.", + notes = "Store a resource configuration snapshot, identified by its resourceId and resourceType, " + + "and optionally its status, either RUNNING or CANDIDATE.", + response = ResourceConfigSnapshot::class, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + @PreAuthorize("hasRole('USER')") + fun postWithResourceIdAndResourceType( + @ApiParam(value = "Resource Type associated with the resolution.", required = false) + @PathVariable(value = "resourceType", required = true) resourceType: String, + @ApiParam(value = "Resource Id associated with the resolution.", required = false) + @PathVariable(value = "resourceId", required = true) resourceId: String, + @ApiParam(value = "Status of the snapshot being retrieved.", defaultValue = "RUNNING", required = true) + @PathVariable(value = "status", required = true) status: String, + @ApiParam(value = "Config snapshot to store.", required = true) + @RequestBody snapshot: String): ResponseEntity<ResourceConfigSnapshot> = runBlocking { + + val resultStored = + resourceConfigSnapshotService.write(snapshot, resourceId, resourceType, + ResourceConfigSnapshot.Status.valueOf(status.toUpperCase())) + + ResponseEntity.ok().body(resultStored) + } +} diff --git a/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotException.kt b/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotException.kt new file mode 100644 index 000000000..1eeea9893 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotException.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2019 Bell Canada. + * + * 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.onap.ccsdk.cds.blueprintsprocessor.configs.api + +class ResourceConfigSnapshotException(message: String) : RuntimeException(message) { + var code: Int = 404 +} diff --git a/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotExceptionHandler.kt b/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotExceptionHandler.kt new file mode 100644 index 000000000..d21464ef5 --- /dev/null +++ b/ms/blueprintsprocessor/modules/inbounds/configs-api/src/main/kotlin/org/onap/ccsdk/cds/blueprintsprocessor/configs/api/ResourceConfigSnapshotExceptionHandler.kt @@ -0,0 +1,118 @@ +/* + * Copyright © 2019 Bell Canada + * + * 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.onap.ccsdk.cds.blueprintsprocessor.configs.api + +import com.fasterxml.jackson.annotation.JsonFormat +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.annotation.JsonTypeName +import org.onap.ccsdk.cds.controllerblueprints.core.BluePrintProcessorException +import org.onap.ccsdk.cds.controllerblueprints.core.data.ErrorCode +import org.slf4j.LoggerFactory +import org.springframework.http.HttpStatus +import org.springframework.http.ResponseEntity +import org.springframework.orm.jpa.JpaObjectRetrievalFailureException +import org.springframework.dao.EmptyResultDataAccessException +import org.springframework.dao.IncorrectResultSizeDataAccessException +import org.springframework.web.server.ServerWebInputException +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.RestControllerAdvice +import java.io.Serializable +import java.util.* + +/** + * Handle exceptions in ResourceConfigSnapshot API and provide relevant HTTP status codes and messages + * + * @author Serge Simard + * @version 1.0 + */ +@RestControllerAdvice("org.onap.ccsdk.cds.blueprintsprocessor.configs.api") +open class ResourceConfigSnapshotExceptionHandler { + + private val log = LoggerFactory.getLogger(ResourceConfigSnapshotExceptionHandler::class.toString()) + + private val debugMsg = "Resource_Config_Snapshot_ExceptionHandler_Error_Message" + + @ExceptionHandler + fun resourceConfigSnapshotExceptionHandler(e: BluePrintProcessorException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.BLUEPRINT_PATH_MISSING + return returnError(e, errorCode) + } + + @ExceptionHandler + fun resourceConfigSnapshotExceptionHandler(e: ServerWebInputException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.INVALID_REQUEST_FORMAT + return returnError(e, errorCode, false) + } + + @ExceptionHandler + fun resourceConfigSnapshotExceptionHandler(e: IllegalArgumentException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.INVALID_REQUEST_FORMAT + return returnError(e, errorCode, false) + } + + @ExceptionHandler + fun resourceConfigSnapshotExceptionHandler(e: IncorrectResultSizeDataAccessException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.DUPLICATE_DATA + return returnError(e, errorCode) + } + + @ExceptionHandler + fun resourceConfigSnapshotExceptionHandler(e: EmptyResultDataAccessException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.RESOURCE_NOT_FOUND + return returnError(e, errorCode, false) + } + + @ExceptionHandler + fun resourceConfigSnapshotExceptionHandler(e: JpaObjectRetrievalFailureException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.RESOURCE_NOT_FOUND + return returnError(e, errorCode, false) + } + + @ExceptionHandler + fun resourceConfigSnapshotExceptionHandler(e: Exception): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.GENERIC_FAILURE + return returnError(e, errorCode) + } + + @ExceptionHandler + fun resourceConfigSnapshotExceptionHandler(e: ResourceConfigSnapshotException): ResponseEntity<ErrorMessage> { + val errorCode = ErrorCode.RESOURCE_NOT_FOUND + return returnError(e, errorCode, false) + } + + fun returnError(e: Exception, errorCode: ErrorCode, toBeLogged: Boolean = true): ResponseEntity<ErrorMessage> { + if (toBeLogged) { + log.error(e.message, e) + } else { + log.error(e.message) + } + val errorMessage = + ErrorMessage(errorCode.message(e.message!!), + errorCode.value, + debugMsg) + return ResponseEntity(errorMessage, HttpStatus.resolve(errorCode.httpCode)!!) + } +} + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonTypeName("errorMessage") +@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME) +class ErrorMessage(var message: String?, var code: Int?, var debugMessage: String?) : Serializable { + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") + var timestamp = Date() +}
\ No newline at end of file |