From 4bb9df9cdad3108cbaf360b2217fae61d7cff9c7 Mon Sep 17 00:00:00 2001 From: matejpopda <20053580+matejpopda@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:47:47 +0200 Subject: [PATCH] chore: Improve conformance validation (#3023) * feat(gateway): Improvement of existing conformance testing Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * refactor(gateway): Added missing comments Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Removed a print statement. Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * feat(gateway): Better response from the conformance endpoint Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * feat(gateway): Better response from the conformance endpoint Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * refactor(gateway): Got rid of duplicate discovery client calls Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * docs(gateway): Changed text in gateway-log-messages.yml based on comments on the PR Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * feat(gateway): Addressed the rest of comments on the PR Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * feat(gateway): Addressed the new comments on the PR Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Fixed a method not following a naming convention Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Fixed reason why build was failing Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Addressed Pablo's comments on PR Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Updated an integration test for conformance Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Updated integration tests Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * feat(gateway): Improvement of existing conformance testing Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * refactor(gateway): Added missing comments Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Removed a print statement. Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * feat(gateway): Better response from the conformance endpoint Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * refactor(gateway): Got rid of duplicate discovery client calls Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * docs(gateway): Changed text in gateway-log-messages.yml based on comments on the PR Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * feat(gateway): Addressed the rest of comments on the PR Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * feat(gateway): Addressed the new comments on the PR Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Fixed a method not following a naming convention Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Fixed reason why build was failing Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Addressed Pablo's comments on PR Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Updated an integration test for conformance Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Updated integration tests Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Fixed code smells Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Fixed 1 bug from sonar Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Disabled caching on a github action as a test Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Enabled cache again + removed some tests to see if they are the reason why the build fails Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Enabled cache again + removed some tests to see if they are the reason why the build fails Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Enabled cache again + removed some tests to see if they are the reason why the build fails Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Disabled cache again, changed unit tests :gateway-service:test shouldn't fail anymore Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Disabled cache again, changed unit tests :gateway-service:test shouldn't fail anymore Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Modified unit tests Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Modified unit tests Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Modified unit tests Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Fixed unit tests Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Enabled build cache Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Disabled build cache Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Enabled build cache + should create an artifact after failing Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> * fix(gateway): Disabled the creation of artifact from previous commit Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> --------- Signed-off-by: matejpopda <20053580+matejpopda@users.noreply.github.com> Co-authored-by: ShobhaJayanna <36433611+Shobhajayanna@users.noreply.github.com> Co-authored-by: Andrea Tabone <39694626+taban03@users.noreply.github.com> --- .../ConformanceProblemsContainer.java | 141 +++++++++ .../conformance/ValidateAPIController.java | 197 ++++++++++--- .../VerificationOnboardService.java | 50 ++-- .../main/resources/gateway-log-messages.yml | 16 +- .../ConformanceProblemsContainerTest.java | 128 ++++++++ .../ValidateAPIControllerTest.java | 275 +++++++++++++----- .../VerificationOnboardServiceTest.java | 78 ++--- .../functional/gateway/ValidateAPITest.java | 82 ++++-- 8 files changed, 739 insertions(+), 228 deletions(-) create mode 100644 gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/ConformanceProblemsContainer.java create mode 100644 gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/ConformanceProblemsContainerTest.java diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/ConformanceProblemsContainer.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/ConformanceProblemsContainer.java new file mode 100644 index 0000000000..9f243f475f --- /dev/null +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/ConformanceProblemsContainer.java @@ -0,0 +1,141 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.gateway.conformance; + + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang.text.StrSubstitutor; +import org.zowe.apiml.message.api.ApiMessage; + +import java.util.*; + + +/** + * Java class that is used to keep track of found conformance issues + */ +public class ConformanceProblemsContainer extends HashMap> { + + + private final String serviceId; + private static final String RESPONSE_MESSAGE_TEMPLATE = "{\n" + "\"messageAction\": \"${messageAction}\",\n" + "\"messageContent\": {\n" + " \"The service ${serviceId} is not conformant\": \n" + " ${messageContent}\n" + "},\n" + "\"messageKey\": \"${messageKey}\",\n" + "\"messageNumber\": \"${messageNumber}\",\n" + "\"messageReason\": \"${messageReason}\",\n" + "\"messageType\": \"${messageType}\"\n" + "}"; + + ConformanceProblemsContainer(String serviceId) { + super(); + this.serviceId = serviceId; + } + + @Override + public ArrayList put(String key, ArrayList values) { + if (values == null) { + return new ArrayList<>(); + } + if (this.get(key) == null || this.get(key).isEmpty()) { + return super.put(key, new ArrayList<>(values)); + } + for (String value : values) { + if (this.get(key).contains(value)) { + this.get(key).add(value); + } + } + return new ArrayList<>(); + } + + public List put(String key, String value) { + if (value == null || value.equals("")) { + return new ArrayList<>(); + } + return put(key, new ArrayList<>(Collections.singleton(value))); + } + + public List put(String key, List value) { + return put(key, new ArrayList<>(value)); + } + + @Override + public int size() { + int result = 0; + for (ArrayList value : this.values()) { + if (value == null) { + continue; + } + result += value.size(); + } + return result; + } + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + + @Override + public String toString() { + StringBuilder result = new StringBuilder(); + result.append("{"); + boolean firstLoop = true; + + ArrayList sortedKeySet = new ArrayList<>(this.keySet()); + sortedKeySet.remove(null); // since it used to be a set this removes all nulls + sortedKeySet.sort(null); + for (String key : sortedKeySet) { + + if (this.get(key) == null || this.get(key).isEmpty()) { + continue; + } + + if (!firstLoop) { + result.append(","); + } + + result.append("\"").append(key).append("\""); + result.append(":["); + + boolean firstInnerLoop = true; + for (String i : get(key)) { + if (!firstInnerLoop) { + result.append(","); + } + try { + result.append(new ObjectMapper().writeValueAsString(i)); + } catch (JsonProcessingException e) { + continue; + } + firstInnerLoop = false; + } + result.append("]"); + firstLoop = false; + } + result.append("}"); + return result.toString(); + } + + + public String createBadRequestAPIResponseBody(String key, ApiMessage correspondingAPIMessage) { + Map valuesMap = new HashMap<>(); + + valuesMap.put("messageKey", key); + valuesMap.put("messageContent", this.toString()); + valuesMap.put("serviceId", serviceId); + valuesMap.put("messageReason", correspondingAPIMessage.getMessageReason()); + valuesMap.put("messageNumber", correspondingAPIMessage.getMessageNumber()); + valuesMap.put("messageType", correspondingAPIMessage.getMessageType().toString()); + valuesMap.put("messageAction", correspondingAPIMessage.getMessageAction()); + + return new StrSubstitutor(valuesMap).replace(RESPONSE_MESSAGE_TEMPLATE); + } +} diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/ValidateAPIController.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/ValidateAPIController.java index ce60d0a217..0c10ec91a3 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/ValidateAPIController.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/ValidateAPIController.java @@ -10,96 +10,197 @@ package org.zowe.apiml.gateway.conformance; -import java.util.regex.Pattern; -import java.util.function.Predicate; -import java.util.regex.Matcher; - +import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; +import lombok.RequiredArgsConstructor; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.*; +import org.zowe.apiml.message.core.Message; import org.zowe.apiml.message.core.MessageService; -import io.swagger.v3.oas.annotations.parameters.RequestBody; -import lombok.RequiredArgsConstructor; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** - * Controller offered methods for validating serviceID under conformance criteria, it offer methods to - * check the validation of the given serviceID + * Controller offers method to check the validation of the given serviceID under conformance criteria */ -@Controller +@RestController @RequiredArgsConstructor public class ValidateAPIController { - private static final Pattern symbolPattern = Pattern.compile("[^a-z0-9]"); - private static final Predicate isTooLong = serviceId -> (serviceId).length() <= 64; + private static final int MAXIMUM_SERVICE_ID_LENGTH = 64; + private static final String INVALID_SERVICE_ID_REGEX_PATTERN = "[^a-z0-9]"; + + + private static final String WRONG_SERVICE_ID_KEY = "org.zowe.apiml.gateway.verifier.wrongServiceId"; + private static final String NO_METADATA_KEY = "org.zowe.apiml.gateway.verifier.noMetadata"; + private static final String NON_CONFORMANT_KEY = "org.zowe.apiml.gateway.verifier.nonConformant"; + + + private static final String REGISTRATION_PROBLEMS = "Registration problems"; + private static final String METADATA_PROBLEMS = "Metadata problems"; + private static final String CONFORMANCE_PROBLEMS = "Conformance problems"; + + private final MessageService messageService; private final VerificationOnboardService verificationOnboardService; - private String invalidKey = "org.zowe.apiml.gateway.verifier.wrongServiceId"; + + private final DiscoveryClient discoveryClient; + /** - * Accept serviceID and return the JSON file with appropirate message to show if it is valid - * - * @param serviceID accepted serviceID to check validation - * @return return the JSON file message of whether the serviceID is valid + * Accepts serviceID and checks conformance criteria * + * @param serviceId accepted serviceID to check for conformance + * @return 200 if service is conformant, 400 + JSON explanation if not */ - @PostMapping( - value = "/validate", + @GetMapping( + value = "/gateway/conformance/{serviceId}", produces = MediaType.APPLICATION_JSON_VALUE ) - public ResponseEntity checkValidate(@RequestBody String serviceID) { + @HystrixCommand + public ResponseEntity checkConformance(@PathVariable String serviceId) { + ConformanceProblemsContainer foundNonConformanceIssues = new ConformanceProblemsContainer(serviceId); + foundNonConformanceIssues.put(CONFORMANCE_PROBLEMS, validateServiceIdFormat(serviceId)); + + if (foundNonConformanceIssues.size() != 0) { + return generateBadRequestResponseEntity(NON_CONFORMANT_KEY, foundNonConformanceIssues); + } - String message = validator(serviceID); + foundNonConformanceIssues.put(REGISTRATION_PROBLEMS, checkOnboarding(serviceId)); + if (foundNonConformanceIssues.size() != 0) { // cant continue if a service isn't registered + return generateBadRequestResponseEntity(WRONG_SERVICE_ID_KEY, foundNonConformanceIssues); + } - if (!message.isEmpty()) { - return new ResponseEntity<>(messageService.createMessage(invalidKey, message).mapToApiMessage(), HttpStatus.BAD_REQUEST); + List serviceInstances = discoveryClient.getInstances(serviceId); + foundNonConformanceIssues.put(REGISTRATION_PROBLEMS, instanceCheck(serviceInstances)); + if (foundNonConformanceIssues.size() != 0) { // cant continue if we cant retrieve an instance + return generateBadRequestResponseEntity(WRONG_SERVICE_ID_KEY, foundNonConformanceIssues); } - return new ResponseEntity<>(HttpStatus.OK); + ServiceInstance serviceInstance = serviceInstances.get(0); + Map metadata = getMetadata(serviceInstance); + + foundNonConformanceIssues.put(METADATA_PROBLEMS, metaDataCheck(metadata)); + if (foundNonConformanceIssues.size() != 0) { // cant continue without metadata + return generateBadRequestResponseEntity(NO_METADATA_KEY, foundNonConformanceIssues); + } + return new ResponseEntity<>("{\"message\":\"Service " + serviceId + " fulfills all checked conformance criteria\"}", HttpStatus.OK); + } + + + /** + * Mapping so the old endpoint keeps working. + * + * @param serviceId serviceId to check for conformance + * @return 200 if service is conformant, 400 + JSON explanation if not + */ + @PostMapping(value = "/validate", produces = MediaType.APPLICATION_JSON_VALUE) + @HystrixCommand + public ResponseEntity checkValidateLegacy(@RequestBody String serviceId) { + if (serviceId.startsWith("serviceID")) { + serviceId = serviceId.replace("serviceID=", ""); + } + return checkConformance(serviceId); } + /** - * Accept serviceID and check if it contains only lower case characters without symbols, return true if it meets the criteria - * otherwise return false + * Creates a response when a conformance criteria is failed. * - * @param serviceID accept serviceID to check - * @return return boolean variable False if it only contains lower case characters without symbols + * @param foundNonConformanceIssues list of found issues + * @return Response that this controller returns */ - private boolean checkValidPatternAPI(String serviceID) { + private ResponseEntity generateBadRequestResponseEntity(String key, ConformanceProblemsContainer foundNonConformanceIssues) { + Message message = messageService.createMessage(key, "ThisWillBeRemoved"); + return new ResponseEntity<>(foundNonConformanceIssues.createBadRequestAPIResponseBody(key, message.mapToApiMessage()), HttpStatus.BAD_REQUEST); + } - Matcher findSymbol = symbolPattern.matcher(serviceID); - return findSymbol.find(); + /** + * Accepts serviceId and checks if the service is onboarded to the API Mediation Layer + * If it's not than it doesn't fulfill Item 1 of conformance criteria + * + * @param serviceId serviceId to check + * @return string describing the issue or an empty string + */ + public String checkOnboarding(String serviceId) { + if (!verificationOnboardService.checkOnboarding(serviceId)) { + return "The service is not registered"; + } + return ""; + } /** - * Accept serviceId and check conformant criteria and return invalid message if it does not meet - * the requirement + * Retrieves metadata * - * @param serviceId accept serviceID to check - * @return return invalid message if it is wrong otherwise an empty string + * @param serviceInstance serviceInstance from which to retrieve the metadata. + * @return Metadata of the instance */ - private String validator(String serviceId) { + private Map getMetadata(ServiceInstance serviceInstance) { + return serviceInstance.getMetadata(); + } - if (!isTooLong.test(serviceId)) { - return "The serviceid is longer than 64 characters"; + + /** + * Checks if metadata was retrieved. + * + * @param metadata which to test + * @return string describing the issue or an empty string + */ + public String metaDataCheck(Map metadata) { + if (metadata != null && !metadata.isEmpty()) { + return ""; } - else if (checkValidPatternAPI(serviceId)) { - return "The serviceid contains symbols or upper case letters"; + return "Cannot Retrieve MetaData"; + } + + /** + * Checks if a single instance can be retrieved. + * + * @param serviceInstances to check + * @return string describing the issue or an empty string + */ + public String instanceCheck(List serviceInstances) { + if (serviceInstances.isEmpty()) { + return "Cannot retrieve metadata - no active instance of the service"; } - else if (!verificationOnboardService.checkOnboarding(serviceId)) { - return "The service is not registered"; + return ""; + } + + + /** + * Accept serviceId and checks if it is Zowe conformant according to the specification, + * Item 5 from the conformance criteria list. That means that the serviceId contains only lower case + * characters without symbols and is shorter than 64 characters + * + * @param serviceId to check + * @return list of found issues, empty when conformant + */ + public List validateServiceIdFormat(String serviceId) { + ArrayList result = new ArrayList<>(); + if (serviceId.length() > MAXIMUM_SERVICE_ID_LENGTH) { + result.add("The serviceId is longer than 64 characters"); } - else if (verificationOnboardService.retrieveMetaData(serviceId).isEmpty()) { - return "Cannot Retrieve MetaData"; + // Check for invalid characters + final Pattern symbolPattern = Pattern.compile(INVALID_SERVICE_ID_REGEX_PATTERN); + Matcher findSymbol = symbolPattern.matcher(serviceId); + if (findSymbol.find()) { + result.add("The serviceId contains symbols or upper case letters"); } - return ""; + return result; } - + + } diff --git a/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/VerificationOnboardService.java b/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/VerificationOnboardService.java index 434aff9b2e..d6e42d9360 100644 --- a/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/VerificationOnboardService.java +++ b/gateway-service/src/main/java/org/zowe/apiml/gateway/conformance/VerificationOnboardService.java @@ -10,18 +10,16 @@ package org.zowe.apiml.gateway.conformance; -import java.util.List; -import java.util.Map; - -import org.springframework.cloud.client.ServiceInstance; +import lombok.RequiredArgsConstructor; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.stereotype.Service; -import lombok.RequiredArgsConstructor; +import java.util.List; +import java.util.Map; /** - * service class offered methods for checking onboarding information and also retrieve metadata from - * provided serviceid. + * Service class that offers methods for checking onboarding information and also checks availability metadata from + * a provided serviceId. */ @Service @RequiredArgsConstructor @@ -30,37 +28,33 @@ public class VerificationOnboardService { private final DiscoveryClient discoveryClient; /** - * Accept serviceId and check if the service is onboarded to the API Mediation Layer - * @param serviceId accept serviceId to check - * @return return true if the service is known by Eureka otherwise false. + * Accepts serviceId and checks if the service is onboarded to the API Mediation Layer + * + * @param serviceId serviceId to check + * @return true if the service is known by Eureka otherwise false. */ public boolean checkOnboarding(String serviceId) { - + List serviceLists = discoveryClient.getServices(); + return serviceLists.contains(serviceId); } + /** - * Accept serviceId and check if the - * @param serviceId accept serviceId to check - * @return return swagger Url if the metadata can be retrieved, otherwise an empty string. + * Accepts metadata and retrieves the Swagger url if it existsd + * + * @param metadata to grab swagger from + * @return SwaggerUrl when able, empty string otherwise */ - public String retrieveMetaData(String serviceId) { - - List serviceInstances = discoveryClient.getInstances(serviceId); - - if (!serviceInstances.isEmpty()) { - ServiceInstance serviceInstance = serviceInstances.get(0); - Map metadata = serviceInstance.getMetadata(); - if (metadata != null && !metadata.isEmpty()) { - String swaggerUrl = metadata.get("apiml.apiInfo.api-v2.swaggerUrl"); - if (swaggerUrl != null) { - return swaggerUrl; - } - } + public String retrieveSwagger(Map metadata) { + String swaggerUrl = metadata.get("apiml.apiInfo.api-v2.swaggerUrl"); + if (swaggerUrl != null) { + return swaggerUrl; } return ""; } -} \ No newline at end of file + +} diff --git a/gateway-service/src/main/resources/gateway-log-messages.yml b/gateway-service/src/main/resources/gateway-log-messages.yml index c7c8b44af8..967798bf73 100644 --- a/gateway-service/src/main/resources/gateway-log-messages.yml +++ b/gateway-service/src/main/resources/gateway-log-messages.yml @@ -159,9 +159,23 @@ messages: number: ZWEAG717 type: ERROR text: "The service id provided is invalid: '%s'" - reason: "The provided id is not valid under conformance criteria." + reason: "The provided id is not valid under the conformance criteria." action: "Verify the conformance criteria, provide valid service id." + - key: org.zowe.apiml.gateway.verifier.noMetadata + number: ZWEAG718 + type: ERROR + text: "Cannot retrieve metadata: '%s'" + reason: "Metadata aren't accessible" + action: "Verify that the metadata are accessible and not empty" + + - key: org.zowe.apiml.gateway.verifier.nonConformant + number: ZWEAG719 + type: INFO + text: "The service is not conformant: %s" + reason: "The provided service does not satisfy the conformance criteria and is therefore not valid." + action: "Verify the conformance criteria." + # Legacy messages - key: org.zowe.apiml.security.generic diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/ConformanceProblemsContainerTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/ConformanceProblemsContainerTest.java new file mode 100644 index 0000000000..0a2f4de425 --- /dev/null +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/ConformanceProblemsContainerTest.java @@ -0,0 +1,128 @@ +/* + * This program and the accompanying materials are made available under the terms of the + * Eclipse Public License v2.0 which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-v20.html + * + * SPDX-License-Identifier: EPL-2.0 + * + * Copyright Contributors to the Zowe Project. + */ + +package org.zowe.apiml.gateway.conformance; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.ArrayList; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ConformanceProblemsContainerTest { + + ConformanceProblemsContainer container; + + @Nested + class GivenInputs { + + @BeforeEach + void setup() { + container = new ConformanceProblemsContainer("dummy"); + } + + @ParameterizedTest + @ValueSource(ints = {1, 5, 0}) + void whenInserting_thenCorrectSize(int size) { + ArrayList testList = new ArrayList<>(); + for (int i = 0; i < size; i++) { + testList.add("testString"); + } + container.put("test", testList); + + assertEquals(container.size(), testList.size()); + } + + @Test + void whenInserting_thenCanRetrieve() { + ArrayList testList = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + testList.add("testString"); + } + container.put("test", testList); + assertEquals(container.size(), testList.size()); + assertTrue(container.get("test").contains("testString")); + } + + @ParameterizedTest + @ValueSource(ints = {10, 5, 0}) + void whenInsertingToSameKey_thenCorrectSize(int size) { + ArrayList testList = new ArrayList<>(Collections.singleton("TestString")); + for (int i = 0; i < size; i++) { + container.put("test", testList); + } + assertEquals(container.size(), size); + } + + @ParameterizedTest + @ValueSource(ints = {10, 5, 0}) + void whenInsertingToMultipleKeys_thenCorrectSize(int size) { + ArrayList testList = new ArrayList<>(Collections.singleton("TestString")); + ArrayList testList2 = new ArrayList<>(Collections.singleton("TestString")); + for (int i = 0; i < size; i++) { + container.put("test", testList); + container.put("test2", testList2); + } + assertEquals(container.size(), 2 * size); + } + + @Test + void whenAddingNullValue_thenCorrectSize() { + ArrayList test = null; + container.put("test", test); + assertEquals(0, container.size()); + } + + @Test + void whenAddingNullValue2_thenCorrectSize() { + String test = null; + container.put("test", test); + assertEquals(0, container.size()); + + } + } + + @Nested + class GivenAContainer { + + @BeforeEach + void setup() { + container = new ConformanceProblemsContainer("Dummy"); + } + + @Test + void whenOneItemIn_thenToString() { + ArrayList result = new ArrayList<>(); + result.add("One"); + result.add("Two"); + container.put("Key", result); + assertEquals("{\"Key\":[\"One\",\"Two\"]}", container.toString()); + } + + @Test + void whenTwoItemsIn_thenToString() { + ArrayList first = new ArrayList<>(); + first.add("One"); + first.add("Two"); + ArrayList second = new ArrayList<>(); + second.add("One"); + second.add("Two"); + container.put("Key", first); + container.put("Key2", second); + assertEquals("{\"Key\":[\"One\",\"Two\"],\"Key2\":[\"One\",\"Two\"]}", container.toString()); + } + } +} diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/ValidateAPIControllerTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/ValidateAPIControllerTest.java index df0522c334..98cc74dbc9 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/ValidateAPIControllerTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/ValidateAPIControllerTest.java @@ -10,101 +10,236 @@ package org.zowe.apiml.gateway.conformance; -import org.apache.http.HttpStatus; -import org.junit.jupiter.api.BeforeEach; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.zowe.apiml.acceptance.common.AcceptanceTest; -import org.zowe.apiml.acceptance.common.AcceptanceTestWithTwoServices; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.zowe.apiml.message.core.Message; import org.zowe.apiml.message.core.MessageService; import org.zowe.apiml.message.yaml.YamlMessageService; -import static io.restassured.RestAssured.given; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.CoreMatchers.containsString; -import static io.restassured.module.mockmvc.RestAssuredMockMvc.standaloneSetup; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; -import java.io.IOException; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; -@AcceptanceTest -public class ValidateAPIControllerTest extends AcceptanceTestWithTwoServices { - - private String validatePath; - private MessageService messageService; + +@ExtendWith(MockitoExtension.class) +public class ValidateAPIControllerTest { + + @InjectMocks private ValidateAPIController validateAPIController; - @Autowired + @Mock private VerificationOnboardService verificationOnboardService; - - @BeforeEach - void setup() throws IOException { - validatePath = "/validate"; + + @Mock + private DiscoveryClient discoveryClient; + + @Mock + private MessageService messageService; + @Mock + ServiceInstance serviceInstance; + + + ResponseEntity result; + + + private static final String WRONG_SERVICE_ID_KEY = "org.zowe.apiml.gateway.verifier.wrongServiceId"; + private static final String NO_METADATA_KEY = "org.zowe.apiml.gateway.verifier.noMetadata"; + private static final String NON_CONFORMANT_KEY = "org.zowe.apiml.gateway.verifier.nonConformant"; + + private static final Message WRONG_SERVICE_ID_MESSAGE = new YamlMessageService("/gateway-log-messages.yml").createMessage(WRONG_SERVICE_ID_KEY, "ThisWillBeRemoved"); + private static final Message NO_METADATA_MESSAGE = new YamlMessageService("/gateway-log-messages.yml").createMessage(NO_METADATA_KEY, "ThisWillBeRemoved"); + private static final Message NON_CONFORMANT_MESSAGE = new YamlMessageService("/gateway-log-messages.yml").createMessage(NON_CONFORMANT_KEY, "ThisWillBeRemoved"); + + @AfterEach + void cleanup() { + result = null; + } + + @Nested + class GivenWrongServiceId { + + @AfterEach + void checkValidJson() { + ObjectMapper mapper = new ObjectMapper() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); + + boolean valid; + + try { + mapper.readTree(result.getBody()); + valid = true; + } catch (JsonProcessingException e) { + valid = false; + } + assertTrue(valid); + } + + @Test + void whenServiceIdTooLong_thenNonconformant() { + when(messageService.createMessage(NON_CONFORMANT_KEY, "ThisWillBeRemoved")).thenReturn(NON_CONFORMANT_MESSAGE); + String testString = "qwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiopqwertyuiop"; + result = validateAPIController.checkConformance(testString); + assertNotNull(result.getBody()); + assertTrue(result.getBody().contains("The serviceId is longer than 64 characters")); + } + + @Test + void whenServiceIdTooLongAndSymbols_thenNonconformant() { + when(messageService.createMessage(NON_CONFORMANT_KEY, "ThisWillBeRemoved")).thenReturn(NON_CONFORMANT_MESSAGE); + String testString = "qwertyuiopqwertyuiop--qwertyuiopqwertyuio-pqwertyuio-pqwertyuiopqwertyuiop"; + result = validateAPIController.checkConformance(testString); + assertNotNull(result.getBody()); + assertTrue(result.getBody().contains("The serviceId is longer than 64 characters")); + assertTrue(result.getBody().contains("The serviceId contains symbols or upper case letters")); + + } + + @ParameterizedTest + @ValueSource(strings = {"test-test", "TEST", "Test"}) + void whenServiceIdNonAlphaNumeric_thenNonconformant(String testString) { + when(messageService.createMessage(NON_CONFORMANT_KEY, "ThisWillBeRemoved")).thenReturn(NON_CONFORMANT_MESSAGE); + result = validateAPIController.checkConformance(testString); + assertNotNull(result.getBody()); + assertTrue(result.getBody().contains("The serviceId contains symbols or upper case letters")); + } + + @Test + void notInvalidTextFormat() { + when(messageService.createMessage(WRONG_SERVICE_ID_KEY, "ThisWillBeRemoved")).thenReturn(WRONG_SERVICE_ID_MESSAGE); + String testString = "test"; + result = validateAPIController.checkConformance(testString); + assertNotNull(result.getBody()); + assertFalse(result.getBody().contains("Message service is requested to create a message with an invalid text format")); + } } @Nested - class GivenControllerServiceID { + class ServiceNotOnboarded { + @AfterEach + void checkValidJson() { + ObjectMapper mapper = new ObjectMapper() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); + boolean valid; + try { + mapper.readTree(result.getBody()); + valid = true; + } catch (JsonProcessingException e) { + valid = false; + } + assertTrue(valid); + } + - @BeforeEach - void setup() throws IOException { - messageService = new YamlMessageService("/gateway-log-messages.yml"); - validateAPIController = new ValidateAPIController(messageService, verificationOnboardService); - standaloneSetup(validateAPIController); + @Test + void whenServiceNotOboarded_thenError() { + when(messageService.createMessage(WRONG_SERVICE_ID_KEY, "ThisWillBeRemoved")).thenReturn(WRONG_SERVICE_ID_MESSAGE); + String testString = "notonboarded"; + result = validateAPIController.checkConformance(testString); + assertNotNull(result.getBody()); + assertTrue(result.getBody().contains("The service is not registered")); } @Test - void whenServiceId_validate() throws Exception { - given() - .param("serviceID","validserviceid") - .when() - .post(basePath + validatePath) - .then() - .assertThat() - .body("messageNumber", equalTo("ZWEAG717E"), - "messageContent", containsString("The service is not registered")) - .statusCode(HttpStatus.SC_BAD_REQUEST); + void legacyWhenServiceNotOboarded_thenError() { + when(messageService.createMessage(WRONG_SERVICE_ID_KEY, "ThisWillBeRemoved")).thenReturn(WRONG_SERVICE_ID_MESSAGE); + String testString = "notonboarded"; + result = validateAPIController.checkValidateLegacy(testString); + assertNotNull(result.getBody()); + assertTrue(result.getBody().contains("The service is not registered")); + } + } + + @Nested + class GivenMetadata { @Test - void whenServiceId_InvalidateUpper() throws Exception { - - given() - .param("serviceID", "Invalidserviceidcontainupperletter") - .when() - .post(basePath + validatePath) - .then() - .assertThat() - .body("messageNumber", equalTo("ZWEAG717E"), - "messageContent", containsString("The serviceid contains symbols or upper case letters")) - .statusCode(HttpStatus.SC_BAD_REQUEST); + void whenEmpty_thenCorrectResponse() { + HashMap metadata = new HashMap<>(); + assertEquals("Cannot Retrieve MetaData", validateAPIController.metaDataCheck(metadata)); } @Test - void whenServiceId_InvalidateSymbol() throws Exception { - given() - .param("serviceID", "invalid@serviceid_containsymbols") - .when() - .post(basePath + validatePath) - .then() - .assertThat() - .body("messageNumber", equalTo("ZWEAG717E"), - "messageContent", containsString("The serviceid contains symbols or upper case letters")) - .statusCode(HttpStatus.SC_BAD_REQUEST); + void whenNotEmpty_thenCorrectResponse() { + HashMap metadata = new HashMap<>(); + metadata.put("key", "value"); + assertEquals("", validateAPIController.metaDataCheck(metadata)); } - @Test - void whenServiceId_InvalidateLength() throws Exception { - given() - .param("serviceID", "qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklwsezxcvbnmqwertyuiop") - .when() - .post(basePath + validatePath) - .then() - .assertThat() - .body("messageNumber", equalTo("ZWEAG717E"), - "messageContent", containsString("The serviceid is longer than 64 characters")) - .statusCode(HttpStatus.SC_BAD_REQUEST); + void whenEmpty_thenCorrectConformanceResponse() { + String serviceId = "testservice"; + HashMap mockMetadata = new HashMap<>(); + when(verificationOnboardService.checkOnboarding(serviceId)).thenReturn(true); + when(discoveryClient.getInstances(serviceId)).thenReturn(new ArrayList<>(Collections.singleton(serviceInstance))); + when(serviceInstance.getMetadata()).thenReturn(mockMetadata); + when(messageService.createMessage(NO_METADATA_KEY, "ThisWillBeRemoved")).thenReturn(NO_METADATA_MESSAGE); + result = validateAPIController.checkConformance(serviceId); + assertEquals(HttpStatus.BAD_REQUEST, result.getStatusCode()); } - + } -} \ No newline at end of file + + + @Nested + class GivenInstanceList { + @Test + void whenEmpty_thenCorrectResponse() { + List list = new ArrayList<>(); + assertTrue(validateAPIController.instanceCheck(list).contains("Cannot retrieve metadata")); + } + } + + + @Nested + class GivenValidEverything { + + + @AfterEach + void checkValidJson() { + ObjectMapper mapper = new ObjectMapper() + .enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); + boolean valid; + try { + mapper.readTree(result.getBody()); + valid = true; + } catch (JsonProcessingException e) { + valid = false; + } + assertTrue(valid); + } + + + @Test + void thenOkResponse() { + String serviceId = "testservice"; + HashMap mockMetadata = new HashMap<>(); + mockMetadata.put("key", "value"); + when(verificationOnboardService.checkOnboarding(serviceId)).thenReturn(true); + when(discoveryClient.getInstances(serviceId)).thenReturn(new ArrayList<>(Collections.singleton(serviceInstance))); + when(serviceInstance.getMetadata()).thenReturn(mockMetadata); + result = validateAPIController.checkConformance(serviceId); + assertEquals(HttpStatus.OK, result.getStatusCode()); + } + + } +} + diff --git a/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/VerificationOnboardServiceTest.java b/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/VerificationOnboardServiceTest.java index 1d263be7f4..ee25d11a05 100644 --- a/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/VerificationOnboardServiceTest.java +++ b/gateway-service/src/test/java/org/zowe/apiml/gateway/conformance/VerificationOnboardServiceTest.java @@ -10,28 +10,22 @@ package org.zowe.apiml.gateway.conformance; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.cloud.client.DefaultServiceInstance; -import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + @ExtendWith(MockitoExtension.class) -public class VerificationOnboardServiceTest { +class VerificationOnboardServiceTest { @InjectMocks private VerificationOnboardService verificationOnboardService; @@ -39,50 +33,28 @@ public class VerificationOnboardServiceTest { @Mock private DiscoveryClient discoveryClient; - private static final String GATEWAY_ID = "gateway"; - private static final String SWAGGER_NAME = "apiml.apiInfo.api-v2.swaggerUrl"; - private static final String SWAGGER_URL = "https://hostname/sampleclient/api-doc"; - - @ParameterizedTest - @MethodSource("provideGatewayConfiguration") - void givenGatewayConfiguration_thenReturnCorrectResponse(Map metadata, String expectedUrl, String serviceId, boolean expectServiceInstance) { - DefaultServiceInstance defaultServiceInstance = new DefaultServiceInstance("sys1.acme.net", serviceId, "localhost", 10010, true, metadata); - List serviceInstances = new ArrayList(); - if (expectServiceInstance) { - serviceInstances.add(defaultServiceInstance); - } - when(discoveryClient.getInstances(GATEWAY_ID)).thenReturn(serviceInstances); - String actualUrl = verificationOnboardService.retrieveMetaData(GATEWAY_ID); - assertEquals(expectedUrl, actualUrl); + @Test + void whenCheckingOnboardedService() { + when(discoveryClient.getServices()).thenReturn(new ArrayList<>(Collections.singleton("OnboardedService"))); + assertFalse(verificationOnboardService.checkOnboarding("Test")); + assertTrue(verificationOnboardService.checkOnboarding("OnboardedService")); } - private static Stream provideGatewayConfiguration() { - return Stream.of( - Arguments.of(Collections.singletonMap(SWAGGER_NAME, SWAGGER_URL), SWAGGER_URL, GATEWAY_ID, true), - Arguments.of(Collections.singletonMap(SWAGGER_NAME, SWAGGER_URL), "", GATEWAY_ID, false), - Arguments.of(Collections.emptyMap(), "", GATEWAY_ID, true), - Arguments.of(Collections.singletonMap("randomName", "randomValue"), "", GATEWAY_ID, true) - ); + @Test + void whenRetrievingSwagger() { + final String swaggerUrl = "https://hostname/sampleclient/api-doc"; + HashMap metadata = new HashMap<>(); + metadata.put("apiml.apiInfo.api-v2.swaggerUrl", swaggerUrl); + assertEquals(swaggerUrl, verificationOnboardService.retrieveSwagger(metadata)); } - @ParameterizedTest - @MethodSource("provideOnboardConfiguration") - void givenOnboardInfo_thenReturnCorrectResponse(List serviceList, boolean expectedOnboard, String serviceId) { - when(discoveryClient.getServices()).thenReturn(serviceList); - assertEquals(expectedOnboard, verificationOnboardService.checkOnboarding(serviceId)); - } - private static Stream provideOnboardConfiguration() { - return Stream.of( - Arguments.of(new ArrayList() {{ - add(GATEWAY_ID); - }}, true, GATEWAY_ID), - Arguments.of(new ArrayList() {{ - add("zowesample"); - }}, false, GATEWAY_ID), - Arguments.of(new ArrayList(), false, GATEWAY_ID) - ); + @Test + void whenRetrievingEmptySwagger() { + HashMap metadata = new HashMap<>(); + metadata.put("apiml.apiInfo.api-v2.swaggerUrl", null); + assertEquals("", verificationOnboardService.retrieveSwagger(metadata)); } } diff --git a/integration-tests/src/test/java/org/zowe/apiml/functional/gateway/ValidateAPITest.java b/integration-tests/src/test/java/org/zowe/apiml/functional/gateway/ValidateAPITest.java index 957fcd867f..6afec748bb 100644 --- a/integration-tests/src/test/java/org/zowe/apiml/functional/gateway/ValidateAPITest.java +++ b/integration-tests/src/test/java/org/zowe/apiml/functional/gateway/ValidateAPITest.java @@ -10,7 +10,9 @@ package org.zowe.apiml.functional.gateway; +import io.restassured.RestAssured; import org.apache.http.HttpStatus; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.zowe.apiml.util.TestWithStartedInstances; import org.zowe.apiml.util.categories.GatewayTest; @@ -18,61 +20,85 @@ import org.zowe.apiml.util.config.ConfigReader; import org.zowe.apiml.util.config.GatewayServiceConfiguration; - -import io.restassured.RestAssured; - - import static io.restassured.RestAssured.given; -import java.net.URISyntaxException; - /** * This is an integration test class for ValidateAPIController.java * It tests ValidateAPIController with service verificationOnboardService.java - * - * The test senario is: + *

+ * The test scenario is: * Send serviceId to validateAPIController then the controller will call verificationOnboardService.java * to check if the service is registered and metadata can be retrieved then it will return appropriate message - * The controller recieve the response from the service and then will post response. - * + * The controller receive the response from the service and then will post response. */ @GatewayTest public class ValidateAPITest implements TestWithStartedInstances { - private String gatewayScheme; - private String gatewayHost; - private int gatewayPort; - private String gatewayUrl; + @BeforeEach + public void relaxedHTTPS() { + RestAssured.useRelaxedHTTPSValidation(); + } + @Test + @TestsNotMeantForZowe + void testPostEndpoint() { + given() + .log().all() + .param("serviceID","discoverableclient") + .when() + .post(getLegacyEndpointURLPost()) + .then() + .assertThat() + .statusCode(HttpStatus.SC_OK); - public ValidateAPITest() throws URISyntaxException { - initGatewayService(); } @Test @TestsNotMeantForZowe - void givenValidServiceId() { - given() + void testGetEndpoint() { + given() .log().all() - .param("serviceID","discoverableclient") - .when() - .post(gatewayUrl) - .then() + .when() + .get(getEndpointURLGet() + "/discoverableclient") + .then() .assertThat() .statusCode(HttpStatus.SC_OK); } + @Test + @TestsNotMeantForZowe + void testGetEndpointNonConformant() { + given() + .log().all() + .when() + .get(getEndpointURLGet() + "/nonConformantServiceBecauseNameIsTooLongAndContainsCapitalLettersqwertyuiop") + .then() + .assertThat() + .statusCode(HttpStatus.SC_BAD_REQUEST); + + } + + private String getEndpointURLGet() { + GatewayServiceConfiguration gatewayServiceConfiguration = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration(); + String gatewayScheme = gatewayServiceConfiguration.getScheme(); + String gatewayHost = gatewayServiceConfiguration.getHost(); + int gatewayPort = gatewayServiceConfiguration.getExternalPort(); + RestAssured.port = gatewayPort; + RestAssured.useRelaxedHTTPSValidation(); + + return String.format("%s://%s:%d%s", gatewayScheme, gatewayHost, gatewayPort, "/gateway/api/v1/conformance"); + } - private void initGatewayService() { + private String getLegacyEndpointURLPost() { GatewayServiceConfiguration gatewayServiceConfiguration = ConfigReader.environmentConfiguration().getGatewayServiceConfiguration(); - gatewayScheme = gatewayServiceConfiguration.getScheme(); - gatewayHost = gatewayServiceConfiguration.getHost(); - gatewayPort = gatewayServiceConfiguration.getExternalPort(); + String gatewayScheme = gatewayServiceConfiguration.getScheme(); + String gatewayHost = gatewayServiceConfiguration.getHost(); + int gatewayPort = gatewayServiceConfiguration.getExternalPort(); RestAssured.port = gatewayPort; RestAssured.useRelaxedHTTPSValidation(); - gatewayUrl = String.format("%s://%s:%d%s", gatewayScheme, gatewayHost, gatewayPort, "/validate"); + return String.format("%s://%s:%d%s", gatewayScheme, gatewayHost, gatewayPort, "/validate"); } -} \ No newline at end of file +}