Add yaml support to pap api's 66/127066/1
authorRam Krishna Verma <ram_krishna.verma@bell.ca>
Thu, 10 Feb 2022 19:35:12 +0000 (14:35 -0500)
committerRam Krishna Verma <ram_krishna.verma@bell.ca>
Thu, 10 Feb 2022 19:35:17 +0000 (14:35 -0500)
Adding yaml support to pap rest api's.
Along with related unit tests.

Issue-ID: POLICY-3864
Change-Id: I43bdbbd4151bcae5dcf4752a9385b115efa947d3
Signed-off-by: Ram Krishna Verma <ram_krishna.verma@bell.ca>
main/src/main/java/org/onap/policy/pap/main/config/WebConfig.java [new file with mode: 0644]
main/src/main/java/org/onap/policy/pap/main/config/converter/YamlHttpMessageConverter.java [new file with mode: 0644]
main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java
main/src/test/java/org/onap/policy/pap/main/rest/e2e/PdpGroupCreateOrUpdateTest.java
main/src/test/resources/e2e/createGroups.yaml [new file with mode: 0644]

diff --git a/main/src/main/java/org/onap/policy/pap/main/config/WebConfig.java b/main/src/main/java/org/onap/policy/pap/main/config/WebConfig.java
new file mode 100644 (file)
index 0000000..751ca61
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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.policy.pap.main.config;
+
+import java.util.Arrays;
+import java.util.List;
+import org.onap.policy.pap.main.config.converter.YamlHttpMessageConverter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * Register custom converters to Spring configuration.
+ */
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+
+    @Override
+    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
+        YamlHttpMessageConverter yamlConverter = new YamlHttpMessageConverter();
+        yamlConverter.setSupportedMediaTypes(Arrays.asList(MediaType.parseMediaType("application/yaml")));
+        converters.add(yamlConverter);
+    }
+}
\ No newline at end of file
diff --git a/main/src/main/java/org/onap/policy/pap/main/config/converter/YamlHttpMessageConverter.java b/main/src/main/java/org/onap/policy/pap/main/config/converter/YamlHttpMessageConverter.java
new file mode 100644 (file)
index 0000000..5678b83
--- /dev/null
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 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.policy.pap.main.config.converter;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import org.onap.policy.common.utils.coder.YamlJsonTranslator;
+import org.springframework.core.GenericTypeResolver;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.AbstractGenericHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+import org.springframework.lang.Nullable;
+
+/**
+ * Custom converter to marshal/unmarshall data structured with YAML media type.
+ */
+public class YamlHttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
+
+    public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
+
+    private static final YamlJsonTranslator TRANSLATOR = new YamlJsonTranslator();
+
+    public YamlHttpMessageConverter() {
+        super(new MediaType("application", "yaml"));
+        setDefaultCharset(DEFAULT_CHARSET);
+    }
+
+    @Override
+    public final Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
+        throws IOException {
+        return readResolved(GenericTypeResolver.resolveType(type, contextClass), inputMessage);
+    }
+
+    @Override
+    protected final Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException {
+        return readResolved(clazz, inputMessage);
+    }
+
+    private Object readInternal(Type resolvedType, Reader reader) {
+        Class<?> clazz = (Class<?>) resolvedType;
+        return TRANSLATOR.fromYaml(reader, clazz);
+    }
+
+    @Override
+    protected final void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
+        throws IOException {
+        var writer = getWriter(outputMessage);
+        try {
+            writeInternal(object, type, writer);
+        } catch (Exception ex) {
+            throw new HttpMessageNotWritableException("Could not write YAML: " + ex.getMessage(), ex);
+        }
+        writer.flush();
+    }
+
+    private void writeInternal(Object object, @Nullable Type type, Writer writer) {
+        TRANSLATOR.toYaml(writer, object);
+    }
+
+    private Object readResolved(Type resolvedType, HttpInputMessage inputMessage) throws IOException {
+        var reader = getReader(inputMessage);
+        try {
+            return readInternal(resolvedType, reader);
+        } catch (Exception ex) {
+            throw new HttpMessageNotReadableException("Could not read YAML: " + ex.getMessage(), ex, inputMessage);
+        }
+    }
+
+    private static Reader getReader(HttpInputMessage inputMessage) throws IOException {
+        return new InputStreamReader(inputMessage.getBody(), getCharset(inputMessage.getHeaders()));
+    }
+
+    private static Writer getWriter(HttpOutputMessage outputMessage) throws IOException {
+        return new OutputStreamWriter(outputMessage.getBody(), getCharset(outputMessage.getHeaders()));
+    }
+
+    private static Charset getCharset(HttpHeaders headers) {
+        Charset charset = (headers.getContentType() == null ? null : headers.getContentType().getCharset());
+        return (charset != null ? charset : DEFAULT_CHARSET);
+    }
+}
\ No newline at end of file
index e42cfd2..c2d9f03 100644 (file)
@@ -46,6 +46,7 @@ import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
+import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
 import org.onap.policy.common.gson.GsonMessageBodyHandler;
 import org.onap.policy.common.utils.network.NetworkUtil;
 import org.onap.policy.common.utils.security.SelfSignedKeyStore;
