Add annotation for OpenAPI 3.0 documentation generation
authorBartosz Gardziejewski <bartosz.gardziejewski@nokia.com>
Tue, 10 Mar 2020 15:05:09 +0000 (16:05 +0100)
committerBartosz Gardziejewski <bartosz.gardziejewski@nokia.com>
Tue, 10 Mar 2020 15:05:09 +0000 (16:05 +0100)
Issue-ID: AAF-997
Signed-off-by: Bartosz Gardziejewski <bartosz.gardziejewski@nokia.com>
Change-Id: I6dcbe2076d7e0095c210b1cae95309fe31ebf243

14 files changed:
certService/OpenAPI.yaml
certService/src/main/java/org/onap/aaf/certservice/api/CertificationController.java
certService/src/main/java/org/onap/aaf/certservice/api/ReadinessController.java
certService/src/main/java/org/onap/aaf/certservice/api/ReloadConfigController.java
certService/src/main/java/org/onap/aaf/certservice/api/advice/CertificationExceptionAdvice.java [moved from certService/src/main/java/org/onap/aaf/certservice/certification/CertificationExceptionController.java with 77% similarity]
certService/src/main/java/org/onap/aaf/certservice/api/advice/ReloadConfigExceptionAdvice.java [new file with mode: 0644]
certService/src/main/java/org/onap/aaf/certservice/api/configuration/OpenApiConfig.java [new file with mode: 0644]
certService/src/main/java/org/onap/aaf/certservice/certification/exception/Cmpv2ServerNotFoundException.java
certService/src/main/resources/application.properties
certService/src/test/java/org/onap/aaf/certservice/api/CertificationControllerTest.java
certService/src/test/java/org/onap/aaf/certservice/api/ReloadConfigControllerTest.java
certService/src/test/java/org/onap/aaf/certservice/api/advice/CertificationExceptionAdviceTest.java [moved from certService/src/test/java/org/onap/aaf/certservice/certification/CertificationExceptionControllerTest.java with 64% similarity]
certService/src/test/java/org/onap/aaf/certservice/api/advice/ReloadConfigExceptionAdviceTest.java [new file with mode: 0644]
pom.xml

index 631a1fb..cee5a40 100644 (file)
 openapi: 3.0.1
 info:
   title: CertService Documentation
-  description: certification service API documentation
-  version: v1
+  description: Certification service API documentation
+  version: 1.0.0
 servers:
-  - url: 'http://localhost:8080'
+  - url: http://localhost:8080
     description: Generated server url
 tags:
   - name: Actuator
     description: Monitor and interact
     externalDocs:
       description: Spring Boot Actuator Web API Documentation
-      url: 'https://docs.spring.io/spring-boot/docs/current/actuator-api/html/'
+      url: https://docs.spring.io/spring-boot/docs/current/actuator-api/html/
 paths:
-  '/v1/certificate/{caName}':
+  /v1/certificate/{caName}:
     get:
       tags:
-        - certification-service
-      operationId: sign certificate
+        - CertificationService
+      summary: sign certificate
+      description: Web endpoint for requesting certificate signing. Used by system
+        components to gain certificate signed by CA.
+      operationId: signCertificate
       parameters:
         - name: caName
           in: path
+          description: Name of certification authority that will sign CSR.
           required: true
           schema:
             type: string
         - name: CSR
           in: header
+          description: Certificate signing request in form of PEM object encoded in
+            Base64 (with header and footer).
           required: true
           schema:
             type: string
         - name: PK
           in: header
+          description: Private key in form of PEM object encoded in Base64 (with header
+            and footer).
           required: true
           schema:
             type: string
       responses:
-        '200':
-          description: csr is signed
+        "200":
+          description: certificate successfully signed
+          content:
+            application/json; charset=utf-8:
+              schema:
+                $ref: '#/components/schemas/CertificationModel'
+        "500":
+          description: something went wrong during connecting to cmp client
+          content:
+            application/json; charset=utf-8:
+              schema:
+                $ref: '#/components/schemas/ErrorResponseModel'
+        "404":
+          description: CA not found for given name
+          content:
+            application/json; charset=utf-8:
+              schema:
+                $ref: '#/components/schemas/ErrorResponseModel'
+        "400":
+          description: given CSR or/and PK is incorrect
+          content:
+            application/json; charset=utf-8:
+              schema:
+                $ref: '#/components/schemas/ErrorResponseModel'
+  /ready:
+    get:
+      tags:
+        - CertificationService
+      summary: check is container is ready
+      description: Web endpoint for checking if service is ready to be used.
+      operationId: checkReady
+      responses:
+        "200":
+          description: configuration is loaded and service is ready to use
           content:
             application/json; charset=utf-8:
               schema:
                 type: string
