diff options
Diffstat (limited to 'cps-rest/src')
8 files changed, 511 insertions, 0 deletions
diff --git a/cps-rest/src/main/java/org/onap/cps/Application.java b/cps-rest/src/main/java/org/onap/cps/Application.java new file mode 100644 index 0000000000..9f6c2edce9 --- /dev/null +++ b/cps-rest/src/main/java/org/onap/cps/Application.java @@ -0,0 +1,32 @@ +/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2020 Nordix Foundation. All rights reserved.
+ * ================================================================================
+ * 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;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
\ No newline at end of file diff --git a/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestController.java b/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestController.java new file mode 100644 index 0000000000..90c287cdaf --- /dev/null +++ b/cps-rest/src/main/java/org/onap/cps/rest/controller/CpsRestController.java @@ -0,0 +1,182 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Nordix Foundation + * Modifications Copyright (C) 2020 Bell Canada. All rights reserved. + * ================================================================================ + * 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.rest.controller; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import javax.validation.Valid; +import org.onap.cps.api.CpService; +import org.onap.cps.exceptions.CpsException; +import org.onap.cps.exceptions.CpsValidationException; +import org.onap.cps.rest.api.CpsRestApi; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +public class CpsRestController implements CpsRestApi { + + @Autowired + private CpService cpService; + + @Override + public ResponseEntity<Object> createAnchor(@Valid MultipartFile multipartFile, String dataspaceName) { + return null; + } + + @Override + public ResponseEntity<Object> createModules(@Valid MultipartFile multipartFile, String dataspaceName) { + final File fileToParse = saveToFile(multipartFile); + final SchemaContext schemaContext = cpService.parseAndValidateModel(fileToParse); + cpService.storeSchemaContext(schemaContext, dataspaceName); + return new ResponseEntity<>("Resource successfully created", HttpStatus.CREATED); + } + + @Override + public ResponseEntity<Object> createNode(@Valid MultipartFile multipartFile, String dataspaceName) { + return null; + } + + @Override + public ResponseEntity<Object> deleteAnchor(String dataspaceName, String anchorName) { + return null; + } + + @Override + public ResponseEntity<Object> deleteDataspace(String dataspaceName) { + return null; + } + + @Override + public ResponseEntity<Object> getAnchor(String dataspaceName, String anchorName) { + return null; + } + + @Override + public ResponseEntity<Object> getAnchors(String dataspaceName) { + return null; + } + + @Override + public ResponseEntity<Object> getModule(String dataspaceName, @Valid String namespaceName, @Valid String revision) { + return null; + } + + @Override + public ResponseEntity<Object> getNode(@Valid String body, String dataspaceName) { + return null; + } + + @Override + public ResponseEntity<Object> getNodeByDataspaceAndAnchor(@Valid String body, String dataspaceName, + String anchorName) { + return null; + } + + /* + Old rest endpoints before contract first approach (Need to be removed). + */ + + /** + * Upload a JSON file. + * + * @param uploadedFile the JSON Multipart file. + * @return a ResponseEntity. + */ + @PostMapping("/upload-yang-json-data-file") + public final ResponseEntity<String> uploadYangJsonDataFile( + @RequestParam("file") MultipartFile uploadedFile) { + validateJsonStructure(uploadedFile); + final int persistenceObjectId = cpService.storeJsonStructure(getJsonString(uploadedFile)); + return new ResponseEntity<String>( + "Object stored in CPS with identity: " + persistenceObjectId, HttpStatus.OK); + + } + + /** + * Read a JSON Object using the object identifier. + * + * @param jsonObjectId the JSON object identifier. + * @return a ResponseEntity. + */ + @GetMapping("/json-object/{id}") + public final ResponseEntity<String> getJsonObjectById( + @PathVariable("id") final int jsonObjectId) { + return new ResponseEntity<String>(cpService.getJsonById(jsonObjectId), HttpStatus.OK); + } + + /** + * Delete a JSON Object using the object identifier. + * + * @param jsonObjectId the JSON object identifier. + * @return a ResponseEntity. + */ + @DeleteMapping("json-object/{id}") + public final ResponseEntity<Object> deleteJsonObjectById( + @PathVariable("id") final int jsonObjectId) { + cpService.deleteJsonById(jsonObjectId); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + private static void validateJsonStructure(final MultipartFile multipartFile) { + try { + final Gson gson = new Gson(); + gson.fromJson(getJsonString(multipartFile), Object.class); + } catch (JsonSyntaxException e) { + throw new CpsValidationException("Not a valid JSON file.", e); + } + } + + private static File saveToFile(final MultipartFile multipartFile) { + try { + final File file = File.createTempFile("tempFile", ".yang"); + file.deleteOnExit(); + + try (OutputStream outputStream = new FileOutputStream(file)) { + outputStream.write(multipartFile.getBytes()); + } + return file; + + } catch (IOException e) { + throw new CpsException(e); + } + } + + private static String getJsonString(final MultipartFile multipartFile) { + try { + return new String(multipartFile.getBytes()); + } catch (IOException e) { + throw new CpsException(e); + } + } +}
\ No newline at end of file diff --git a/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java b/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java new file mode 100644 index 0000000000..9cf4935d4e --- /dev/null +++ b/cps-rest/src/main/java/org/onap/cps/rest/exceptions/CpsRestExceptionHandler.java @@ -0,0 +1,98 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * 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.rest.exceptions; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.hibernate.exception.ConstraintViolationException; +import org.onap.cps.exceptions.CpsException; +import org.onap.cps.exceptions.CpsNotFoundException; +import org.onap.cps.exceptions.CpsValidationException; +import org.onap.cps.rest.controller.CpsRestController; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice(assignableTypes = {CpsRestController.class}) +public class CpsRestExceptionHandler { + + /** + * Default exception handler. + * + * @param exception the exception to handle + * @return response with response code 500. + */ + @ExceptionHandler + public ResponseEntity<Object> handleInternalErrorException(Exception exception) { + return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception); + } + + /* + TODO: Rid off extra dependencies. + + Generic exception handler and CpsException (incl. children) are the only + exceptions to be handled here. All the other exceptions which require a special + treatment should be rethrown as CpsException in the place of occurrence -> + e.g. persistence exceptions are to be handled in cps-ri module. + */ + + @ExceptionHandler({ConstraintViolationException.class}) + public ResponseEntity<Object> handleBadRequestException(Exception exception) { + return buildErrorResponse(HttpStatus.BAD_REQUEST, exception); + } + + @ExceptionHandler({EmptyResultDataAccessException.class}) + public ResponseEntity<Object> handleNotFoundException(Exception exception) { + return buildErrorResponse(HttpStatus.NOT_FOUND, exception); + } + + @ExceptionHandler({CpsException.class}) + public ResponseEntity<Object> handleCpsException(CpsException exception) { + return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, exception.getMessage(), extractDetails(exception)); + } + + @ExceptionHandler({CpsValidationException.class}) + public ResponseEntity<Object> handleCpsValidationException(CpsException exception) { + return buildErrorResponse(HttpStatus.BAD_REQUEST, exception.getMessage(), extractDetails(exception)); + } + + @ExceptionHandler({CpsNotFoundException.class}) + public ResponseEntity<Object> handleCpsNotFoundException(CpsException exception) { + return buildErrorResponse(HttpStatus.NOT_FOUND, exception.getMessage(), extractDetails(exception)); + } + + private static ResponseEntity<Object> buildErrorResponse(HttpStatus status, Exception exception) { + return buildErrorResponse(status, exception.getMessage(), ExceptionUtils.getStackTrace(exception)); + } + + private static ResponseEntity<Object> buildErrorResponse(HttpStatus status, String message, String details) { + return new ResponseEntity<>( + ErrorMessage.builder().status(status.toString()).message(message).details(details).build(), + status + ); + } + + private static String extractDetails(CpsException exception) { + return exception.getCause() == null + ? exception.getDetails() + : ExceptionUtils.getStackTrace(exception.getCause()); + } +} diff --git a/cps-rest/src/main/java/org/onap/cps/rest/exceptions/ErrorMessage.java b/cps-rest/src/main/java/org/onap/cps/rest/exceptions/ErrorMessage.java new file mode 100644 index 0000000000..1f2a0b7860 --- /dev/null +++ b/cps-rest/src/main/java/org/onap/cps/rest/exceptions/ErrorMessage.java @@ -0,0 +1,37 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Pantheon.tech + * ================================================================================ + * 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.rest.exceptions; + +import lombok.Builder; +import lombok.Data; + +/** + * Temporary POJO class used as error response model. + * TODO: To replace with model class generated from Open API specification. + * + */ +@Builder +@Data +public class ErrorMessage { + private String status; + private String message; + private String details; +} + diff --git a/cps-rest/src/main/java/org/onap/cps/swagger/config/SpringFoxConfig.java b/cps-rest/src/main/java/org/onap/cps/swagger/config/SpringFoxConfig.java new file mode 100644 index 0000000000..73e179511b --- /dev/null +++ b/cps-rest/src/main/java/org/onap/cps/swagger/config/SpringFoxConfig.java @@ -0,0 +1,46 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2020 Bell Canada. All rights reserved. + * ================================================================================ + * 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.swagger.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +/** + * Swagger configuration. + */ +@Configuration +public class SpringFoxConfig { + + /** + * Define api configuration. + */ + @Bean + public Docket api() { + return new Docket(DocumentationType.OAS_30) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build(); + } +} diff --git a/cps-rest/src/main/resources/application.yml b/cps-rest/src/main/resources/application.yml new file mode 100644 index 0000000000..e8af0bcfb2 --- /dev/null +++ b/cps-rest/src/main/resources/application.yml @@ -0,0 +1,40 @@ +server:
+ port: 8080
+ servlet:
+ context-path: /api/cps
+
+spring:
+ main:
+ banner-mode: "off"
+# for POC only, later this should move to cpi-ri module
+ jpa:
+ ddl-auto: create
+ open-in-view: false
+ properties:
+ hibernate:
+ enable_lazy_load_no_trans: true
+ dialect: org.hibernate.dialect.PostgreSQLDialect
+
+ datasource:
+ url: jdbc:postgresql://${DB_HOST}:5432/cpsdb
+ username: ${DB_USERNAME}
+ password: ${DB_PASSWORD}
+ driverClassName: org.postgresql.Driver
+ initialization-mode: always
+
+# Actuator
+management:
+ endpoints:
+ web:
+ base-path: /manage
+ endpoint:
+ health:
+ show-details: always
+ # kubernetes probes: liveness and readiness
+ probes:
+ enabled: true
+
+logging:
+ level:
+ org:
+ springframework: INFO
diff --git a/cps-rest/src/main/resources/logback-spring.xml b/cps-rest/src/main/resources/logback-spring.xml new file mode 100644 index 0000000000..8a4ae07c5b --- /dev/null +++ b/cps-rest/src/main/resources/logback-spring.xml @@ -0,0 +1,48 @@ +<configuration scan="true" debug="false"> + <include resource="org/springframework/boot/logging/logback/base.xml" /> + + <property name="queueSize" value="256" /> + <property name="maxFileSize" value="20MB" /> + <property name="maxHistory" value="30" /> + <property name="totalSizeCap" value="20MB" /> + + <!-- log file names --> + <property name="logName" value="cps" /> + + <property name="currentTimeStamp" value="%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",UTC}"/> + + <property name="debugPattern" + value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}|%thread|%X{RequestID}| %logger{50} - %msg%n" /> + + <appender name="Debug" + class="ch.qos.logback.core.rolling.RollingFileAppender"> + <file>../log/${logName}.log</file> + <rollingPolicy + class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> + <fileNamePattern>${logName}.%d{yyyy-MM-dd}.%i.log.zip + </fileNamePattern> + <maxFileSize>${maxFileSize}</maxFileSize> + <maxHistory>${maxHistory}</maxHistory> + <totalSizeCap>${totalSizeCap}</totalSizeCap> + </rollingPolicy> + <encoder> + <pattern>${debugPattern}</pattern> + </encoder> + </appender> + + <appender name="asyncDebug" class="ch.qos.logback.classic.AsyncAppender"> + <queueSize>256</queueSize> + <appender-ref ref="Debug" /> + <includeCallerData>true</includeCallerData> + </appender> + + <logger name="org.onap.cps" level="DEBUG" additivity="false"> + <appender-ref ref="asyncDebug" /> + </logger> + + + <root level="INFO"> + <appender-ref ref="asyncDebug" /> + </root> + +</configuration>
\ No newline at end of file diff --git a/cps-rest/src/main/resources/openapi-configuration.json b/cps-rest/src/main/resources/openapi-configuration.json new file mode 100644 index 0000000000..ad5998feb7 --- /dev/null +++ b/cps-rest/src/main/resources/openapi-configuration.json @@ -0,0 +1,28 @@ +{
+ "resourcePackages": [
+ "org.onap.cps.rest.controller"
+ ],
+ "prettyPrint": true,
+ "cacheTTL": 0,
+ "openAPI": {
+ "info": {
+ "title": "ONAP Open API v3 CPS Spec",
+ "description": "The API Description may be multiline, and GitHub Flavored Markdown, GFM syntax, can be used for rich text representation.",
+ "x-logo": {
+ "url": "logo.png"
+ },
+ "contact": {
+ "name": "ONAP",
+ "url": "https://onap.readthedocs.io",
+ "email": "onap-discuss@lists.onap.org"
+ },
+ "license": {
+ "name": "Apache 2.0",
+ "url": "http://www.apache.org/licenses/LICENSE-2.0"
+ },
+ "version": "1.2.34",
+ "x-planned-retirement-date": "202207",
+ "x-component": "Modeling"
+ }
+ }
+}
|