@@ -143,7 +144,8 @@ public abstract class CommonPapRestServer {
      * @throws Exception if an error occurs
      */
     protected void testSwagger(final String endpoint) throws Exception {
-        final Invocation.Builder invocationBuilder = sendFqeRequest(httpsPrefix + "v2/api-docs", true);
+        final Invocation.Builder invocationBuilder =
+                        sendFqeRequest(httpsPrefix + "v2/api-docs", true, MediaType.APPLICATION_JSON);
         final String resp = invocationBuilder.get(String.class);
         assertTrue(resp.contains(ENDPOINT_PREFIX + endpoint));
     }
@@ -198,7 +200,19 @@ public abstract class CommonPapRestServer {
      * @throws Exception if an error occurs
      */
     protected Invocation.Builder sendRequest(final String endpoint) throws Exception {
-        return sendFqeRequest(httpsPrefix + ENDPOINT_PREFIX + endpoint, true);
+        return sendFqeRequest(httpsPrefix + ENDPOINT_PREFIX + endpoint, true, MediaType.APPLICATION_JSON);
+    }
+
+    /**
+     * Sends a request to an endpoint.
+     *
+     * @param endpoint the target endpoint
+     * @param mediaType the media type for the request
+     * @return a request builder
+     * @throws Exception if an error occurs
+     */
+    protected Invocation.Builder sendRequest(final String endpoint, String mediaType) throws Exception {
+        return sendFqeRequest(httpsPrefix + ENDPOINT_PREFIX + endpoint, true, mediaType);
     }
 
     /**
@@ -209,7 +223,7 @@ public abstract class CommonPapRestServer {
      * @throws Exception if an error occurs
      */
     protected Invocation.Builder sendNoAuthRequest(final String endpoint) throws Exception {
-        return sendFqeRequest(httpsPrefix + ENDPOINT_PREFIX + endpoint, false);
+        return sendFqeRequest(httpsPrefix + ENDPOINT_PREFIX + endpoint, false, MediaType.APPLICATION_JSON);
     }
 
     /**
@@ -220,8 +234,8 @@ public abstract class CommonPapRestServer {
      * @return a request builder
      * @throws Exception if an error occurs
      */
-    protected Invocation.Builder sendFqeRequest(final String fullyQualifiedEndpoint, boolean includeAuth)
-                    throws Exception {
+    protected Invocation.Builder sendFqeRequest(final String fullyQualifiedEndpoint, boolean includeAuth,
+                    String mediaType) throws Exception {
         final SSLContext sc = SSLContext.getInstance("TLSv1.2");
         sc.init(null, NetworkUtil.getAlwaysTrustingManager(), new SecureRandom());
         final ClientBuilder clientBuilder =
@@ -229,7 +243,8 @@ public abstract class CommonPapRestServer {
         final Client client = clientBuilder.build();
 
         client.property(ClientProperties.METAINF_SERVICES_LOOKUP_DISABLE, "true");
-        client.register(GsonMessageBodyHandler.class);
+        client.register((mediaType.equalsIgnoreCase(MediaType.APPLICATION_JSON) ? GsonMessageBodyHandler.class
+                        : YamlMessageBodyHandler.class));
 
         if (includeAuth) {
             final HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("policyadmin", "zb!XztG34");
@@ -238,6 +253,6 @@ public abstract class CommonPapRestServer {
 
         final WebTarget webTarget = client.target(fullyQualifiedEndpoint);
 
-        return webTarget.request(MediaType.APPLICATION_JSON);
+        return webTarget.request(mediaType);
     }
 }
index 3f76386..650dd41 100644 (file)
@@ -77,18 +77,30 @@ public class PdpGroupCreateOrUpdateTest extends End2EndBase {
     }
 
     @Test
-    public void testCreateGroups() throws Exception {
+    public void testCreateGroupsJson() throws Exception {
 
+        createPdpGroups("createGroups.json", MediaType.APPLICATION_JSON);
+    }
+
+    @Test
+    public void testCreateGroupsYaml() throws Exception {
+
+        createPdpGroups("createGroups.yaml", "application/yaml");
+    }
+
+    private void createPdpGroups(String fileName, String mediaType) throws Exception, InterruptedException {
         context.addPdp("pdpAA_1", CREATE_SUBGROUP);
         context.addPdp("pdpAA_2", CREATE_SUBGROUP);
         context.addPdp("pdpAB_1", "pdpTypeB");
 
         context.startThreads();
 
-        Invocation.Builder invocationBuilder = sendRequest(CREATEORUPDATE_GROUPS_ENDPOINT);
+        Invocation.Builder invocationBuilder = sendRequest(CREATEORUPDATE_GROUPS_ENDPOINT, mediaType);
 
-        PdpGroups groups = loadJsonFile("createGroups.json", PdpGroups.class);
-        Entity<PdpGroups> entity = Entity.entity(groups, MediaType.APPLICATION_JSON);
+        PdpGroups groups = (mediaType.equalsIgnoreCase(MediaType.APPLICATION_JSON)
+                        ? loadJsonFile(fileName, PdpGroups.class)
+                        : loadYamlFile(fileName, PdpGroups.class));
+        Entity<PdpGroups> entity = Entity.entity(groups, mediaType);
         Response rawresp = invocationBuilder.post(entity);
         PdpGroupUpdateResponse resp = rawresp.readEntity(PdpGroupUpdateResponse.class);
         assertEquals(Response.Status.OK.getStatusCode(), rawresp.getStatus());
diff --git a/main/src/test/resources/e2e/createGroups.yaml b/main/src/test/resources/e2e/createGroups.yaml
new file mode 100644 (file)
index 0000000..fc98e97
--- /dev/null
@@ -0,0 +1,31 @@
+groups:
+  - name: createGroups
+    pdpGroupState: PASSIVE
+    properties:
+      hello: world
+    pdpSubgroups:
+      - pdpType: pdpTypeA
+        desiredInstanceCount: 2
+        properties: {}
+        pdpInstances:
+          - instanceId: pdpAA_1
+            pdpState: ACTIVE
+            healthy: HEALTHY
+          - instanceId: pdpAA_2
+            pdpState: ACTIVE
+            healthy: HEALTHY
+        supportedPolicyTypes:
+          - name: onap.policies.monitoring.cdap.tca.hi.lo.app
+            version: 1.0.0
+        policies: []
+      - pdpType: pdpTypeB
+        desiredInstanceCount: 1
+        properties: {}
+        pdpInstances:
+          - instanceId: pdpAB_1
+            pdpState: ACTIVE
+            healthy: HEALTHY
+        supportedPolicyTypes:
+          - name: onap.policies.monitoring.cdap.tca.hi.lo.app
+            version: 1.0.0
+        policies: []