-                example: {
-                  "certificateChain": [
-                    "-----BEGIN CERTIFICATE-----\nMIIDjDCCAnSgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwgYQxCzAJBgNVBAYTAlVT\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkw\nFwYDVQQKDBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMR4wHAYDVQQD\nDBVpbnRlcm1lZGlhdGUub25hcC5vcmcwHhcNMjAwMjEyMDk1MTI2WhcNMjIxMTA4\nMDk1MTI2WjB7MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG\nA1UEBwwNU2FuLUZyYW5jaXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjEN\nMAsGA1UECwwET05BUDEVMBMGA1UEAwwMdmlkLm9uYXAub3JnMIIBIjANBgkqhkiG\n9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw+GIRzJzUOh0gtc+wzFJEdTnn+q5F10L0Yhr\nG1xKdjPieHIFGsoiXwcuCU8arNSqlz7ocx62KQRkcA8y6edlOAsYtdOEJvqEI9vc\neyTB/HYsbzw3URPGch4AmibrQkKU9QvGwouHtHn4R2Ft2Y0tfEqv9hxj9v4njq4A\nEiDLAFLl5FmVyCZu/MtKngSgu1smcaFKTYySPMxytgJZexoa/ALZyyE0gRhsvwHm\nNLGCPt1bmE/PEGZybsCqliyTO0S56ncD55The7+D/UDS4kE1Wg0svlWon/YsE6QW\nB3oeJDX7Kr8ebDTIAErevIAD7Sm4ee5se2zxYrsYlj0MzHZtvwIDAQABoxAwDjAM\nBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCvQ1pTvjON6vSlcJRKSY4r\n8q7L4/9ZaVXWJAjzEYJtPIqsgGiPWz0vGfgklowU6tZxp9zRZFXfMil+mPQSe+yo\nULrZSQ/z48YHPueE/BNO/nT4aaVBEhPLR5aVwC7uQVX8H+m1V1UGT8lk9vdI9rej\nCI9l524sLCpdE4dFXiWK2XHEZ0Vfylk221u3IYEogVVA+UMX7BFPSsOnI2vtYK/i\nlwZtlri8LtTusNe4oiTkYyq+RSyDhtAswg8ANgvfHolhCHoLFj6w1IkG88UCmbwN\nd7BoGMy06y5MJxyXEZG0vR7eNeLey0TIh+rAszAFPsIQvrOHW+HuA+WLQAj1mhnm\n-----END CERTIFICATE-----",
-                    "-----BEGIN CERTIFICATE-----\nMIIDqTCCApGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkw\nFwYDVQQKDBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMREwDwYDVQQD\nDAhvbmFwLm9yZzEeMBwGCSqGSIb3DQEJARYPdGVzdGVyQG9uYXAub3JnMB4XDTIw\nMDIxMjA5NDAxMloXDTIyMTEwODA5NDAxMlowgYQxCzAJBgNVBAYTAlVTMRMwEQYD\nVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4tRnJhbmNpc2NvMRkwFwYDVQQK\nDBBMaW51eC1Gb3VuZGF0aW9uMQ0wCwYDVQQLDARPTkFQMR4wHAYDVQQDDBVpbnRl\ncm1lZGlhdGUub25hcC5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB\nAQC1oOYMZ6G+2DGDAizYnzdCNiogivlht1s4oqgem7fM1XFPxD2p31ATIibOdqr/\ngv1qemO9Q4r1xn6w1Ufq7T1K7PjnMzdSeTqZefurE2JM/HHx2QvW4TjMlz2ILgaD\nL1LN60kmMQSOi5VxKJpsrCQxbOsxhvefd212gny5AZMcjJe23kUd9OxUrtvpdLEv\nwI3vFEvT7oRUnEUg/XNz7qeg33vf1C39yMR+6O4s6oevgsEebVKjb+yOoS6zzGtz\n72wZjm07C54ZlO+4Uy+QAlMjRiU3mgWkKbkOy+4CvwehjhpTikdBs2DX39ZLGHhn\nL/0a2NYtGulp9XEqmTvRoI+PAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8wDQYJKoZI\nhvcNAQELBQADggEBADcitdJ6YswiV8jAD9GK0gf3+zqcGegt4kt+79JXlXYbb1sY\nq3o6prcB7nSUoClgF2xUPCslFGpM0Er9FCSFElQM/ru0l/KVmJS6kSpwEHvsYIH3\nq5anta+Pyk8JSQWAAw+qrind0uBQMnhR8Tn13tgV+Kjvg/xlH/nZIEdN5YtLB1cA\nbeVsZRyRfVL9DeZU8s/MZ5wC3kgcEp5A4m5lg7HyBxBdqhzFcDr6xiy6OGqW8Yep\nxrwfc8Fw8a/lOv4U+tBeGNKPQDYaL9hh+oM+qMkNXsHXDqdJsuEGJtU4i3Wcwzoc\nXGN5NWV//4bP+NFmwgcn7AYCdRvz04A8GU/0Cwg=\n-----END CERTIFICATE-----"
-                  ],
-                  "trustedCertificates": [
-                    "-----BEGIN CERTIFICATE-----\nMIIDtzCCAp8CFAwqQddh4/iyGfP8UZ3dpXlxfAN8MA0GCSqGSIb3DQEBCwUAMIGX\nMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu\nLUZyYW5jaXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECwwE\nT05BUDERMA8GA1UEAwwIb25hcC5vcmcxHjAcBgkqhkiG9w0BCQEWD3Rlc3RlckBv\nbmFwLm9yZzAeFw0yMDAyMTIwOTM0MjdaFw0yMTAyMTEwOTM0MjdaMIGXMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuLUZyYW5j\naXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECwwET05BUDER\nMA8GA1UEAwwIb25hcC5vcmcxHjAcBgkqhkiG9w0BCQEWD3Rlc3RlckBvbmFwLm9y\nZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMCFrnO7/eT6V+7XkPPd\neiL/6xXreuegvit/1/jTVjG+3AOVcmTn2WXwXXRcQLvkWQfJVPoltsY8E3FqFRti\n797XjY6cdQJFVDyzNU0+Fb4vJL9FK5wSvnS6EFjBEn3JvXRlENorDCs/mfjkjJoa\nDl74gXQEJYcg4nsTeNIj7cm3Q7VK3mZt1t7LSJJ+czxv69UJDuNJpmQ/2WOKyLZA\ngTtBJ+Hyol45/OLsrqwq1dAn9ZRWIFPvRt/XQYH9bI/6MtqSreRVUrdYCiTe/XpP\nB/OM6NEi2+p5QLi3Yi70CEbqP3HqUVbkzF+r7bwIb6M5/HxfqzLmGwLvD+6rYnUn\nBm8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAhXoO65DXth2X/zFRNsCNpLwmDy7r\nPxT9ZAIZAzSxx3/aCYiuTrKP1JnqjkO+F2IbikrI4n6sKO49SKnRf9SWTFhd+5dX\nvxq5y7MaqxHAY9J7+Qzq33+COVFQnaF7ddel2NbyUVb2b9ZINNsaZkkPXui6DtQ7\n/Fb/1tmAGWd3hMp75G2thBSzs816JMKKa9WD+4VGATEs6OSll4sv2fOZEn+0mAD3\n9q9c+WtLGIudOwcHwzPb2njtNntQSCK/tVOqbY+vzhMY3JW+p9oSrLDSdGC+pAKK\nm/wB+2VPIYcsPMtIhHC4tgoSaiCqjXYptaOh4b8ye8CPBUCpX/AYYkN0Ow==\n-----END CERTIFICATE-----",
-                    "-----BEGIN CERTIFICATE-----\nMIIDvzCCAqcCFF5DejiyfoNfPiiMmBXulniBewBGMA0GCSqGSIb3DQEBCwUAMIGb\nMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2Fu\nLUZyYW5jaXNjbzEZMBcGA1UECgwQTGludXgtRm91bmRhdGlvbjENMAsGA1UECwwE\nT05BUDEVMBMGA1UEAwwMbmV3Lm9uYXAub3JnMR4wHAYJKoZIhvcNAQkBFg90ZXN0\nZXJAb25hcC5vcmcwHhcNMjAwMjEyMDk1OTM3WhcNMjEwMjExMDk1OTM3WjCBmzEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbi1G\ncmFuY2lzY28xGTAXBgNVBAoMEExpbnV4LUZvdW5kYXRpb24xDTALBgNVBAsMBE9O\nQVAxFTATBgNVBAMMDG5ldy5vbmFwLm9yZzEeMBwGCSqGSIb3DQEJARYPdGVzdGVy\nQG9uYXAub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtF4FXeDV\nng/inC/bTACmZnLC9IiC7PyG/vVbMxxN1bvQLRAwC/Hbl3i9zD68Vs/jPPr/SDr9\n2rgItdDdUY1V30Y3PT06F11XdEaRb+t++1NX0rDf1AqPaBZgnBmB86s1wbqHdJTr\nwEImDZ5xMPfP3fiWy/9Yw/U7iRMIi1/oI0lWuHJV0bn908shuJ6dvInpRCoDnoTX\nYP/FiDSZCFVewQcq4TigB7kRqZrDcPZWbSlqHklDMXRwbCxAiFSziuX6TBwru9Rn\nHhIeXVSgMU1ZSSopVbJGtQ4zSsU1nvTK5Bhc2UHGcAOZy1xTN5D9EEbTqh7l+Wtx\ny8ojkEXvFG8lVwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAE+bUphwHit78LK8sb\nOMjt4DiEu32KeSJOpYgPLeBeAIynaNsa7sQrpuxerGNTmQWIcw6olXI0J+OOwkik\nII7elrYtd5G1uALxXWdamNsaY0Du34moVL1YjexJ7qQ4oBUxg2tuY8NAQGDK+23I\nnCA+ZwzdTJo73TYS6sx64d/YLWkX4nHGUoMlF+xUH34csDyhpuTSzQhC2quB5N8z\ntSFdpe4z2jqx07qo2EBFxi03EQ8Q0ex6l421QM2gbs7cZQ66K0DkpPcF2+iHZnyx\nxq1lnlsWHklElF2bhyXTn3fPp5wtan00P8IolKx7CAWb92QjkW6M0RvTW/xuwIzh\n0rTO\n-----END CERTIFICATE-----"
-                  ]
-                }
-        '400':
-          description: incorrect/missing CSR and/or private key
+        "503":
+          description: configuration loading failed and service is unavailable
           content:
             application/json; charset=utf-8:
               schema:
                 type: string
