From f5fda6421dad76d369774c59e89a448a6c4e8c07 Mon Sep 17 00:00:00 2001 From: Ram Krishna Verma Date: Thu, 10 Feb 2022 14:35:12 -0500 Subject: Add yaml support to pap api's 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 --- .../org/onap/policy/pap/main/config/WebConfig.java | 43 ++++++++ .../config/converter/YamlHttpMessageConverter.java | 109 +++++++++++++++++++++ .../policy/pap/main/rest/CommonPapRestServer.java | 29 ++++-- .../main/rest/e2e/PdpGroupCreateOrUpdateTest.java | 20 +++- main/src/test/resources/e2e/createGroups.yaml | 31 ++++++ 5 files changed, 221 insertions(+), 11 deletions(-) create mode 100644 main/src/main/java/org/onap/policy/pap/main/config/WebConfig.java create mode 100644 main/src/main/java/org/onap/policy/pap/main/config/converter/YamlHttpMessageConverter.java create mode 100644 main/src/test/resources/e2e/createGroups.yaml 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 index 00000000..751ca611 --- /dev/null +++ b/main/src/main/java/org/onap/policy/pap/main/config/WebConfig.java @@ -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> 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 index 00000000..5678b837 --- /dev/null +++ b/main/src/main/java/org/onap/policy/pap/main/config/converter/YamlHttpMessageConverter.java @@ -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 { + + 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 diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java b/main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java index e42cfd21..c2d9f038 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/CommonPapRestServer.java @@ -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); } } diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/e2e/PdpGroupCreateOrUpdateTest.java b/main/src/test/java/org/onap/policy/pap/main/rest/e2e/PdpGroupCreateOrUpdateTest.java index 3f763863..650dd41a 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/e2e/PdpGroupCreateOrUpdateTest.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/e2e/PdpGroupCreateOrUpdateTest.java @@ -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 entity = Entity.entity(groups, MediaType.APPLICATION_JSON); + PdpGroups groups = (mediaType.equalsIgnoreCase(MediaType.APPLICATION_JSON) + ? loadJsonFile(fileName, PdpGroups.class) + : loadYamlFile(fileName, PdpGroups.class)); + Entity 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 index 00000000..fc98e971 --- /dev/null +++ b/main/src/test/resources/e2e/createGroups.yaml @@ -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: [] -- cgit 1.2.3-korg