-                example: {
-                  "errorMessage": "Wrong key (PK) format"
-                }
-        '500':
-          description: exception occurred on server side
+  /reload:
+    get:
+      tags:
+        - CertificationService
+      summary: reload service configuration from file
+      description: Web endpoint for performing configuration reload. Used to reload
+        configuration file from file.
+      operationId: reloadConfiguration
+      responses:
+        "200":
+          description: configuration has been successfully reloaded
           content:
             application/json; charset=utf-8:
               schema:
                 type: string
+        "500":
+          description: something went wrong during configuration loading
+          content:
+            application/json; charset=utf-8:
+              schema:
+                $ref: '#/components/schemas/ErrorResponseModel'
   /actuator/health:
     get:
       tags:
         - Actuator
-      operationId: health check
       summary: Actuator web endpoint 'health'
+      operationId: handle_0
       responses:
-        '200':
-          description: service is healthy
+        "200":
+          description: default response
           content: {}
-  /actuator/refresh:
-    post:
+  /actuator/health/**:
+    get:
       tags:
         - Actuator
-      operationId: refresh configuration
-      summary: Actuator web endpoint 'refresh'
+      summary: Actuator web endpoint 'health-path'
+      operationId: handle_1
       responses:
-        '200':
-          description: configuration is successfully reloaded
+        "200":
+          description: default response
           content: {}
-        '500':
-          description: fail to reload configuration
+  /actuator:
+    get:
+      tags:
+        - Actuator
+      summary: Actuator root web endpoint
+      operationId: links_2
+      responses:
+        "200":
+          description: default response
           content: {}
-components: {}
+components:
+  schemas:
+    ErrorResponseModel:
+      type: object
+      properties:
+        errorMessage:
+          type: string
+    CertificationModel:
+      type: object
+      properties:
+        certificateChain:
+          type: array
+          items:
+            type: string
+        trustedCertificates:
+          type: array
+          items:
+            type: string
index abb6811..fe941f5 100644 (file)
 
 package org.onap.aaf.certservice.api;
 
-import com.google.gson.Gson;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import org.onap.aaf.certservice.certification.CertificationModelFactory;
-import org.onap.aaf.certservice.certification.CsrModelFactory;
-import org.onap.aaf.certservice.certification.CsrModelFactory.StringBase64;
-import org.onap.aaf.certservice.certification.configuration.Cmpv2ServerProvider;
-import org.onap.aaf.certservice.certification.configuration.model.Cmpv2Server;
 import org.onap.aaf.certservice.certification.exception.Cmpv2ClientAdapterException;
 import org.onap.aaf.certservice.certification.exception.DecryptionException;
+import org.onap.aaf.certservice.certification.exception.ErrorResponseModel;
 import org.onap.aaf.certservice.certification.model.CertificationModel;
-import org.onap.aaf.certservice.certification.model.CsrModel;
 import org.onap.aaf.certservice.cmpv2client.exceptions.CmpClientException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -43,6 +45,7 @@ import org.springframework.web.bind.annotation.RestController;
 
 
 @RestController
+@Tag(name = "CertificationService")
 public class CertificationController {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(CertificationController.class);
@@ -64,17 +67,32 @@ public class CertificationController {
      * @return JSON containing trusted certificates and certificate chain
      */
     @GetMapping(value = "v1/certificate/{caName}", produces = "application/json; charset=utf-8")
-    public ResponseEntity<String> signCertificate(
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "certificate successfully signed"),
+            @ApiResponse(responseCode = "400", description = "given CSR or/and PK is incorrect",
+                    content = @Content(schema = @Schema(implementation = ErrorResponseModel.class))),
+            @ApiResponse(responseCode = "404", description = "CA not found for given name",
+                    content = @Content(schema = @Schema(implementation = ErrorResponseModel.class))),
+            @ApiResponse(responseCode = "500", description = "something went wrong during connecting to cmp client",
+                    content = @Content(schema = @Schema(implementation = ErrorResponseModel.class)))
+    })
+    @Operation(
+            summary = "sign certificate",
+            description = "Web endpoint for requesting certificate signing. Used by system components to gain certificate signed by CA.",
+            tags = { "CertificationService" })
+    public ResponseEntity<CertificationModel> signCertificate(
+            @Parameter(description="Name of certification authority that will sign CSR.")
             @PathVariable String caName,
+            @Parameter(description="Certificate signing request in form of PEM object encoded in Base64 (with header and footer).")
             @RequestHeader("CSR") String encodedCsr,
+            @Parameter(description="Private key in form of PEM object encoded in Base64 (with header and footer).")
             @RequestHeader("PK") String encodedPrivateKey
     ) throws DecryptionException, CmpClientException, Cmpv2ClientAdapterException {
         caName = caName.replaceAll("[\n|\r|\t]", "_");
         LOGGER.info("Received certificate signing request for CA named: {}", caName);
         CertificationModel certificationModel = certificationModelFactory
                 .createCertificationModel(encodedCsr, encodedPrivateKey, caName);
-        return new ResponseEntity<>(new Gson().toJson(certificationModel), HttpStatus.OK);
-
+        return new ResponseEntity<>(certificationModel, HttpStatus.OK);
     }
 
 }
index e33bf51..288957c 100644 (file)
 
 package org.onap.aaf.certservice.api;
 
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import org.onap.aaf.certservice.certification.configuration.CmpServersConfig;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
@@ -28,6 +32,7 @@ import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
+@Tag(name = "CertificationService")
 public class ReadinessController {
 
     private final CmpServersConfig cmpServersConfig;
@@ -37,7 +42,15 @@ public class ReadinessController {
         this.cmpServersConfig = cmpServersConfig;
     }
 
-    @GetMapping("/ready")
+    @GetMapping(value = "/ready", produces = "application/json; charset=utf-8")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "configuration is loaded and service is ready to use"),
+            @ApiResponse(responseCode = "503", description = "configuration loading failed and service is unavailable")
+    })
+    @Operation(
+            summary = "check is container is ready",
+            description = "Web endpoint for checking if service is ready to be used.",
+            tags = { "CertificationService" })
     public ResponseEntity<String> checkReady() {
         if (cmpServersConfig.isReady()) {
             return new ResponseEntity<>(HttpStatus.OK);
index 5390a00..b6673ce 100644 (file)
 
 package org.onap.aaf.certservice.api;
 
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.media.Content;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.tags.Tag;
 import org.onap.aaf.certservice.certification.configuration.CmpServersConfig;
 import org.onap.aaf.certservice.certification.configuration.CmpServersConfigLoadingException;
+import org.onap.aaf.certservice.certification.exception.ErrorResponseModel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
+@Tag(name = "CertificationService")
 public class ReloadConfigController {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(ReloadConfigController.class);
@@ -43,16 +50,19 @@ public class ReloadConfigController {
         this.cmpServersConfig = cmpServersConfig;
     }
 
-    @GetMapping("/reload")
+    @GetMapping(value = "/reload", produces = "application/json; charset=utf-8")
+    @ApiResponses(value = {
+            @ApiResponse(responseCode = "200", description = "configuration has been successfully reloaded"),
+            @ApiResponse(responseCode = "500", description = "something went wrong during configuration loading",
+                    content = @Content(schema = @Schema(implementation = ErrorResponseModel.class)))
+    })
+    @Operation(
+            summary = "reload service configuration from file",
+            description = "Web endpoint for performing configuration reload. Used to reload configuration file from file.",
+            tags = { "CertificationService" })
     public ResponseEntity<String> reloadConfiguration() throws CmpServersConfigLoadingException {
         cmpServersConfig.reloadConfiguration();
         return new ResponseEntity<>(HttpStatus.OK);
     }
 
-    @ExceptionHandler(value = CmpServersConfigLoadingException.class)
-    public ResponseEntity<String> handle(CmpServersConfigLoadingException exception) {
-        LOGGER.error(exception.getMessage(), exception.getCause());
-        return new ResponseEntity<>(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
-    }
-
 }
@@ -18,9 +18,9 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.aaf.certservice.certification;
+package org.onap.aaf.certservice.api.advice;
 
-import com.google.gson.Gson;
+import org.onap.aaf.certservice.api.CertificationController;
 import org.onap.aaf.certservice.certification.exception.Cmpv2ClientAdapterException;
 import org.onap.aaf.certservice.certification.exception.Cmpv2ServerNotFoundException;
 import org.onap.aaf.certservice.certification.exception.CsrDecryptionException;
@@ -31,16 +31,16 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
 
-@ControllerAdvice
-public class CertificationExceptionController {
+@RestControllerAdvice(assignableTypes = CertificationController.class)
+public class CertificationExceptionAdvice {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(CertificationExceptionController.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(CertificationExceptionAdvice.class);
 
     @ExceptionHandler(value = CsrDecryptionException.class)
-    public ResponseEntity<String> handle(CsrDecryptionException exception) {
+    public ResponseEntity<ErrorResponseModel> handle(CsrDecryptionException exception) {
         LOGGER.error("Exception occurred during decoding certificate sign request:", exception);
         return getErrorResponseEntity(
                 "Wrong certificate signing request (CSR) format",
@@ -49,7 +49,7 @@ public class CertificationExceptionController {
     }
 
     @ExceptionHandler(value = KeyDecryptionException.class)
-    public ResponseEntity<String> handle(KeyDecryptionException exception) {
+    public ResponseEntity<ErrorResponseModel> handle(KeyDecryptionException exception) {
         LOGGER.error("Exception occurred during decoding key:", exception);
         return getErrorResponseEntity(
                 "Wrong key (PK) format",
@@ -58,7 +58,7 @@ public class CertificationExceptionController {
     }
 
     @ExceptionHandler(value = Cmpv2ServerNotFoundException.class)
-    public ResponseEntity<String> handle(Cmpv2ServerNotFoundException exception) {
+    public ResponseEntity<ErrorResponseModel> handle(Cmpv2ServerNotFoundException exception) {
         LOGGER.error("Exception occurred selecting CMPv2 server:", exception);
         return getErrorResponseEntity(
                 "Certification authority not found for given CAName",
@@ -66,8 +66,13 @@ public class CertificationExceptionController {
         );
     }
 
+    @ExceptionHandler(value = RuntimeException.class)
+    public ResponseEntity<ErrorResponseModel> handle(RuntimeException exception) throws CmpClientException {
+        throw new CmpClientException("Runtime exception occurred calling cmp client business logic", exception);
+    }
+
     @ExceptionHandler(value = CmpClientException.class)
-    public ResponseEntity<String> handle(CmpClientException exception) {
+    public ResponseEntity<ErrorResponseModel> handle(CmpClientException exception) {
         LOGGER.error("Exception occurred calling cmp client:", exception);
         return getErrorResponseEntity(
                 "Exception occurred during call to cmp client",
@@ -75,13 +80,8 @@ public class CertificationExceptionController {
         );
     }
 
-    @ExceptionHandler(value = RuntimeException.class)
-    public ResponseEntity<String> handle(RuntimeException exception) throws CmpClientException {
-        throw new CmpClientException("Runtime exception occurred calling cmp client business logic", exception);
-    }
-    
     @ExceptionHandler(value = Cmpv2ClientAdapterException.class)
-    public ResponseEntity<String> handle(Cmpv2ClientAdapterException exception) {
+    public ResponseEntity<ErrorResponseModel> handle(Cmpv2ClientAdapterException exception) {
         LOGGER.error("Exception occurred parsing cmp client response:", exception);
         return getErrorResponseEntity(
                 "Exception occurred parsing cmp client response",
@@ -89,10 +89,10 @@ public class CertificationExceptionController {
         );
     }
 
-    private ResponseEntity<String> getErrorResponseEntity(String errorMessage, HttpStatus status) {
+    private ResponseEntity<ErrorResponseModel> getErrorResponseEntity(String errorMessage, HttpStatus status) {
         ErrorResponseModel errorResponse = new ErrorResponseModel(errorMessage);
         return new ResponseEntity<>(
-                new Gson().toJson(errorResponse),
+               errorResponse,
                 status
         );
     }
diff --git a/certService/src/main/java/org/onap/aaf/certservice/api/advice/ReloadConfigExceptionAdvice.java b/certService/src/main/java/org/onap/aaf/certservice/api/advice/ReloadConfigExceptionAdvice.java
new file mode 100644 (file)
index 0000000..bf83ece
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.api.advice;
+
+import org.onap.aaf.certservice.api.ReloadConfigController;
+import org.onap.aaf.certservice.certification.configuration.CmpServersConfigLoadingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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 = ReloadConfigController.class)
+public class ReloadConfigExceptionAdvice {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ReloadConfigExceptionAdvice.class);
+
+    @ExceptionHandler(value = CmpServersConfigLoadingException.class)
+    public ResponseEntity<String> handle(CmpServersConfigLoadingException exception) {
+        LOGGER.error(exception.getMessage(), exception.getCause());
+        return new ResponseEntity<>(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+
+}
diff --git a/certService/src/main/java/org/onap/aaf/certservice/api/configuration/OpenApiConfig.java b/certService/src/main/java/org/onap/aaf/certservice/api/configuration/OpenApiConfig.java
new file mode 100644 (file)
index 0000000..1832704
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.api.configuration;
+
+import io.swagger.v3.oas.models.Components;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class OpenApiConfig {
+
+    @Bean
+    public OpenAPI customOpenAPI() {
+        return new OpenAPI()
+                .components(new Components())
+                .info(
+                        new Info()
+                                .title("CertService Documentation")
+                                .description("Certification service API documentation")
+                                .version("1.0.0")
+                );
+    }
+
+}
index 95d4cd6..35acb0c 100644 (file)
@@ -20,9 +20,7 @@
 
 package org.onap.aaf.certservice.certification.exception;
 
-import org.webjars.NotFoundException;
-
-public class Cmpv2ServerNotFoundException extends NotFoundException {
+public class Cmpv2ServerNotFoundException extends RuntimeException {
     public Cmpv2ServerNotFoundException(String message) {
         super(message);
     }
index aac9b96..9ccdd32 100644 (file)
@@ -1,13 +1,11 @@
 # Actuator configuration
-springdoc.show-actuator=true
-
 management.endpoints.enabled-by-default=true
 management.endpoint.configprops.enabled=true
 management.endpoints.web.exposure.include=health
 
-
 # Swagger configuration
+springdoc.show-actuator=true
 springdoc.swagger-ui.path=/docs
 
 # AAF CertService app specific configuration
-app.config.path=/etc/onap/aaf/certservice
\ No newline at end of file
+app.config.path=/etc/onap/aaf/certservice
index 802b2ea..8ebac68 100644 (file)
@@ -25,7 +25,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.when;
 
-import com.google.gson.Gson;
 import java.util.Arrays;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -75,14 +74,12 @@ class CertificationControllerTest {
                 .thenReturn(testCertificationModel);
 
         // When
-        ResponseEntity<String> testResponse =
+        ResponseEntity<CertificationModel> responseCertificationModel =
                 certificationController.signCertificate(TEST_CA_NAME, TEST_ENCODED_CSR, TEST_ENCODED_PK);
 
-        CertificationModel responseCertificationModel = new Gson().fromJson(testResponse.getBody(), CertificationModel.class);
-
         // Then
-        assertEquals(HttpStatus.OK, testResponse.getStatusCode());
-        assertThat(responseCertificationModel
+        assertEquals(HttpStatus.OK, responseCertificationModel.getStatusCode());
+        assertThat(responseCertificationModel.getBody()
         ).isEqualToComparingFieldByField(testCertificationModel);
 
     }
index 17db24b..5e0e355 100644 (file)
@@ -21,7 +21,6 @@
 package org.onap.aaf.certservice.api;
 
 import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.doThrow;
 
@@ -47,12 +46,12 @@ public class ReloadConfigControllerTest {
     public CmpServersConfig cmpServersConfig;
 
     @BeforeEach
-    public void setUp() {
+    void setUp() {
         this.reloadConfigController = new ReloadConfigController(cmpServersConfig);
     }
 
     @Test
-    public void shouldReturnStatusOkWhenSuccessfullyReloaded() throws CmpServersConfigLoadingException {
+    void shouldReturnStatusOkWhenSuccessfullyReloaded() throws CmpServersConfigLoadingException {
         // When
         ResponseEntity<String> response = reloadConfigController.reloadConfiguration();
 
@@ -61,7 +60,7 @@ public class ReloadConfigControllerTest {
     }
 
     @Test
-    public void shouldRethrowSameErrorWhenFailedToReload() throws CmpServersConfigLoadingException {
+    void shouldRethrowSameErrorWhenFailedToReload() throws CmpServersConfigLoadingException {
         // Given
         doThrow(new CmpServersConfigLoadingException(ERROR_MESSAGE)).when(cmpServersConfig).reloadConfiguration();
 
@@ -74,16 +73,5 @@ public class ReloadConfigControllerTest {
         Assertions.assertThat(exception.getMessage()).isEqualTo(ERROR_MESSAGE);
     }
 
-    @Test
-    void shouldReturnErrorStatusAndMessageWhenExceptionOccurred() {
-        // Given
-        CmpServersConfigLoadingException exception = new CmpServersConfigLoadingException(ERROR_MESSAGE);
-
-        // When
-        ResponseEntity<String> response = reloadConfigController.handle(exception);
 
-        // Then
-        assertEquals(ERROR_MESSAGE, response.getBody());
-        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
-    }
 }
@@ -18,7 +18,7 @@
  * ============LICENSE_END=========================================================
  */
 
-package org.onap.aaf.certservice.certification;
+package org.onap.aaf.certservice.api.advice;
 
 import com.google.gson.Gson;
 import org.junit.jupiter.api.BeforeEach;
@@ -35,14 +35,14 @@ import org.springframework.http.ResponseEntity;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-class CertificationExceptionControllerTest {
+class CertificationExceptionAdviceTest {
 
-    private CertificationExceptionController certificationExceptionController;
+    private CertificationExceptionAdvice certificationExceptionAdvice;
 
     @BeforeEach
     void setUp() {
-        certificationExceptionController =
-                new CertificationExceptionController();
+        certificationExceptionAdvice =
+                new CertificationExceptionAdvice();
     }
 
     @Test
@@ -52,13 +52,11 @@ class CertificationExceptionControllerTest {
         CsrDecryptionException csrDecryptionException = new CsrDecryptionException("test csr exception");
 
         // When
-        ResponseEntity<String> responseEntity = certificationExceptionController.handle(csrDecryptionException);
-
-        ErrorResponseModel response = new Gson().fromJson(responseEntity.getBody(), ErrorResponseModel.class);
+        ResponseEntity<ErrorResponseModel> response = certificationExceptionAdvice.handle(csrDecryptionException);
 
         // Then
-        assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode());
-        assertEquals(expectedMessage, response.getErrorMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        assertEquals(expectedMessage, response.getBody().getErrorMessage());
     }
 
     @Test
@@ -68,13 +66,11 @@ class CertificationExceptionControllerTest {
         KeyDecryptionException csrDecryptionException = new KeyDecryptionException("test pk exception");
 
         // When
-        ResponseEntity<String> responseEntity = certificationExceptionController.handle(csrDecryptionException);
-
-        ErrorResponseModel response = new Gson().fromJson(responseEntity.getBody(), ErrorResponseModel.class);
+        ResponseEntity<ErrorResponseModel> response = certificationExceptionAdvice.handle(csrDecryptionException);
 
         // Then
-        assertEquals(HttpStatus.BAD_REQUEST, responseEntity.getStatusCode());
-        assertEquals(expectedMessage, response.getErrorMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        assertEquals(expectedMessage, response.getBody().getErrorMessage());
     }
 
     @Test
@@ -84,13 +80,11 @@ class CertificationExceptionControllerTest {
         Cmpv2ServerNotFoundException csrDecryptionException = new Cmpv2ServerNotFoundException("test Ca exception");
 
         // When
-        ResponseEntity<String> responseEntity = certificationExceptionController.handle(csrDecryptionException);
-
-        ErrorResponseModel response = new Gson().fromJson(responseEntity.getBody(), ErrorResponseModel.class);
+        ResponseEntity<ErrorResponseModel> response = certificationExceptionAdvice.handle(csrDecryptionException);
 
         // Then
-        assertEquals(HttpStatus.NOT_FOUND, responseEntity.getStatusCode());
-        assertEquals(expectedMessage, response.getErrorMessage());
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        assertEquals(expectedMessage, response.getBody().getErrorMessage());
     }
 
     @Test
@@ -100,13 +94,11 @@ class CertificationExceptionControllerTest {
         CmpClientException cmpClientException = new CmpClientException("Calling CMPv2 client failed");
 
         // When
-        ResponseEntity<String> responseEntity = certificationExceptionController.handle(cmpClientException);
-
-        ErrorResponseModel response = new Gson().fromJson(responseEntity.getBody(), ErrorResponseModel.class);
+        ResponseEntity<ErrorResponseModel> response = certificationExceptionAdvice.handle(cmpClientException);
 
         // Then
-        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
-        assertEquals(expectedMessage, response.getErrorMessage());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertEquals(expectedMessage, response.getBody().getErrorMessage());
     }
 
     @Test
@@ -116,13 +108,11 @@ class CertificationExceptionControllerTest {
         Cmpv2ClientAdapterException cmpv2ClientAdapterException = new Cmpv2ClientAdapterException(new Throwable());
 
         // When
-        ResponseEntity<String> responseEntity = certificationExceptionController.handle(cmpv2ClientAdapterException);
-
-        ErrorResponseModel response = new Gson().fromJson(responseEntity.getBody(), ErrorResponseModel.class);
+        ResponseEntity<ErrorResponseModel> response = certificationExceptionAdvice.handle(cmpv2ClientAdapterException);
 
         // Then
-        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseEntity.getStatusCode());
-        assertEquals(expectedMessage, response.getErrorMessage());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+        assertEquals(expectedMessage, response.getBody().getErrorMessage());
     }
 
     @Test
@@ -134,7 +124,7 @@ class CertificationExceptionControllerTest {
         // When
         Exception exception = assertThrows(
                 CmpClientException.class, () ->
-                        certificationExceptionController.handle(runtimeException)
+                        certificationExceptionAdvice.handle(runtimeException)
         );
 
         // Then
diff --git a/certService/src/test/java/org/onap/aaf/certservice/api/advice/ReloadConfigExceptionAdviceTest.java b/certService/src/test/java/org/onap/aaf/certservice/api/advice/ReloadConfigExceptionAdviceTest.java
new file mode 100644 (file)
index 0000000..9abecec
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * ============LICENSE_START=======================================================
+ * PROJECT
+ * ================================================================================
+ * Copyright (C) 2020 Nokia. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aaf.certservice.api.advice;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.onap.aaf.certservice.certification.configuration.CmpServersConfigLoadingException;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ReloadConfigExceptionAdviceTest {
+
+    private static final String ERROR_MESSAGE = "Exception occurred during CMP Servers configuration loading";
+
+    private ReloadConfigExceptionAdvice reloadConfigExceptionAdvice;
+
+    @BeforeEach
+    void setUp() {
+        reloadConfigExceptionAdvice =
+                new ReloadConfigExceptionAdvice();
+    }
+
+    @Test
+    void shouldReturnErrorStatusAndMessageWhenExceptionOccurred() {
+        // Given
+        CmpServersConfigLoadingException exception = new CmpServersConfigLoadingException(ERROR_MESSAGE);
+
+        // When
+        ResponseEntity<String> response = reloadConfigExceptionAdvice.handle(exception);
+
+        // Then
+        assertEquals(ERROR_MESSAGE, response.getBody());
+        assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+    }
+
+}
diff --git a/pom.xml b/pom.xml
index 3eef61b..36904e9 100644 (file)
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,7 @@
         <stagingNexusPath>/content/repositories/staging/</stagingNexusPath>
         <sitePath>/content/sites/site/org/onap/aaf/cert-service/${project.artifactId}/${project.version}</sitePath>
         <java.version>11</java.version>
-        <springdoc-openapi-maven-plugin.apiDocsUrl>http://localhost:8080/v3/api-docs</springdoc-openapi-maven-plugin.apiDocsUrl>
+        <springdoc-openapi-maven-plugin.apiDocsUrl>http://localhost:8080/v3/api-docs.yaml</springdoc-openapi-maven-plugin.apiDocsUrl>
 
         <!-- Dependencies -->
         <assertj-core.version>3.15.0</assertj-core.version>
@@ -94,7 +94,7 @@
                     </executions>
                     <configuration>
                         <apiDocsUrl>${springdoc-openapi-maven-plugin.apiDocsUrl}</apiDocsUrl>
-                        <outputFileName>api-docs.json</outputFileName>
+                        <outputFileName>api-docs.yaml</outputFileName>
                         <outputDir>${project.build.directory}</outputDir>
                     </configuration>
                 </plugin>